From 363de53b82c1d940d15d6530c2e723971efb6a04 Mon Sep 17 00:00:00 2001 From: Morten Christensen Date: Mon, 12 Aug 2013 12:42:21 +0200 Subject: [PATCH 01/11] Updates query to limit results in subquery for MySql according to U4-2329 Browsing Media Slow in 6.1.1 --- .../Repositories/EntityRepository.cs | 37 +++++++++---------- 1 file changed, 18 insertions(+), 19 deletions(-) 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; From 86811fe5075ec3d5ce0d2e0bc97260857c19a252 Mon Sep 17 00:00:00 2001 From: Morten Christensen Date: Mon, 12 Aug 2013 16:20:32 +0200 Subject: [PATCH 02/11] Fixes U4-2397 IContent Properties added with incorrect Value when read from Cache, and adds unit test for verifying types saved and returned for 24 standard DataTypes. --- src/Umbraco.Core/Models/ContentBase.cs | 5 ++- .../Migrations/Initial/BaseDataCreation.cs | 20 --------- .../Services/ContentServiceTests.cs | 42 +++++++++++++++++ .../TestHelpers/Entities/MockedContent.cs | 32 +++++++++++++ .../Entities/MockedContentTypes.cs | 45 +++++++++++++++++++ .../BaseTreePickerEditor.cs | 2 +- 6 files changed, 124 insertions(+), 22 deletions(-) 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/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.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/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 131e02dad1..f3f09e5fae 100644 --- a/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs +++ b/src/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs @@ -166,6 +166,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.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; } From c52c452b3633db0bdcb21ebee477684e49a5f428 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 13 Aug 2013 11:01:49 +1000 Subject: [PATCH 03/11] Fixes issue relating to the fix for U4-2589 where we need to clear the published db flag for previous versions when creating a new published version. --- src/Umbraco.Core/Models/ContentExtensions.cs | 47 ++++++++++++++++ .../Repositories/ContentRepository.cs | 3 +- .../Models/ContentExtensionsTests.cs | 55 +++++++++++++++++++ 3 files changed, 104 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Models/ContentExtensions.cs b/src/Umbraco.Core/Models/ContentExtensions.cs index c08983c866..f88fe7f4d3 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/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index 954eac582a..d02c48b639 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -328,7 +328,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.Tests/Models/ContentExtensionsTests.cs b/src/Umbraco.Tests/Models/ContentExtensionsTests.cs index 0e0cd41c15..fa9797e155 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 From aa1c411c8cef2bba32943e0da7098edde896744f Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 13 Aug 2013 13:47:57 +1000 Subject: [PATCH 04/11] Fixes U4-2618 PDF indexer does not ensure spaces are added to textual content and fixes med trust compat relating to U4-2180 Problem with Examine MultiIndexSearcher after upgrading from 4.11.3 to 4.11.8 --- src/Umbraco.Core/StringExtensions.cs | 10 ++ src/UmbracoExamine.PDF/PDFIndexer.cs | 112 +++++++++++-------- src/UmbracoExamine/BaseUmbracoIndexer.cs | 1 + src/UmbracoExamine/UmbracoContentIndexer.cs | 1 + src/UmbracoExamine/UmbracoExamineSearcher.cs | 1 + 5 files changed, 76 insertions(+), 49 deletions(-) diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs index 33a5730444..df7c0d07b0 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(); + } + /// /// Encrypt the string using the MachineKey in medium trust /// 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/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/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"); From bfabfd9fa5f50a83be0ac5b35b1eb9888f88129c Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 13 Aug 2013 15:41:40 +1000 Subject: [PATCH 05/11] Updated to latest Examine version (contains bug fix for U4-2180 Problem with Examine MultiIndexSearcher after upgrading from 4.11.3 to 4.11.8) and removes the partially trusted callers attribute from the PDF lib because itextsharp by default doesn't support med trust. --- src/Umbraco.Tests/Umbraco.Tests.csproj | 4 +-- src/Umbraco.Tests/packages.config | 2 +- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 4 +-- src/Umbraco.Web.UI/config/ExamineIndex.config | 3 +++ .../config/ExamineSettings.config | 26 ++++++++++++++++++- src/Umbraco.Web.UI/packages.config | 2 +- src/Umbraco.Web/Umbraco.Web.csproj | 4 +-- src/Umbraco.Web/packages.config | 2 +- .../UmbracoExamine.Azure.csproj | 4 +-- src/UmbracoExamine.Azure/packages.config | 2 +- .../UmbracoExamine.PDF.Azure.csproj | 4 +-- src/UmbracoExamine.PDF.Azure/packages.config | 2 +- .../Properties/AssemblyInfo.cs | 3 ++- .../UmbracoExamine.PDF.csproj | 4 +-- src/UmbracoExamine.PDF/packages.config | 2 +- src/UmbracoExamine/UmbracoExamine.csproj | 4 +-- src/UmbracoExamine/packages.config | 2 +- src/umbraco.MacroEngines/packages.config | 2 +- .../umbraco.MacroEngines.csproj | 4 +-- 19 files changed, 54 insertions(+), 26 deletions(-) diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index d95b720b73..72950fe466 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -48,9 +48,9 @@ 4 - + 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.Tests/packages.config b/src/Umbraco.Tests/packages.config index f1e2902ce8..fbf89daf7d 100644 --- a/src/Umbraco.Tests/packages.config +++ b/src/Umbraco.Tests/packages.config @@ -1,6 +1,6 @@  - + diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index 4fe113f747..c4c0ef70f4 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -108,9 +108,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/config/ExamineIndex.config b/src/Umbraco.Web.UI/config/ExamineIndex.config index 9cea8cb7c6..8a2942eb00 100644 --- a/src/Umbraco.Web.UI/config/ExamineIndex.config +++ b/src/Umbraco.Web.UI/config/ExamineIndex.config @@ -27,5 +27,8 @@ 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..79f6b08914 100644 --- a/src/Umbraco.Web.UI/config/ExamineSettings.config +++ b/src/Umbraco.Web.UI/config/ExamineSettings.config @@ -21,7 +21,25 @@ More information and documentation can be found on CodePlex: http://umbracoexami - + + + + + + + @@ -34,6 +52,12 @@ More information and documentation can be found on CodePlex: http://umbracoexami analyzer="Lucene.Net.Analysis.Standard.StandardAnalyzer, Lucene.Net" enableLeadingWildcards="true"/> + + + + + + 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/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index c804a064ee..96bb6f800a 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -101,9 +101,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 diff --git a/src/Umbraco.Web/packages.config b/src/Umbraco.Web/packages.config index 69c5c12585..eec795bd35 100644 --- a/src/Umbraco.Web/packages.config +++ b/src/Umbraco.Web/packages.config @@ -2,7 +2,7 @@ - + diff --git a/src/UmbracoExamine.Azure/UmbracoExamine.Azure.csproj b/src/UmbracoExamine.Azure/UmbracoExamine.Azure.csproj index 1cdc604a52..9bb434a10d 100644 --- a/src/UmbracoExamine.Azure/UmbracoExamine.Azure.csproj +++ b/src/UmbracoExamine.Azure/UmbracoExamine.Azure.csproj @@ -37,9 +37,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 5e3b5cb567..861014d3cc 100644 --- a/src/UmbracoExamine.PDF.Azure/UmbracoExamine.PDF.Azure.csproj +++ b/src/UmbracoExamine.PDF.Azure/UmbracoExamine.PDF.Azure.csproj @@ -37,9 +37,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/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 0aadf1404d..9fc85d46a0 100644 --- a/src/UmbracoExamine.PDF/UmbracoExamine.PDF.csproj +++ b/src/UmbracoExamine.PDF/UmbracoExamine.PDF.csproj @@ -44,9 +44,9 @@ bin\Release\UmbracoExamine.PDF.XML - + 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/UmbracoExamine.csproj b/src/UmbracoExamine/UmbracoExamine.csproj index 3e6a6ed750..d70780b678 100644 --- a/src/UmbracoExamine/UmbracoExamine.csproj +++ b/src/UmbracoExamine/UmbracoExamine.csproj @@ -80,9 +80,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/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 90f8e01edc..3b6be4351d 100644 --- a/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj +++ b/src/umbraco.MacroEngines/umbraco.MacroEngines.csproj @@ -42,9 +42,9 @@ bin\Release\umbraco.MacroEngines.xml - + False - ..\packages\Examine.0.1.51.2941\lib\Examine.dll + ..\packages\Examine.0.1.52.2941\lib\Examine.dll False From 599caeb9339bacc783913fa459003e201f71d7b8 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 14 Aug 2013 10:08:59 +1000 Subject: [PATCH 06/11] removed test examine settings --- src/Umbraco.Web.UI.Client/src/routes.js | 5 ---- src/Umbraco.Web.UI/config/ExamineIndex.config | 4 ---- .../config/ExamineSettings.config | 24 ------------------- 3 files changed, 33 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/routes.js b/src/Umbraco.Web.UI.Client/src/routes.js index 799f375557..3ff78c119b 100644 --- a/src/Umbraco.Web.UI.Client/src/routes.js +++ b/src/Umbraco.Web.UI.Client/src/routes.js @@ -33,11 +33,6 @@ app.config(function ($routeProvider) { 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'; } }) diff --git a/src/Umbraco.Web.UI/config/ExamineIndex.config b/src/Umbraco.Web.UI/config/ExamineIndex.config index 8a2942eb00..a853847ecb 100644 --- a/src/Umbraco.Web.UI/config/ExamineIndex.config +++ b/src/Umbraco.Web.UI/config/ExamineIndex.config @@ -26,9 +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 79f6b08914..6744426c13 100644 --- a/src/Umbraco.Web.UI/config/ExamineSettings.config +++ b/src/Umbraco.Web.UI/config/ExamineSettings.config @@ -22,24 +22,6 @@ More information and documentation can be found on CodePlex: http://umbracoexami - - - - - - @@ -52,12 +34,6 @@ More information and documentation can be found on CodePlex: http://umbracoexami analyzer="Lucene.Net.Analysis.Standard.StandardAnalyzer, Lucene.Net" enableLeadingWildcards="true"/> - - - - - - From 4a7fbfa3fa6a6ab53b3279a4da08c404709c97be Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 14 Aug 2013 11:08:40 +1000 Subject: [PATCH 07/11] Fixes the datepicker control, updates it so that it supports time and a format that is configurable (pre-values) fixes toIsoDateTimeString() method. --- .../lib/umbraco/Extensions.js | 2 +- .../src/common/mocks/resources/_utils.js | 2 +- .../bootstrap-datetimepicker.min.css | 8 + .../bootstrap-datetimepicker.min.js | 26 + .../datepicker/bootstrap.datepicker.css | 18 - .../datepicker/bootstrap.datepicker.js | 1174 ----------------- .../datepicker/datepicker.controller.js | 54 +- .../datepicker/datepicker.html | 12 +- 8 files changed, 77 insertions(+), 1219 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/bootstrap-datetimepicker.min.css create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/bootstrap-datetimepicker.min.js delete mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/bootstrap.datepicker.css delete mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/bootstrap.datepicker.js diff --git a/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js b/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js index ca4833584b..43e957d6ed 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; } 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/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..da111b9d5b 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,41 @@ 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) { + $scope.model.value = e.localDate.toIsoDateTimeString(); + } + 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..3c1b9e3372 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,8 @@
-
- - -
-
\ No newline at end of file +
+ + + + +
+ From c3619d9ac59befe83a297bb79f24b7f1d3c6db04 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 14 Aug 2013 13:38:28 +1000 Subject: [PATCH 08/11] Fixes plugin mgr to not resolve types that shouldn't be resolved. Fixes mntp legacy editor to not throw exceptions when initializing because the legacy tree no longer exists. Fixes authentication when an invalid cookie is detected. --- src/Umbraco.Core/PluginManager.cs | 8 +++- .../PropertyEditors/PreValueEditor.cs | 6 +++ .../PropertyEditors/PropertyEditor.cs | 8 ++++ .../PropertyEditors/ValueEditor.cs | 3 ++ src/Umbraco.Web/UmbracoModule.cs | 45 +++++++++++-------- .../MultiNodeTreePicker/MNTP_DataEditor.cs | 21 ++++++++- 6 files changed, 69 insertions(+), 22 deletions(-) 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..650c41a613 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyEditor.cs @@ -1,9 +1,17 @@ using System; +using System.Collections.Generic; 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 { /// diff --git a/src/Umbraco.Core/PropertyEditors/ValueEditor.cs b/src/Umbraco.Core/PropertyEditors/ValueEditor.cs index c0d1aa39ec..0cc3fe60a6 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 { /// 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.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, From 6d3008b0530ac30672ed85548c38d75b0c7b7058 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 14 Aug 2013 17:52:16 +1000 Subject: [PATCH 09/11] Fixed up some more validation issues with the reshuffling of how properties are rendered. --- .../src/common/directives/umbeditor.directive.js | 9 ++++++++- .../src/common/directives/valpropertymsg.directive.js | 4 +++- .../src/common/directives/valserver.directive.js | 2 -- .../src/common/directives/valserverfield.directive.js | 2 -- .../src/common/directives/valtab.directive.js | 1 + .../src/common/directives/valtogglemsg.directive.js | 5 ++++- .../MyPackage/PropertyEditors/Js/CsvEditor.js | 6 +++--- .../MyPackage/PropertyEditors/Views/CsvEditor.html | 2 +- .../MyPackage/PropertyEditors/Views/PostcodeEditor.html | 4 ++-- .../MyPackage/PropertyEditors/Views/RegexEditor.html | 6 ++++-- 10 files changed, 26 insertions(+), 15 deletions(-) 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/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/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}}

