diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs index cc5cfcdee5..738a896155 100644 --- a/src/Umbraco.Core/Models/ContentBase.cs +++ b/src/Umbraco.Core/Models/ContentBase.cs @@ -293,7 +293,10 @@ namespace Umbraco.Core.Models /// Value as a public virtual TPassType GetValue(string propertyTypeAlias) { - return (TPassType)Properties[propertyTypeAlias].Value; + if (Properties[propertyTypeAlias].Value is TPassType) + return (TPassType)Properties[propertyTypeAlias].Value; + + return (TPassType)Convert.ChangeType(Properties[propertyTypeAlias].Value, typeof(TPassType)); } /// diff --git a/src/Umbraco.Core/Models/ContentExtensions.cs b/src/Umbraco.Core/Models/ContentExtensions.cs index 127f60e643..77f90d22a9 100644 --- a/src/Umbraco.Core/Models/ContentExtensions.cs +++ b/src/Umbraco.Core/Models/ContentExtensions.cs @@ -78,6 +78,53 @@ namespace Umbraco.Core.Models return (propertyValueChanged && publishedState == PublishedState.Published) || contentDataChanged; } + /// + /// Determines if the published db flag should be set to true for the current entity version and all other db + /// versions should have their flag set to false. + /// + /// + /// + /// + /// This is determined by: + /// * If a new version is being created and the entity is published + /// * If the published state has changed and the entity is published OR the entity has been un-published. + /// + internal static bool ShouldClearPublishedFlagForPreviousVersions(this IContent entity) + { + var publishedState = ((Content)entity).PublishedState; + return entity.ShouldClearPublishedFlagForPreviousVersions(publishedState, entity.ShouldCreateNewVersion(publishedState)); + } + + /// + /// Determines if the published db flag should be set to true for the current entity version and all other db + /// versions should have their flag set to false. + /// + /// + /// + /// + /// + /// + /// This is determined by: + /// * If a new version is being created and the entity is published + /// * If the published state has changed and the entity is published OR the entity has been un-published. + /// + internal static bool ShouldClearPublishedFlagForPreviousVersions(this IContent entity, PublishedState publishedState, bool isCreatingNewVersion) + { + if (isCreatingNewVersion && entity.Published) + { + return true; + } + + //If Published state has changed then previous versions should have their publish state reset. + //If state has been changed to unpublished the previous versions publish state should also be reset. + if (((ICanBeDirty)entity).IsPropertyDirty("Published") && (entity.Published || publishedState == PublishedState.Unpublished)) + { + return true; + } + + return false; + } + /// /// Returns a list of the current contents ancestors, not including the content itself. /// diff --git a/src/Umbraco.Core/Models/PreValueCollection.cs b/src/Umbraco.Core/Models/PreValueCollection.cs new file mode 100644 index 0000000000..995f6422c2 --- /dev/null +++ b/src/Umbraco.Core/Models/PreValueCollection.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; + +namespace Umbraco.Core.Models +{ + /// + /// Represents the pre-value data for a DataType + /// + /// + /// Due to the legacy nature of the data that can be stored for pre-values, we have this class which encapsulates the 2 different + /// ways that pre-values are stored: A string array or a Dictionary. + /// + /// Most legacy property editors won't support the dictionary format but new property editors should always use the dictionary format. + /// In order to get overrideable pre-values working we need a dictionary since we'll have to reference a pre-value by a key. + /// + public class PreValueCollection + { + private IDictionary _preValuesAsDictionary; + private IEnumerable _preValuesAsArray; + public IEnumerable PreValuesAsArray + { + get + { + if (_preValuesAsArray == null) + { + throw new InvalidOperationException("The current pre-value collection is dictionary based, use the PreValuesAsDictionary property instead"); + } + return _preValuesAsArray; + } + set { _preValuesAsArray = value; } + } + + public IDictionary PreValuesAsDictionary + { + get + { + if (_preValuesAsDictionary == null) + { + throw new InvalidOperationException("The current pre-value collection is array based, use the PreValuesAsArray property instead"); + } + return _preValuesAsDictionary; + } + set { _preValuesAsDictionary = value; } + } + + /// + /// Check if it is a dictionary based collection + /// + public bool IsDictionaryBased + { + get { return _preValuesAsDictionary != null; } + } + + public PreValueCollection(IEnumerable preVals) + { + _preValuesAsArray = preVals; + } + + public PreValueCollection(IDictionary preVals) + { + _preValuesAsDictionary = preVals; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/PropertyExtensions.cs b/src/Umbraco.Core/Models/PropertyExtensions.cs index 53ed83baca..5e3328de56 100644 --- a/src/Umbraco.Core/Models/PropertyExtensions.cs +++ b/src/Umbraco.Core/Models/PropertyExtensions.cs @@ -64,8 +64,15 @@ namespace Umbraco.Core.Models } else { - var dt = (DateTime)property.Value; - xmlNode.AppendChild(xd.CreateTextNode(dt.ToXmlString())); + var date = property.Value.TryConvertTo(); + if (date.Success == false || date.Result == null) + { + xmlNode.AppendChild(xd.CreateTextNode(string.Empty)); + } + else + { + xmlNode.AppendChild(xd.CreateTextNode(date.Result.ToXmlString())); + } } break; default: diff --git a/src/Umbraco.Core/ObjectExtensions.cs b/src/Umbraco.Core/ObjectExtensions.cs index 5f5e41b1ef..6a4ba31f05 100644 --- a/src/Umbraco.Core/ObjectExtensions.cs +++ b/src/Umbraco.Core/ObjectExtensions.cs @@ -77,7 +77,14 @@ namespace Umbraco.Core /// public static Attempt TryConvertTo(this object input, Type destinationType) { - if (input == null) return Attempt.False; + //if it is null and it is nullable, then return success with null + if (input == null && destinationType.IsGenericType && destinationType.GetGenericTypeDefinition() == typeof (Nullable<>)) + { + return new Attempt(true, null); + } + + //if its not nullable then return false + if (input == null) return Attempt.False; if (destinationType == typeof(object)) return new Attempt(true, input); diff --git a/src/Umbraco.Core/ObjectResolution/Resolution.cs b/src/Umbraco.Core/ObjectResolution/Resolution.cs index d9e5977e4a..2c6dab4225 100644 --- a/src/Umbraco.Core/ObjectResolution/Resolution.cs +++ b/src/Umbraco.Core/ObjectResolution/Resolution.cs @@ -13,8 +13,9 @@ namespace Umbraco.Core.ObjectResolution internal static class Resolution { private static readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); + private volatile static bool _isFrozen; - /// + /// /// Occurs when resolution is frozen. /// /// Occurs only once, since resolution can be frozen only once. @@ -23,12 +24,17 @@ namespace Umbraco.Core.ObjectResolution /// /// Gets or sets a value indicating whether resolution of objects is frozen. /// - public static bool IsFrozen { get; private set; } - - public static void EnsureIsFrozen() + public static bool IsFrozen { - if (!IsFrozen) + get { return _isFrozen; } + private set { _isFrozen = value; } + } + + public static void EnsureIsFrozen() + { + if (!_isFrozen) throw new InvalidOperationException("Resolution is not frozen, it is not yet possible to get values from it."); + } /// @@ -40,7 +46,7 @@ namespace Umbraco.Core.ObjectResolution get { IDisposable l = new WriteLock(_lock); - if (Resolution.IsFrozen) + if (_isFrozen) { l.Dispose(); throw new InvalidOperationException("Resolution is frozen, it is not possible to configure it anymore."); @@ -73,13 +79,13 @@ namespace Umbraco.Core.ObjectResolution public DirtyBackdoor() { _lock = new WriteLock(_dirtyLock); - _frozen = Resolution.IsFrozen; - Resolution.IsFrozen = false; + _frozen = _isFrozen; + _isFrozen = false; } public void Dispose() { - Resolution.IsFrozen = _frozen; + _isFrozen = _frozen; _lock.Dispose(); } } @@ -90,10 +96,10 @@ namespace Umbraco.Core.ObjectResolution /// resolution is already frozen. public static void Freeze() { - if (Resolution.IsFrozen) + if (_isFrozen) throw new InvalidOperationException("Resolution is frozen. It is not possible to freeze it again."); - IsFrozen = true; + _isFrozen = true; if (Frozen != null) Frozen(null, null); } @@ -104,7 +110,7 @@ namespace Umbraco.Core.ObjectResolution /// To be used in unit tests. internal static void Reset() { - IsFrozen = false; + _isFrozen = false; Frozen = null; } } diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs index 7fc81b1db7..b50102396a 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs @@ -223,8 +223,6 @@ namespace Umbraco.Core.Persistence.Migrations.Initial _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 4, DataTypeId = -88, ControlId = new Guid(Constants.PropertyEditors.Textbox), DbType = "Nvarchar" }); _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 5, DataTypeId = -89, ControlId = new Guid(Constants.PropertyEditors.TextboxMultiple), DbType = "Ntext" }); _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 6, DataTypeId = -90, ControlId = new Guid(Constants.PropertyEditors.UploadField), DbType = "Nvarchar" }); - //Dropdown list - //_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 12, DataTypeId = -91, ControlId = new Guid("a74ea9c9-8e18-4d2a-8cf6-73c6206c5da6"), DbType = "Nvarchar" }); _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 7, DataTypeId = -92, ControlId = new Guid(Constants.PropertyEditors.NoEdit), DbType = "Nvarchar" }); _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 8, DataTypeId = -36, ControlId = new Guid(Constants.PropertyEditors.DateTime), DbType = "Date" }); _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 9, DataTypeId = -37, ControlId = new Guid(Constants.PropertyEditors.ColorPicker), DbType = "Nvarchar" }); @@ -234,24 +232,6 @@ namespace Umbraco.Core.Persistence.Migrations.Initial _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 13, DataTypeId = -41, ControlId = new Guid(Constants.PropertyEditors.Date), DbType = "Date" }); _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 14, DataTypeId = -42, ControlId = new Guid(Constants.PropertyEditors.DropDownList), DbType = "Integer" }); _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 15, DataTypeId = -43, ControlId = new Guid(Constants.PropertyEditors.CheckBoxList), DbType = "Nvarchar" }); - // Fix for rich text editor backwards compatibility -> 83722133-f80c-4273-bdb6-1befaa04a612 TinyMCE DataType - //_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 22, DataTypeId = -44, ControlId = new Guid("a3776494-0574-4d93-b7de-efdfdec6f2d1"), DbType = "Ntext" }); - //Radiobutton list - //_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 23, DataTypeId = -128, ControlId = new Guid("a52c7c1c-c330-476e-8605-d63d3b84b6a6"), DbType = "Nvarchar" }); - //Dropdown list multiple - //_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 24, DataTypeId = -129, ControlId = new Guid("928639ed-9c73-4028-920c-1e55dbb68783"), DbType = "Nvarchar" }); - //Dropdown list - //_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 25, DataTypeId = -130, ControlId = new Guid("a74ea9c9-8e18-4d2a-8cf6-73c6206c5da6"), DbType = "Nvarchar" }); - //Dropdown list - //_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 26, DataTypeId = -131, ControlId = new Guid("a74ea9c9-8e18-4d2a-8cf6-73c6206c5da6"), DbType = "Nvarchar" }); - //Dropdown list - //_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 27, DataTypeId = -132, ControlId = new Guid("a74ea9c9-8e18-4d2a-8cf6-73c6206c5da6"), DbType = "Nvarchar" }); - //No edit - //_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 28, DataTypeId = -133, ControlId = new Guid("6c738306-4c17-4d88-b9bd-6546f3771597"), DbType = "Ntext" }); - //Dropdown list multiple - //_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 29, DataTypeId = -134, ControlId = new Guid("928639ed-9c73-4028-920c-1e55dbb68783"), DbType = "Nvarchar" }); - //Not found - maybe a legacy thing? - //_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 30, DataTypeId = -50, ControlId = new Guid("aaf99bb2-dbbe-444d-a296-185076bf0484"), DbType = "Date" }); _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 16, DataTypeId = 1034, ControlId = new Guid(Constants.PropertyEditors.ContentPicker), DbType = "Integer" }); _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 17, DataTypeId = 1035, ControlId = new Guid(Constants.PropertyEditors.MediaPicker), DbType = "Integer" }); _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 18, DataTypeId = 1036, ControlId = new Guid(Constants.PropertyEditors.MemberPicker), DbType = "Integer" }); diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index 4a2bb81eb8..7acd2cb7a0 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -332,7 +332,8 @@ namespace Umbraco.Core.Persistence.Repositories //If Published state has changed then previous versions should have their publish state reset. //If state has been changed to unpublished the previous versions publish state should also be reset. - if (((ICanBeDirty)entity).IsPropertyDirty("Published") && (entity.Published || publishedState == PublishedState.Unpublished)) + //if (((ICanBeDirty)entity).IsPropertyDirty("Published") && (entity.Published || publishedState == PublishedState.Unpublished)) + if (entity.ShouldClearPublishedFlagForPreviousVersions(publishedState, shouldCreateNewVersion)) { var publishedDocs = Database.Fetch("WHERE nodeId = @Id AND published = @IsPublished", new { Id = entity.Id, IsPublished = true }); foreach (var doc in publishedDocs) diff --git a/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs b/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs index a73c7c943a..8651068030 100644 --- a/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs @@ -84,7 +84,7 @@ namespace Umbraco.Core.Persistence.Repositories { bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document); bool isMedia = objectTypeId == new Guid(Constants.ObjectTypes.Media); - var sql = GetBaseWhere(GetBase, isContent, isMedia, objectTypeId).Append(GetGroupBy(isContent, isMedia)); + var sql = GetBaseWhere(GetBase, isContent, isMedia, string.Empty, objectTypeId).Append(GetGroupBy(isContent, isMedia)); var dtos = isMedia ? _work.Database.Fetch( new UmbracoEntityRelator().Map, sql) @@ -101,7 +101,8 @@ namespace Umbraco.Core.Persistence.Repositories public virtual IEnumerable GetByQuery(IQuery query) { - var sqlClause = GetBase(false, false); + var wheres = string.Concat(" AND ", string.Join(" AND ", ((Query) query).WhereClauses())); + var sqlClause = GetBase(false, false, wheres); var translator = new SqlTranslator(sqlClause, query); var sql = translator.Translate().Append(GetGroupBy(false, false)); @@ -117,7 +118,9 @@ namespace Umbraco.Core.Persistence.Repositories { bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document); bool isMedia = objectTypeId == new Guid(Constants.ObjectTypes.Media); - var sqlClause = GetBaseWhere(GetBase, isContent, isMedia, objectTypeId); + + var wheres = string.Concat(" AND ", string.Join(" AND ", ((Query)query).WhereClauses())); + var sqlClause = GetBaseWhere(GetBase, isContent, isMedia, wheres, objectTypeId); var translator = new SqlTranslator(sqlClause, query); var sql = translator.Translate().Append(GetGroupBy(isContent, isMedia)); @@ -136,7 +139,7 @@ namespace Umbraco.Core.Persistence.Repositories #region Sql Statements - protected virtual Sql GetBase(bool isContent, bool isMedia) + protected virtual Sql GetBase(bool isContent, bool isMedia, string additionWhereStatement = "") { var columns = new List { @@ -187,42 +190,38 @@ namespace Umbraco.Core.Persistence.Repositories .On("umbracoNode.id = latest.nodeId"); } - /*if (isContent) - { - sql.LeftJoin( - "(SELECT contentNodeId, versionId, dataNvarchar, controlId FROM cmsPropertyData INNER JOIN cmsPropertyType ON cmsPropertyType.id = cmsPropertyData.propertytypeid" + - " INNER JOIN cmsDataType ON cmsPropertyType.dataTypeId = cmsDataType.nodeId) as property") - .On("umbracoNode.id = property.contentNodeId AND latest.versionId = property.versionId"); - }*/ if (isMedia) { sql.LeftJoin( - "(SELECT contentNodeId, versionId, dataNvarchar, controlId FROM cmsPropertyData INNER JOIN cmsPropertyType ON cmsPropertyType.id = cmsPropertyData.propertytypeid" + - " INNER JOIN cmsDataType ON cmsPropertyType.dataTypeId = cmsDataType.nodeId) as property") + "(SELECT contentNodeId, versionId, dataNvarchar, controlId FROM cmsPropertyData " + + "INNER JOIN umbracoNode ON cmsPropertyData.contentNodeId = umbracoNode.id " + + "INNER JOIN cmsPropertyType ON cmsPropertyType.id = cmsPropertyData.propertytypeid " + + "INNER JOIN cmsDataType ON cmsPropertyType.dataTypeId = cmsDataType.nodeId "+ + "WHERE umbracoNode.nodeObjectType = '" + Constants.ObjectTypes.Media + "'" + additionWhereStatement + ") as property") .On("umbracoNode.id = property.contentNodeId"); } return sql; } - protected virtual Sql GetBaseWhere(Func baseQuery, bool isContent, bool isMedia, Guid id) + protected virtual Sql GetBaseWhere(Func baseQuery, bool isContent, bool isMedia, string additionWhereStatement, Guid id) { - var sql = baseQuery(isContent, isMedia) + var sql = baseQuery(isContent, isMedia, additionWhereStatement) .Where("umbracoNode.nodeObjectType = @NodeObjectType", new { NodeObjectType = id }); return sql; } - protected virtual Sql GetBaseWhere(Func baseQuery, bool isContent, bool isMedia, int id) + protected virtual Sql GetBaseWhere(Func baseQuery, bool isContent, bool isMedia, int id) { - var sql = baseQuery(isContent, isMedia) + var sql = baseQuery(isContent, isMedia, " AND umbracoNode.id = '"+ id +"'") .Where("umbracoNode.id = @Id", new { Id = id }) .Append(GetGroupBy(isContent, isMedia)); return sql; } - protected virtual Sql GetBaseWhere(Func baseQuery, bool isContent, bool isMedia, Guid objectId, int id) + protected virtual Sql GetBaseWhere(Func baseQuery, bool isContent, bool isMedia, Guid objectId, int id) { - var sql = baseQuery(isContent, isMedia) + var sql = baseQuery(isContent, isMedia, " AND umbracoNode.id = '"+ id +"'") .Where("umbracoNode.id = @Id AND umbracoNode.nodeObjectType = @NodeObjectType", new {Id = id, NodeObjectType = objectId}); return sql; diff --git a/src/Umbraco.Core/PluginManager.cs b/src/Umbraco.Core/PluginManager.cs index 031d4f587d..960b0df16a 100644 --- a/src/Umbraco.Core/PluginManager.cs +++ b/src/Umbraco.Core/PluginManager.cs @@ -430,7 +430,9 @@ namespace Umbraco.Core /// internal IEnumerable ResolvePropertyEditors() { - return ResolveTypes(); + //return all proeprty editor types found except for the base property editor type + return ResolveTypes() + .Except(new[] {typeof (PropertyEditor)}); } /// @@ -466,7 +468,9 @@ namespace Umbraco.Core /// internal IEnumerable ResolveDataTypes() { - return ResolveTypes(); + //ensure we ignore types that should not be loaded + return ResolveTypes() + .Except(new[] {Type.GetType("umbraco.presentation.LiveEditing.Modules.ItemEditing.PageElementEditor,umbraco")}); } /// diff --git a/src/Umbraco.Core/PropertyEditors/PreValueEditor.cs b/src/Umbraco.Core/PropertyEditors/PreValueEditor.cs index 4d8697bf90..3bc978177a 100644 --- a/src/Umbraco.Core/PropertyEditors/PreValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/PreValueEditor.cs @@ -3,6 +3,12 @@ using Newtonsoft.Json; namespace Umbraco.Core.PropertyEditors { + /// + /// Defines a pre-value editor + /// + /// + /// The Json serialization attributes are required for manifest property editors to work + /// public class PreValueEditor { /// diff --git a/src/Umbraco.Core/PropertyEditors/PropertyEditor.cs b/src/Umbraco.Core/PropertyEditors/PropertyEditor.cs index 82b52e8b38..bb69b0a445 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyEditor.cs @@ -1,9 +1,18 @@ using System; +using System.Collections.Generic; +using System.Linq; using Newtonsoft.Json; using Umbraco.Core.IO; +using Umbraco.Core.Models; namespace Umbraco.Core.PropertyEditors { + /// + /// Basic definition of a property editor + /// + /// + /// The Json serialization attributes are required for manifest property editors to work + /// public class PropertyEditor { /// @@ -20,14 +29,23 @@ namespace Umbraco.Core.PropertyEditors { Id = Guid.Parse(att.Id); Name = att.Name; - + StaticallyDefinedValueEditor.ValueType = att.ValueType; StaticallyDefinedValueEditor.View = att.EditorView; StaticallyDefinedPreValueEditor.View = att.PreValueEditorView; } } + /// + /// These are assigned by default normally based on property editor attributes or manifest definitions, + /// developers have the chance to override CreateValueEditor if they don't want to use the pre-defined instance + /// internal ValueEditor StaticallyDefinedValueEditor = null; + + /// + /// These are assigned by default normally based on property editor attributes or manifest definitions, + /// developers have the chance to override CreatePreValueEditor if they don't want to use the pre-defined instance + /// internal PreValueEditor StaticallyDefinedPreValueEditor = null; /// @@ -54,6 +72,9 @@ namespace Umbraco.Core.PropertyEditors get { return CreatePreValueEditor(); } } + [JsonProperty("defaultConfig")] + public virtual IDictionary DefaultPreValues { get; set; } + //TODO: Now we need to implement a couple of methods for saving the data for editors and pre-value editors // generally we can handle that automatically in this base class but people should be allowed to override // it so they can perform custom operations on saving the data. @@ -88,6 +109,48 @@ namespace Umbraco.Core.PropertyEditors return StaticallyDefinedPreValueEditor; } + /// + /// This can be used to re-format the currently saved pre-values that will be passed to the editor, + /// by default this returns the merged default and persisted pre-values. + /// + /// + /// The default/static pre-vals for the property editor + /// + /// + /// The persisted pre-vals for the property editor + /// + /// + /// + /// This is generally not going to be used by anything unless a property editor wants to change the merging + /// functionality or needs to convert some legacy persisted data, or convert the string values to strongly typed values in json (i.e. booleans) + /// + public virtual IDictionary FormatPreValues(IDictionary defaultPreVals, PreValueCollection persistedPreVals) + { + if (defaultPreVals == null) + { + defaultPreVals = new Dictionary(); + } + + if (persistedPreVals.IsDictionaryBased) + { + //we just need to merge the dictionaries now, the persisted will replace default. + foreach (var item in persistedPreVals.PreValuesAsDictionary) + { + defaultPreVals[item.Key] = item.Value; + } + return defaultPreVals; + } + + //it's an array so need to format it + var result = new Dictionary(); + var asArray = persistedPreVals.PreValuesAsArray.ToArray(); + for (var i = 0; i < asArray.Length; i++) + { + result.Add(i.ToInvariantString(), asArray[i]); + } + return result; + } + protected bool Equals(PropertyEditor other) { return Id.Equals(other.Id); diff --git a/src/Umbraco.Core/PropertyEditors/ValueEditor.cs b/src/Umbraco.Core/PropertyEditors/ValueEditor.cs index c0d1aa39ec..5929b96512 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueEditor.cs @@ -10,6 +10,9 @@ namespace Umbraco.Core.PropertyEditors /// /// Represents the value editor for the property editor during content editing /// + /// + /// The Json serialization attributes are required for manifest property editors to work + /// public class ValueEditor { /// @@ -126,10 +129,12 @@ namespace Umbraco.Core.PropertyEditors valueType = typeof(string); break; case DataTypeDatabaseType.Integer: - valueType = typeof(int); + //ensure these are nullable so we can return a null if required + valueType = typeof(int?); break; case DataTypeDatabaseType.Date: - valueType = typeof(DateTime); + //ensure these are nullable so we can return a null if required + valueType = typeof(DateTime?); break; default: throw new ArgumentOutOfRangeException(); @@ -180,9 +185,14 @@ namespace Umbraco.Core.PropertyEditors case DataTypeDatabaseType.Integer: //we can just ToString() any of these types return dbValue.ToString(); - case DataTypeDatabaseType.Date: - //Dates will be formatted in 'o' format (otherwise known as xml format) - return dbValue.ToXmlString(); + case DataTypeDatabaseType.Date: + var date = dbValue.TryConvertTo(); + if (date.Success == false || date.Result == null) + { + return string.Empty; + } + //Dates will be formatted as yyyy-MM-dd HH:mm:ss + return date.Result.Value.ToIsoString(); default: throw new ArgumentOutOfRangeException(); } diff --git a/src/Umbraco.Core/Services/DataTypeService.cs b/src/Umbraco.Core/Services/DataTypeService.cs index 68c0f308bd..3a1e2e8f46 100644 --- a/src/Umbraco.Core/Services/DataTypeService.cs +++ b/src/Umbraco.Core/Services/DataTypeService.cs @@ -133,6 +133,22 @@ namespace Umbraco.Core.Services } } + /// + /// Returns the PreValueCollection for the specified data type + /// + /// + /// + internal PreValueCollection GetPreValuesCollectionByDataTypeId(int id) + { + using (var uow = _uowProvider.GetUnitOfWork()) + { + var dtos = uow.Database.Fetch("WHERE datatypeNodeId = @Id", new { Id = id }); + var list = dtos.Select(x => new Tuple(x.Id, x.Alias, x.SortOrder, x.Value)).ToList(); + + return PreValueConverter.ConvertToPreValuesCollection(list); + } + } + /// /// Gets a specific PreValue by its Id /// @@ -327,5 +343,41 @@ namespace Umbraco.Core.Services /// public static event TypedEventHandler> Saved; #endregion + + internal static class PreValueConverter + { + /// + /// Converts the tuple to a pre-value collection + /// + /// + /// + internal static PreValueCollection ConvertToPreValuesCollection(IEnumerable> list) + { + //now we need to determine if they are dictionary based, otherwise they have to be array based + var dictionary = new Dictionary(); + + //need to check all of the keys, if there's only one and it is empty then it's an array + var keys = list.Select(x => x.Item2).Distinct().ToArray(); + if (keys.Length == 1 && keys[0].IsNullOrWhiteSpace()) + { + return new PreValueCollection(list.OrderBy(x => x.Item3).Select(x => x.Item4)); + } + + foreach (var item in list + .OrderBy(x => x.Item3) //we'll order them first so we maintain the order index in the dictionary + .GroupBy(x => x.Item2)) + { + if (item.Count() > 1) + { + //if there's more than 1 item per key, then it cannot be a dictionary, just return the array + return new PreValueCollection(list.OrderBy(x => x.Item3).Select(x => x.Item4)); + } + + dictionary.Add(item.Key, item.First().Item4); + } + + return new PreValueCollection(dictionary); + } + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index f2c976d144..d8394e6fd3 100644 --- a/src/Umbraco.Core/StringExtensions.cs +++ b/src/Umbraco.Core/StringExtensions.cs @@ -28,6 +28,16 @@ namespace Umbraco.Core [UmbracoWillObsolete("Do not use this constants. See IShortStringHelper.CleanStringForSafeAliasJavaScriptCode.")] public const string UmbracoInvalidFirstCharacters = "01234567890"; + public static string ExceptChars(this string str, HashSet toExclude) + { + var sb = new StringBuilder(str.Length); + foreach (var c in str.Where(c => toExclude.Contains(c) == false)) + { + sb.Append(c); + } + return sb.ToString(); + } + /// /// Returns a stream from a string /// diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index ae5ccb1e60..0d67bbbdae 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -225,6 +225,7 @@ + diff --git a/src/Umbraco.Tests/AngularIntegration/ContentModelSerializationTests.cs b/src/Umbraco.Tests/AngularIntegration/ContentModelSerializationTests.cs index d33b9045e5..cee6bf00f8 100644 --- a/src/Umbraco.Tests/AngularIntegration/ContentModelSerializationTests.cs +++ b/src/Umbraco.Tests/AngularIntegration/ContentModelSerializationTests.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using NUnit.Framework; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using Umbraco.Core; using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Tests.AngularIntegration @@ -29,7 +30,7 @@ namespace Umbraco.Tests.AngularIntegration Label = "Property " + propertyIndex, Id = propertyIndex, Value = "value" + propertyIndex, - Config = new[] {"config" + propertyIndex}, + Config = new Dictionary {{ propertyIndex.ToInvariantString(), "value" }}, Description = "Description " + propertyIndex, View = "~/Views/View" + propertyIndex, HideLabel = false @@ -68,7 +69,7 @@ namespace Umbraco.Tests.AngularIntegration 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("[\"config" + prop + "\"]", jObject["tabs"][tab]["properties"][prop]["config"].ToString(Formatting.None)); + 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()); } diff --git a/src/Umbraco.Tests/Models/ContentExtensionsTests.cs b/src/Umbraco.Tests/Models/ContentExtensionsTests.cs index e57b5e6361..155fa4ac0b 100644 --- a/src/Umbraco.Tests/Models/ContentExtensionsTests.cs +++ b/src/Umbraco.Tests/Models/ContentExtensionsTests.cs @@ -82,6 +82,61 @@ namespace Umbraco.Tests.Models } + [Test] + public void Should_Clear_Published_Flag_When_Newly_Published_Version() + { + var contentType = MockedContentTypes.CreateTextpageContentType(); + var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); + + content.ResetDirtyProperties(false); + + content.ChangePublishedState(PublishedState.Published); + Assert.IsTrue(content.ShouldClearPublishedFlagForPreviousVersions()); + } + + [Test] + public void Should_Not_Clear_Published_Flag_When_Saving_Version() + { + var contentType = MockedContentTypes.CreateTextpageContentType(); + var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); + + content.ResetDirtyProperties(false); + content.ChangePublishedState(PublishedState.Published); + content.ResetDirtyProperties(false); + + content.ChangePublishedState(PublishedState.Saved); + Assert.IsFalse(content.ShouldClearPublishedFlagForPreviousVersions()); + } + + [Test] + public void Should_Clear_Published_Flag_When_Unpublishing_From_Published() + { + var contentType = MockedContentTypes.CreateTextpageContentType(); + var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); + + content.ResetDirtyProperties(false); + content.ChangePublishedState(PublishedState.Published); + content.ResetDirtyProperties(false); + + content.ChangePublishedState(PublishedState.Unpublished); + Assert.IsTrue(content.ShouldClearPublishedFlagForPreviousVersions()); + } + + [Test] + public void Should_Not_Clear_Published_Flag_When_Unpublishing_From_Saved() + { + var contentType = MockedContentTypes.CreateTextpageContentType(); + var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1); + + content.ResetDirtyProperties(false); + content.ChangePublishedState(PublishedState.Saved); + content.ResetDirtyProperties(false); + + content.ChangePublishedState(PublishedState.Unpublished); + Assert.IsFalse(content.ShouldClearPublishedFlagForPreviousVersions()); + } + + } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs b/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs index b951380698..44d3149e85 100644 --- a/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs +++ b/src/Umbraco.Tests/Models/Mapping/ContentWebModelMappingTests.cs @@ -31,8 +31,8 @@ namespace Umbraco.Tests.Models.Mapping protected override void FreezeResolution() { - PropertyEditorResolver.Current = new PropertyEditorResolver( - () => new List {typeof (TestPropertyEditor)}); + //PropertyEditorResolver.Current = new PropertyEditorResolver( + // () => new List {typeof (TestPropertyEditor)}); base.FreezeResolution(); } @@ -96,6 +96,13 @@ namespace Umbraco.Tests.Models.Mapping { var contentType = MockedContentTypes.CreateSimpleContentType(); var content = MockedContent.CreateSimpleContent(contentType); + //need ids for tabs + var id = 1; + foreach (var g in content.PropertyGroups) + { + g.Id = id; + id++; + } var result = Mapper.Map(content); @@ -131,6 +138,13 @@ namespace Umbraco.Tests.Models.Mapping p.Id = idSeed; idSeed++; } + //need ids for tabs + var id = 1; + foreach (var g in content.PropertyGroups) + { + g.Id = id; + id++; + } //ensure that nothing is marked as dirty contentType.ResetDirtyProperties(false); //ensure that nothing is marked as dirty @@ -145,7 +159,7 @@ namespace Umbraco.Tests.Models.Mapping } Assert.AreEqual(content.PropertyGroups.Count(), result.Tabs.Count() - 1); Assert.IsTrue(result.Tabs.Any(x => x.Label == "Generic properties")); - Assert.AreEqual(2, result.Tabs.Where(x => x.Label == "Generic properties").SelectMany(x => x.Properties).Count()); + Assert.AreEqual(2, result.Tabs.Where(x => x.Label == "Generic properties").SelectMany(x => x.Properties.Where(p => p.Alias.StartsWith("_umb_") == false)).Count()); } #region Assertions @@ -158,10 +172,11 @@ namespace Umbraco.Tests.Models.Mapping var pDto = result.Properties.SingleOrDefault(x => x.Alias == p.Alias); Assert.IsNotNull(pDto); - pDto.Alias = p.Alias; - pDto.Description = p.PropertyType.Description; - pDto.Label = p.PropertyType.Name; - pDto.Config = applicationContext.Services.DataTypeService.GetPreValuesByDataTypeId(p.PropertyType.DataTypeDefinitionId); + + //pDto.Alias = p.Alias; + //pDto.Description = p.PropertyType.Description; + //pDto.Label = p.PropertyType.Name; + //pDto.Config = applicationContext.Services.DataTypeService.GetPreValuesByDataTypeId(p.PropertyType.DataTypeDefinitionId); } @@ -176,7 +191,7 @@ namespace Umbraco.Tests.Models.Mapping Assert.AreEqual(content.UpdateDate, result.UpdateDate); Assert.AreEqual(content.CreateDate, result.CreateDate); Assert.AreEqual(content.Name, result.Name); - Assert.AreEqual(content.Properties.Count(), result.Properties.Count()); + Assert.AreEqual(content.Properties.Count(), result.Properties.Count(x => x.Alias.StartsWith("_umb_") == false)); } private void AssertBasicProperty(ContentItemBasic result, Property p) diff --git a/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs b/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs index 4e5cfe56dd..989895f50d 100644 --- a/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs +++ b/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueConverterTests.cs @@ -1,13 +1,12 @@ using System; using System.Collections.Generic; using NUnit.Framework; -using Umbraco.Core; using Umbraco.Core.PropertyEditors; using Umbraco.Tests.TestHelpers; namespace Umbraco.Tests.PropertyEditors { - [TestFixture] + [TestFixture] public class PropertyEditorValueConverterTests { [TestCase("2012-11-10", true)] diff --git a/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueEditorTests.cs b/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueEditorTests.cs new file mode 100644 index 0000000000..730dd4ad56 --- /dev/null +++ b/src/Umbraco.Tests/PropertyEditors/PropertyEditorValueEditorTests.cs @@ -0,0 +1,72 @@ +using System; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Tests.PropertyEditors +{ + [TestFixture] + public class PropertyEditorValueEditorTests + { + [TestCase("STRING", "hello", "hello")] + [TestCase("TEXT", "hello", "hello")] + [TestCase("INT", "123", 123)] + [TestCase("INTEGER", "123", 123)] + [TestCase("INTEGER", "", null)] //test empty string for int + [TestCase("DATETIME", "", null)] //test empty string for date + public void Value_Editor_Can_Convert_To_Clr_Type(string valueType, string val, object expected) + { + var valueEditor = new ValueEditor + { + ValueType = valueType + }; + + var result = valueEditor.TryConvertValueToCrlType(val); + Assert.IsTrue(result.Success); + Assert.AreEqual(expected, result.Result); + } + + [Test] + public void Value_Editor_Can_Convert_To_Date_Clr_Type() + { + var valueEditor = new ValueEditor + { + ValueType = "DATE" + }; + + var result = valueEditor.TryConvertValueToCrlType("2010-02-05"); + Assert.IsTrue(result.Success); + Assert.AreEqual(new DateTime(2010, 2, 5), result.Result); + } + + [TestCase("STRING", "hello", "hello")] + [TestCase("TEXT", "hello", "hello")] + [TestCase("INT", 123, "123")] + [TestCase("INTEGER", 123, "123")] + [TestCase("INTEGER", "", "")] //test empty string for int + [TestCase("DATETIME", "", "")] //test empty string for date + public void Value_Editor_Can_Serialize_Value(string valueType, object val, string expected) + { + var valueEditor = new ValueEditor + { + ValueType = valueType + }; + + var result = valueEditor.SerializeValue(val); + Assert.AreEqual(expected, result); + } + + [Test] + public void Value_Editor_Can_Serialize_Date_Value() + { + var now = DateTime.Now; + var valueEditor = new ValueEditor + { + ValueType = "DATE" + }; + + var result = valueEditor.SerializeValue(now); + Assert.AreEqual(now.ToXmlString(), result); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Services/ContentServiceTests.cs b/src/Umbraco.Tests/Services/ContentServiceTests.cs index b087d42827..706322b81d 100644 --- a/src/Umbraco.Tests/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentServiceTests.cs @@ -813,6 +813,48 @@ namespace Umbraco.Tests.Services Assert.That(hasPublishedVersion, Is.True); } + [Test] + public void Can_Verify_Property_Types_On_Content() + { + // Arrange + var contentTypeService = ServiceContext.ContentTypeService; + var contentType = MockedContentTypes.CreateAllTypesContentType("allDataTypes", "All DataTypes"); + contentTypeService.Save(contentType); + var contentService = ServiceContext.ContentService; + var content = MockedContent.CreateAllTypesContent(contentType, "Random Content", -1); + contentService.Save(content); + var id = content.Id; + + // Act + var sut = contentService.GetById(id); + + // Arrange + Assert.That(sut.GetValue("isTrue"), Is.True); + Assert.That(sut.GetValue("number"), Is.EqualTo(42)); + Assert.That(sut.GetValue("bodyText"), Is.EqualTo("Lorem Ipsum Body Text Test")); + Assert.That(sut.GetValue("singleLineText"), Is.EqualTo("Single Line Text Test")); + Assert.That(sut.GetValue("multilineText"), Is.EqualTo("Multiple lines \n in one box")); + Assert.That(sut.GetValue("upload"), Is.EqualTo("/media/1234/koala.jpg")); + Assert.That(sut.GetValue("label"), Is.EqualTo("Non-editable label")); + Assert.That(sut.GetValue("dateTime"), Is.EqualTo(content.GetValue("dateTime"))); + Assert.That(sut.GetValue("colorPicker"), Is.EqualTo("black")); + Assert.That(sut.GetValue("folderBrowser"), Is.Empty); + Assert.That(sut.GetValue("ddlMultiple"), Is.EqualTo("1234,1235")); + Assert.That(sut.GetValue("rbList"), Is.EqualTo("random")); + Assert.That(sut.GetValue("date"), Is.EqualTo(content.GetValue("date"))); + Assert.That(sut.GetValue("ddl"), Is.EqualTo("1234")); + Assert.That(sut.GetValue("chklist"), Is.EqualTo("randomc")); + Assert.That(sut.GetValue("contentPicker"), Is.EqualTo(1090)); + Assert.That(sut.GetValue("mediaPicker"), Is.EqualTo(1091)); + Assert.That(sut.GetValue("memberPicker"), Is.EqualTo(1092)); + Assert.That(sut.GetValue("simpleEditor"), Is.EqualTo("This is simply edited")); + Assert.That(sut.GetValue("ultimatePicker"), Is.EqualTo("1234,1235")); + Assert.That(sut.GetValue("relatedLinks"), Is.EqualTo("")); + Assert.That(sut.GetValue("tags"), Is.EqualTo("this,is,tags")); + Assert.That(sut.GetValue("macroContainer"), Is.Empty); + Assert.That(sut.GetValue("imgCropper"), Is.Empty); + } + private IEnumerable CreateContentHierarchy() { var contentType = ServiceContext.ContentTypeService.GetContentType("umbTextpage"); diff --git a/src/Umbraco.Tests/Services/DataTypeServiceTests.cs b/src/Umbraco.Tests/Services/DataTypeServiceTests.cs index 31b0489949..259c28ba25 100644 --- a/src/Umbraco.Tests/Services/DataTypeServiceTests.cs +++ b/src/Umbraco.Tests/Services/DataTypeServiceTests.cs @@ -23,6 +23,8 @@ namespace Umbraco.Tests.Services base.TearDown(); } + + [Test] public void DataTypeService_Can_Persist_New_DataTypeDefinition() { diff --git a/src/Umbraco.Tests/Services/PreValueConverterTests.cs b/src/Umbraco.Tests/Services/PreValueConverterTests.cs new file mode 100644 index 0000000000..d02544052b --- /dev/null +++ b/src/Umbraco.Tests/Services/PreValueConverterTests.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using Umbraco.Core.Services; + +namespace Umbraco.Tests.Services +{ + [TestFixture] + public class PreValueConverterTests + { + [Test] + public void Can_Convert_To_Dictionary_Pre_Value_Collection() + { + var list = new List> + { + new Tuple(10, "key1", 0, "value1"), + new Tuple(11, "key2", 3, "value2"), + new Tuple(12, "key3", 2, "value3"), + new Tuple(13, "key4", 1, "value4") + }; + + var result = DataTypeService.PreValueConverter.ConvertToPreValuesCollection(list); + + Assert.Throws(() => + { + var blah = result.PreValuesAsArray; + }); + + Assert.AreEqual(4, result.PreValuesAsDictionary.Count); + Assert.AreEqual("key1", result.PreValuesAsDictionary.ElementAt(0).Key); + Assert.AreEqual("key4", result.PreValuesAsDictionary.ElementAt(1).Key); + Assert.AreEqual("key3", result.PreValuesAsDictionary.ElementAt(2).Key); + Assert.AreEqual("key2", result.PreValuesAsDictionary.ElementAt(3).Key); + + } + + [Test] + public void Can_Convert_To_Array_Pre_Value_Collection_When_Empty_Key() + { + var list = new List> + { + new Tuple(10, "", 0, "value1"), + new Tuple(11, "", 3, "value2"), + new Tuple(12, "", 2, "value3"), + new Tuple(13, "", 1, "value4") + }; + + var result = DataTypeService.PreValueConverter.ConvertToPreValuesCollection(list); + + Assert.Throws(() => + { + var blah = result.PreValuesAsDictionary; + }); + + Assert.AreEqual(4, result.PreValuesAsArray.Count()); + Assert.AreEqual("value1", result.PreValuesAsArray.ElementAt(0)); + Assert.AreEqual("value4", result.PreValuesAsArray.ElementAt(1)); + Assert.AreEqual("value3", result.PreValuesAsArray.ElementAt(2)); + Assert.AreEqual("value2", result.PreValuesAsArray.ElementAt(3)); + + } + + [Test] + public void Can_Convert_To_Array_Pre_Value_Collection() + { + var list = new List> + { + new Tuple(10, "key1", 0, "value1"), + new Tuple(11, "key1", 3, "value2"), + new Tuple(12, "key3", 2, "value3"), + new Tuple(13, "key4", 1, "value4") + }; + + var result = DataTypeService.PreValueConverter.ConvertToPreValuesCollection(list); + + Assert.Throws(() => + { + var blah = result.PreValuesAsDictionary; + }); + + Assert.AreEqual(4, result.PreValuesAsArray.Count()); + Assert.AreEqual("value1", result.PreValuesAsArray.ElementAt(0)); + Assert.AreEqual("value4", result.PreValuesAsArray.ElementAt(1)); + Assert.AreEqual("value3", result.PreValuesAsArray.ElementAt(2)); + Assert.AreEqual("value2", result.PreValuesAsArray.ElementAt(3)); + + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs index 02929dcc76..529421725c 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs @@ -79,6 +79,38 @@ namespace Umbraco.Tests.TestHelpers.Entities return content; } + public static Content CreateAllTypesContent(IContentType contentType, string name, int parentId) + { + var content = new Content("Random Content Name", parentId, contentType) { Language = "en-US", Level = 1, SortOrder = 1, CreatorId = 0, WriterId = 0 }; + + content.SetValue("isTrue", true); + content.SetValue("number", 42); + content.SetValue("bodyText", "Lorem Ipsum Body Text Test"); + content.SetValue("singleLineText", "Single Line Text Test"); + content.SetValue("multilineText", "Multiple lines \n in one box"); + content.SetValue("upload", "/media/1234/koala.jpg"); + content.SetValue("label", "Non-editable label"); + content.SetValue("dateTime", DateTime.Now.AddDays(-20)); + content.SetValue("colorPicker", "black"); + content.SetValue("folderBrowser", ""); + content.SetValue("ddlMultiple", "1234,1235"); + content.SetValue("rbList", "random"); + content.SetValue("date", DateTime.Now.AddDays(-10)); + content.SetValue("ddl", "1234"); + content.SetValue("chklist", "randomc"); + content.SetValue("contentPicker", 1090); + content.SetValue("mediaPicker", 1091); + content.SetValue("memberPicker", 1092); + content.SetValue("simpleEditor", "This is simply edited"); + content.SetValue("ultimatePicker", "1234,1235"); + content.SetValue("relatedLinks", ""); + content.SetValue("tags", "this,is,tags"); + content.SetValue("macroContainer", ""); + content.SetValue("imgCropper", ""); + + return content; + } + public static IEnumerable CreateTextpageContent(IContentType contentType, int parentId, int amount) { var list = new List(); diff --git a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs index b4c1ec99e5..9c144fe1ca 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs @@ -180,6 +180,51 @@ namespace Umbraco.Tests.TestHelpers.Entities return contentType; } + public static ContentType CreateAllTypesContentType(string alias, string name) + { + var contentType = new ContentType(-1) + { + Alias = alias, + Name = name, + Description = "ContentType containing all standard DataTypes", + Icon = ".sprTreeDoc3", + Thumbnail = "doc.png", + SortOrder = 1, + CreatorId = 0, + Trashed = false + }; + + var contentCollection = new PropertyTypeCollection(); + contentCollection.Add(new PropertyType(new Guid(Constants.PropertyEditors.TrueFalse), DataTypeDatabaseType.Integer) { Alias = "isTrue", Name = "Is True or False", Mandatory = false, SortOrder = 1, DataTypeDefinitionId = -49 }); + contentCollection.Add(new PropertyType(new Guid(Constants.PropertyEditors.Integer), DataTypeDatabaseType.Integer) { Alias = "number", Name = "Number", Mandatory = false, SortOrder = 2, DataTypeDefinitionId = -51 }); + contentCollection.Add(new PropertyType(new Guid(Constants.PropertyEditors.TinyMCEv3), DataTypeDatabaseType.Ntext) { Alias = "bodyText", Name = "Body Text", Mandatory = false, SortOrder = 3, DataTypeDefinitionId = -87 }); + contentCollection.Add(new PropertyType(new Guid(Constants.PropertyEditors.Textbox), DataTypeDatabaseType.Nvarchar) { Alias = "singleLineText", Name = "Text String", Mandatory = false, SortOrder = 4, DataTypeDefinitionId = -88 }); + contentCollection.Add(new PropertyType(new Guid(Constants.PropertyEditors.TextboxMultiple), DataTypeDatabaseType.Ntext) { Alias = "multilineText", Name = "Multiple Text Strings", Mandatory = false, SortOrder = 5, DataTypeDefinitionId = -89 }); + contentCollection.Add(new PropertyType(new Guid(Constants.PropertyEditors.UploadField), DataTypeDatabaseType.Nvarchar) { Alias = "upload", Name = "Upload Field", Mandatory = false, SortOrder = 6, DataTypeDefinitionId = -90 }); + contentCollection.Add(new PropertyType(new Guid(Constants.PropertyEditors.NoEdit), DataTypeDatabaseType.Nvarchar) { Alias = "label", Name = "Label", Mandatory = false, SortOrder = 7, DataTypeDefinitionId = -92 }); + contentCollection.Add(new PropertyType(new Guid(Constants.PropertyEditors.DateTime), DataTypeDatabaseType.Date) { Alias = "dateTime", Name = "Date Time", Mandatory = false, SortOrder = 8, DataTypeDefinitionId = -36 }); + contentCollection.Add(new PropertyType(new Guid(Constants.PropertyEditors.ColorPicker), DataTypeDatabaseType.Nvarchar) { Alias = "colorPicker", Name = "Color Picker", Mandatory = false, SortOrder = 9, DataTypeDefinitionId = -37 }); + contentCollection.Add(new PropertyType(new Guid(Constants.PropertyEditors.FolderBrowser), DataTypeDatabaseType.Nvarchar) { Alias = "folderBrowser", Name = "Folder Browser", Mandatory = false, SortOrder = 10, DataTypeDefinitionId = -38 }); + contentCollection.Add(new PropertyType(new Guid(Constants.PropertyEditors.DropDownListMultiple), DataTypeDatabaseType.Nvarchar) { Alias = "ddlMultiple", Name = "Dropdown List Multiple", Mandatory = false, SortOrder = 11, DataTypeDefinitionId = -39 }); + contentCollection.Add(new PropertyType(new Guid(Constants.PropertyEditors.RadioButtonList), DataTypeDatabaseType.Nvarchar) { Alias = "rbList", Name = "Radio Button List", Mandatory = false, SortOrder = 12, DataTypeDefinitionId = -40 }); + contentCollection.Add(new PropertyType(new Guid(Constants.PropertyEditors.Date), DataTypeDatabaseType.Date) { Alias = "date", Name = "Date", Mandatory = false, SortOrder = 13, DataTypeDefinitionId = -41 }); + contentCollection.Add(new PropertyType(new Guid(Constants.PropertyEditors.DropDownList), DataTypeDatabaseType.Integer) { Alias = "ddl", Name = "Dropdown List", Mandatory = false, SortOrder = 14, DataTypeDefinitionId = -42 }); + contentCollection.Add(new PropertyType(new Guid(Constants.PropertyEditors.CheckBoxList), DataTypeDatabaseType.Nvarchar) { Alias = "chklist", Name = "Checkbox List", Mandatory = false, SortOrder = 15, DataTypeDefinitionId = -43 }); + contentCollection.Add(new PropertyType(new Guid(Constants.PropertyEditors.ContentPicker), DataTypeDatabaseType.Integer) { Alias = "contentPicker", Name = "Content Picker", Mandatory = false, SortOrder = 16, DataTypeDefinitionId = 1034 }); + contentCollection.Add(new PropertyType(new Guid(Constants.PropertyEditors.MediaPicker), DataTypeDatabaseType.Integer) { Alias = "mediaPicker", Name = "Media Picker", Mandatory = false, SortOrder = 17, DataTypeDefinitionId = 1035 }); + contentCollection.Add(new PropertyType(new Guid(Constants.PropertyEditors.MemberPicker), DataTypeDatabaseType.Integer) { Alias = "memberPicker", Name = "Member Picker", Mandatory = false, SortOrder = 18, DataTypeDefinitionId = 1036 }); + contentCollection.Add(new PropertyType(new Guid(Constants.PropertyEditors.UltraSimpleEditor), DataTypeDatabaseType.Ntext) { Alias = "simpleEditor", Name = "Ultra Simple Editor", Mandatory = false, SortOrder = 19, DataTypeDefinitionId = 1038 }); + contentCollection.Add(new PropertyType(new Guid(Constants.PropertyEditors.UltimatePicker), DataTypeDatabaseType.Ntext) { Alias = "ultimatePicker", Name = "Ultimate Picker", Mandatory = false, SortOrder = 20, DataTypeDefinitionId = 1039 }); + contentCollection.Add(new PropertyType(new Guid(Constants.PropertyEditors.RelatedLinks), DataTypeDatabaseType.Ntext) { Alias = "relatedLinks", Name = "Related Links", Mandatory = false, SortOrder = 21, DataTypeDefinitionId = 1040 }); + contentCollection.Add(new PropertyType(new Guid(Constants.PropertyEditors.Tags), DataTypeDatabaseType.Ntext) { Alias = "tags", Name = "Tags", Mandatory = false, SortOrder = 22, DataTypeDefinitionId = 1041 }); + contentCollection.Add(new PropertyType(new Guid(Constants.PropertyEditors.MacroContainer), DataTypeDatabaseType.Ntext) { Alias = "macroContainer", Name = "Macro Container", Mandatory = false, SortOrder = 23, DataTypeDefinitionId = 1042 }); + contentCollection.Add(new PropertyType(new Guid(Constants.PropertyEditors.ImageCropper), DataTypeDatabaseType.Ntext) { Alias = "imgCropper", Name = "Image Cropper", Mandatory = false, SortOrder = 24, DataTypeDefinitionId = 1043 }); + + contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 }); + + return contentType; + } + public static MediaType CreateVideoMediaType() { var mediaType = new MediaType(-1) diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index bf90e63a2c..a05aeee774 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -55,9 +55,9 @@ False ..\packages\AutoMapper.2.2.1\lib\net40\AutoMapper.dll - + False - ..\packages\Examine.0.1.51.2941\lib\Examine.dll + ..\packages\Examine.0.1.52.2941\lib\Examine.dll False @@ -213,8 +213,10 @@ + + diff --git a/src/Umbraco.Tests/packages.config b/src/Umbraco.Tests/packages.config index bfa801a67a..5ceb87abb1 100644 --- a/src/Umbraco.Tests/packages.config +++ b/src/Umbraco.Tests/packages.config @@ -1,8 +1,8 @@  + - diff --git a/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js b/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js index ca4833584b..306b9d01c6 100644 --- a/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js +++ b/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js @@ -5,7 +5,7 @@ /** Converts a Date object to a globally acceptable ISO string, NOTE: This is different from the built in JavaScript toISOString method which returns date/time like "2013-08-07T02:04:11.487Z" but we want "yyyy-MM-dd HH:mm:ss" */ Date.prototype.toIsoDateTimeString = function (str) { - var month = this.getMonth().toString(); + var month = (this.getMonth() + 1).toString(); if (month.length === 1) { month = "0" + month; } @@ -28,6 +28,23 @@ return this.getFullYear() + "-" + month + "-" + day + " " + hour + ":" + mins + ":" + secs; }; } + + if (!Date.prototype.toIsoDateString) { + /** Converts a Date object to a globally acceptable ISO string, NOTE: This is different from the built in + JavaScript toISOString method which returns date/time like "2013-08-07T02:04:11.487Z" but we want "yyyy-MM-dd" */ + Date.prototype.toIsoDateString = function (str) { + var month = (this.getMonth() + 1).toString(); + if (month.length === 1) { + month = "0" + month; + } + var day = this.getDate().toString(); + if (day.length === 1) { + day = "0" + day; + } + + return this.getFullYear() + "-" + month + "-" + day; + }; + } //create guid method on the String if (String.CreateGuid == null) { diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/umbeditor.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/umbeditor.directive.js index e9b77722e7..dfb802e4b3 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/umbeditor.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/umbeditor.directive.js @@ -2,6 +2,7 @@ * @ngdoc directive * @function * @name umbraco.directives.directive:umbEditor +* @requires formController * @restrict E **/ angular.module("umbraco.directives") @@ -10,11 +11,17 @@ angular.module("umbraco.directives") scope: { model: "=" }, + require: "^form", restrict: 'E', replace: true, templateUrl: 'views/directives/umb-editor.html', link: function (scope, element, attrs, ctrl) { - + + //we need to copy the form controller val to our isolated scope so that + //it get's carried down to the child scopes of this! + //we'll also maintain the current form name. + scope[ctrl.$name] = ctrl; + if(!scope.model.alias){ scope.model.alias = Math.random().toString(36).slice(2); } diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/umbtreeitem.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/umbtreeitem.directive.js index e349ea81d4..3311d5005e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/umbtreeitem.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/umbtreeitem.directive.js @@ -33,8 +33,8 @@ angular.module("umbraco.directives") template: '
  • ' + '' + '' + - '' + - '{{node.name}}' + + '' + + '{{node.name}}' + '' + '
    ' + '
    ' + diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/valpropertymsg.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/valpropertymsg.directive.js index 1cf32f5198..8ad52c4df4 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/valpropertymsg.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/valpropertymsg.directive.js @@ -94,7 +94,9 @@ function valPropertyMsg(serverValidationManager) { // 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. scope.$watch("property.value", function (newValue) { - if (formCtrl.$invalid) { + //we are explicitly checking for valServer errors here, since we shouldn't auto clear + // based on other errors. + if (formCtrl.$invalid && scope.formCtrl.$error.valServer !== undefined) { scope.errorMsg = ""; formCtrl.$setValidity('valPropertyMsg', true); } diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/valserver.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/valserver.directive.js index fa98ccc572..ae2412a24e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/valserver.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/valserver.directive.js @@ -30,8 +30,6 @@ function valServer(serverValidationManager) { ctrl.$viewChangeListeners.push(function () { if (ctrl.$invalid) { ctrl.$setValidity('valServer', true); - //emit the event upwards - scope.$emit("serverRevalidated", { modelCtrl: ctrl }); } }); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/valserverfield.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/valserverfield.directive.js index 6343d614e9..da79253c2f 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/valserverfield.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/valserverfield.directive.js @@ -25,8 +25,6 @@ function valServerField(serverValidationManager) { ctrl.$viewChangeListeners.push(function () { if (ctrl.$invalid) { ctrl.$setValidity('valServerField', true); - //emit the event upwards - scope.$emit("serverRevalidated", { modelCtrl: ctrl }); } }); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/valtab.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/valtab.directive.js index d49618c0dc..5d6d3ec5d9 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/valtab.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/valtab.directive.js @@ -13,6 +13,7 @@ function valTab() { var tabId = "tab" + scope.tab.id; + //assign the form control to our isolated scope so we can watch it's values scope.formCtrl = formCtrl; scope.tabHasError = false; diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/valtogglemsg.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/valtogglemsg.directive.js index 794a9a5b03..e324e6ae4e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/valtogglemsg.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/valtogglemsg.directive.js @@ -15,10 +15,13 @@ function valToggleMsg(serverValidationManager) { throw "valToggleMsg requires that the attribute valMsgFor exists on the element"; } + //assign the form control to our isolated scope so we can watch it's values + scope.formCtrl = formCtrl; + //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; - + //add a watch to the validator for the value (i.e. myForm.value.$error.required ) scope.$watch(formCtrl.$name + "." + attr.valMsgFor + ".$error." + attr.valToggleMsg, function (isInvalid, oldValue) { hasError = isInvalid; diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/_utils.js b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/_utils.js index a0f48e7414..5bcf77242d 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/_utils.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/_utils.js @@ -45,7 +45,7 @@ angular.module('umbraco.mocks'). label: "Sample Editor", id: 3, properties: [ - { alias: "datepicker", label: "Datepicker", view: "datepicker", config: { rows: 7 } }, + { alias: "datepicker", label: "Datepicker", view: "datepicker", config: { pickTime: false, format: "yyyy-MM-dd" } }, { alias: "tags", label: "Tags", view: "tags", value: "" } ] }, diff --git a/src/Umbraco.Web.UI.Client/src/routes.js b/src/Umbraco.Web.UI.Client/src/routes.js index 34f194e94f..7fbb9d0562 100644 --- a/src/Umbraco.Web.UI.Client/src/routes.js +++ b/src/Umbraco.Web.UI.Client/src/routes.js @@ -1,69 +1,77 @@ -app.config(function ($routeProvider) { - $routeProvider - .when('/framed/:url', { - //This occurs when we need to launch some content in an iframe - templateUrl: function (rp) { - if (!rp.url) - throw "A framed resource must have a url route parameter"; - - return 'views/common/legacy.html'; - } - }) - .when('/:section', { - templateUrl: function (rp) { - if (rp.section === "default") - { - rp.section = "content"; - } - - rp.url = "dashboard.aspx?app=" + rp.section; - return 'views/common/legacy.html'; - } - }) - .when('/:section/:tree', { - templateUrl: function (rp) { - if (rp.section === "default") - { - rp.section = "content"; - } - - if (rp.tree === "") - { - rp.tree = "default"; - } - - rp.url = "dashboard.aspx?app=" + rp.section; - return 'views/common/legacy.html'; - } - }) - .when('/:section/:tree/:method', { - templateUrl: function(rp) { - if (!rp.method){ - return "views/common/dashboard.html"; - } - - if(rp.tree === "default" || rp.tree === ""){ - return 'views/' + rp.section + '/' + rp.method + '.html'; - }else{ - return 'views/' + rp.section + '/' + rp.tree + '/' + rp.method + '.html'; - } - } - }) - .when('/:section/:tree/:method/:id', { - templateUrl: function (rp) { - return 'views/' + rp.section + '/' + rp.tree + '/' + rp.method + '.html'; - } - }) - .otherwise({ redirectTo: '/content/document' }); - }).config(function ($locationProvider) { - - //$locationProvider.html5Mode(false).hashPrefix('!'); //turn html5 mode off - // $locationProvider.html5Mode(true); //turn html5 mode on -}); - - -app.run(['userService', function (userService) { - // Get the current user when the application starts - // (in case they are still logged in from a previous session) - userService.isAuthenticated(); -}]); +app.config(function ($routeProvider) { + $routeProvider + .when('/framed/:url', { + //This occurs when we need to launch some content in an iframe + templateUrl: function (rp) { + if (!rp.url) + throw "A framed resource must have a url route parameter"; + + return 'views/common/legacy.html'; + } + }) + .when('/:section', { + templateUrl: function (rp) { + if (rp.section === "default") + { + rp.section = "content"; + } + + rp.url = "dashboard.aspx?app=" + rp.section; + return 'views/common/legacy.html'; + } + }) + .when('/:section/:tree', { + templateUrl: function (rp) { + if (rp.section === "default") + { + rp.section = "content"; + } + + if (rp.tree === "") + { + rp.tree = "default"; + } + + rp.url = "dashboard.aspx?app=" + rp.section; + return 'views/common/legacy.html'; + } + }) + .when('/:section/:tree/:method', { + templateUrl: function(rp) { + if (!rp.method){ + return "views/common/dashboard.html"; + } + + if(rp.tree === "default" || rp.tree === ""){ + return 'views/' + rp.section + '/' + rp.method + '.html'; + }else{ + return 'views/' + rp.section + '/' + rp.tree + '/' + rp.method + '.html'; + } + } + }) + .when('/:section/:tree/:method/:id', { + templateUrl: function (rp) { + if (!rp.method) + return "views/common/dashboard.html"; + + ////here we detect recycle bins, all recycle bins start with -2* (i.e. -20, -21) + //if (rp.id.startsWith("-2")) { + // return 'views/' + rp.section + '/recyclebin.html'; + //} + + return 'views/' + rp.section + '/' + rp.tree + '/' + rp.method + '.html'; + } + }) + .otherwise({ redirectTo: '/content/document' }); + }).config(function ($locationProvider) { + + //$locationProvider.html5Mode(false).hashPrefix('!'); //turn html5 mode off + // $locationProvider.html5Mode(true); //turn html5 mode on +}); + + +app.run(['userService', function (userService) { + // Get the current user when the application starts + // (in case they are still logged in from a previous session) + userService.isAuthenticated(); +}]); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/bootstrap-datetimepicker.min.css b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/bootstrap-datetimepicker.min.css new file mode 100644 index 0000000000..36394e276b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/bootstrap-datetimepicker.min.css @@ -0,0 +1,8 @@ +/*! + * Datepicker for Bootstrap + * + * Copyright 2012 Stefan Petre + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:"";line-height:0}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-datetimepicker-widget{top:0;left:0;width:250px;padding:4px;margin-top:1px;z-index:3000;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.bootstrap-datetimepicker-widget:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0,0,0,0.2);position:absolute;top:-7px;left:6px}.bootstrap-datetimepicker-widget:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;position:absolute;top:-6px;left:7px}.bootstrap-datetimepicker-widget.pull-right:before{left:auto;right:6px}.bootstrap-datetimepicker-widget.pull-right:after{left:auto;right:7px}.bootstrap-datetimepicker-widget>ul{list-style-type:none;margin:0}.bootstrap-datetimepicker-widget .timepicker-hour,.bootstrap-datetimepicker-widget .timepicker-minute,.bootstrap-datetimepicker-widget .timepicker-second{width:100%;font-weight:bold;font-size:1.2em}.bootstrap-datetimepicker-widget table[data-hour-format="12"] .separator{width:4px;padding:0;margin:0}.bootstrap-datetimepicker-widget .datepicker>div{display:none}.bootstrap-datetimepicker-widget .picker-switch{text-align:center}.bootstrap-datetimepicker-widget table{width:100%;margin:0}.bootstrap-datetimepicker-widget td,.bootstrap-datetimepicker-widget th{text-align:center;width:20px;height:20px;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.bootstrap-datetimepicker-widget td.day:hover,.bootstrap-datetimepicker-widget td.hour:hover,.bootstrap-datetimepicker-widget td.minute:hover,.bootstrap-datetimepicker-widget td.second:hover{background:#eee;cursor:pointer}.bootstrap-datetimepicker-widget td.old,.bootstrap-datetimepicker-widget td.new{color:#999}.bootstrap-datetimepicker-widget td.active,.bootstrap-datetimepicker-widget td.active:hover{color:#fff;background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0044cc',GradientType=0);border-color:#04c #04c #002a80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);*background-color:#04c;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.bootstrap-datetimepicker-widget td.active:hover,.bootstrap-datetimepicker-widget td.active:hover:hover,.bootstrap-datetimepicker-widget td.active:active,.bootstrap-datetimepicker-widget td.active:hover:active,.bootstrap-datetimepicker-widget td.active.active,.bootstrap-datetimepicker-widget td.active:hover.active,.bootstrap-datetimepicker-widget td.active.disabled,.bootstrap-datetimepicker-widget td.active:hover.disabled,.bootstrap-datetimepicker-widget td.active[disabled],.bootstrap-datetimepicker-widget td.active:hover[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.bootstrap-datetimepicker-widget td.active:active,.bootstrap-datetimepicker-widget td.active:hover:active,.bootstrap-datetimepicker-widget td.active.active,.bootstrap-datetimepicker-widget td.active:hover.active{background-color:#039 \9}.bootstrap-datetimepicker-widget td.disabled,.bootstrap-datetimepicker-widget td.disabled:hover{background:0;color:#999;cursor:not-allowed}.bootstrap-datetimepicker-widget td span{display:block;width:47px;height:54px;line-height:54px;float:left;margin:2px;cursor:pointer;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.bootstrap-datetimepicker-widget td span:hover{background:#eee}.bootstrap-datetimepicker-widget td span.active{color:#fff;background-color:#006dcc;background-image:-moz-linear-gradient(top,#08c,#04c);background-image:-webkit-gradient(linear,0 0,0 100%,from(#08c),to(#04c));background-image:-webkit-linear-gradient(top,#08c,#04c);background-image:-o-linear-gradient(top,#08c,#04c);background-image:linear-gradient(to bottom,#08c,#04c);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc',endColorstr='#ff0044cc',GradientType=0);border-color:#04c #04c #002a80;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);*background-color:#04c;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.bootstrap-datetimepicker-widget td span.active:hover,.bootstrap-datetimepicker-widget td span.active:active,.bootstrap-datetimepicker-widget td span.active.active,.bootstrap-datetimepicker-widget td span.active.disabled,.bootstrap-datetimepicker-widget td span.active[disabled]{color:#fff;background-color:#04c;*background-color:#003bb3}.bootstrap-datetimepicker-widget td span.active:active,.bootstrap-datetimepicker-widget td span.active.active{background-color:#039 \9}.bootstrap-datetimepicker-widget td span.old{color:#999}.bootstrap-datetimepicker-widget td span.disabled,.bootstrap-datetimepicker-widget td span.disabled:hover{background:0;color:#999;cursor:not-allowed}.bootstrap-datetimepicker-widget th.switch{width:145px}.bootstrap-datetimepicker-widget th.next,.bootstrap-datetimepicker-widget th.prev{font-size:21px}.bootstrap-datetimepicker-widget th.disabled,.bootstrap-datetimepicker-widget th.disabled:hover{background:0;color:#999;cursor:not-allowed}.bootstrap-datetimepicker-widget thead tr:first-child th{cursor:pointer}.bootstrap-datetimepicker-widget thead tr:first-child th:hover{background:#eee}.input-append.date .add-on i,.input-prepend.date .add-on i{display:block;cursor:pointer;width:16px;height:16px}.bootstrap-datetimepicker-widget.left-oriented:before{left:auto;right:6px}.bootstrap-datetimepicker-widget.left-oriented:after{left:auto;right:7px} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/bootstrap-datetimepicker.min.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/bootstrap-datetimepicker.min.js new file mode 100644 index 0000000000..a30f776452 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/bootstrap-datetimepicker.min.js @@ -0,0 +1,26 @@ +/** + * @license + * ========================================================= + * bootstrap-datetimepicker.js + * http://www.eyecon.ro/bootstrap-datepicker + * ========================================================= + * Copyright 2012 Stefan Petre + * + * Contributions: + * - Andrew Rowls + * - Thiago de Arruda + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================================================= + */ +(function($){var smartPhone=window.orientation!=undefined;var DateTimePicker=function(element,options){this.id=dpgId++;this.init(element,options)};var dateToDate=function(dt){if(typeof dt==="string"){return new Date(dt)}return dt};DateTimePicker.prototype={constructor:DateTimePicker,init:function(element,options){var icon;if(!(options.pickTime||options.pickDate))throw new Error("Must choose at least one picker");this.options=options;this.$element=$(element);this.language=options.language in dates?options.language:"en";this.pickDate=options.pickDate;this.pickTime=options.pickTime;this.isInput=this.$element.is("input");this.component=false;if(this.$element.find(".input-append")||this.$element.find(".input-prepend"))this.component=this.$element.find(".add-on");this.format=options.format;if(!this.format){if(this.isInput)this.format=this.$element.data("format");else this.format=this.$element.find("input").data("format");if(!this.format)this.format="MM/dd/yyyy"}this._compileFormat();if(this.component){icon=this.component.find("i")}if(this.pickTime){if(icon&&icon.length)this.timeIcon=icon.data("time-icon");if(!this.timeIcon)this.timeIcon="icon-time";icon.addClass(this.timeIcon)}if(this.pickDate){if(icon&&icon.length)this.dateIcon=icon.data("date-icon");if(!this.dateIcon)this.dateIcon="icon-calendar";icon.removeClass(this.timeIcon);icon.addClass(this.dateIcon)}this.widget=$(getTemplate(this.timeIcon,options.pickDate,options.pickTime,options.pick12HourFormat,options.pickSeconds,options.collapse)).appendTo("body");this.minViewMode=options.minViewMode||this.$element.data("date-minviewmode")||0;if(typeof this.minViewMode==="string"){switch(this.minViewMode){case"months":this.minViewMode=1;break;case"years":this.minViewMode=2;break;default:this.minViewMode=0;break}}this.viewMode=options.viewMode||this.$element.data("date-viewmode")||0;if(typeof this.viewMode==="string"){switch(this.viewMode){case"months":this.viewMode=1;break;case"years":this.viewMode=2;break;default:this.viewMode=0;break}}this.startViewMode=this.viewMode;this.weekStart=options.weekStart||this.$element.data("date-weekstart")||0;this.weekEnd=this.weekStart===0?6:this.weekStart-1;this.setStartDate(options.startDate||this.$element.data("date-startdate"));this.setEndDate(options.endDate||this.$element.data("date-enddate"));this.fillDow();this.fillMonths();this.fillHours();this.fillMinutes();this.fillSeconds();this.update();this.showMode();this._attachDatePickerEvents()},show:function(e){this.widget.show();this.height=this.component?this.component.outerHeight():this.$element.outerHeight();this.place();this.$element.trigger({type:"show",date:this._date});this._attachDatePickerGlobalEvents();if(e){e.stopPropagation();e.preventDefault()}},disable:function(){this.$element.find("input").prop("disabled",true);this._detachDatePickerEvents()},enable:function(){this.$element.find("input").prop("disabled",false);this._attachDatePickerEvents()},hide:function(){var collapse=this.widget.find(".collapse");for(var i=0;i");while(dowCnt'+dates[this.language].daysMin[dowCnt++%7]+"")}this.widget.find(".datepicker-days thead").append(html)},fillMonths:function(){var html="";var i=0;while(i<12){html+=''+dates[this.language].monthsShort[i++]+""}this.widget.find(".datepicker-months td").append(html)},fillDate:function(){var year=this.viewDate.getUTCFullYear();var month=this.viewDate.getUTCMonth();var currentDate=UTCDate(this._date.getUTCFullYear(),this._date.getUTCMonth(),this._date.getUTCDate(),0,0,0,0);var startYear=typeof this.startDate==="object"?this.startDate.getUTCFullYear():-Infinity;var startMonth=typeof this.startDate==="object"?this.startDate.getUTCMonth():-1;var endYear=typeof this.endDate==="object"?this.endDate.getUTCFullYear():Infinity;var endMonth=typeof this.endDate==="object"?this.endDate.getUTCMonth():12;this.widget.find(".datepicker-days").find(".disabled").removeClass("disabled");this.widget.find(".datepicker-months").find(".disabled").removeClass("disabled");this.widget.find(".datepicker-years").find(".disabled").removeClass("disabled");this.widget.find(".datepicker-days th:eq(1)").text(dates[this.language].months[month]+" "+year);var prevMonth=UTCDate(year,month-1,28,0,0,0,0);var day=DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(),prevMonth.getUTCMonth());prevMonth.setUTCDate(day);prevMonth.setUTCDate(day-(prevMonth.getUTCDay()-this.weekStart+7)%7);if(year==startYear&&month<=startMonth||year=endMonth||year>endYear){this.widget.find(".datepicker-days th:eq(2)").addClass("disabled")}var nextMonth=new Date(prevMonth.valueOf());nextMonth.setUTCDate(nextMonth.getUTCDate()+42);nextMonth=nextMonth.valueOf();var html=[];var row;var clsName;while(prevMonth.valueOf()");html.push(row)}clsName="";if(prevMonth.getUTCFullYear()year||prevMonth.getUTCFullYear()==year&&prevMonth.getUTCMonth()>month){clsName+=" new"}if(prevMonth.valueOf()===currentDate.valueOf()){clsName+=" active"}if(prevMonth.valueOf()+864e5<=this.startDate){clsName+=" disabled"}if(prevMonth.valueOf()>this.endDate){clsName+=" disabled"}row.append(''+prevMonth.getUTCDate()+"");prevMonth.setUTCDate(prevMonth.getUTCDate()+1)}this.widget.find(".datepicker-days tbody").empty().append(html);var currentYear=this._date.getUTCFullYear();var months=this.widget.find(".datepicker-months").find("th:eq(1)").text(year).end().find("span").removeClass("active");if(currentYear===year){months.eq(this._date.getUTCMonth()).addClass("active")}if(currentYear-1endYear){this.widget.find(".datepicker-months th:eq(2)").addClass("disabled")}for(var i=0;i<12;i++){if(year==startYear&&startMonth>i||yearendYear){$(months[i]).addClass("disabled")}}html="";year=parseInt(year/10,10)*10;var yearCont=this.widget.find(".datepicker-years").find("th:eq(1)").text(year+"-"+(year+9)).end().find("td");this.widget.find(".datepicker-years").find("th").removeClass("disabled");if(startYear>year){this.widget.find(".datepicker-years").find("th:eq(0)").addClass("disabled")}if(endYearendYear?" disabled":"")+'">'+year+"";year+=1}yearCont.html(html)},fillHours:function(){var table=this.widget.find(".timepicker .timepicker-hours table");table.parent().hide();var html="";if(this.options.pick12HourFormat){var current=1;for(var i=0;i<3;i+=1){html+="";for(var j=0;j<4;j+=1){var c=current.toString();html+=''+padLeft(c,2,"0")+"";current++}html+=""}}else{var current=0;for(var i=0;i<6;i+=1){html+="";for(var j=0;j<4;j+=1){var c=current.toString();html+=''+padLeft(c,2,"0")+"";current++}html+=""}}table.html(html)},fillMinutes:function(){var table=this.widget.find(".timepicker .timepicker-minutes table");table.parent().hide();var html="";var current=0;for(var i=0;i<5;i++){html+="";for(var j=0;j<4;j+=1){var c=current.toString();html+=''+padLeft(c,2,"0")+"";current+=3}html+=""}table.html(html)},fillSeconds:function(){var table=this.widget.find(".timepicker .timepicker-seconds table");table.parent().hide();var html="";var current=0;for(var i=0;i<5;i++){html+="";for(var j=0;j<4;j+=1){var c=current.toString();html+=''+padLeft(c,2,"0")+"";current+=3}html+=""}table.html(html)},fillTime:function(){if(!this._date)return;var timeComponents=this.widget.find(".timepicker span[data-time-component]");var table=timeComponents.closest("table");var is12HourFormat=this.options.pick12HourFormat;var hour=this._date.getUTCHours();var period="AM";if(is12HourFormat){if(hour>=12)period="PM";if(hour===0)hour=12;else if(hour!=12)hour=hour%12;this.widget.find(".timepicker [data-action=togglePeriod]").text(period)}hour=padLeft(hour.toString(),2,"0");var minute=padLeft(this._date.getUTCMinutes().toString(),2,"0");var second=padLeft(this._date.getUTCSeconds().toString(),2,"0");timeComponents.filter("[data-time-component=hours]").text(hour);timeComponents.filter("[data-time-component=minutes]").text(minute);timeComponents.filter("[data-time-component=seconds]").text(second)},click:function(e){e.stopPropagation();e.preventDefault();this._unset=false;var target=$(e.target).closest("span, td, th");if(target.length===1){if(!target.is(".disabled")){switch(target[0].nodeName.toLowerCase()){case"th":switch(target[0].className){case"switch":this.showMode(1);break;case"prev":case"next":var vd=this.viewDate;var navFnc=DPGlobal.modes[this.viewMode].navFnc;var step=DPGlobal.modes[this.viewMode].navStep;if(target[0].className==="prev")step=step*-1;vd["set"+navFnc](vd["get"+navFnc]()+step);this.fillDate();this.set();break}break;case"span":if(target.is(".month")){var month=target.parent().find("span").index(target);this.viewDate.setUTCMonth(month)}else{var year=parseInt(target.text(),10)||0;this.viewDate.setUTCFullYear(year)}if(this.viewMode!==0){this._date=UTCDate(this.viewDate.getUTCFullYear(),this.viewDate.getUTCMonth(),this.viewDate.getUTCDate(),this._date.getUTCHours(),this._date.getUTCMinutes(),this._date.getUTCSeconds(),this._date.getUTCMilliseconds());this.notifyChange()}this.showMode(-1);this.fillDate();this.set();break;case"td":if(target.is(".day")){var day=parseInt(target.text(),10)||1;var month=this.viewDate.getUTCMonth();var year=this.viewDate.getUTCFullYear();if(target.is(".old")){if(month===0){month=11;year-=1}else{month-=1}}else if(target.is(".new")){if(month==11){month=0;year+=1}else{month+=1}}this._date=UTCDate(year,month,day,this._date.getUTCHours(),this._date.getUTCMinutes(),this._date.getUTCSeconds(),this._date.getUTCMilliseconds());this.viewDate=UTCDate(year,month,Math.min(28,day),0,0,0,0);this.fillDate();this.set();this.notifyChange()}break}}}},actions:{incrementHours:function(e){this._date.setUTCHours(this._date.getUTCHours()+1)},incrementMinutes:function(e){this._date.setUTCMinutes(this._date.getUTCMinutes()+1)},incrementSeconds:function(e){this._date.setUTCSeconds(this._date.getUTCSeconds()+1)},decrementHours:function(e){this._date.setUTCHours(this._date.getUTCHours()-1)},decrementMinutes:function(e){this._date.setUTCMinutes(this._date.getUTCMinutes()-1)},decrementSeconds:function(e){this._date.setUTCSeconds(this._date.getUTCSeconds()-1)},togglePeriod:function(e){var hour=this._date.getUTCHours();if(hour>=12)hour-=12;else hour+=12;this._date.setUTCHours(hour)},showPicker:function(){this.widget.find(".timepicker > div:not(.timepicker-picker)").hide();this.widget.find(".timepicker .timepicker-picker").show()},showHours:function(){this.widget.find(".timepicker .timepicker-picker").hide();this.widget.find(".timepicker .timepicker-hours").show()},showMinutes:function(){this.widget.find(".timepicker .timepicker-picker").hide();this.widget.find(".timepicker .timepicker-minutes").show()},showSeconds:function(){this.widget.find(".timepicker .timepicker-picker").hide();this.widget.find(".timepicker .timepicker-seconds").show()},selectHour:function(e){var tgt=$(e.target);var value=parseInt(tgt.text(),10);if(this.options.pick12HourFormat){var current=this._date.getUTCHours();if(current>=12){if(value!=12)value=(value+12)%24}else{if(value===12)value=0;else value=value%12}}this._date.setUTCHours(value);this.actions.showPicker.call(this)},selectMinute:function(e){var tgt=$(e.target);var value=parseInt(tgt.text(),10);this._date.setUTCMinutes(value);this.actions.showPicker.call(this)},selectSecond:function(e){var tgt=$(e.target);var value=parseInt(tgt.text(),10);this._date.setUTCSeconds(value);this.actions.showPicker.call(this)}},doAction:function(e){e.stopPropagation();e.preventDefault();if(!this._date)this._date=UTCDate(1970,0,0,0,0,0,0);var action=$(e.currentTarget).data("action");var rv=this.actions[action].apply(this,arguments);this.set();this.fillTime();this.notifyChange();return rv},stopEvent:function(e){e.stopPropagation();e.preventDefault()},keydown:function(e){var self=this,k=e.which,input=$(e.target);if(k==8||k==46){setTimeout(function(){self._resetMaskPos(input)})}},keypress:function(e){var k=e.which;if(k==8||k==46){return}var input=$(e.target);var c=String.fromCharCode(k);var val=input.val()||"";val+=c;var mask=this._mask[this._maskPos];if(!mask){return false}if(mask.end!=val.length){return}if(!mask.pattern.test(val.slice(mask.start))){val=val.slice(0,val.length-1);while((mask=this._mask[this._maskPos])&&mask.character){val+=mask.character;this._maskPos++}val+=c;if(mask.end!=val.length){input.val(val);return false}else{if(!mask.pattern.test(val.slice(mask.start))){input.val(val.slice(0,mask.start));return false}else{input.val(val);this._maskPos++;return false}}}else{this._maskPos++}},change:function(e){var input=$(e.target);var val=input.val();if(this._formatPattern.test(val)){this.update();this.setValue(this._date.getTime());this.notifyChange();this.set()}else if(val&&val.trim()){this.setValue(this._date.getTime());if(this._date)this.set();else input.val("")}else{if(this._date){this.setValue(null);this.notifyChange();this._unset=true}}this._resetMaskPos(input)},showMode:function(dir){if(dir){this.viewMode=Math.max(this.minViewMode,Math.min(2,this.viewMode+dir))}this.widget.find(".datepicker > div").hide().filter(".datepicker-"+DPGlobal.modes[this.viewMode].clsName).show()},destroy:function(){this._detachDatePickerEvents();this._detachDatePickerGlobalEvents();this.widget.remove();this.$element.removeData("datetimepicker");this.component.removeData("datetimepicker")},formatDate:function(d){return this.format.replace(formatReplacer,function(match){var methodName,property,rv,len=match.length;if(match==="ms")len=1;property=dateFormatComponents[match].property;if(property==="Hours12"){rv=d.getUTCHours();if(rv===0)rv=12;else if(rv!==12)rv=rv%12}else if(property==="Period12"){if(d.getUTCHours()>=12)return"PM";else return"AM"}else{methodName="get"+property;rv=d[methodName]()}if(methodName==="getUTCMonth")rv=rv+1;if(methodName==="getUTCYear")rv=rv+1900-2e3;return padLeft(rv.toString(),len,"0")})},parseDate:function(str){var match,i,property,methodName,value,parsed={};if(!(match=this._formatPattern.exec(str)))return null;for(i=1;ival.length){this._maskPos=i;break}else if(this._mask[i].end===val.length){this._maskPos=i+1;break}}},_finishParsingDate:function(parsed){var year,month,date,hours,minutes,seconds,milliseconds;year=parsed.UTCFullYear;if(parsed.UTCYear)year=2e3+parsed.UTCYear;if(!year)year=1970;if(parsed.UTCMonth)month=parsed.UTCMonth-1;else month=0;date=parsed.UTCDate||1;hours=parsed.UTCHours||0;minutes=parsed.UTCMinutes||0;seconds=parsed.UTCSeconds||0;milliseconds=parsed.UTCMilliseconds||0;if(parsed.Hours12){hours=parsed.Hours12}if(parsed.Period12){if(/pm/i.test(parsed.Period12)){if(hours!=12)hours=(hours+12)%24}else{hours=hours%12}}return UTCDate(year,month,date,hours,minutes,seconds,milliseconds)},_compileFormat:function(){var match,component,components=[],mask=[],str=this.format,propertiesByIndex={},i=0,pos=0;while(match=formatComponent.exec(str)){component=match[0];if(component in dateFormatComponents){i++;propertiesByIndex[i]=dateFormatComponents[component].property;components.push("\\s*"+dateFormatComponents[component].getPattern(this)+"\\s*");mask.push({pattern:new RegExp(dateFormatComponents[component].getPattern(this)),property:dateFormatComponents[component].property,start:pos,end:pos+=component.length})}else{components.push(escapeRegExp(component));mask.push({pattern:new RegExp(escapeRegExp(component)),character:component,start:pos,end:++pos})}str=str.slice(component.length)}this._mask=mask;this._maskPos=0;this._formatPattern=new RegExp("^\\s*"+components.join("")+"\\s*$");this._propertiesByIndex=propertiesByIndex},_attachDatePickerEvents:function(){var self=this;this.widget.on("click",".datepicker *",$.proxy(this.click,this));this.widget.on("click","[data-action]",$.proxy(this.doAction,this));this.widget.on("mousedown",$.proxy(this.stopEvent,this));if(this.pickDate&&this.pickTime){this.widget.on("click.togglePicker",".accordion-toggle",function(e){e.stopPropagation();var $this=$(this);var $parent=$this.closest("ul");var expanded=$parent.find(".collapse.in");var closed=$parent.find(".collapse:not(.in)");if(expanded&&expanded.length){var collapseData=expanded.data("collapse");if(collapseData&&collapseData.transitioning)return;expanded.collapse("hide");closed.collapse("show");$this.find("i").toggleClass(self.timeIcon+" "+self.dateIcon);self.$element.find(".add-on i").toggleClass(self.timeIcon+" "+self.dateIcon)}})}if(this.isInput){this.$element.on({focus:$.proxy(this.show,this),change:$.proxy(this.change,this)});if(this.options.maskInput){this.$element.on({keydown:$.proxy(this.keydown,this),keypress:$.proxy(this.keypress,this)})}}else{this.$element.on({change:$.proxy(this.change,this)},"input");if(this.options.maskInput){this.$element.on({keydown:$.proxy(this.keydown,this),keypress:$.proxy(this.keypress,this)},"input")}if(this.component){this.component.on("click",$.proxy(this.show,this))}else{this.$element.on("click",$.proxy(this.show,this))}}},_attachDatePickerGlobalEvents:function(){$(window).on("resize.datetimepicker"+this.id,$.proxy(this.place,this));if(!this.isInput){$(document).on("mousedown.datetimepicker"+this.id,$.proxy(this.hide,this))}},_detachDatePickerEvents:function(){this.widget.off("click",".datepicker *",this.click);this.widget.off("click","[data-action]");this.widget.off("mousedown",this.stopEvent);if(this.pickDate&&this.pickTime){this.widget.off("click.togglePicker")}if(this.isInput){this.$element.off({focus:this.show,change:this.change});if(this.options.maskInput){this.$element.off({keydown:this.keydown,keypress:this.keypress})}}else{this.$element.off({change:this.change},"input");if(this.options.maskInput){this.$element.off({keydown:this.keydown,keypress:this.keypress},"input")}if(this.component){this.component.off("click",this.show)}else{this.$element.off("click",this.show)}}},_detachDatePickerGlobalEvents:function(){$(window).off("resize.datetimepicker"+this.id);if(!this.isInput){$(document).off("mousedown.datetimepicker"+this.id)}},_isInFixed:function(){if(this.$element){var parents=this.$element.parents();var inFixed=false;for(var i=0;i'+"
      "+""+'
      '+DPGlobal.template+"
      "+""+'
    • '+""+'
      '+TPGlobal.getTemplate(is12Hours,showSeconds)+"
      "+""+"
    "+""}else if(pickTime){return'"}else{return'"}}function UTCDate(){return new Date(Date.UTC.apply(Date,arguments))}var DPGlobal={modes:[{clsName:"days",navFnc:"UTCMonth",navStep:1},{clsName:"months",navFnc:"UTCFullYear",navStep:1},{clsName:"years",navFnc:"UTCFullYear",navStep:10}],isLeapYear:function(year){return year%4===0&&year%100!==0||year%400===0},getDaysInMonth:function(year,month){return[31,DPGlobal.isLeapYear(year)?29:28,31,30,31,30,31,31,30,31,30,31][month]},headTemplate:""+""+'‹'+''+'›'+""+"",contTemplate:''};DPGlobal.template='
    '+''+DPGlobal.headTemplate+""+"
    "+"
    "+'
    '+''+DPGlobal.headTemplate+DPGlobal.contTemplate+"
    "+"
    "+'
    '+''+DPGlobal.headTemplate+DPGlobal.contTemplate+"
    "+"
    ";var TPGlobal={hourTemplate:'',minuteTemplate:'',secondTemplate:''};TPGlobal.getTemplate=function(is12Hours,showSeconds){return'
    '+'"+""+''+''+''+(showSeconds?''+'':"")+(is12Hours?'':"")+""+""+" "+''+" "+(showSeconds?''+"":"")+(is12Hours?''+"":"")+""+""+''+''+''+(showSeconds?''+'':"")+(is12Hours?'':"")+""+"
    "+TPGlobal.hourTemplate+":"+TPGlobal.minuteTemplate+":"+TPGlobal.secondTemplate+""+''+"
    "+"
    "+'
    '+''+"
    "+"
    "+'
    '+''+"
    "+"
    "+(showSeconds?'
    '+''+"
    "+"
    ":"")}})(window.jQuery); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/bootstrap.datepicker.css b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/bootstrap.datepicker.css deleted file mode 100644 index 9c85fc8c9c..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/bootstrap.datepicker.css +++ /dev/null @@ -1,18 +0,0 @@ -/* Show cursor when hovering dates and navigation arrows, to indicate they are clickable */ -.datepicker-days .day, -.datepicker .add-on, -.datepicker-days .icon-arrow-right { - cursor: pointer; -} -.datepicker-days .day:hover { - background: #f8f8f7; -} -/* Ensures that the "select" color is not shown when clicking arrows in the datepicker window */ -.datepicker-days .icon-arrow-right { - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/bootstrap.datepicker.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/bootstrap.datepicker.js deleted file mode 100644 index 3ef33a0d5e..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/bootstrap.datepicker.js +++ /dev/null @@ -1,1174 +0,0 @@ -/* ========================================================= - * bootstrap-datepicker.js - * http://www.eyecon.ro/bootstrap-datepicker - * ========================================================= - * Copyright 2012 Stefan Petre - * Improvements by Andrew Rowls - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ========================================================= */ - -!function( $ ) { - - function UTCDate(){ - return new Date(Date.UTC.apply(Date, arguments)); - } - function UTCToday(){ - var today = new Date(); - return UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate()); - } - - // Picker object - - var Datepicker = function(element, options) { - var that = this; - - this.element = $(element); - this.language = options.language||this.element.data('date-language')||"en"; - this.language = this.language in dates ? this.language : this.language.split('-')[0]; //Check if "de-DE" style date is available, if not language should fallback to 2 letter code eg "de" - this.language = this.language in dates ? this.language : "en"; - this.isRTL = dates[this.language].rtl||false; - this.format = DPGlobal.parseFormat(options.format||this.element.data('date-format')||dates[this.language].format||'mm/dd/yyyy'); - this.isInline = false; - this.isInput = this.element.is('input'); - this.component = this.element.is('.date') ? this.element.find('.add-on, .btn') : false; - this.hasInput = this.component && this.element.find('input').length; - if(this.component && this.component.length === 0) - this.component = false; - - this.forceParse = true; - if ('forceParse' in options) { - this.forceParse = options.forceParse; - } else if ('dateForceParse' in this.element.data()) { - this.forceParse = this.element.data('date-force-parse'); - } - - this.picker = $(DPGlobal.template); - this._buildEvents(); - this._attachEvents(); - - if(this.isInline) { - this.picker.addClass('datepicker-inline').appendTo(this.element); - } else { - this.picker.addClass('datepicker-dropdown dropdown-menu'); - } - if (this.isRTL){ - this.picker.addClass('datepicker-rtl'); - this.picker.find('.prev i, .next i') - .toggleClass('icon-arrow-left icon-arrow-right'); - } - - this.autoclose = false; - if ('autoclose' in options) { - this.autoclose = options.autoclose; - } else if ('dateAutoclose' in this.element.data()) { - this.autoclose = this.element.data('date-autoclose'); - } - - this.keyboardNavigation = true; - if ('keyboardNavigation' in options) { - this.keyboardNavigation = options.keyboardNavigation; - } else if ('dateKeyboardNavigation' in this.element.data()) { - this.keyboardNavigation = this.element.data('date-keyboard-navigation'); - } - - this.viewMode = this.startViewMode = 0; - switch(options.startView || this.element.data('date-start-view')){ - case 2: - case 'decade': - this.viewMode = this.startViewMode = 2; - break; - case 1: - case 'year': - this.viewMode = this.startViewMode = 1; - break; - } - - this.minViewMode = options.minViewMode||this.element.data('date-min-view-mode')||0; - if (typeof this.minViewMode === 'string') { - switch (this.minViewMode) { - case 'months': - this.minViewMode = 1; - break; - case 'years': - this.minViewMode = 2; - break; - default: - this.minViewMode = 0; - break; - } - } - - this.viewMode = this.startViewMode = Math.max(this.startViewMode, this.minViewMode); - - this.todayBtn = (options.todayBtn||this.element.data('date-today-btn')||false); - this.todayHighlight = (options.todayHighlight||this.element.data('date-today-highlight')||false); - - this.calendarWeeks = false; - if ('calendarWeeks' in options) { - this.calendarWeeks = options.calendarWeeks; - } else if ('dateCalendarWeeks' in this.element.data()) { - this.calendarWeeks = this.element.data('date-calendar-weeks'); - } - if (this.calendarWeeks) - this.picker.find('tfoot th.today') - .attr('colspan', function(i, val){ - return parseInt(val) + 1; - }); - - this._allow_update = false; - - this.weekStart = ((options.weekStart||this.element.data('date-weekstart')||dates[this.language].weekStart||0) % 7); - this.weekEnd = ((this.weekStart + 6) % 7); - this.startDate = -Infinity; - this.endDate = Infinity; - this.daysOfWeekDisabled = []; - this.beforeShowDay = options.beforeShowDay || $.noop; - this.setStartDate(options.startDate||this.element.data('date-startdate')); - this.setEndDate(options.endDate||this.element.data('date-enddate')); - this.setDaysOfWeekDisabled(options.daysOfWeekDisabled||this.element.data('date-days-of-week-disabled')); - this.fillDow(); - this.fillMonths(); - this.setRange(options.range); - - this._allow_update = true; - - this.update(); - this.showMode(); - - if(this.isInline) { - this.show(); - } - }; - - Datepicker.prototype = { - constructor: Datepicker, - - _events: [], - _secondaryEvents: [], - _applyEvents: function(evs){ - for (var i=0, el, ev; i this.endDate) { - this.viewDate = new Date(this.endDate); - } else { - this.viewDate = new Date(this.date); - } - this.fill(); - }, - - fillDow: function(){ - var dowCnt = this.weekStart, - html = ''; - if(this.calendarWeeks){ - var cell = ' '; - html += cell; - this.picker.find('.datepicker-days thead tr:first-child').prepend(cell); - } - while (dowCnt < this.weekStart + 7) { - html += ''+dates[this.language].daysMin[(dowCnt++)%7]+''; - } - html += ''; - this.picker.find('.datepicker-days thead').append(html); - }, - - fillMonths: function(){ - var html = '', - i = 0; - while (i < 12) { - html += ''+dates[this.language].monthsShort[i++]+''; - } - this.picker.find('.datepicker-months td').html(html); - }, - - setRange: function(range){ - if (!range || !range.length) - delete this.range; - else - this.range = $.map(range, function(d){ return d.valueOf(); }); - this.fill(); - }, - - getClassNames: function(date){ - var cls = [], - year = this.viewDate.getUTCFullYear(), - month = this.viewDate.getUTCMonth(), - currentDate = this.date.valueOf(), - today = new Date(); - if (date.getUTCFullYear() < year || (date.getUTCFullYear() == year && date.getUTCMonth() < month)) { - cls.push('old'); - } else if (date.getUTCFullYear() > year || (date.getUTCFullYear() == year && date.getUTCMonth() > month)) { - cls.push('new'); - } - // Compare internal UTC date with local today, not UTC today - if (this.todayHighlight && - date.getUTCFullYear() == today.getFullYear() && - date.getUTCMonth() == today.getMonth() && - date.getUTCDate() == today.getDate()) { - cls.push('today'); - } - if (currentDate && date.valueOf() == currentDate) { - cls.push('active'); - } - if (date.valueOf() < this.startDate || date.valueOf() > this.endDate || - $.inArray(date.getUTCDay(), this.daysOfWeekDisabled) !== -1) { - cls.push('disabled'); - } - if (this.range){ - if (date > this.range[0] && date < this.range[this.range.length-1]){ - cls.push('range'); - } - if ($.inArray(date.valueOf(), this.range) != -1){ - cls.push('selected'); - } - } - return cls; - }, - - fill: function() { - var d = new Date(this.viewDate), - year = d.getUTCFullYear(), - month = d.getUTCMonth(), - startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity, - startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity, - endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity, - endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity, - currentDate = this.date && this.date.valueOf(), - tooltip; - this.picker.find('.datepicker-days thead th.datepicker-switch') - .text(dates[this.language].months[month]+' '+year); - this.picker.find('tfoot th.today') - .text(dates[this.language].today) - .toggle(this.todayBtn !== false); - this.updateNavArrows(); - this.fillMonths(); - var prevMonth = UTCDate(year, month-1, 28,0,0,0,0), - day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth()); - prevMonth.setUTCDate(day); - prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7); - var nextMonth = new Date(prevMonth); - nextMonth.setUTCDate(nextMonth.getUTCDate() + 42); - nextMonth = nextMonth.valueOf(); - var html = []; - var clsName; - while(prevMonth.valueOf() < nextMonth) { - if (prevMonth.getUTCDay() == this.weekStart) { - html.push(''); - if(this.calendarWeeks){ - // ISO 8601: First week contains first thursday. - // ISO also states week starts on Monday, but we can be more abstract here. - var - // Start of current week: based on weekstart/current date - ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5), - // Thursday of this week - th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5), - // First Thursday of year, year from thursday - yth = new Date(+(yth = UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5), - // Calendar week: ms between thursdays, div ms per day, div 7 days - calWeek = (th - yth) / 864e5 / 7 + 1; - html.push(''+ calWeek +''); - - } - } - clsName = this.getClassNames(prevMonth); - clsName.push('day'); - - var before = this.beforeShowDay(prevMonth); - if (before === undefined) - before = {}; - else if (typeof(before) === 'boolean') - before = {enabled: before}; - else if (typeof(before) === 'string') - before = {classes: before}; - if (before.enabled === false) - clsName.push('disabled'); - if (before.classes) - clsName = clsName.concat(before.classes.split(/\s+/)); - if (before.tooltip) - tooltip = before.tooltip; - - clsName = $.unique(clsName); - html.push(''+prevMonth.getUTCDate() + ''); - if (prevMonth.getUTCDay() == this.weekEnd) { - html.push(''); - } - prevMonth.setUTCDate(prevMonth.getUTCDate()+1); - } - this.picker.find('.datepicker-days tbody').empty().append(html.join('')); - var currentYear = this.date && this.date.getUTCFullYear(); - - var months = this.picker.find('.datepicker-months') - .find('th:eq(1)') - .text(year) - .end() - .find('span').removeClass('active'); - if (currentYear && currentYear == year) { - months.eq(this.date.getUTCMonth()).addClass('active'); - } - if (year < startYear || year > endYear) { - months.addClass('disabled'); - } - if (year == startYear) { - months.slice(0, startMonth).addClass('disabled'); - } - if (year == endYear) { - months.slice(endMonth+1).addClass('disabled'); - } - - html = ''; - year = parseInt(year/10, 10) * 10; - var yearCont = this.picker.find('.datepicker-years') - .find('th:eq(1)') - .text(year + '-' + (year + 9)) - .end() - .find('td'); - year -= 1; - for (var i = -1; i < 11; i++) { - html += ''+year+''; - year += 1; - } - yearCont.html(html); - }, - - updateNavArrows: function() { - if (!this._allow_update) return; - - var d = new Date(this.viewDate), - year = d.getUTCFullYear(), - month = d.getUTCMonth(); - switch (this.viewMode) { - case 0: - if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) { - this.picker.find('.prev').css({visibility: 'hidden'}); - } else { - this.picker.find('.prev').css({visibility: 'visible'}); - } - if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) { - this.picker.find('.next').css({visibility: 'hidden'}); - } else { - this.picker.find('.next').css({visibility: 'visible'}); - } - break; - case 1: - case 2: - if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) { - this.picker.find('.prev').css({visibility: 'hidden'}); - } else { - this.picker.find('.prev').css({visibility: 'visible'}); - } - if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) { - this.picker.find('.next').css({visibility: 'hidden'}); - } else { - this.picker.find('.next').css({visibility: 'visible'}); - } - break; - } - }, - - click: function(e) { - e.preventDefault(); - var target = $(e.target).closest('span, td, th'); - if (target.length == 1) { - switch(target[0].nodeName.toLowerCase()) { - case 'th': - switch(target[0].className) { - case 'datepicker-switch': - this.showMode(1); - break; - case 'prev': - case 'next': - var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className == 'prev' ? -1 : 1); - switch(this.viewMode){ - case 0: - this.viewDate = this.moveMonth(this.viewDate, dir); - break; - case 1: - case 2: - this.viewDate = this.moveYear(this.viewDate, dir); - break; - } - this.fill(); - break; - case 'today': - var date = new Date(); - date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0); - - this.showMode(-2); - var which = this.todayBtn == 'linked' ? null : 'view'; - this._setDate(date, which); - break; - } - break; - case 'span': - if (!target.is('.disabled')) { - this.viewDate.setUTCDate(1); - if (target.is('.month')) { - var day = 1; - var month = target.parent().find('span').index(target); - var year = this.viewDate.getUTCFullYear(); - this.viewDate.setUTCMonth(month); - this._trigger('changeMonth', this.viewDate); - if ( this.minViewMode == 1 ) { - this._setDate(UTCDate(year, month, day,0,0,0,0)); - } - } else { - var year = parseInt(target.text(), 10)||0; - var day = 1; - var month = 0; - this.viewDate.setUTCFullYear(year); - this._trigger('changeYear', this.viewDate); - if ( this.minViewMode == 2 ) { - this._setDate(UTCDate(year, month, day,0,0,0,0)); - } - } - this.showMode(-1); - this.fill(); - } - break; - case 'td': - if (target.is('.day') && !target.is('.disabled')){ - var day = parseInt(target.text(), 10)||1; - var year = this.viewDate.getUTCFullYear(), - month = this.viewDate.getUTCMonth(); - if (target.is('.old')) { - if (month === 0) { - month = 11; - year -= 1; - } else { - month -= 1; - } - } else if (target.is('.new')) { - if (month == 11) { - month = 0; - year += 1; - } else { - month += 1; - } - } - this._setDate(UTCDate(year, month, day,0,0,0,0)); - } - break; - } - } - }, - - _setDate: function(date, which){ - if (!which || which == 'date') - this.date = date; - if (!which || which == 'view') - this.viewDate = date; - this.fill(); - this.setValue(); - this._trigger('changeDate'); - var element; - if (this.isInput) { - element = this.element; - } else if (this.component){ - element = this.element.find('input'); - } - if (element) { - element.change(); - if (this.autoclose && (!which || which == 'date')) { - this.hide(); - } - } - }, - - moveMonth: function(date, dir){ - if (!dir) return date; - var new_date = new Date(date.valueOf()), - day = new_date.getUTCDate(), - month = new_date.getUTCMonth(), - mag = Math.abs(dir), - new_month, test; - dir = dir > 0 ? 1 : -1; - if (mag == 1){ - test = dir == -1 - // If going back one month, make sure month is not current month - // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02) - ? function(){ return new_date.getUTCMonth() == month; } - // If going forward one month, make sure month is as expected - // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02) - : function(){ return new_date.getUTCMonth() != new_month; }; - new_month = month + dir; - new_date.setUTCMonth(new_month); - // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11 - if (new_month < 0 || new_month > 11) - new_month = (new_month + 12) % 12; - } else { - // For magnitudes >1, move one month at a time... - for (var i=0; i= this.startDate && date <= this.endDate; - }, - - keydown: function(e){ - if (this.picker.is(':not(:visible)')){ - if (e.keyCode == 27) // allow escape to hide and re-show picker - this.show(); - return; - } - var dateChanged = false, - dir, day, month, - newDate, newViewDate; - switch(e.keyCode){ - case 27: // escape - this.hide(); - e.preventDefault(); - break; - case 37: // left - case 39: // right - if (!this.keyboardNavigation) break; - dir = e.keyCode == 37 ? -1 : 1; - if (e.ctrlKey){ - newDate = this.moveYear(this.date, dir); - newViewDate = this.moveYear(this.viewDate, dir); - } else if (e.shiftKey){ - newDate = this.moveMonth(this.date, dir); - newViewDate = this.moveMonth(this.viewDate, dir); - } else { - newDate = new Date(this.date); - newDate.setUTCDate(this.date.getUTCDate() + dir); - newViewDate = new Date(this.viewDate); - newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir); - } - if (this.dateWithinRange(newDate)){ - this.date = newDate; - this.viewDate = newViewDate; - this.setValue(); - this.update(); - e.preventDefault(); - dateChanged = true; - } - break; - case 38: // up - case 40: // down - if (!this.keyboardNavigation) break; - dir = e.keyCode == 38 ? -1 : 1; - if (e.ctrlKey){ - newDate = this.moveYear(this.date, dir); - newViewDate = this.moveYear(this.viewDate, dir); - } else if (e.shiftKey){ - newDate = this.moveMonth(this.date, dir); - newViewDate = this.moveMonth(this.viewDate, dir); - } else { - newDate = new Date(this.date); - newDate.setUTCDate(this.date.getUTCDate() + dir * 7); - newViewDate = new Date(this.viewDate); - newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7); - } - if (this.dateWithinRange(newDate)){ - this.date = newDate; - this.viewDate = newViewDate; - this.setValue(); - this.update(); - e.preventDefault(); - dateChanged = true; - } - break; - case 13: // enter - this.hide(); - e.preventDefault(); - break; - case 9: // tab - this.hide(); - break; - } - if (dateChanged){ - this._trigger('changeDate'); - var element; - if (this.isInput) { - element = this.element; - } else if (this.component){ - element = this.element.find('input'); - } - if (element) { - element.change(); - } - } - }, - - showMode: function(dir) { - if (dir) { - this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir)); - } - /* - vitalets: fixing bug of very special conditions: - jquery 1.7.1 + webkit + show inline datepicker in bootstrap popover. - Method show() does not set display css correctly and datepicker is not shown. - Changed to .css('display', 'block') solve the problem. - See https://github.com/vitalets/x-editable/issues/37 - - In jquery 1.7.2+ everything works fine. - */ - //this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).show(); - this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).css('display', 'block'); - this.updateNavArrows(); - } - }; - - var DateRangePicker = function(element, options){ - this.element = $(element); - this.inputs = $.map(options.inputs, function(i){ return i.jquery ? i[0] : i; }); - delete options.inputs; - - $(this.inputs) - .datepicker(options) - .bind('changeDate', $.proxy(this.dateUpdated, this)); - - this.pickers = $.map(this.inputs, function(i){ return $(i).data('datepicker'); }); - this.updateDates(); - }; - DateRangePicker.prototype = { - updateDates: function(){ - this.dates = $.map(this.pickers, function(i){ return i.date; }); - this.updateRanges(); - }, - updateRanges: function(){ - var range = $.map(this.dates, function(d){ return d.valueOf(); }); - $.each(this.pickers, function(i, p){ - p.setRange(range); - }); - }, - dateUpdated: function(e){ - var dp = $(e.target).data('datepicker'), - new_date = e.date, - i = $.inArray(e.target, this.inputs), - l = this.inputs.length; - if (i == -1) return; - - if (new_date < this.dates[i]){ - // Date being moved earlier/left - while (i>=0 && new_date < this.dates[i]){ - this.pickers[i--].setUTCDate(new_date); - } - } - else if (new_date > this.dates[i]){ - // Date being moved later/right - while (i this.dates[i]){ - this.pickers[i++].setUTCDate(new_date); - } - } - this.updateDates(); - }, - remove: function(){ - $.map(this.pickers, function(p){ p.remove(); }); - delete this.element.data().datepicker; - } - }; - - var old = $.fn.datepicker; - $.fn.datepicker = function ( option ) { - var args = Array.apply(null, arguments); - args.shift(); - return this.each(function () { - var $this = $(this), - data = $this.data('datepicker'), - options = typeof option == 'object' && option; - if (!data) { - if ($this.is('.input-daterange') || options.inputs){ - var opts = { - inputs: options.inputs || $this.find('input').toArray() - }; - $this.data('datepicker', (data = new DateRangePicker(this, $.extend(opts, $.fn.datepicker.defaults,options)))); - } - else{ - $this.data('datepicker', (data = new Datepicker(this, $.extend({}, $.fn.datepicker.defaults,options)))); - } - } - if (typeof option == 'string' && typeof data[option] == 'function') { - data[option].apply(data, args); - } - }); - }; - - $.fn.datepicker.defaults = { - }; - $.fn.datepicker.Constructor = Datepicker; - var dates = $.fn.datepicker.dates = { - en: { - days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"], - daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], - daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"], - months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], - monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], - today: "Today" - } - }; - - var DPGlobal = { - modes: [ - { - clsName: 'days', - navFnc: 'Month', - navStep: 1 - }, - { - clsName: 'months', - navFnc: 'FullYear', - navStep: 1 - }, - { - clsName: 'years', - navFnc: 'FullYear', - navStep: 10 - }], - isLeapYear: function (year) { - return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0)); - }, - getDaysInMonth: function (year, month) { - return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]; - }, - validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g, - nonpunctuation: /[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g, - parseFormat: function(format){ - // IE treats \0 as a string end in inputs (truncating the value), - // so it's a bad format delimiter, anyway - var separators = format.replace(this.validParts, '\0').split('\0'), - parts = format.match(this.validParts); - if (!separators || !separators.length || !parts || parts.length === 0){ - throw new Error("Invalid date format."); - } - return {separators: separators, parts: parts}; - }, - parseDate: function(date, format, language) { - if (date instanceof Date) return date; - if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(date)) { - var part_re = /([\-+]\d+)([dmwy])/, - parts = date.match(/([\-+]\d+)([dmwy])/g), - part, dir; - date = new Date(); - for (var i=0; i'+ - ''+ - ''+ - ''+ - ''+ - ''+ - '', - contTemplate: '', - footTemplate: '' - }; - DPGlobal.template = '
    '+ - '
    '+ - ''+ - DPGlobal.headTemplate+ - ''+ - DPGlobal.footTemplate+ - '
    '+ - '
    '+ - '
    '+ - ''+ - DPGlobal.headTemplate+ - DPGlobal.contTemplate+ - DPGlobal.footTemplate+ - '
    '+ - '
    '+ - '
    '+ - ''+ - DPGlobal.headTemplate+ - DPGlobal.contTemplate+ - DPGlobal.footTemplate+ - '
    '+ - '
    '+ - '
    '; - - $.fn.datepicker.DPGlobal = DPGlobal; - - - /* DATEPICKER NO CONFLICT - * =================== */ - - $.fn.datepicker.noConflict = function(){ - $.fn.datepicker = old; - return this; - }; - - - /* DATEPICKER DATA-API - * ================== */ - - $(document).on( - 'focus.datepicker.data-api click.datepicker.data-api', - '[data-provide="datepicker"]', - function(e){ - var $this = $(this); - if ($this.data('datepicker')) return; - e.preventDefault(); - // component click requires us to explicitly show it - $this.datepicker('show'); - } - ); - $(function(){ - $('[data-provide="datepicker-inline"]').datepicker(); - }); - -}( window.jQuery ); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js index 2d76f3a657..5eca3e1323 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js @@ -1,27 +1,46 @@ angular.module("umbraco").controller("Umbraco.Editors.DatepickerController", function ($scope, notificationsService, assetsService) { - - assetsService.loadJs( - 'views/propertyeditors/datepicker/bootstrap.datepicker.js' - ).then( - function () { - //The Datepicker js and css files are available and all components are ready to use. - // Get the id of the datepicker button that was clicked - var pickerId = $scope.model.alias; + //setup the default config + var config = { + pickDate: true, + pickTime: true, + format: "yyyy-MM-dd HH:mm:ss" + }; + //map the user config + angular.extend(config, $scope.model.config); + //map back to the model + $scope.model.config = config; - // Open the datepicker and add a changeDate eventlistener - $("#" + pickerId).datepicker({ - format: "dd/mm/yyyy", - autoclose: true - }).on("changeDate", function (e) { - // When a date is clicked the date is stored in model.value as a ISO 8601 date - $scope.model.value = e.date.toIsoDateTimeString(); + assetsService.loadJs( + 'views/propertyeditors/datepicker/bootstrap-datetimepicker.min.js' + ).then( + function () { + //The Datepicker js and css files are available and all components are ready to use. + + // Get the id of the datepicker button that was clicked + var pickerId = $scope.model.alias; + + // Open the datepicker and add a changeDate eventlistener + $("#" + pickerId).datetimepicker($scope.model.config).on("changeDate", function (e) { + // when a date is changed, update the model + if (e.localDate) { + if ($scope.model.config.format == "yyyy-MM-dd HH:mm:ss") { + $scope.model.value = e.localDate.toIsoDateTimeString(); + } + else { + $scope.model.value = e.localDate.toIsoDateString(); + } + } + else { + $scope.model.value = null; + } + + }); }); - }); - assetsService.loadCss( - 'views/propertyeditors/datepicker/bootstrap.datepicker.css' - ); -}); + assetsService.loadCss( + 'views/propertyeditors/datepicker/bootstrap-datetimepicker.min.css' + ); + }); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html index 3d88778673..25921f3c0e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html @@ -1,6 +1,11 @@
    -
    - - -
    -
    \ No newline at end of file +
    + + + + +
    + {{propertyForm.datepicker.errorMsg}} + diff --git a/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/Js/CsvEditor.js b/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/Js/CsvEditor.js index 6f22454786..2a45197e4a 100644 --- a/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/Js/CsvEditor.js +++ b/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/Js/CsvEditor.js @@ -7,8 +7,8 @@ var values = []; //this will be comma delimited - if ($scope.value && (typeof $scope.value == "string")) { - var splitVals = $scope.value.split(","); + if ($scope.model.value && (typeof $scope.model.value == "string")) { + var splitVals = $scope.model.value.split(","); //set the values of our object for (var i = 0; i < splitVals.length; i++) { values.push({ @@ -35,7 +35,7 @@ csv.push(newValue[v].value); } //write the csv value back to the property - $scope.value = csv.join(); + $scope.model.value = csv.join(); }, true); }; diff --git a/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/Views/CsvEditor.html b/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/Views/CsvEditor.html index f34dde1bfd..a301800a4d 100644 --- a/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/Views/CsvEditor.html +++ b/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/Views/CsvEditor.html @@ -11,5 +11,5 @@ - {{value}} + {{model.value}} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/Views/PostcodeEditor.html b/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/Views/PostcodeEditor.html index 8a4ac7f6be..1e2a9e525d 100644 --- a/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/Views/PostcodeEditor.html +++ b/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/Views/PostcodeEditor.html @@ -1,8 +1,8 @@ 

    - Enter a postcode for country {{config.country}} + Enter a postcode for country {{model.config.country}}

    - diff --git a/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/Views/RegexEditor.html b/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/Views/RegexEditor.html index 25c80e39c4..75e6633415 100644 --- a/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/Views/RegexEditor.html +++ b/src/Umbraco.Web.UI/App_Plugins/MyPackage/PropertyEditors/Views/RegexEditor.html @@ -1,10 +1,12 @@ 
    + {{model.value}} +

    - Value in the format of {{config}} + Value in the format of {{model.config}}

    diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index ad3a825bb2..3c774aef7e 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -114,9 +114,9 @@ False ..\packages\ClientDependency-Mvc.1.7.0.4\lib\ClientDependency.Core.Mvc.dll - + False - ..\packages\Examine.0.1.51.2941\lib\Examine.dll + ..\packages\Examine.0.1.52.2941\lib\Examine.dll False diff --git a/src/Umbraco.Web.UI/Umbraco/js/routes.js.BACKUP.83341.js b/src/Umbraco.Web.UI/Umbraco/js/routes.js.BACKUP.83341.js new file mode 100644 index 0000000000..4cf8930596 --- /dev/null +++ b/src/Umbraco.Web.UI/Umbraco/js/routes.js.BACKUP.83341.js @@ -0,0 +1,76 @@ +app.config(function ($routeProvider) { + $routeProvider + .when('/framed/:url', { + //This occurs when we need to launch some content in an iframe + templateUrl: function (rp) { + if (!rp.url) + throw "A framed resource must have a url route parameter"; + + return 'views/common/legacy.html'; + } + }) + .when('/:section', { + templateUrl: function (rp) { + if (rp.section === "default") + { + rp.section = "content"; + } + + rp.url = "dashboard.aspx?app=" + rp.section; + return 'views/common/legacy.html'; + } + }) + .when('/:section/:tree', { + templateUrl: function (rp) { + if (rp.section === "default") + { + rp.section = "content"; + } + + if (rp.tree === "") + { + rp.tree = "default"; + } + + rp.url = "dashboard.aspx?app=" + rp.section; + return 'views/common/legacy.html'; + } + }) + .when('/:section/:tree/:method', { + templateUrl: function(rp) { + if (!rp.method){ + return "views/common/dashboard.html"; + } + + if(rp.tree === "default" || rp.tree === ""){ + return 'views/' + rp.section + '/' + rp.method + '.html'; + }else{ + return 'views/' + rp.section + '/' + rp.tree + '/' + rp.method + '.html'; + } + } + }) + .when('/:section/:tree/:method/:id', { + templateUrl: function (rp) { +<<<<<<< HEAD + return 'views/' + rp.section + '/' + rp.tree + '/' + rp.method + '.html'; +======= + if (!rp.method) + return "views/common/dashboard.html"; + + return 'views/' + rp.section + '/' + rp.method + '.html'; +>>>>>>> 4a8026c902fd1f6afa32749882611c7e4d37e882 + } + }) + .otherwise({ redirectTo: '/content/document' }); + }).config(function ($locationProvider) { + + //$locationProvider.html5Mode(false).hashPrefix('!'); //turn html5 mode off + // $locationProvider.html5Mode(true); //turn html5 mode on +}); + + +app.run(['userService', function (userService) { + // Get the current user when the application starts + // (in case they are still logged in from a previous session) + userService.isAuthenticated(); +}]); diff --git a/src/Umbraco.Web.UI/Umbraco/js/routes.js.BASE.83341.js b/src/Umbraco.Web.UI/Umbraco/js/routes.js.BASE.83341.js new file mode 100644 index 0000000000..799f375557 --- /dev/null +++ b/src/Umbraco.Web.UI/Umbraco/js/routes.js.BASE.83341.js @@ -0,0 +1,56 @@ +app.config(function ($routeProvider) { + $routeProvider + .when('/:section', { + templateUrl: function (rp) { + if (rp.section === "default") + { + rp.section = "content"; + } + + rp.url = "dashboard.aspx?app=" + rp.section; + return 'views/common/legacy.html'; + } + }) + .when('/framed/:url', { + //This occurs when we need to launch some content in an iframe + templateUrl: function (rp) { + if (!rp.url) + throw "A framed resource must have a url route parameter"; + + return 'views/common/legacy.html'; + } + }) + .when('/:section/:method', { + templateUrl: function(rp) { + if (!rp.method) + return "views/common/dashboard.html"; + + return 'views/' + rp.section + '/' + rp.method + '.html'; + } + }) + .when('/:section/:method/:id', { + templateUrl: function (rp) { + if (!rp.method) + return "views/common/dashboard.html"; + + ////here we detect recycle bins, all recycle bins start with -2* (i.e. -20, -21) + //if (rp.id.startsWith("-2")) { + // return 'views/' + rp.section + '/recyclebin.html'; + //} + + return 'views/' + rp.section + '/' + rp.method + '.html'; + } + }) + .otherwise({ redirectTo: '/content' }); + }).config(function ($locationProvider) { + + //$locationProvider.html5Mode(false).hashPrefix('!'); //turn html5 mode off + // $locationProvider.html5Mode(true); //turn html5 mode on +}); + + +app.run(['userService', function (userService) { + // Get the current user when the application starts + // (in case they are still logged in from a previous session) + userService.isAuthenticated(); +}]); diff --git a/src/Umbraco.Web.UI/Umbraco/js/routes.js.LOCAL.83341.js b/src/Umbraco.Web.UI/Umbraco/js/routes.js.LOCAL.83341.js new file mode 100644 index 0000000000..34f194e94f --- /dev/null +++ b/src/Umbraco.Web.UI/Umbraco/js/routes.js.LOCAL.83341.js @@ -0,0 +1,69 @@ +app.config(function ($routeProvider) { + $routeProvider + .when('/framed/:url', { + //This occurs when we need to launch some content in an iframe + templateUrl: function (rp) { + if (!rp.url) + throw "A framed resource must have a url route parameter"; + + return 'views/common/legacy.html'; + } + }) + .when('/:section', { + templateUrl: function (rp) { + if (rp.section === "default") + { + rp.section = "content"; + } + + rp.url = "dashboard.aspx?app=" + rp.section; + return 'views/common/legacy.html'; + } + }) + .when('/:section/:tree', { + templateUrl: function (rp) { + if (rp.section === "default") + { + rp.section = "content"; + } + + if (rp.tree === "") + { + rp.tree = "default"; + } + + rp.url = "dashboard.aspx?app=" + rp.section; + return 'views/common/legacy.html'; + } + }) + .when('/:section/:tree/:method', { + templateUrl: function(rp) { + if (!rp.method){ + return "views/common/dashboard.html"; + } + + if(rp.tree === "default" || rp.tree === ""){ + return 'views/' + rp.section + '/' + rp.method + '.html'; + }else{ + return 'views/' + rp.section + '/' + rp.tree + '/' + rp.method + '.html'; + } + } + }) + .when('/:section/:tree/:method/:id', { + templateUrl: function (rp) { + return 'views/' + rp.section + '/' + rp.tree + '/' + rp.method + '.html'; + } + }) + .otherwise({ redirectTo: '/content/document' }); + }).config(function ($locationProvider) { + + //$locationProvider.html5Mode(false).hashPrefix('!'); //turn html5 mode off + // $locationProvider.html5Mode(true); //turn html5 mode on +}); + + +app.run(['userService', function (userService) { + // Get the current user when the application starts + // (in case they are still logged in from a previous session) + userService.isAuthenticated(); +}]); diff --git a/src/Umbraco.Web.UI/Umbraco/js/routes.js.REMOTE.83341.js b/src/Umbraco.Web.UI/Umbraco/js/routes.js.REMOTE.83341.js new file mode 100644 index 0000000000..3ff78c119b --- /dev/null +++ b/src/Umbraco.Web.UI/Umbraco/js/routes.js.REMOTE.83341.js @@ -0,0 +1,51 @@ +app.config(function ($routeProvider) { + $routeProvider + .when('/:section', { + templateUrl: function (rp) { + if (rp.section === "default") + { + rp.section = "content"; + } + + rp.url = "dashboard.aspx?app=" + rp.section; + return 'views/common/legacy.html'; + } + }) + .when('/framed/:url', { + //This occurs when we need to launch some content in an iframe + templateUrl: function (rp) { + if (!rp.url) + throw "A framed resource must have a url route parameter"; + + return 'views/common/legacy.html'; + } + }) + .when('/:section/:method', { + templateUrl: function(rp) { + if (!rp.method) + return "views/common/dashboard.html"; + + return 'views/' + rp.section + '/' + rp.method + '.html'; + } + }) + .when('/:section/:method/:id', { + templateUrl: function (rp) { + if (!rp.method) + return "views/common/dashboard.html"; + + return 'views/' + rp.section + '/' + rp.method + '.html'; + } + }) + .otherwise({ redirectTo: '/content' }); + }).config(function ($locationProvider) { + + //$locationProvider.html5Mode(false).hashPrefix('!'); //turn html5 mode off + // $locationProvider.html5Mode(true); //turn html5 mode on +}); + + +app.run(['userService', function (userService) { + // Get the current user when the application starts + // (in case they are still logged in from a previous session) + userService.isAuthenticated(); +}]); diff --git a/src/Umbraco.Web.UI/config/ExamineIndex.config b/src/Umbraco.Web.UI/config/ExamineIndex.config index 9cea8cb7c6..a853847ecb 100644 --- a/src/Umbraco.Web.UI/config/ExamineIndex.config +++ b/src/Umbraco.Web.UI/config/ExamineIndex.config @@ -26,6 +26,5 @@ More information and documentation can be found on CodePlex: http://umbracoexami - \ No newline at end of file diff --git a/src/Umbraco.Web.UI/config/ExamineSettings.config b/src/Umbraco.Web.UI/config/ExamineSettings.config index 6eb98eaa69..6744426c13 100644 --- a/src/Umbraco.Web.UI/config/ExamineSettings.config +++ b/src/Umbraco.Web.UI/config/ExamineSettings.config @@ -21,7 +21,7 @@ More information and documentation can be found on CodePlex: http://umbracoexami - + diff --git a/src/Umbraco.Web.UI/packages.config b/src/Umbraco.Web.UI/packages.config index bcc32a46e1..bbf37ffe8c 100644 --- a/src/Umbraco.Web.UI/packages.config +++ b/src/Umbraco.Web.UI/packages.config @@ -2,7 +2,7 @@ - + diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index c64e035b79..e18ca1c529 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -195,7 +195,9 @@ namespace Umbraco.Web.Editors contentItem.PersistedContent.ExpireDate = contentItem.ExpireDate; contentItem.PersistedContent.ReleaseDate = contentItem.ReleaseDate; //only set the template if it didn't change - if (contentItem.PersistedContent.Template.Alias != contentItem.TemplateAlias) + var templateChanged = (contentItem.PersistedContent.Template == null && contentItem.TemplateAlias.IsNullOrWhiteSpace() == false) + || (contentItem.PersistedContent.Template != null && contentItem.PersistedContent.Template.Alias != contentItem.TemplateAlias); + if (templateChanged) { var template = Services.FileService.GetTemplate(contentItem.TemplateAlias); if (template == null) diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs index 57bfcbe006..df1182ba9b 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs @@ -1,5 +1,4 @@ using System; -using System.ComponentModel.DataAnnotations; using System.Linq; using System.Runtime.Serialization; using System.Web.Http; @@ -32,20 +31,4 @@ namespace Umbraco.Web.Models.ContentEditing public string[] Urls { get; set; } } - - [DataContract(Name = "template", Namespace = "")] - public class TemplateBasic - { - [DataMember(Name = "id", IsRequired = true)] - [Required] - public int Id { get; set; } - - [DataMember(Name = "name", IsRequired = true)] - [Required(AllowEmptyStrings = false)] - public string Name { get; set; } - - [DataMember(Name = "alias", IsRequired = true)] - [Required(AllowEmptyStrings = false)] - public string Alias { get; set; } - } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/ContentPropertyDisplay.cs index ac01601e27..0e78f125c1 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentPropertyDisplay.cs @@ -22,7 +22,7 @@ namespace Umbraco.Web.Models.ContentEditing public string View { get; set; } [DataMember(Name = "config")] - public IEnumerable Config { get; set; } + public IDictionary Config { get; set; } [DataMember(Name = "hideLabel")] public bool HideLabel { get; set; } diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyDto.cs b/src/Umbraco.Web/Models/ContentEditing/ContentPropertyDto.cs index 42782b1b14..17f4f39680 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyDto.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentPropertyDto.cs @@ -12,7 +12,6 @@ namespace Umbraco.Web.Models.ContentEditing public IDataTypeDefinition DataType { get; set; } public string Label { get; set; } public string Description { get; set; } - public PropertyEditor PropertyEditor { get; set; } public bool IsRequired { get; set; } public string ValidationRegExp { get; set; } } diff --git a/src/Umbraco.Web/Models/ContentEditing/TemplateBasic.cs b/src/Umbraco.Web/Models/ContentEditing/TemplateBasic.cs new file mode 100644 index 0000000000..dbb3d6c8bd --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/TemplateBasic.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations; +using System.Runtime.Serialization; + +namespace Umbraco.Web.Models.ContentEditing +{ + [DataContract(Name = "template", Namespace = "")] + public class TemplateBasic + { + [DataMember(Name = "id", IsRequired = true)] + [Required] + public int Id { get; set; } + + [DataMember(Name = "name", IsRequired = true)] + [Required(AllowEmptyStrings = false)] + public string Name { get; set; } + + [DataMember(Name = "alias", IsRequired = true)] + [Required(AllowEmptyStrings = false)] + public string Alias { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs b/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs index ac8bb18b8b..32313cf75e 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs @@ -14,6 +14,11 @@ namespace Umbraco.Web.Models.Mapping internal class ContentPropertyBasicConverter : TypeConverter where T : ContentPropertyBasic, new() { + /// + /// Assigns the PropertyEditor, Id, Alias and Value to the property + /// + /// + /// protected override T ConvertCore(Property property) { var editor = PropertyEditorResolver.Current.GetById(property.PropertyType.DataTypeId); diff --git a/src/Umbraco.Web/Models/Mapping/ContentPropertyDisplayConverter.cs b/src/Umbraco.Web/Models/Mapping/ContentPropertyDisplayConverter.cs index c51ac48ae5..e149b1c5ef 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentPropertyDisplayConverter.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentPropertyDisplayConverter.cs @@ -1,6 +1,8 @@ -using Umbraco.Core; +using System.Linq; +using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Models; +using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Mapping @@ -25,7 +27,13 @@ namespace Umbraco.Web.Models.Mapping display.Alias = originalProp.Alias; display.Description = originalProp.PropertyType.Description; display.Label = originalProp.PropertyType.Name; - display.Config = _applicationContext.Services.DataTypeService.GetPreValuesByDataTypeId(originalProp.PropertyType.DataTypeDefinitionId); + var dataTypeService = (DataTypeService) _applicationContext.Services.DataTypeService; + + var preVals = dataTypeService.GetPreValuesCollectionByDataTypeId(originalProp.PropertyType.DataTypeDefinitionId); + + //let the property editor format the pre-values + display.Config = display.PropertyEditor.FormatPreValues(display.PropertyEditor.DefaultPreValues, preVals); + if (display.PropertyEditor == null) { //if there is no property editor it means that it is a legacy data type diff --git a/src/Umbraco.Web/Models/Mapping/ContentPropertyDtoConverter.cs b/src/Umbraco.Web/Models/Mapping/ContentPropertyDtoConverter.cs index 9fe38cc988..297406e10d 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentPropertyDtoConverter.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentPropertyDtoConverter.cs @@ -23,11 +23,9 @@ namespace Umbraco.Web.Models.Mapping propertyDto.IsRequired = originalProperty.PropertyType.Mandatory; propertyDto.ValidationRegExp = originalProperty.PropertyType.ValidationRegExp; - propertyDto.Alias = originalProperty.Alias; propertyDto.Description = originalProperty.PropertyType.Description; propertyDto.Label = originalProperty.PropertyType.Name; propertyDto.DataType = _applicationContext.Services.DataTypeService.GetDataTypeDefinitionById(originalProperty.PropertyType.DataTypeDefinitionId); - propertyDto.PropertyEditor = PropertyEditorResolver.Current.GetById(originalProperty.PropertyType.DataTypeId); return propertyDto; } diff --git a/src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs new file mode 100644 index 0000000000..135a76ee3a --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using Newtonsoft.Json.Linq; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Web.PropertyEditors +{ + [PropertyEditor(Constants.PropertyEditors.Date, "Date", "datepicker", ValueType = "DATE")] + public class DatePropertyEditor : PropertyEditor + { + public DatePropertyEditor() + { + _defaultPreVals = new Dictionary + { + {"format", "yyyy-MM-dd"}, + {"pickTime", false} + }; + } + + private IDictionary _defaultPreVals; + + /// + /// Overridden because we ONLY support Date (no time) format and we don't have pre-values in the db. + /// + public override IDictionary DefaultPreValues + { + get { return _defaultPreVals; } + set { _defaultPreVals = value; } + } + + protected override ValueEditor CreateValueEditor() + { + var baseEditor = base.CreateValueEditor(); + + return new DateValueEditor + { + View = baseEditor.View + }; + } + + /// + /// CUstom value editor so we can serialize with the correct date format (excluding time) + /// and includes the date validator + /// + private class DateValueEditor : ValueEditor + { + public DateValueEditor() + { + Validators = new List { new DateTimeValidator() }; + } + + public override string SerializeValue(object dbValue) + { + var date = dbValue.TryConvertTo(); + if (date.Success == false || date.Result == null) + { + return string.Empty; + } + //Dates will be formatted as yyyy-MM-dd + return date.Result.Value.ToString("yyyy-MM-dd"); + } + + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/PropertyEditors/DateTimePropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/DateTimePropertyEditor.cs new file mode 100644 index 0000000000..55198a36a1 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/DateTimePropertyEditor.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Web.PropertyEditors +{ + [PropertyEditor(Constants.PropertyEditors.DateTime, "Date/Time", "datepicker", ValueType = "DATETIME")] + public class DateTimePropertyEditor : PropertyEditor + { + public DateTimePropertyEditor() + { + _defaultPreVals = new Dictionary + { + {"format", "yyyy-MM-dd HH:mm:ss"} + }; + } + + private IDictionary _defaultPreVals; + + /// + /// Overridden because we ONLY support Date + Time format and we don't have pre-values in the db. + /// + public override IDictionary DefaultPreValues + { + get { return _defaultPreVals; } + set { _defaultPreVals = value; } + } + + protected override ValueEditor CreateValueEditor() + { + var editor = base.CreateValueEditor(); + + editor.Validators = new List { new DateTimeValidator() }; + + return editor; + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/PropertyEditors/DateTimeValidator.cs b/src/Umbraco.Web/PropertyEditors/DateTimeValidator.cs new file mode 100644 index 0000000000..c1f7277a05 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/DateTimeValidator.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core; + +namespace Umbraco.Web.PropertyEditors +{ + /// + /// Used to validate if the value is a valid date/time + /// + internal class DateTimeValidator : ValidatorBase + { + public override IEnumerable Validate(string value, string preValues, PropertyEditor editor) + { + DateTime dt; + if (value.IsNullOrWhiteSpace() == false && DateTime.TryParse(value, out dt) == false) + { + yield return new ValidationResult(string.Format("The string value {0} cannot be parsed into a DateTime", value), + new[] + { + //we only store a single value for this editor so the 'member' or 'field' + // we'll associate this error with will simply be called 'value' + "value" + }); + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 8244f001e9..168657080a 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -106,9 +106,9 @@ False ..\packages\xmlrpcnet.2.5.0\lib\net20\CookComputing.XmlRpcV2.dll - + False - ..\packages\Examine.0.1.51.2941\lib\Examine.dll + ..\packages\Examine.0.1.52.2941\lib\Examine.dll False @@ -299,7 +299,11 @@ + + + + diff --git a/src/Umbraco.Web/UmbracoModule.cs b/src/Umbraco.Web/UmbracoModule.cs index 90b5f978fc..f24c0199ba 100644 --- a/src/Umbraco.Web/UmbracoModule.cs +++ b/src/Umbraco.Web/UmbracoModule.cs @@ -35,6 +35,7 @@ namespace Umbraco.Web /// static void BeginRequest(HttpContextBase httpContext) { + //we need to set the initial url in our ApplicationContext, this is so our keep alive service works and this must //exist on a global context because the keep alive service doesn't run in a web context. //we are NOT going to put a lock on this because locking will slow down the application and we don't really care @@ -170,26 +171,34 @@ namespace Umbraco.Web var ticket = http.GetUmbracoAuthTicket(); if (ticket != null && !ticket.Expired && http.RenewUmbracoAuthTicket()) { - //create the Umbraco user identity - var identity = new UmbracoBackOfficeIdentity(ticket); - - //set the principal object - var principal = new GenericPrincipal(identity, identity.Roles); - - //It is actually not good enough to set this on the current app Context and the thread, it also needs - // to be set explicitly on the HttpContext.Current !! This is a strange web api thing that is actually - // an underlying fault of asp.net not propogating the User correctly. - if (HttpContext.Current != null) + try { - HttpContext.Current.User = principal; - } - app.Context.User = principal; - Thread.CurrentPrincipal = principal; + //create the Umbraco user identity + var identity = new UmbracoBackOfficeIdentity(ticket); - //This is a back office request, we will also set the culture/ui culture - Thread.CurrentThread.CurrentCulture = - Thread.CurrentThread.CurrentUICulture = - new System.Globalization.CultureInfo(identity.Culture); + //set the principal object + var principal = new GenericPrincipal(identity, identity.Roles); + + //It is actually not good enough to set this on the current app Context and the thread, it also needs + // to be set explicitly on the HttpContext.Current !! This is a strange web api thing that is actually + // an underlying fault of asp.net not propogating the User correctly. + if (HttpContext.Current != null) + { + HttpContext.Current.User = principal; + } + app.Context.User = principal; + Thread.CurrentPrincipal = principal; + + //This is a back office request, we will also set the culture/ui culture + Thread.CurrentThread.CurrentCulture = + Thread.CurrentThread.CurrentUICulture = + new System.Globalization.CultureInfo(identity.Culture); + } + catch (FormatException) + { + //this will occur if the cookie data is invalid + http.UmbracoLogout(); + } } } diff --git a/src/Umbraco.Web/packages.config b/src/Umbraco.Web/packages.config index 5e1aa8e393..5a3333a50d 100644 --- a/src/Umbraco.Web/packages.config +++ b/src/Umbraco.Web/packages.config @@ -3,7 +3,7 @@ - + diff --git a/src/UmbracoExamine.Azure/UmbracoExamine.Azure.csproj b/src/UmbracoExamine.Azure/UmbracoExamine.Azure.csproj index c697022db3..db7b95bd7a 100644 --- a/src/UmbracoExamine.Azure/UmbracoExamine.Azure.csproj +++ b/src/UmbracoExamine.Azure/UmbracoExamine.Azure.csproj @@ -40,9 +40,9 @@ ..\packages\AzureDirectory.1.0.5\lib\AzureDirectory.dll - + False - ..\packages\Examine.0.1.51.2941\lib\Examine.dll + ..\packages\Examine.0.1.52.2941\lib\Examine.dll False diff --git a/src/UmbracoExamine.Azure/packages.config b/src/UmbracoExamine.Azure/packages.config index 7721836e09..54eb3830da 100644 --- a/src/UmbracoExamine.Azure/packages.config +++ b/src/UmbracoExamine.Azure/packages.config @@ -2,7 +2,7 @@ - + diff --git a/src/UmbracoExamine.PDF.Azure/UmbracoExamine.PDF.Azure.csproj b/src/UmbracoExamine.PDF.Azure/UmbracoExamine.PDF.Azure.csproj index f136c096b3..d17a4c4921 100644 --- a/src/UmbracoExamine.PDF.Azure/UmbracoExamine.PDF.Azure.csproj +++ b/src/UmbracoExamine.PDF.Azure/UmbracoExamine.PDF.Azure.csproj @@ -40,9 +40,9 @@ ..\packages\AzureDirectory.1.0.5\lib\AzureDirectory.dll - + False - ..\packages\Examine.0.1.51.2941\lib\Examine.dll + ..\packages\Examine.0.1.52.2941\lib\Examine.dll False diff --git a/src/UmbracoExamine.PDF.Azure/packages.config b/src/UmbracoExamine.PDF.Azure/packages.config index 7721836e09..54eb3830da 100644 --- a/src/UmbracoExamine.PDF.Azure/packages.config +++ b/src/UmbracoExamine.PDF.Azure/packages.config @@ -2,7 +2,7 @@ - + diff --git a/src/UmbracoExamine.PDF/PDFIndexer.cs b/src/UmbracoExamine.PDF/PDFIndexer.cs index 8f8237cbbb..475a75c3ce 100644 --- a/src/UmbracoExamine.PDF/PDFIndexer.cs +++ b/src/UmbracoExamine.PDF/PDFIndexer.cs @@ -12,6 +12,7 @@ using iTextSharp.text.pdf; using System.Text; using Lucene.Net.Analysis; using UmbracoExamine.DataServices; +using iTextSharp.text.pdf.parser; namespace UmbracoExamine.PDF @@ -105,6 +106,7 @@ namespace UmbracoExamine.PDF ///
  • /// /// + [SecuritySafeCritical] public override void Initialize(string name, NameValueCollection config) { base.Initialize(name, config); @@ -133,7 +135,7 @@ namespace UmbracoExamine.PDF Action onError = (e) => OnIndexingError(new IndexingErrorEventArgs("Could not read PDF", -1, e)); - var txt = pdf.ParsePdfText(file.FullName, onError); + var txt = pdf.GetTextFromAllPages(file.FullName, onError); return txt; } @@ -193,16 +195,21 @@ namespace UmbracoExamine.PDF static PDFParser() { - lock (m_Locker) + lock (Locker) { - m_UnsupportedRange = new List(); - m_UnsupportedRange.AddRange(Enumerable.Range(0x0000, 0x001F)); - m_UnsupportedRange.Add(0x1F); + UnsupportedRange = new HashSet(); + foreach (var c in Enumerable.Range(0x0000, 0x001F)) + { + UnsupportedRange.Add((char) c); + } + UnsupportedRange.Add((char)0x1F); + //replace line breaks with space + ReplaceWithSpace = new HashSet {'\r', '\n'}; } } - private static readonly object m_Locker = new object(); + private static readonly object Locker = new object(); /// /// Stores the unsupported range of character @@ -214,61 +221,68 @@ namespace UmbracoExamine.PDF /// http://en.wikipedia.org/wiki/Unicode /// http://en.wikipedia.org/wiki/Basic_Multilingual_Plane /// - private static List m_UnsupportedRange; + private static HashSet UnsupportedRange; - /// - /// Return only the valid string contents of the PDF - /// - /// - /// - /// - [SecuritySafeCritical] - public string ParsePdfText(string sourcePDF, Action onError) + private static HashSet ReplaceWithSpace; + + [SecuritySafeCritical] + public string GetTextFromAllPages(string pdfPath, Action onError) { - var sb = new StringBuilder(); + var output = new StringWriter(); - var reader = new PdfReader(sourcePDF); - PRTokeniser token = null; - var tknValue = String.Empty; - - for (var i = 1; (i <= reader.NumberOfPages); i++) + try { - var pageBytes = reader.GetPageContent(i); - if (pageBytes != null) - { - token = new PRTokeniser(pageBytes); - try - { - while (token.NextToken()) - { - var tknType = token.TokenType; - tknValue = token.StringValue; - if ((tknType == PRTokeniser.TokType.STRING)) - { - foreach (var s in tknValue) - { - //strip out unsupported characters, based on unicode tables. - if (!m_UnsupportedRange.Contains(s)) - { - sb.Append(s); - } - } + var reader = new PdfReader(pdfPath); - } - } - } - catch (InvalidPdfException ex) - { - onError(ex); - } + for (int i = 1; i <= reader.NumberOfPages; i++) + { + var result = + ExceptChars( + PdfTextExtractor.GetTextFromPage(reader, i, new SimpleTextExtractionStrategy()), + UnsupportedRange, + ReplaceWithSpace); + output.Write(result); } } + catch (Exception ex) + { + onError(ex); + } - return sb.ToString(); + return output.ToString(); } } + + /// + /// remove all toExclude chars from string + /// + /// + /// + /// + /// + private static string ExceptChars(string str, HashSet toExclude, HashSet replaceWithSpace) + { + var sb = new StringBuilder(str.Length); + for (var i = 0; i < str.Length; i++) + { + var c = str[i]; + if (toExclude.Contains(c) == false) + { + if (replaceWithSpace.Contains(c)) + { + sb.Append(" "); + } + else + { + sb.Append(c); + } + } + + } + return sb.ToString(); + } #endregion } diff --git a/src/UmbracoExamine.PDF/Properties/AssemblyInfo.cs b/src/UmbracoExamine.PDF/Properties/AssemblyInfo.cs index 72fe9de54a..04b415b98d 100644 --- a/src/UmbracoExamine.PDF/Properties/AssemblyInfo.cs +++ b/src/UmbracoExamine.PDF/Properties/AssemblyInfo.cs @@ -28,4 +28,5 @@ using System.Security; [assembly: AssemblyVersion("0.6.0.*")] [assembly: AssemblyFileVersion("0.6.0.*")] -[assembly: AllowPartiallyTrustedCallers] \ No newline at end of file +//Unfortunately itextsharp does not natively support full trust +//[assembly: AllowPartiallyTrustedCallers] \ No newline at end of file diff --git a/src/UmbracoExamine.PDF/UmbracoExamine.PDF.csproj b/src/UmbracoExamine.PDF/UmbracoExamine.PDF.csproj index b5a4cca007..6bdc3d3666 100644 --- a/src/UmbracoExamine.PDF/UmbracoExamine.PDF.csproj +++ b/src/UmbracoExamine.PDF/UmbracoExamine.PDF.csproj @@ -46,9 +46,9 @@ false - + False - ..\packages\Examine.0.1.51.2941\lib\Examine.dll + ..\packages\Examine.0.1.52.2941\lib\Examine.dll ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll diff --git a/src/UmbracoExamine.PDF/packages.config b/src/UmbracoExamine.PDF/packages.config index abf2bfc49c..1d29bc3cc6 100644 --- a/src/UmbracoExamine.PDF/packages.config +++ b/src/UmbracoExamine.PDF/packages.config @@ -1,6 +1,6 @@  - + diff --git a/src/UmbracoExamine/BaseUmbracoIndexer.cs b/src/UmbracoExamine/BaseUmbracoIndexer.cs index 217eac73ec..8ccef8b22e 100644 --- a/src/UmbracoExamine/BaseUmbracoIndexer.cs +++ b/src/UmbracoExamine/BaseUmbracoIndexer.cs @@ -96,6 +96,7 @@ namespace UmbracoExamine /// /// /// + [SecuritySafeCritical] public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config) { if (config["dataService"] != null && !string.IsNullOrEmpty(config["dataService"])) diff --git a/src/UmbracoExamine/UmbracoContentIndexer.cs b/src/UmbracoExamine/UmbracoContentIndexer.cs index 81a9dbe41b..9da53a77f2 100644 --- a/src/UmbracoExamine/UmbracoContentIndexer.cs +++ b/src/UmbracoExamine/UmbracoContentIndexer.cs @@ -124,6 +124,7 @@ namespace UmbracoExamine /// /// An attempt is made to call on a provider after the provider has already been initialized. /// + [SecuritySafeCritical] public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config) { diff --git a/src/UmbracoExamine/UmbracoExamine.csproj b/src/UmbracoExamine/UmbracoExamine.csproj index aeb3a0ce05..5db7139a00 100644 --- a/src/UmbracoExamine/UmbracoExamine.csproj +++ b/src/UmbracoExamine/UmbracoExamine.csproj @@ -82,9 +82,9 @@ ..\Solution Items\TheFARM-Public.snk - + False - ..\packages\Examine.0.1.51.2941\lib\Examine.dll + ..\packages\Examine.0.1.52.2941\lib\Examine.dll False diff --git a/src/UmbracoExamine/UmbracoExamineSearcher.cs b/src/UmbracoExamine/UmbracoExamineSearcher.cs index 68f07f45e1..7406e98f8e 100644 --- a/src/UmbracoExamine/UmbracoExamineSearcher.cs +++ b/src/UmbracoExamine/UmbracoExamineSearcher.cs @@ -45,6 +45,7 @@ namespace UmbracoExamine } } + [SecuritySafeCritical] public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config) { if (name == null) throw new ArgumentNullException("name"); diff --git a/src/UmbracoExamine/packages.config b/src/UmbracoExamine/packages.config index 0d7f689f95..31026832a5 100644 --- a/src/UmbracoExamine/packages.config +++ b/src/UmbracoExamine/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/src/umbraco.MacroEngines/packages.config b/src/umbraco.MacroEngines/packages.config index 023ebfc6b9..404506a7aa 100644 --- a/src/umbraco.MacroEngines/packages.config +++ b/src/umbraco.MacroEngines/packages.config @@ -1,6 +1,6 @@  - + diff --git a/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj b/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj index d6fecdbaca..1b028c6901 100644 --- a/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj +++ b/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj @@ -45,9 +45,9 @@ false - + False - ..\packages\Examine.0.1.51.2941\lib\Examine.dll + ..\packages\Examine.0.1.52.2941\lib\Examine.dll False diff --git a/src/umbraco.businesslogic/ui.cs b/src/umbraco.businesslogic/ui.cs index cfb0a1832d..4416ed2451 100644 --- a/src/umbraco.businesslogic/ui.cs +++ b/src/umbraco.businesslogic/ui.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Text.RegularExpressions; using System.Threading; using System.Web; @@ -308,27 +309,35 @@ namespace umbraco { var cacheKey = "uitext_" + language; - return ApplicationContext.Current.ApplicationCache.GetCacheItem( - cacheKey, - CacheItemPriority.Default, - new CacheDependency(IOHelper.MapPath(UmbracoPath + "/config/lang/" + language + ".xml")), - () => - { - using (var langReader = new XmlTextReader(IOHelper.MapPath(UmbracoPath + "/config/lang/" + language + ".xml"))) + var file = IOHelper.MapPath(UmbracoPath + "/config/lang/" + language + ".xml"); + if (File.Exists(file)) + { + return ApplicationContext.Current.ApplicationCache.GetCacheItem( + cacheKey, + CacheItemPriority.Default, + new CacheDependency(IOHelper.MapPath(UmbracoPath + "/config/lang/" + language + ".xml")), + () => { - try + using (var langReader = new XmlTextReader(IOHelper.MapPath(UmbracoPath + "/config/lang/" + language + ".xml"))) { - var langFile = new XmlDocument(); - langFile.Load(langReader); - return langFile; + try + { + var langFile = new XmlDocument(); + langFile.Load(langReader); + return langFile; + } + catch (Exception e) + { + LogHelper.Error("Error reading umbraco language xml source (" + language + ")", e); + return null; + } } - catch (Exception e) - { - LogHelper.Error("Error reading umbraco language xml source (" + language + ")", e); - return null; - } - } - }); + }); + } + else + { + return null; + } } diff --git a/src/umbraco.editorControls/BaseTreePickerEditor.cs b/src/umbraco.editorControls/BaseTreePickerEditor.cs index 0984a2375b..9908d07e46 100644 --- a/src/umbraco.editorControls/BaseTreePickerEditor.cs +++ b/src/umbraco.editorControls/BaseTreePickerEditor.cs @@ -106,7 +106,7 @@ namespace umbraco.editorControls public void Save() { if (ItemIdValue.Value.Trim() != "") - _data.Value = ItemIdValue.Value.Trim(); + _data.Value = int.Parse(ItemIdValue.Value.Trim()); else _data.Value = null; } diff --git a/src/umbraco.editorControls/MultiNodeTreePicker/MNTP_DataEditor.cs b/src/umbraco.editorControls/MultiNodeTreePicker/MNTP_DataEditor.cs index b002218bd8..979eb06e80 100644 --- a/src/umbraco.editorControls/MultiNodeTreePicker/MNTP_DataEditor.cs +++ b/src/umbraco.editorControls/MultiNodeTreePicker/MNTP_DataEditor.cs @@ -10,6 +10,7 @@ using System.Xml.Linq; using ClientDependency.Core; using Umbraco.Core; using Umbraco.Core.IO; +using umbraco.BusinessLogic; using umbraco.cms.presentation.Trees; using umbraco.controls.Images; using umbraco.controls.Tree; @@ -45,7 +46,15 @@ namespace umbraco.editorControls.MultiNodeTreePicker //need to add our tree definitions to the collection. //find the content tree to duplicate - var contentTree = TreeDefinitionCollection.Instance.Single(x => string.Equals(x.Tree.Alias, Umbraco.Core.Constants.Applications.Content, StringComparison.OrdinalIgnoreCase)); + var contentTree = TreeDefinitionCollection.Instance.SingleOrDefault(x => string.Equals(x.Tree.Alias, Umbraco.Core.Constants.Applications.Content, StringComparison.OrdinalIgnoreCase)); + //have put this here because the legacy content tree no longer exists as a tree def + if (contentTree == null) + { + contentTree = new TreeDefinition( + typeof (loadContent), + new ApplicationTree(false, true, 0, "content", "content", "Content", ".sprTreeFolder", ".sprTreeFolder_o", "", typeof (loadContent).GetFullNameWithAssembly(), ""), + new Application("Content", "content", "content")); + } var filteredContentTree = new TreeDefinition(typeof(FilteredContentTree), new umbraco.BusinessLogic.ApplicationTree(true, false, 0, contentTree.Tree.ApplicationAlias, @@ -59,7 +68,15 @@ namespace umbraco.editorControls.MultiNodeTreePicker contentTree.App); //find the media tree to duplicate - var mediaTree = TreeDefinitionCollection.Instance.Single(x => string.Equals(x.Tree.Alias, Umbraco.Core.Constants.Applications.Media, StringComparison.OrdinalIgnoreCase)); + var mediaTree = TreeDefinitionCollection.Instance.SingleOrDefault(x => string.Equals(x.Tree.Alias, Umbraco.Core.Constants.Applications.Media, StringComparison.OrdinalIgnoreCase)); + //have put this here because the legacy content tree no longer exists as a tree def + if (mediaTree == null) + { + mediaTree = new TreeDefinition( + typeof(loadMedia), + new ApplicationTree(false, true, 0, "media", "media", "Media", ".sprTreeFolder", ".sprTreeFolder_o", "", typeof(loadMedia).GetFullNameWithAssembly(), ""), + new Application("Media", "media", "media")); + } var filteredMediaTree = new TreeDefinition(typeof(FilteredMediaTree), new umbraco.BusinessLogic.ApplicationTree(true, false, 0, mediaTree.Tree.ApplicationAlias,