From 3e675ff2cce79353a5793fab10d5772a415b8660 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 14 Aug 2013 19:24:20 +1000 Subject: [PATCH 10/11] Converted over pre-value config to be a dictionary, not an array. To do that I needed to create a new PreValueCollection object which encapsulates support for legacy (array based) pre-vals which get converted to a dictionary and newer dictionary pre-vals... which we will now require because we'll support pre-value overrides eventually. Fixed up the mapper unit tests and added other unit tests. --- src/Umbraco.Core/Models/PreValueCollection.cs | 64 +++++++++++++ .../ObjectResolution/Resolution.cs | 30 ++++--- .../PropertyEditors/PropertyEditor.cs | 57 +++++++++++- .../PropertyEditors/ValueEditor.cs | 14 ++- src/Umbraco.Core/Services/DataTypeService.cs | 52 +++++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../ContentModelSerializationTests.cs | 5 +- .../Mapping/ContentWebModelMappingTests.cs | 31 +++++-- .../PropertyEditorValueConverterTests.cs | 3 +- .../PropertyEditorValueEditorTests.cs | 72 +++++++++++++++ .../Services/DataTypeServiceTests.cs | 2 + .../Services/PreValueConverterTests.cs | 90 +++++++++++++++++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 4 +- .../directives/umbtreeitem.directive.js | 4 +- .../datepicker/datepicker.html | 7 +- src/Umbraco.Web/Editors/ContentController.cs | 4 +- .../ContentEditing/ContentItemDisplay.cs | 17 ---- .../ContentEditing/ContentPropertyDisplay.cs | 2 +- .../ContentEditing/ContentPropertyDto.cs | 1 - .../Models/ContentEditing/TemplateBasic.cs | 21 +++++ .../Mapping/ContentPropertyBasicConverter.cs | 5 ++ .../ContentPropertyDisplayConverter.cs | 12 ++- .../Mapping/ContentPropertyDtoConverter.cs | 2 - .../PropertyEditors/DatePropertyEditor.cs | 22 +++++ .../PropertyEditors/DateTimePropertyEditor.cs | 32 +++++++ .../PropertyEditors/DateTimeValidator.cs | 28 ++++++ src/Umbraco.Web/Umbraco.Web.csproj | 4 + src/umbraco.businesslogic/ui.cs | 45 ++++++---- 28 files changed, 557 insertions(+), 74 deletions(-) create mode 100644 src/Umbraco.Core/Models/PreValueCollection.cs create mode 100644 src/Umbraco.Tests/PropertyEditors/PropertyEditorValueEditorTests.cs create mode 100644 src/Umbraco.Tests/Services/PreValueConverterTests.cs create mode 100644 src/Umbraco.Web/Models/ContentEditing/TemplateBasic.cs create mode 100644 src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs create mode 100644 src/Umbraco.Web/PropertyEditors/DateTimePropertyEditor.cs create mode 100644 src/Umbraco.Web/PropertyEditors/DateTimeValidator.cs diff --git a/src/Umbraco.Core/Models/PreValueCollection.cs b/src/Umbraco.Core/Models/PreValueCollection.cs new file mode 100644 index 0000000000..154e4a58ad --- /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/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/PropertyEditors/PropertyEditor.cs b/src/Umbraco.Core/PropertyEditors/PropertyEditor.cs index 650c41a613..3c23bc9991 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyEditor.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using Newtonsoft.Json; using Umbraco.Core.IO; using Umbraco.Core.Models; @@ -28,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; /// @@ -62,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. @@ -96,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 something else ? + /// + 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 0cc3fe60a6..8f8f23a056 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueEditor.cs @@ -129,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(); @@ -184,6 +186,14 @@ namespace Umbraco.Core.PropertyEditors //we can just ToString() any of these types return dbValue.ToString(); case DataTypeDatabaseType.Date: + var s = dbValue as string; + if (s != null) + { + if (s.IsNullOrWhiteSpace()) + { + return string.Empty; + } + } //Dates will be formatted in 'o' format (otherwise known as xml format) return dbValue.ToXmlString(); default: diff --git a/src/Umbraco.Core/Services/DataTypeService.cs b/src/Umbraco.Core/Services/DataTypeService.cs index 68c0f308bd..3a2bc3a4f2 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/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/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/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/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 29abbe2015..a05aeee774 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -55,7 +55,7 @@ False ..\packages\AutoMapper.2.2.1\lib\net40\AutoMapper.dll - + False ..\packages\Examine.0.1.52.2941\lib\Examine.dll @@ -213,8 +213,10 @@ + + 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/views/propertyeditors/datepicker/datepicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html index 3c1b9e3372..10f1a715ee 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,8 +1,11 @@
    - + - +
    + {{propertyForm.datepicker.errorMsg}}
    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..a087840f34 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..06458928f0 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs @@ -0,0 +1,22 @@ +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 + { + 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/DateTimePropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/DateTimePropertyEditor.cs new file mode 100644 index 0000000000..3ec863b7ab --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/DateTimePropertyEditor.cs @@ -0,0 +1,32 @@ +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() ; + } + + private IDictionary _defaultPreVals; + + 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..734e34622f --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/DateTimeValidator.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Umbraco.Core.PropertyEditors; + +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 (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 40cdd91586..168657080a 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -299,7 +299,11 @@ + + + + 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; + } } From 4a8026c902fd1f6afa32749882611c7e4d37e882 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 14 Aug 2013 20:08:45 +1000 Subject: [PATCH 11/11] Got the Date and Date/Time new property editors created, fixed up a few date casting issues with data. These editors 'should' be compatible with the previous legacy date/datetime editors, just need to test it. --- src/Umbraco.Core/Models/PreValueCollection.cs | 6 +-- src/Umbraco.Core/Models/PropertyExtensions.cs | 11 +++- src/Umbraco.Core/ObjectExtensions.cs | 9 +++- .../PropertyEditors/PropertyEditor.cs | 10 ++-- .../PropertyEditors/ValueEditor.cs | 15 +++--- src/Umbraco.Core/Services/DataTypeService.cs | 2 +- .../lib/umbraco/Extensions.js | 17 ++++++ .../datepicker/datepicker.controller.js | 7 ++- .../datepicker/datepicker.html | 2 +- .../ContentEditing/ContentPropertyDisplay.cs | 2 +- .../PropertyEditors/DatePropertyEditor.cs | 52 +++++++++++++++++-- .../PropertyEditors/DateTimePropertyEditor.cs | 13 +++-- .../PropertyEditors/DateTimeValidator.cs | 3 +- 13 files changed, 118 insertions(+), 31 deletions(-) diff --git a/src/Umbraco.Core/Models/PreValueCollection.cs b/src/Umbraco.Core/Models/PreValueCollection.cs index 154e4a58ad..995f6422c2 100644 --- a/src/Umbraco.Core/Models/PreValueCollection.cs +++ b/src/Umbraco.Core/Models/PreValueCollection.cs @@ -15,7 +15,7 @@ namespace Umbraco.Core.Models /// public class PreValueCollection { - private IDictionary _preValuesAsDictionary; + private IDictionary _preValuesAsDictionary; private IEnumerable _preValuesAsArray; public IEnumerable PreValuesAsArray { @@ -30,7 +30,7 @@ namespace Umbraco.Core.Models set { _preValuesAsArray = value; } } - public IDictionary PreValuesAsDictionary + public IDictionary PreValuesAsDictionary { get { @@ -56,7 +56,7 @@ namespace Umbraco.Core.Models _preValuesAsArray = preVals; } - public PreValueCollection(IDictionary preVals) + public PreValueCollection(IDictionary preVals) { _preValuesAsDictionary = preVals; } 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/PropertyEditors/PropertyEditor.cs b/src/Umbraco.Core/PropertyEditors/PropertyEditor.cs index 3c23bc9991..bb69b0a445 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyEditor.cs @@ -73,7 +73,7 @@ namespace Umbraco.Core.PropertyEditors } [JsonProperty("defaultConfig")] - public virtual IDictionary DefaultPreValues { get; set; } + 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 @@ -122,13 +122,13 @@ namespace Umbraco.Core.PropertyEditors /// /// /// 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 something else ? + /// 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) + public virtual IDictionary FormatPreValues(IDictionary defaultPreVals, PreValueCollection persistedPreVals) { if (defaultPreVals == null) { - defaultPreVals = new Dictionary(); + defaultPreVals = new Dictionary(); } if (persistedPreVals.IsDictionaryBased) @@ -142,7 +142,7 @@ namespace Umbraco.Core.PropertyEditors } //it's an array so need to format it - var result = new Dictionary(); + var result = new Dictionary(); var asArray = persistedPreVals.PreValuesAsArray.ToArray(); for (var i = 0; i < asArray.Length; i++) { diff --git a/src/Umbraco.Core/PropertyEditors/ValueEditor.cs b/src/Umbraco.Core/PropertyEditors/ValueEditor.cs index 8f8f23a056..5929b96512 100644 --- a/src/Umbraco.Core/PropertyEditors/ValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/ValueEditor.cs @@ -185,17 +185,14 @@ namespace Umbraco.Core.PropertyEditors case DataTypeDatabaseType.Integer: //we can just ToString() any of these types return dbValue.ToString(); - case DataTypeDatabaseType.Date: - var s = dbValue as string; - if (s != null) + case DataTypeDatabaseType.Date: + var date = dbValue.TryConvertTo(); + if (date.Success == false || date.Result == null) { - if (s.IsNullOrWhiteSpace()) - { - return string.Empty; - } + return string.Empty; } - //Dates will be formatted in 'o' format (otherwise known as xml format) - return dbValue.ToXmlString(); + //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 3a2bc3a4f2..3a1e2e8f46 100644 --- a/src/Umbraco.Core/Services/DataTypeService.cs +++ b/src/Umbraco.Core/Services/DataTypeService.cs @@ -354,7 +354,7 @@ namespace Umbraco.Core.Services 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(); + 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(); diff --git a/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js b/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js index 43e957d6ed..306b9d01c6 100644 --- a/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js +++ b/src/Umbraco.Web.UI.Client/lib/umbraco/Extensions.js @@ -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/views/propertyeditors/datepicker/datepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js index da111b9d5b..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 @@ -25,7 +25,12 @@ angular.module("umbraco").controller("Umbraco.Editors.DatepickerController", $("#" + pickerId).datetimepicker($scope.model.config).on("changeDate", function (e) { // when a date is changed, update the model if (e.localDate) { - $scope.model.value = e.localDate.toIsoDateTimeString(); + 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; 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 10f1a715ee..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,6 @@
    - diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/ContentPropertyDisplay.cs index a087840f34..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 IDictionary Config { get; set; } + public IDictionary Config { get; set; } [DataMember(Name = "hideLabel")] public bool HideLabel { get; set; } diff --git a/src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs index 06458928f0..135a76ee3a 100644 --- a/src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/DatePropertyEditor.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Text.RegularExpressions; using Newtonsoft.Json.Linq; @@ -10,13 +11,58 @@ 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 editor = base.CreateValueEditor(); + var baseEditor = base.CreateValueEditor(); - editor.Validators = new List { new DateTimeValidator() }; + 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"); + } - return editor; } } } \ No newline at end of file diff --git a/src/Umbraco.Web/PropertyEditors/DateTimePropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/DateTimePropertyEditor.cs index 3ec863b7ab..55198a36a1 100644 --- a/src/Umbraco.Web/PropertyEditors/DateTimePropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/DateTimePropertyEditor.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using Umbraco.Core; using Umbraco.Core.PropertyEditors; @@ -9,12 +10,18 @@ namespace Umbraco.Web.PropertyEditors { public DateTimePropertyEditor() { - _defaultPreVals = new Dictionary() ; + _defaultPreVals = new Dictionary + { + {"format", "yyyy-MM-dd HH:mm:ss"} + }; } - private IDictionary _defaultPreVals; + private IDictionary _defaultPreVals; - public override IDictionary DefaultPreValues + /// + /// 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; } diff --git a/src/Umbraco.Web/PropertyEditors/DateTimeValidator.cs b/src/Umbraco.Web/PropertyEditors/DateTimeValidator.cs index 734e34622f..c1f7277a05 100644 --- a/src/Umbraco.Web/PropertyEditors/DateTimeValidator.cs +++ b/src/Umbraco.Web/PropertyEditors/DateTimeValidator.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using Umbraco.Core.PropertyEditors; +using Umbraco.Core; namespace Umbraco.Web.PropertyEditors { @@ -13,7 +14,7 @@ namespace Umbraco.Web.PropertyEditors public override IEnumerable Validate(string value, string preValues, PropertyEditor editor) { DateTime dt; - if (DateTime.TryParse(value, out dt) == false) + 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[]