From a05cc03254684d2baf55e56315432b9f4458ef2c Mon Sep 17 00:00:00 2001 From: AndyButland Date: Sun, 15 Nov 2015 21:18:21 +0100 Subject: [PATCH 001/132] Allows position of the list view child items tab to be positioned within the tab list --- .../listview/listview.controller.js | 1 + .../Mapping/TabsAndPropertiesResolver.cs | 39 +++++++++++++++---- .../PropertyEditors/ListViewPropertyEditor.cs | 12 ++---- 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js index 0569c9faca..069fd04988 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js @@ -57,6 +57,7 @@ function listViewController($rootScope, $scope, $routeParams, $injector, notific }; $scope.options = { + displayAtTabNumber: $scope.model.config.displayAtTabNumber ? $scope.model.config.displayAtTabNumber : 1, pageSize: $scope.model.config.pageSize ? $scope.model.config.pageSize : 10, pageNumber: ($routeParams.page && Number($routeParams.page) != NaN && Number($routeParams.page) > 0) ? $routeParams.page : 1, filter: '', diff --git a/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesResolver.cs b/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesResolver.cs index ecf5e2da42..1856d4382e 100644 --- a/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesResolver.cs +++ b/src/Umbraco.Web/Models/Mapping/TabsAndPropertiesResolver.cs @@ -119,9 +119,6 @@ namespace Umbraco.Web.Models.Mapping //re-assign genericProps.Properties = contentProps; - - - } /// @@ -191,12 +188,38 @@ namespace Umbraco.Web.Models.Mapping }); listViewTab.Properties = listViewProperties; - //Is there a better way? - var tabs = new List>(); - tabs.Add(listViewTab); - tabs.AddRange(display.Tabs); - display.Tabs = tabs; + SetChildItemsTabPosition(display, listViewConfig, listViewTab); + } + private static void SetChildItemsTabPosition(TabbedContentItem display, + IDictionary listViewConfig, + Tab listViewTab) + where TPersisted : IContentBase + { + // Find position of tab from config + var tabIndexForChildItems = 0; + if (listViewConfig["displayAtTabNumber"] != null && int.TryParse((string)listViewConfig["displayAtTabNumber"], out tabIndexForChildItems)) + { + // Tab position is recorded 1-based but we insert into collection 0-based + tabIndexForChildItems--; + + // Ensure within bounds + if (tabIndexForChildItems < 0) + { + tabIndexForChildItems = 0; + } + + if (tabIndexForChildItems > display.Tabs.Count()) + { + tabIndexForChildItems = display.Tabs.Count(); + } + } + + // Recreate tab list with child items tab at configured position + var tabs = new List>(); + tabs.AddRange(display.Tabs); + tabs.Insert(tabIndexForChildItems, listViewTab); + display.Tabs = tabs; } protected override IEnumerable> ResolveCore(IContentBase content) diff --git a/src/Umbraco.Web/PropertyEditors/ListViewPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/ListViewPropertyEditor.cs index 09dfacc94f..086004a501 100644 --- a/src/Umbraco.Web/PropertyEditors/ListViewPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/ListViewPropertyEditor.cs @@ -1,10 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Collections.Generic; using Umbraco.Core; -using Umbraco.Core.Models; using Umbraco.Core.PropertyEditors; namespace Umbraco.Web.PropertyEditors @@ -24,6 +19,7 @@ namespace Umbraco.Web.PropertyEditors return new Dictionary { {"pageSize", "10"}, + {"displayAtTabNumber", "1"}, {"orderBy", "SortOrder"}, {"orderDirection", "asc"}, { @@ -40,6 +36,8 @@ namespace Umbraco.Web.PropertyEditors internal class ListViewPreValueEditor : PreValueEditor { + [PreValueField("displayAtTabNumber", "Display At Tab Number", "number", Description = "Which tab position that the list of child items will be displayed")] + public int DisplayAtTabNumber { get; set; } [PreValueField("pageSize", "Page Size", "number", Description = "Number of items per page")] public int PageSize { get; set; } @@ -55,7 +53,5 @@ namespace Umbraco.Web.PropertyEditors Description = "The properties that will be displayed for each column")] public object IncludeProperties { get; set; } } - - } } From cd3aaa47ca60954d4a48fe109631f67efebf6d9c Mon Sep 17 00:00:00 2001 From: bjarnef Date: Thu, 19 Nov 2015 00:25:05 +0100 Subject: [PATCH 002/132] Add retina version of Umbraco logo in login screen --- src/Umbraco.Web.UI.Client/src/less/login.less | 38 ++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/login.less b/src/Umbraco.Web.UI.Client/src/less/login.less index 751ea8f3f0..a5ced443ff 100644 --- a/src/Umbraco.Web.UI.Client/src/less/login.less +++ b/src/Umbraco.Web.UI.Client/src/less/login.less @@ -9,13 +9,41 @@ color: @white; position: absolute; z-index: 10000; - top: 0px; - left: 0px; - margin: 0 !Important; + top: 0; + left: 0; + margin: 0 !important; padding: 0; border-radius: 0; } +/* 1.5 dpr */ +@media only screen and (-webkit-min-device-pixel-ratio: 1.5), + only screen and (min-resolution: 144dpi) +{ + .login-overlay { + background-image: url(../img/application/logo@2x.png) !important; + } +} + +/* 2.0 dpr */ +@media only screen and (-webkit-min-device-pixel-ratio: 2), + only screen and (min-resolution: 192dpi) +{ + .login-overlay { + background-image: url(../img/application/logo@2x.png) !important; + } +} + +/* 3.0 dpr - For iPhone 6 Plus, Samsung Galaxy S5 and similar devices */ +@media only screen and (-webkit-min-device-pixel-ratio: 3), + only screen and (min-resolution: 3dppx), /* default way */ + only screen and (min-resolution: 350dpi) /* dppx fallback */ +{ + .login-overlay { + background-image: url(../img/application/logo@3x.png) !important; + } +} + .login-overlay .umb-modalcolumn { background: none; border: none; @@ -62,7 +90,7 @@ } #hrOr hr { - margin: 0px; + margin: 0; border: none; background-color: @gray; height: 1px; @@ -79,4 +107,4 @@ height: 20px; margin: auto; color: @grayLight; -} +} \ No newline at end of file From 855e3b3dcce68be29732ac8dfe9c326b57895f68 Mon Sep 17 00:00:00 2001 From: engern Date: Sun, 29 Nov 2015 12:06:19 +0100 Subject: [PATCH 003/132] Add the possibility to sort the macro parameters --- .../umbraco/developer/Macros/editMacro.aspx | 11 +++++++++++ src/Umbraco.Web/Editors/MacroController.cs | 3 ++- .../Models/ContentEditing/MacroParameter.cs | 3 +++ .../umbraco/developer/Macros/editMacro.aspx.cs | 13 ++++++++----- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI/umbraco/developer/Macros/editMacro.aspx b/src/Umbraco.Web.UI/umbraco/developer/Macros/editMacro.aspx index 2fef5e05bd..9d84f4ff50 100644 --- a/src/Umbraco.Web.UI/umbraco/developer/Macros/editMacro.aspx +++ b/src/Umbraco.Web.UI/umbraco/developer/Macros/editMacro.aspx @@ -137,6 +137,9 @@ <%=umbraco.ui.Text("general", "type",UmbracoUser)%> + + <%=umbraco.ui.Text("general", "sort",UmbracoUser)%> + @@ -161,6 +164,11 @@ DataTextFormatString="" DataTextField='Name' DataValueField="Alias"> + + Required
+ Numbers only
+ + @@ -184,6 +192,9 @@ DataSource='<%# GetMacroParameterEditors()%>'> + + <%-- The macro parameter will automatically get sort order when created. --%> + diff --git a/src/Umbraco.Web/Editors/MacroController.cs b/src/Umbraco.Web/Editors/MacroController.cs index 88af605f61..aed6cec602 100644 --- a/src/Umbraco.Web/Editors/MacroController.cs +++ b/src/Umbraco.Web/Editors/MacroController.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Net; using System.Net.Http; using System.Text; @@ -34,7 +35,7 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(HttpStatusCode.NotFound); } - return Mapper.Map>(macro); + return Mapper.Map>(macro).OrderBy(x => x.SortOrder); } /// diff --git a/src/Umbraco.Web/Models/ContentEditing/MacroParameter.cs b/src/Umbraco.Web/Models/ContentEditing/MacroParameter.cs index a5cf8733c3..2ca9d0fa90 100644 --- a/src/Umbraco.Web/Models/ContentEditing/MacroParameter.cs +++ b/src/Umbraco.Web/Models/ContentEditing/MacroParameter.cs @@ -20,6 +20,9 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "name")] public string Name { get; set; } + + [DataMember(Name = "sortOrder")] + public int SortOrder { get; set; } /// /// The editor view to render for this parameter diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/editMacro.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/editMacro.aspx.cs index f8d15004da..0c375893d6 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/editMacro.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Macros/editMacro.aspx.cs @@ -193,7 +193,7 @@ namespace umbraco.cms.presentation.developer public void macroPropertyBind() { - macroProperties.DataSource = _macro.Properties; + macroProperties.DataSource = _macro.Properties.OrderBy(x => x.SortOrder); macroProperties.DataBind(); } @@ -333,24 +333,25 @@ namespace umbraco.cms.presentation.developer SetMacroValuesFromPostBack(_macro, Convert.ToInt32(tempCachePeriod), tempMacroAssembly, tempMacroType); // Save elements - var sort = 0; foreach (RepeaterItem item in macroProperties.Items) { var macroPropertyId = (HtmlInputHidden)item.FindControl("macroPropertyID"); var macroElementName = (TextBox)item.FindControl("macroPropertyName"); var macroElementAlias = (TextBox)item.FindControl("macroPropertyAlias"); + var macroElementSortOrder = (TextBox)item.FindControl("macroPropertySortOrder"); var macroElementType = (DropDownList)item.FindControl("macroPropertyType"); var prop = _macro.Properties.Single(x => x.Id == int.Parse(macroPropertyId.Value)); - + var sortOrder = 0; + int.TryParse(macroElementSortOrder.Text, out sortOrder); + _macro.Properties.UpdateProperty( prop.Alias, macroElementName.Text.Trim(), - sort, + sortOrder, macroElementType.SelectedValue, macroElementAlias.Text.Trim()); - sort++; } Services.MacroService.Save(_macro); @@ -368,6 +369,8 @@ namespace umbraco.cms.presentation.developer new LiteralControl("
")); + + macroPropertyBind(); } /// From 348694d9d41abb9e1095f2e3e68f2cfe66735870 Mon Sep 17 00:00:00 2001 From: engern Date: Sun, 29 Nov 2015 12:22:22 +0100 Subject: [PATCH 004/132] Use same color as the rest --- src/Umbraco.Web.UI/umbraco/developer/Macros/editMacro.aspx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI/umbraco/developer/Macros/editMacro.aspx b/src/Umbraco.Web.UI/umbraco/developer/Macros/editMacro.aspx index 9d84f4ff50..28cc136a3a 100644 --- a/src/Umbraco.Web.UI/umbraco/developer/Macros/editMacro.aspx +++ b/src/Umbraco.Web.UI/umbraco/developer/Macros/editMacro.aspx @@ -166,7 +166,7 @@ Required
- Numbers only
+ Numbers only
From dada91f006a21f4a8bc0f2682ff1f6be96b8ad2c Mon Sep 17 00:00:00 2001 From: kgiszewski Date: Mon, 14 Dec 2015 13:35:46 -0500 Subject: [PATCH 005/132] Fix u4-7534 --- .../src/common/services/contenteditinghelper.service.js | 2 +- .../src/common/services/formhelper.service.js | 2 +- .../src/views/content/content.edit.controller.js | 9 +++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js index ae4ca3cde8..7c245100ee 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js @@ -50,7 +50,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica var deferred = $q.defer(); - if (!args.scope.busy && formHelper.submitForm({ scope: args.scope, statusMessage: args.statusMessage })) { + if (!args.scope.busy && formHelper.submitForm({ scope: args.scope, statusMessage: args.statusMessage, action: args.action })) { args.scope.busy = true; diff --git a/src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js index 057e0b8cff..289798ab52 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js @@ -50,7 +50,7 @@ function formHelper(angularHelper, serverValidationManager, $timeout, notificati } //the first thing any form must do is broadcast the formSubmitting event - args.scope.$broadcast("formSubmitting", { scope: args.scope }); + args.scope.$broadcast("formSubmitting", { scope: args.scope, action: args.action }); //then check if the form is valid if (!args.skipValidation) { diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js index 03de8af21e..970bdff882 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js @@ -74,7 +74,8 @@ function ContentEditController($scope, $rootScope, $routeParams, $q, $timeout, $ statusMessage: args.statusMessage, saveMethod: args.saveMethod, scope: $scope, - content: $scope.content + content: $scope.content, + action: args.action }).then(function (data) { //success init($scope.content); @@ -166,15 +167,15 @@ function ContentEditController($scope, $rootScope, $routeParams, $q, $timeout, $ }; $scope.sendToPublish = function () { - return performSave({ saveMethod: contentResource.sendToPublish, statusMessage: "Sending..." }); + return performSave({ saveMethod: contentResource.sendToPublish, statusMessage: "Sending...", action: "sendToPublish" }); }; $scope.saveAndPublish = function () { - return performSave({ saveMethod: contentResource.publish, statusMessage: "Publishing..." }); + return performSave({ saveMethod: contentResource.publish, statusMessage: "Publishing...", action: "publish" }); }; $scope.save = function () { - return performSave({ saveMethod: contentResource.save, statusMessage: "Saving..." }); + return performSave({ saveMethod: contentResource.save, statusMessage: "Saving...", action: "save" }); }; $scope.preview = function (content) { From b52c480da35b591455f7521057267263dfa33a83 Mon Sep 17 00:00:00 2001 From: AndyButland Date: Sun, 20 Dec 2015 23:53:58 +0100 Subject: [PATCH 006/132] Added IPublishedContent extension method for filtering nodes based on composition alias Created unit test for IsComposedOf --- .../PublishedContent/PublishedContentType.cs | 12 ++++++------ .../PublishedContentMoreTests.cs | 6 +++--- .../PublishedContentTestElements.cs | 6 +++++- .../PublishedContent/PublishedContentTests.cs | 16 +++++++++++++--- src/Umbraco.Tests/Umbraco.Tests.csproj | 4 +++- src/Umbraco.Web/PublishedContentExtensions.cs | 15 +++++++++++++++ 6 files changed, 45 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs index 5f30c08ce7..de54132e2c 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedContentType.cs @@ -1,9 +1,6 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; -using System.Web.Caching; -using System.Web.UI; using Umbraco.Core.Cache; namespace Umbraco.Core.Models.PublishedContent @@ -27,6 +24,7 @@ namespace Umbraco.Core.Models.PublishedContent { Id = contentType.Id; Alias = contentType.Alias; + CompositionAliases = contentType.CompositionAliases(); _propertyTypes = contentType.CompositionPropertyTypes .Select(x => new PublishedPropertyType(this, x)) .ToArray(); @@ -34,10 +32,11 @@ namespace Umbraco.Core.Models.PublishedContent } // internal so it can be used for unit tests - internal PublishedContentType(int id, string alias, IEnumerable propertyTypes) + internal PublishedContentType(int id, string alias, IEnumerable compositionAliases, IEnumerable propertyTypes) { Id = id; Alias = alias; + CompositionAliases = compositionAliases; _propertyTypes = propertyTypes.ToArray(); foreach (var propertyType in _propertyTypes) propertyType.ContentType = this; @@ -45,8 +44,8 @@ namespace Umbraco.Core.Models.PublishedContent } // create detached content type - ie does not match anything in the DB - internal PublishedContentType(string alias, IEnumerable propertyTypes) - : this (0, alias, propertyTypes) + internal PublishedContentType(string alias, IEnumerable compositionAliases, IEnumerable propertyTypes) + : this(0, alias, compositionAliases, propertyTypes) { } private void InitializeIndexes() @@ -63,6 +62,7 @@ namespace Umbraco.Core.Models.PublishedContent public int Id { get; private set; } public string Alias { get; private set; } + public IEnumerable CompositionAliases { get; private set; } #endregion diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs index ff16c5306c..1c83691d04 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs @@ -234,9 +234,9 @@ namespace Umbraco.Tests.PublishedContent new PublishedPropertyType("prop1", 1, "?"), }; - var contentType1 = new PublishedContentType(1, "ContentType1", props); - var contentType2 = new PublishedContentType(2, "ContentType2", props); - var contentType2s = new PublishedContentType(3, "ContentType2Sub", props); + var contentType1 = new PublishedContentType(1, "ContentType1", Enumerable.Empty(), props); + var contentType2 = new PublishedContentType(2, "ContentType2", Enumerable.Empty(), props); + var contentType2s = new PublishedContentType(3, "ContentType2Sub", Enumerable.Empty(), props); cache.Add(new SolidPublishedContent(contentType1) { diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs index 4abfc6e18d..82115d670e 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs @@ -355,7 +355,11 @@ namespace Umbraco.Tests.PublishedContent private static readonly PublishedPropertyType Default = new PublishedPropertyType("*", 0, "?"); public AutoPublishedContentType(int id, string alias, IEnumerable propertyTypes) - : base(id, alias, propertyTypes) + : base(id, alias, Enumerable.Empty(), propertyTypes) + { } + + public AutoPublishedContentType(int id, string alias, IEnumerable compositionAliases, IEnumerable propertyTypes) + : base(id, alias, compositionAliases, propertyTypes) { } public override PublishedPropertyType GetPropertyType(string alias) diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index 2aa72091de..3967afce79 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -50,7 +50,8 @@ namespace Umbraco.Tests.PublishedContent new PublishedPropertyType("content", 0, Constants.PropertyEditors.TinyMCEAlias), new PublishedPropertyType("testRecursive", 0, "?"), }; - var type = new AutoPublishedContentType(0, "anything", propertyTypes); + var compositionAliases = new[] {"MyCompositionAlias"}; + var type = new AutoPublishedContentType(0, "anything", compositionAliases, propertyTypes); PublishedContentType.GetPublishedContentTypeCallback = (alias) => type; } @@ -468,6 +469,16 @@ namespace Umbraco.Tests.PublishedContent Assert.IsNull(doc.FirstChild()); } + [Test] + public void IsComposedOf() + { + var doc = GetNode(1173); + + var isComposedOf = doc.IsComposedOf("MyCompositionAlias"); + + Assert.IsTrue(isComposedOf); + } + [Test] public void HasProperty() { @@ -475,8 +486,7 @@ namespace Umbraco.Tests.PublishedContent var hasProp = doc.HasProperty(Constants.Conventions.Content.UrlAlias); - Assert.AreEqual(true, (bool)hasProp); - + Assert.IsTrue(hasProp); } [Test] diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 59a9166e5a..1db382a004 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -410,7 +410,9 @@ - + + True + diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index d6cf3b3141..197175366e 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -116,6 +116,21 @@ namespace Umbraco.Web #endregion + #region IsComposedOf + + /// + /// Gets a value indicating whether the content is of a content type composed of the given alias + /// + /// The content. + /// The content type alias. + /// A value indicating whether the content is of a content type composed of a content type identified by the alias. + public static bool IsComposedOf(this IPublishedContent content, string alias) + { + return content.ContentType.CompositionAliases.Contains(alias); + } + + #endregion + #region HasProperty /// From ae85b7bf2ba00abe8f7375e24a90504a6d19e117 Mon Sep 17 00:00:00 2001 From: Alexander Bryukhov Date: Mon, 28 Dec 2015 23:00:08 +0600 Subject: [PATCH 007/132] Nodes sort dialog - adding date formats Adding dates processing for 'dd.MM.yyyy' formats --- .../umbraco_client/Dialogs/SortDialog.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI/umbraco_client/Dialogs/SortDialog.js b/src/Umbraco.Web.UI/umbraco_client/Dialogs/SortDialog.js index eb62afd805..dc74ad95ff 100644 --- a/src/Umbraco.Web.UI/umbraco_client/Dialogs/SortDialog.js +++ b/src/Umbraco.Web.UI/umbraco_client/Dialogs/SortDialog.js @@ -22,11 +22,11 @@ s = s.replace(/\-/g, "/"); //all of these basically transform the string into year-month-day since that //is what JS understands when creating a Date object - if (c.dateFormat.indexOf("dd/MM/yyyy") == 0 || c.dateFormat.indexOf("dd-MM-yyyy") == 0) { - s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3-$2-$1"); + if (c.dateFormat.indexOf("dd/MM/yyyy") == 0 || c.dateFormat.indexOf("dd-MM-yyyy") == 0 || c.dateFormat.indexOf("dd.MM.yyyy") == 0) { + s = s.replace(/(\d{1,2})[\/\-\.](\d{1,2})[\/\-\.](\d{4})/, "$3-$2-$1"); } - else if (c.dateFormat.indexOf("dd/MM/yy") == 0 || c.dateFormat.indexOf("dd-MM-yy") == 0) { - s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/, "$3-$2-$1"); + else if (c.dateFormat.indexOf("dd/MM/yy") == 0 || c.dateFormat.indexOf("dd-MM-yy") == 0 || c.dateFormat.indexOf("dd.MM.yy") == 0) { + s = s.replace(/(\d{1,2})[\/\-\.](\d{1,2})[\/\-\.](\d{2})/, "$3-$2-$1"); } else if (c.dateFormat.indexOf("MM/dd/yyyy") == 0 || c.dateFormat.indexOf("MM-dd-yyyy") == 0) { s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3-$1-$2"); @@ -118,4 +118,4 @@ -})(jQuery); \ No newline at end of file +})(jQuery); From 0bfc5f1272edeef0a19b608823ac8c6f04a8312a Mon Sep 17 00:00:00 2001 From: bjarnef Date: Mon, 8 Feb 2016 15:07:16 +0100 Subject: [PATCH 008/132] Change icons in tinymce config --- .../src/less/property-editors.less | 3 + .../propertyeditors/rte/rte.controller.js | 2 +- .../rte/rte.prevalues.controller.js | 53 +- .../propertyeditors/rte/rte.prevalues.html | 15 +- .../config/tinyMceConfig.Release.config | 477 +++++++++--------- .../config/tinyMceConfig.config | 78 ++- .../ContentEditing/RichTextEditorCommand.cs | 3 + .../RichTextPreValueController.cs | 7 +- 8 files changed, 370 insertions(+), 268 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/property-editors.less b/src/Umbraco.Web.UI.Client/src/less/property-editors.less index 242ea3ee63..baa966f95f 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -81,6 +81,9 @@ div.umb-codeeditor .umb-btn-toolbar { font-size:16px !important; } +/* pre-value editor */ +.rte-editor-preval .control-group .controls > div > label .mce-ico { line-height: 20px; } + // // Color picker diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js index 83ff5443c2..bbb8024a53 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js @@ -261,7 +261,7 @@ angular.module("umbraco") }; }); - + //Create the embedded plugin tinyMceService.createInsertEmbeddedMedia(editor, $scope, function() { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.controller.js index c815bc0c5e..702d19a509 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.controller.js @@ -1,5 +1,5 @@ angular.module("umbraco").controller("Umbraco.PrevalueEditors.RteController", - function ($scope, $timeout, $log, tinyMceService, stylesheetResource) { + function ($scope, $timeout, $log, tinyMceService, stylesheetResource, assetsService) { var cfg = tinyMceService.defaultPrevalues(); if($scope.model.value){ @@ -23,6 +23,14 @@ angular.module("umbraco").controller("Umbraco.PrevalueEditors.RteController", tinyMceService.configuration().then(function(config){ $scope.tinyMceConfig = config; + // extend commands with properties for font-icon and if it is a custom command + $scope.tinyMceConfig.commands = _.map($scope.tinyMceConfig.commands, function (obj) { + var icon = getFontIcon(obj.frontEndCommand); + return angular.extend(obj, { + fontIcon: icon.name, + isCustom: icon.isCustom + }); + }); }); stylesheetResource.getAll().then(function(stylesheets){ @@ -57,6 +65,43 @@ angular.module("umbraco").controller("Umbraco.PrevalueEditors.RteController", $scope.model.value.stylesheets.splice(index, 1); } }; + + // map properties for specific commands + function getFontIcon(alias) { + var icon = { name: alias, isCustom: false }; + + switch (alias) { + case "codemirror": + icon.name = "code"; + icon.isCustom = false; + break; + case "styleselect": + icon.name = "icon-list"; + icon.isCustom = true; + break; + case "umbembeddialog": + icon.name = "icon-tv"; + icon.isCustom = true; + break; + case "umbmediapicker": + icon.name = "icon-picture"; + icon.isCustom = true; + break; + case "umbmacro": + icon.name = "icon-settings-alt"; + icon.isCustom = true; + break; + case "umbmacro": + icon.name = "icon-settings-alt"; + icon.isCustom = true; + break; + default: + icon.name = alias; + icon.isCustom = false; + } + + return icon; + } var unsubscribe = $scope.$on("formSubmitting", function (ev, args) { @@ -65,9 +110,11 @@ angular.module("umbraco").controller("Umbraco.PrevalueEditors.RteController", }); - //when the scope is destroyed we need to unsubscribe + // when the scope is destroyed we need to unsubscribe $scope.$on('$destroy', function () { unsubscribe(); }); - }); + // load TinyMCE skin which contains css for font-icons + assetsService.loadCss("lib/tinymce/skins/umbraco/skin.min.css"); + }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html index fbc97224ab..b9d63df9f3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html @@ -1,4 +1,4 @@ -
+
@@ -8,9 +8,10 @@ ng-model="cmd.selected" ng-change="selectCommand(cmd)" /> - + + - {{cmd.frontEndCommand}} + {{cmd.name}}
@@ -27,12 +28,12 @@
- +
- × + × Pixels -
-
+
+
diff --git a/src/Umbraco.Web.UI/config/tinyMceConfig.Release.config b/src/Umbraco.Web.UI/config/tinyMceConfig.Release.config index 4b0c9fbba4..a18841983d 100644 --- a/src/Umbraco.Web.UI/config/tinyMceConfig.Release.config +++ b/src/Umbraco.Web.UI/config/tinyMceConfig.Release.config @@ -2,221 +2,246 @@ - - - code - images/editor/code.gif - code - 1 - - + + + code + Code + images/editor/code.gif + code + 1 + + codemirror + Code mirror images/editor/code.gif codemirror 1 - removeformat - images/editor/removeformat.gif - removeformat - 2 - - - - Undo - images/editor/undo.gif - undo - 11 - - - Redo - images/editor/redo.gif - redo - 12 - - - Cut - images/editor/cut.gif - cut - 13 - - - Copy - images/editor/copy.gif - copy - 14 - - - - styleselect - images/editor/showStyles.png - styleselect - 20 - - - bold - images/editor/bold.gif - bold - 21 - - - italic - images/editor/italic.gif - italic - 22 - - - Underline - images/editor/underline.gif - underline - 23 - - - Strikethrough - images/editor/strikethrough.gif - strikethrough - 24 - - - - JustifyLeft - images/editor/justifyleft.gif - justifyleft - 31 - - - JustifyCenter - images/editor/justifycenter.gif - justifycenter - 32 - - - JustifyRight - images/editor/justifyright.gif - justifyright - 33 - - - JustifyFull - images/editor/justifyfull.gif - alignjustify - 34 - - - - bullist - images/editor/bullist.gif - bullist - 41 - - - numlist - images/editor/numlist.gif - numlist - 42 - - - Outdent - images/editor/outdent.gif - outdent - 43 - - - Indent - images/editor/indent.gif - indent - 44 - - - - mceLink - images/editor/link.gif - link - 51 - - - unlink - images/editor/unLink.gif - unlink - 52 - - - mceInsertAnchor - images/editor/anchor.gif - anchor - 53 - - - - mceImage - images/editor/image.gif - image - 61 - - - - umbracomacro - images/editor/insMacro.gif - umbracomacro - 62 - - - - - - mceInsertTable - images/editor/table.gif - table - 63 - - - - umbracoembed - images/editor/media.gif - umbracoembed - 66 - - - inserthorizontalrule - images/editor/hr.gif - hr - 71 - - - subscript - images/editor/sub.gif - subscript - 72 - - - - superscript - images/editor/sup.gif - superscript - 73 - - - - mceCharMap - images/editor/charmap.gif - charmap - 74 - - - - + removeformat + Remove format + images/editor/removeformat.gif + removeformat + 2 + + + undo + Undo + Remove Format + images/editor/undo.gif + undo + 11 + + + redo + Redo + images/editor/redo.gif + redo + 12 + + + cut + Cut + images/editor/cut.gif + cut + 13 + + + copy + Copy + images/editor/copy.gif + copy + 14 + + + paste + Paste + images/editor/paste.gif + paste + 15 + + + styleselect + Style select + images/editor/showStyles.png + styleselect + 20 + + + bold + Bold + images/editor/bold.gif + bold + 21 + + + italic + Italic + images/editor/italic.gif + italic + 22 + + + underline + Underline + images/editor/underline.gif + underline + 23 + + + strikethrough + Strikethrough + images/editor/strikethrough.gif + strikethrough + 24 + + + justifyleft + Justify left + images/editor/justifyleft.gif + justifyleft + 31 + + + justifycenter + Justify center + images/editor/justifycenter.gif + justifycenter + 32 + + + justifyright + Justify right + images/editor/justifyright.gif + justifyright + 33 + + + justifyfull + Justify full + images/editor/justifyfull.gif + alignjustify + 34 + + + bullist + Bullet list + images/editor/bullist.gif + bullist + 41 + + + numlist + Numbered list + images/editor/numlist.gif + numlist + 42 + + + outdent + Decrease indent + images/editor/outdent.gif + outdent + 43 + + + indent + Increase indent + images/editor/indent.gif + indent + 44 + + + mceLink + Insert/edit link + images/editor/link.gif + link + 51 + + + unlink + Remove link + images/editor/unLink.gif + unlink + 52 + + + mceInsertAnchor + Anchor + images/editor/anchor.gif + anchor + 53 + + + mceImage + Image + images/editor/image.gif + image + 61 + + + umbracomacro + Macro + images/editor/insMacro.gif + umbracomacro + 62 + + + mceInsertTable + Table + images/editor/table.gif + table + 63 + + + umbracoembed + Embed + images/editor/media.gif + umbracoembed + 66 + + + inserthorizontalrule + Horizontal rule + images/editor/hr.gif + hr + 71 + + + subscript + Subscript + images/editor/sub.gif + subscript + 72 + + + superscript + Superscript + images/editor/sup.gif + superscript + 73 + + + mceCharMap + Character map + images/editor/charmap.gif + charmap + 74 + + + code - codemirror - paste - umbracolink - anchor - charmap - table + codemirror + paste + umbracolink + anchor + charmap + table lists - hr - - - hr + + + - - font + + font - - - - raw - - { - "indentOnInit": false, - "path": "../../../../lib/codemirror", - "config": { - }, - "jsFiles": [ - ], - "cssFiles": [ - ] - } - - + + + + raw + + { + "indentOnInit": false, + "path": "../../../../lib/codemirror", + "config": { + }, + "jsFiles": [ + ], + "cssFiles": [ + ] + } + + \ No newline at end of file diff --git a/src/Umbraco.Web.UI/config/tinyMceConfig.config b/src/Umbraco.Web.UI/config/tinyMceConfig.config index 4da0083d30..0e107deca5 100644 --- a/src/Umbraco.Web.UI/config/tinyMceConfig.config +++ b/src/Umbraco.Web.UI/config/tinyMceConfig.config @@ -5,204 +5,228 @@ code + Code images/editor/code.gif code 1 codemirror + Code mirror images/editor/code.gif codemirror 1 removeformat + Remove format images/editor/removeformat.gif removeformat 2 - - Undo + undo + Undo images/editor/undo.gif undo 11 - Redo + redo + Redo images/editor/redo.gif redo 12 - Cut + cut + Cut images/editor/cut.gif cut 13 - Copy + copy + Copy images/editor/copy.gif copy 14 - + + paste + Paste + images/editor/paste.gif + paste + 15 + styleselect + Style select images/editor/showStyles.png styleselect 20 bold + Bold images/editor/bold.gif bold 21 italic + Italic images/editor/italic.gif italic 22 - Underline + underline + Underline images/editor/underline.gif underline 23 - Strikethrough + strikethrough + Strikethrough images/editor/strikethrough.gif strikethrough 24 - - JustifyLeft + justifyleft + Justify left images/editor/justifyleft.gif justifyleft 31 - JustifyCenter + justifycenter + Justify center images/editor/justifycenter.gif justifycenter 32 - JustifyRight + justifyright + Justify right images/editor/justifyright.gif justifyright 33 - JustifyFull + justifyfull + Justify full images/editor/justifyfull.gif alignjustify 34 - bullist + Bullet list images/editor/bullist.gif bullist 41 numlist + Numbered list images/editor/numlist.gif numlist 42 - Outdent + outdent + Decrease indent images/editor/outdent.gif outdent 43 - Indent + indent + Increase indent images/editor/indent.gif indent 44 - mceLink + Insert/edit link images/editor/link.gif link 51 unlink + Remove link images/editor/unLink.gif unlink 52 mceInsertAnchor + Anchor images/editor/anchor.gif anchor 53 - mceImage + Image images/editor/image.gif image 61 - umbracomacro + Macro images/editor/insMacro.gif umbracomacro 62 - - - mceInsertTable + Table images/editor/table.gif table 63 - umbracoembed + Embed images/editor/media.gif umbracoembed 66 inserthorizontalrule + Horizontal rule images/editor/hr.gif hr 71 subscript + Subscript images/editor/sub.gif subscript 72 - superscript + Superscript images/editor/sup.gif superscript 73 - mceCharMap + Character map images/editor/charmap.gif charmap 74 - code @@ -247,4 +271,4 @@ param[name|value|_value|class],embed[type|width|height|src|class|*],map[name|cla } - + \ No newline at end of file diff --git a/src/Umbraco.Web/Models/ContentEditing/RichTextEditorCommand.cs b/src/Umbraco.Web/Models/ContentEditing/RichTextEditorCommand.cs index 55a1bd9331..fb2a95c2a4 100644 --- a/src/Umbraco.Web/Models/ContentEditing/RichTextEditorCommand.cs +++ b/src/Umbraco.Web/Models/ContentEditing/RichTextEditorCommand.cs @@ -10,6 +10,9 @@ namespace Umbraco.Web.Models.ContentEditing [DataContract(Name = "richtexteditorcommand", Namespace = "")] public class RichTextEditorCommand { + [DataMember(Name = "name")] + public string Name { get; set; } + [DataMember(Name = "icon")] public string Icon { get; set; } diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPreValueController.cs b/src/Umbraco.Web/PropertyEditors/RichTextPreValueController.cs index 9622008a05..ca8df2b9be 100644 --- a/src/Umbraco.Web/PropertyEditors/RichTextPreValueController.cs +++ b/src/Umbraco.Web/PropertyEditors/RichTextPreValueController.cs @@ -42,8 +42,6 @@ namespace Umbraco.Web.PropertyEditors return config; } - - private static void EnsureInit() { @@ -71,9 +69,10 @@ namespace Umbraco.Web.PropertyEditors new RichTextEditorCommand() { IsStylePicker = isStyle, + Name = n.SelectSingleNode("./name") != null ? n.SelectSingleNode("./name").FirstChild.Value : alias, Icon = n.SelectSingleNode("./icon").FirstChild.Value, Command = n.SelectSingleNode("./tinyMceCommand").FirstChild.Value, - Alias = n.SelectSingleNode("./umbracoAlias").FirstChild.Value.ToLower(), + Alias = alias, UserInterface = n.SelectSingleNode("./tinyMceCommand").Attributes.GetNamedItem("userInterface").Value, FrontEndCommand = n.SelectSingleNode("./tinyMceCommand").Attributes.GetNamedItem("frontendCommand").Value, Value = n.SelectSingleNode("./tinyMceCommand").Attributes.GetNamedItem("value").Value, @@ -136,4 +135,4 @@ namespace Umbraco.Web.PropertyEditors } } -} +} \ No newline at end of file From 3cdebf4427470427ec7b1a92b3f0d693f2ed0c84 Mon Sep 17 00:00:00 2001 From: Simon Busborg Date: Fri, 12 Feb 2016 09:38:37 +0100 Subject: [PATCH 009/132] Fixes U4-7918 Make icon picker for document type and list views consistent --- src/Umbraco.Web.UI.Client/src/less/belle.less | 1 + .../src/less/components/umb-iconpicker.less | 46 +++++++++++++++++++ .../src/views/common/dialogs/iconpicker.html | 46 ++++++++----------- .../overlays/iconpicker/iconpicker.html | 16 +++++-- src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 1 + 5 files changed, 79 insertions(+), 31 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/less/components/umb-iconpicker.less diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index a7335d8314..9d01cae4b9 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -107,6 +107,7 @@ @import "components/umb-grid.less"; @import "components/umb-empty-state.less"; @import "components/umb-property-editor.less"; +@import "components/umb-iconpicker.less"; @import "components/buttons/umb-button.less"; @import "components/buttons/umb-button-group.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-iconpicker.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-iconpicker.less new file mode 100644 index 0000000000..de9d26a2af --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-iconpicker.less @@ -0,0 +1,46 @@ +.umb-iconpicker { + display: flex; + flex-direction: row; + flex-wrap: wrap; + + margin: 0; +} + +.umb-iconpicker-item { + display: flex; + flex-direction: row; + flex: 0 0 12.5%; + justify-content: center; + align-items: center; + + margin-bottom: 0; + + overflow: hidden; +} + +.umb-iconpicker-item a { + display: flex; + justify-content: center; + align-items: center; + + width: 100%; + height: 100%; + + padding: 15px 0; + + text-decoration: none; +} + +.umb-iconpicker-item a:hover, +.umb-iconpicker-item a:focus{ + background: darken(@grayLighter, 1%); + outline: none; +} + +.umb-iconpicker-item a:active { + background: darken(@grayLighter, 4%); +} + +.umb-iconpicker-item i { + font-size: 26px; +} diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/iconpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/iconpicker.html index bcf7fe3247..314bfe85e1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/iconpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/iconpicker.html @@ -2,55 +2,49 @@
- + -
+ -
+
- - - No icons were found. - -
+ + + No icons were found. +
/// /// + /// /// /// - public MigrationEventArgs(IList eventObject, SemVersion configuredVersion, SemVersion targetVersion, bool canCancel) - : base(eventObject, canCancel) - { - ConfiguredSemVersion = configuredVersion; - TargetSemVersion = targetVersion; - } + public MigrationEventArgs(IList eventObject, SemVersion configuredVersion, SemVersion targetVersion, string productName, bool canCancel) + : this(eventObject, null, configuredVersion, targetVersion, productName, canCancel) + { } - [Obsolete("Use constructor accepting UmbracoVersion instances instead")] + /// + /// Constructor accepting multiple migrations that are used in the migration runner + /// + /// + /// + /// + /// + [Obsolete("Use constructor accepting a product name instead.")] + [EditorBrowsable(EditorBrowsableState.Never)] + public MigrationEventArgs(IList eventObject, SemVersion configuredVersion, SemVersion targetVersion, bool canCancel) + : this(eventObject, null, configuredVersion, targetVersion, GlobalSettings.UmbracoMigrationName, canCancel) + { } + + [Obsolete("Use constructor accepting SemVersion instances and a product name instead.")] [EditorBrowsable(EditorBrowsableState.Never)] public MigrationEventArgs(IList eventObject, Version configuredVersion, Version targetVersion, bool canCancel) - : base(eventObject, canCancel) - { - ConfiguredSemVersion = new SemVersion(configuredVersion); - TargetSemVersion = new SemVersion(targetVersion); - } + : this(eventObject, null, new SemVersion(configuredVersion), new SemVersion(targetVersion), GlobalSettings.UmbracoMigrationName, canCancel) + { } + + /// + /// Constructor accepting multiple migrations that are used in the migration runner + /// + /// + /// + /// + /// + /// + /// + internal MigrationEventArgs(IList eventObject, MigrationContext migrationContext, SemVersion configuredVersion, SemVersion targetVersion, string productName, bool canCancel) + : base(eventObject, canCancel) + { + MigrationContext = migrationContext; + ConfiguredSemVersion = configuredVersion; + TargetSemVersion = targetVersion; + ProductName = productName; + } /// /// Constructor accepting multiple migrations that are used in the migration runner @@ -39,12 +66,15 @@ namespace Umbraco.Core.Events /// /// /// + [Obsolete("Use constructor accepting a product name instead.")] + [EditorBrowsable(EditorBrowsableState.Never)] internal MigrationEventArgs(IList eventObject, MigrationContext migrationContext, SemVersion configuredVersion, SemVersion targetVersion, bool canCancel) : base(eventObject, canCancel) { MigrationContext = migrationContext; ConfiguredSemVersion = configuredVersion; TargetSemVersion = targetVersion; + ProductName = GlobalSettings.UmbracoMigrationName; } /// @@ -53,21 +83,28 @@ namespace Umbraco.Core.Events /// /// /// - public MigrationEventArgs(IList eventObject, SemVersion configuredVersion, SemVersion targetVersion) - : base(eventObject) - { - ConfiguredSemVersion = configuredVersion; - TargetSemVersion = targetVersion; - } + /// + public MigrationEventArgs(IList eventObject, SemVersion configuredVersion, SemVersion targetVersion, string productName) + : this(eventObject, null, configuredVersion, targetVersion, productName, false) + { } - [Obsolete("Use constructor accepting UmbracoVersion instances instead")] + /// + /// Constructor accepting multiple migrations that are used in the migration runner + /// + /// + /// + /// + [Obsolete("Use constructor accepting a product name instead.")] + [EditorBrowsable(EditorBrowsableState.Never)] + public MigrationEventArgs(IList eventObject, SemVersion configuredVersion, SemVersion targetVersion) + : this(eventObject, null, configuredVersion, targetVersion, GlobalSettings.UmbracoMigrationName, false) + { } + + [Obsolete("Use constructor accepting SemVersion instances and a product name instead.")] [EditorBrowsable(EditorBrowsableState.Never)] public MigrationEventArgs(IList eventObject, Version configuredVersion, Version targetVersion) - : base(eventObject) - { - ConfiguredSemVersion = new SemVersion(configuredVersion); - TargetSemVersion = new SemVersion(targetVersion); - } + : this(eventObject, null, new SemVersion(configuredVersion), new SemVersion(targetVersion), GlobalSettings.UmbracoMigrationName, false) + { } /// /// Returns all migrations that were used in the migration runner @@ -95,6 +132,8 @@ namespace Umbraco.Core.Events public SemVersion TargetSemVersion { get; private set; } + public string ProductName { get; private set; } + internal MigrationContext MigrationContext { get; private set; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs b/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs index 3683e24a00..36ddb2d4ba 100644 --- a/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs +++ b/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs @@ -92,7 +92,7 @@ namespace Umbraco.Core.Persistence.Migrations : OrderedDowngradeMigrations(foundMigrations).ToList(); - if (Migrating.IsRaisedEventCancelled(new MigrationEventArgs(migrations, _currentVersion, _targetVersion, true), this)) + if (Migrating.IsRaisedEventCancelled(new MigrationEventArgs(migrations, _currentVersion, _targetVersion, _productName, true), this)) { _logger.Warn("Migration was cancelled by an event"); return false; @@ -121,7 +121,7 @@ namespace Umbraco.Core.Persistence.Migrations throw; } - Migrated.RaiseEvent(new MigrationEventArgs(migrations, migrationContext, _currentVersion, _targetVersion, false), this); + Migrated.RaiseEvent(new MigrationEventArgs(migrations, migrationContext, _currentVersion, _targetVersion, _productName, false), this); return true; } diff --git a/src/Umbraco.Web/Strategies/Migrations/ClearCsrfCookiesAfterUpgrade.cs b/src/Umbraco.Web/Strategies/Migrations/ClearCsrfCookiesAfterUpgrade.cs index 0b31f53c74..4a316fe96f 100644 --- a/src/Umbraco.Web/Strategies/Migrations/ClearCsrfCookiesAfterUpgrade.cs +++ b/src/Umbraco.Web/Strategies/Migrations/ClearCsrfCookiesAfterUpgrade.cs @@ -1,8 +1,10 @@ -using System.Web; +using System.Linq; +using System.Web; using Umbraco.Core.Events; using Umbraco.Core.Persistence.Migrations; using Umbraco.Web.WebApi.Filters; using umbraco.interfaces; +using Umbraco.Core.Configuration; namespace Umbraco.Web.Strategies.Migrations { @@ -13,6 +15,8 @@ namespace Umbraco.Web.Strategies.Migrations { protected override void AfterMigration(MigrationRunner sender, MigrationEventArgs e) { + if (e.ProductName != GlobalSettings.UmbracoMigrationName) return; + if (HttpContext.Current == null) return; var http = new HttpContextWrapper(HttpContext.Current); diff --git a/src/Umbraco.Web/Strategies/Migrations/ClearMediaXmlCacheForDeletedItemsAfterUpgrade.cs b/src/Umbraco.Web/Strategies/Migrations/ClearMediaXmlCacheForDeletedItemsAfterUpgrade.cs index 54b4144fc0..cf9ddeafb2 100644 --- a/src/Umbraco.Web/Strategies/Migrations/ClearMediaXmlCacheForDeletedItemsAfterUpgrade.cs +++ b/src/Umbraco.Web/Strategies/Migrations/ClearMediaXmlCacheForDeletedItemsAfterUpgrade.cs @@ -5,21 +5,24 @@ using Umbraco.Core.Logging; using Umbraco.Core.Persistence.Migrations; using Umbraco.Core.Persistence.SqlSyntax; using umbraco.interfaces; +using Umbraco.Core.Configuration; namespace Umbraco.Web.Strategies.Migrations { /// /// This will execute after upgrading to remove any xml cache for media that are currently in the bin /// - /// - /// This will execute for specific versions - - /// + /// + /// This will execute for specific versions - + /// /// * If current is less than or equal to 7.0.0 /// public class ClearMediaXmlCacheForDeletedItemsAfterUpgrade : MigrationStartupHander - { + { protected override void AfterMigration(MigrationRunner sender, MigrationEventArgs e) { + if (e.ProductName != GlobalSettings.UmbracoMigrationName) return; + var target70 = new Version(7, 0, 0); if (e.ConfiguredVersion <= target70) @@ -28,7 +31,7 @@ namespace Umbraco.Web.Strategies.Migrations // http://issues.umbraco.org/issue/U4-3876 var sql = @"DELETE FROM cmsContentXml WHERE nodeId IN - (SELECT nodeId FROM (SELECT DISTINCT cmsContentXml.nodeId FROM cmsContentXml + (SELECT nodeId FROM (SELECT DISTINCT cmsContentXml.nodeId FROM cmsContentXml INNER JOIN umbracoNode ON cmsContentXml.nodeId = umbracoNode.id WHERE nodeObjectType = '" + Constants.ObjectTypes.Media + "' AND " + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("path") + " LIKE '%-21%') x)"; diff --git a/src/Umbraco.Web/Strategies/Migrations/EnsureListViewDataTypeIsCreated.cs b/src/Umbraco.Web/Strategies/Migrations/EnsureListViewDataTypeIsCreated.cs index ffdd64dd57..270fe4eace 100644 --- a/src/Umbraco.Web/Strategies/Migrations/EnsureListViewDataTypeIsCreated.cs +++ b/src/Umbraco.Web/Strategies/Migrations/EnsureListViewDataTypeIsCreated.cs @@ -10,6 +10,7 @@ using Umbraco.Core.Persistence.Migrations; using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.UnitOfWork; using umbraco.interfaces; +using Umbraco.Core.Configuration; namespace Umbraco.Web.Strategies.Migrations { @@ -20,6 +21,8 @@ namespace Umbraco.Web.Strategies.Migrations { protected override void AfterMigration(MigrationRunner sender, MigrationEventArgs e) { + if (e.ProductName != GlobalSettings.UmbracoMigrationName) return; + var target720 = new Version(7, 2, 0); if (e.ConfiguredVersion <= target720) @@ -95,7 +98,7 @@ namespace Umbraco.Web.Strategies.Migrations e.MigrationContext.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} OFF;", SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName("cmsDataTypePreValues")))); } - + transaction.Complete(); } diff --git a/src/Umbraco.Web/Strategies/Migrations/OverwriteStylesheetFilesFromTempFiles.cs b/src/Umbraco.Web/Strategies/Migrations/OverwriteStylesheetFilesFromTempFiles.cs index a8ef08cce0..9a8b6a6044 100644 --- a/src/Umbraco.Web/Strategies/Migrations/OverwriteStylesheetFilesFromTempFiles.cs +++ b/src/Umbraco.Web/Strategies/Migrations/OverwriteStylesheetFilesFromTempFiles.cs @@ -6,6 +6,7 @@ using System.Text; using System.Threading.Tasks; using Umbraco.Core.Events; using Umbraco.Core; +using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Persistence.Migrations; @@ -22,6 +23,8 @@ namespace Umbraco.Web.Strategies.Migrations { protected override void AfterMigration(MigrationRunner sender, MigrationEventArgs e) { + if (e.ProductName != GlobalSettings.UmbracoMigrationName) return; + var target73 = new Version(7, 3, 0); if (e.ConfiguredVersion <= target73) diff --git a/src/Umbraco.Web/Strategies/Migrations/PublishAfterUpgradeToVersionSixth.cs b/src/Umbraco.Web/Strategies/Migrations/PublishAfterUpgradeToVersionSixth.cs index 3e0df97e88..7be8d818d3 100644 --- a/src/Umbraco.Web/Strategies/Migrations/PublishAfterUpgradeToVersionSixth.cs +++ b/src/Umbraco.Web/Strategies/Migrations/PublishAfterUpgradeToVersionSixth.cs @@ -8,6 +8,7 @@ using Umbraco.Core.Persistence.Migrations; using Umbraco.Core.Persistence.UnitOfWork; using umbraco.interfaces; using Umbraco.Core; +using Umbraco.Core.Configuration; namespace Umbraco.Web.Strategies.Migrations { @@ -19,6 +20,8 @@ namespace Umbraco.Web.Strategies.Migrations { protected override void AfterMigration(MigrationRunner sender, MigrationEventArgs e) { + if (e.ProductName != GlobalSettings.UmbracoMigrationName) return; + var target = new Version(6, 0, 0); if (e.ConfiguredVersion < target) { @@ -69,7 +72,7 @@ namespace Umbraco.Web.Strategies.Migrations transaction.Complete(); } } - } - + } + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Strategies/Migrations/RebuildMediaXmlCacheAfterUpgrade.cs b/src/Umbraco.Web/Strategies/Migrations/RebuildMediaXmlCacheAfterUpgrade.cs index fad63c66dc..c3920677c5 100644 --- a/src/Umbraco.Web/Strategies/Migrations/RebuildMediaXmlCacheAfterUpgrade.cs +++ b/src/Umbraco.Web/Strategies/Migrations/RebuildMediaXmlCacheAfterUpgrade.cs @@ -4,6 +4,7 @@ using Umbraco.Core.Events; using Umbraco.Core.Persistence.Migrations; using Umbraco.Core.Services; using umbraco.interfaces; +using Umbraco.Core.Configuration; namespace Umbraco.Web.Strategies.Migrations { @@ -12,15 +13,17 @@ namespace Umbraco.Web.Strategies.Migrations /// /// /// This cannot execute as part of a db migration since we need access to the services/repos. - /// - /// This will execute for specific versions - - /// + /// + /// This will execute for specific versions - + /// /// * If current is less than or equal to 7.0.0 /// public class RebuildMediaXmlCacheAfterUpgrade : MigrationStartupHander { protected override void AfterMigration(MigrationRunner sender, MigrationEventArgs e) { + if (e.ProductName != GlobalSettings.UmbracoMigrationName) return; + var target70 = new Version(7, 0, 0); if (e.ConfiguredVersion <= target70) From 293b170a566069c1b24d9cb95f219541bee344c0 Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 24 Feb 2016 14:16:48 +0100 Subject: [PATCH 032/132] U4-8027 - fix ApplicationContext request cache (was null cache) --- src/Umbraco.Web/WebBootManager.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebBootManager.cs index 8d8091cd57..935d3934a6 100644 --- a/src/Umbraco.Web/WebBootManager.cs +++ b/src/Umbraco.Web/WebBootManager.cs @@ -107,7 +107,7 @@ namespace Umbraco.Web public override IBootManager Initialize() { //This is basically a hack for this item: http://issues.umbraco.org/issue/U4-5976 - // when Examine initializes it will try to rebuild if the indexes are empty, however in many cases not all of Examine's + // when Examine initializes it will try to rebuild if the indexes are empty, however in many cases not all of Examine's // event handlers will be assigned during bootup when the rebuilding starts which is a problem. So with the examine 0.1.58.2941 build // it has an event we can subscribe to in order to cancel this rebuilding process, but what we'll do is cancel it and postpone the rebuilding until the // boot process has completed. It's a hack but it works. @@ -248,8 +248,8 @@ namespace Umbraco.Web //all entities are cached properly (cloned in and cloned out) new DeepCloneRuntimeCacheProvider(new HttpRuntimeCacheProvider(HttpRuntime.Cache)), new StaticCacheProvider(), - //we have no request based cache when not running in web-based context - new NullCacheProvider(), + //we need request based cache when running in web-based context + new HttpRequestCacheProvider(), new IsolatedRuntimeCache(type => //we need to have the dep clone runtime cache provider to ensure //all entities are cached properly (cloned in and cloned out) From e8f84cfec0e6a304a7e3c9dd13709f7a29339a9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asbj=C3=B8rn=20Riis-Knudsen?= Date: Thu, 25 Feb 2016 12:20:58 +0100 Subject: [PATCH 033/132] Add parens around all BinaryExpressions to make sure that errors such U4-8026 do not happen --- src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs b/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs index f3cbe5a583..b5846f8650 100644 --- a/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs +++ b/src/Umbraco.Core/Persistence/Querying/BaseExpressionHelper.cs @@ -152,7 +152,7 @@ namespace Umbraco.Core.Persistence.Querying case "COALESCE": return string.Format("{0}({1},{2})", operand, left, right); default: - return left + " " + operand + " " + right; + return "(" + left + " " + operand + " " + right + ")"; } } From 610538261e2094da2e378331b64879e915749a55 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 25 Feb 2016 15:26:03 +0100 Subject: [PATCH 034/132] U4-7641 - enable content type copy --- .../Repositories/ContentTypeBaseRepository.cs | 14 +++++ .../Interfaces/IContentTypeRepository.cs | 7 +++ .../Services/ContentTypeService.cs | 44 ++++++++++++- .../Services/IContentTypeService.cs | 1 + .../common/resources/contenttype.resource.js | 28 +++++++-- .../views/documenttypes/copy.controller.js | 63 +++++++++++++++++++ .../src/views/documenttypes/copy.html | 51 +++++++++++++++ src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 2 + .../umbraco/config/lang/en_us.xml | 2 + .../Editors/ContentTypeController.cs | 36 +++++++---- .../Editors/ContentTypeControllerBase.cs | 52 +++++++-------- .../Editors/MediaTypeController.cs | 21 +++---- .../Trees/ContentTypeTreeController.cs | 1 + 13 files changed, 268 insertions(+), 54 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/views/documenttypes/copy.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/documenttypes/copy.html diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs index 907f66435e..d3e7182783 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs @@ -1212,5 +1212,19 @@ AND umbracoNode.id <> @id", { return PerformExists(id); } + + public string GetUniqueAlias(string alias) + { + var aliasColumn = SqlSyntax.GetQuotedColumnName("alias"); + var aliases = Database.Fetch(@"SELECT cmsContentType." + aliasColumn + @" FROM cmsContentType +INNER JOIN umbracoNode ON cmsContentType.nodeId = umbracoNode.id +WHERE cmsContentType." + aliasColumn + @" LIKE @pattern +AND umbracoNode.nodeObjectType = @objectType", + new { pattern = alias + "%", objectType = NodeObjectTypeId }); + var i = 1; + string test; + while (aliases.Contains(test = alias + i)) i++; + return test; + } } } diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentTypeRepository.cs index 49520387cd..acb1d50a61 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentTypeRepository.cs @@ -32,5 +32,12 @@ namespace Umbraco.Core.Persistence.Repositories /// /// IEnumerable GetAllContentTypeAliases(params Guid[] objectTypes); + + /// + /// Derives a unique alias from an existing alias. + /// + /// The original alias. + /// The original alias with a number appended to it, so that it is unique. + string GetUniqueAlias(string alias); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/ContentTypeService.cs b/src/Umbraco.Core/Services/ContentTypeService.cs index 496e855b60..141f4a5a97 100644 --- a/src/Umbraco.Core/Services/ContentTypeService.cs +++ b/src/Umbraco.Core/Services/ContentTypeService.cs @@ -794,7 +794,7 @@ namespace Umbraco.Core.Services //TODO: This needs to change, if we are deleting a content type, we should just delete the data, // this method will recursively go lookup every content item, check if any of it's descendants are - // of a different type, move them to the recycle bin, then permanently delete the content items. + // of a different type, move them to the recycle bin, then permanently delete the content items. // The main problem with this is that for every content item being deleted, events are raised... // which we need for many things like keeping caches in sync, but we can surely do this MUCH better. @@ -1067,6 +1067,48 @@ namespace Umbraco.Core.Services new OperationStatus(MoveOperationStatusType.Success, evtMsgs)); } + public Attempt> CopyContentType(IContentType toCopy, int containerId) + { + var evtMsgs = EventMessagesFactory.Get(); + + var uow = UowProvider.GetUnitOfWork(); + using (var containerRepository = RepositoryFactory.CreateEntityContainerRepository(uow, Constants.ObjectTypes.DocumentTypeContainerGuid)) + using (var repository = RepositoryFactory.CreateContentTypeRepository(uow)) + { + try + { + if (containerId > 0) + { + var container = containerRepository.Get(containerId); + if (container == null) + throw new DataOperationException(MoveOperationStatusType.FailedParentNotFound); + } + var alias = repository.GetUniqueAlias(toCopy.Alias); + var copy = toCopy.DeepCloneWithResetIdentities(alias); + copy.Name = copy.Name + " (copy)"; // might not be unique + + // if it has a parent, and the parent is a content type, unplug composition + // all other compositions remain in place in the copied content type + if (copy.ParentId > 0) + { + var parent = repository.Get(copy.ParentId); + if (parent != null) + copy.RemoveContentType(parent.Alias); + } + + copy.ParentId = containerId; + repository.AddOrUpdate(copy); + } + catch (DataOperationException ex) + { + return Attempt.Fail(new OperationStatus(ex.Operation, evtMsgs)); + } + uow.Commit(); + } + + return Attempt.Succeed(new OperationStatus(MoveOperationStatusType.Success, evtMsgs)); + } + /// /// Saves a single object /// diff --git a/src/Umbraco.Core/Services/IContentTypeService.cs b/src/Umbraco.Core/Services/IContentTypeService.cs index cd905a5ccc..e55430330c 100644 --- a/src/Umbraco.Core/Services/IContentTypeService.cs +++ b/src/Umbraco.Core/Services/IContentTypeService.cs @@ -291,5 +291,6 @@ namespace Umbraco.Core.Services Attempt> MoveMediaType(IMediaType toMove, int containerId); Attempt> MoveContentType(IContentType toMove, int containerId); + Attempt> CopyContentType(IContentType toCopy, int containerId); } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js index 55cc2974b7..4c7b6f916e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/contenttype.resource.js @@ -29,7 +29,7 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { filterContentTypes: filterContentTypes, filterPropertyTypes: filterPropertyTypes }; - + return umbRequestHelper.resourcePromise( $http.post( umbRequestHelper.getApiUrl( @@ -201,9 +201,9 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { * .then(function() { * alert("node was moved"); * }, function(err){ - * alert("node didnt move:" + err.data.Message); + * alert("node didnt move:" + err.data.Message); * }); - * + * * @param {Object} args arguments object * @param {Int} args.idd the ID of the node to move * @param {Int} args.parentId the ID of the parent node to move to @@ -230,6 +230,26 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { 'Failed to move content'); }, + copy: function(args) { + if (!args) { + throw "args cannot be null"; + } + if (!args.parentId) { + throw "args.parentId cannot be null"; + } + if (!args.id) { + throw "args.id cannot be null"; + } + + return umbRequestHelper.resourcePromise( + $http.post(umbRequestHelper.getApiUrl("contentTypeApiBaseUrl", "PostCopy"), + { + parentId: args.parentId, + id: args.id + }), + 'Failed to copy content'); + }, + createContainer: function(parentId, name) { return umbRequestHelper.resourcePromise( @@ -237,7 +257,7 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { 'Failed to create a folder under parent id ' + parentId); } - + }; } angular.module('umbraco.resources').factory('contentTypeResource', contentTypeResource); diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/copy.controller.js b/src/Umbraco.Web.UI.Client/src/views/documenttypes/copy.controller.js new file mode 100644 index 0000000000..c666a2159c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/copy.controller.js @@ -0,0 +1,63 @@ +angular.module("umbraco") +.controller("Umbraco.Editors.DocumentTypes.CopyController", + function ($scope, contentTypeResource, treeService, navigationService, notificationsService, appState, eventsService) { + var dialogOptions = $scope.dialogOptions; + $scope.dialogTreeEventHandler = $({}); + + function nodeSelectHandler(ev, args) { + args.event.preventDefault(); + args.event.stopPropagation(); + + if ($scope.target) { + //un-select if there's a current one selected + $scope.target.selected = false; + } + + $scope.target = args.node; + $scope.target.selected = true; + } + + $scope.copy = function () { + + $scope.busy = true; + $scope.error = false; + + contentTypeResource.copy({ parentId: $scope.target.id, id: dialogOptions.currentNode.id }) + .then(function (path) { + $scope.error = false; + $scope.success = true; + $scope.busy = false; + + //get the currently edited node (if any) + var activeNode = appState.getTreeState("selectedNode"); + + //we need to do a double sync here: first sync to the copied content - but don't activate the node, + //then sync to the currenlty edited content (note: this might not be the content that was copied!!) + + navigationService.syncTree({ tree: "documentTypes", path: path, forceReload: true, activate: false }).then(function (args) { + if (activeNode) { + var activeNodePath = treeService.getPath(activeNode).join(); + //sync to this node now - depending on what was copied this might already be synced but might not be + navigationService.syncTree({ tree: "documentTypes", path: activeNodePath, forceReload: false, activate: true }); + } + }); + + }, function (err) { + $scope.success = false; + $scope.error = err; + $scope.busy = false; + //show any notifications + if (angular.isArray(err.data.notifications)) { + for (var i = 0; i < err.data.notifications.length; i++) { + notificationsService.showNotification(err.data.notifications[i]); + } + } + }); + }; + + $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); + + $scope.$on('$destroy', function () { + $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); + }); + }); diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/copy.html b/src/Umbraco.Web.UI.Client/src/views/documenttypes/copy.html new file mode 100644 index 0000000000..db1a0db640 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/copy.html @@ -0,0 +1,51 @@ +
+ +
+
+ +

+ Select the folder to copy {{currentNode.name}} to in the tree structure below +

+ +
+
+
+ +
+
{{error.errorMsg}}
+

{{error.data.message}}

+
+ +
+
+ {{currentNode.name}} was copied underneath {{target.name}}
+ +
+ +
+ +
+ + +
+ +
+
+
+ + +
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index 4f6b65dc64..0842755da3 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -1055,7 +1055,9 @@ To manage your website, simply open the Umbraco back office and start adding con Yes, delete was moved underneath + was copied underneath Select the folder to move + Select the folder to copy to in the tree structure below All Document types diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml index 1cc40abd1d..ce25e990c8 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -1051,7 +1051,9 @@ To manage your website, simply open the Umbraco back office and start adding con Yes, delete was moved underneath + was copied underneath Select the folder to move + Select the folder to copy to in the tree structure below All Document types diff --git a/src/Umbraco.Web/Editors/ContentTypeController.cs b/src/Umbraco.Web/Editors/ContentTypeController.cs index eaa587fc6c..5099db8cac 100644 --- a/src/Umbraco.Web/Editors/ContentTypeController.cs +++ b/src/Umbraco.Web/Editors/ContentTypeController.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Configuration; using System.Linq; using System.Net; @@ -22,9 +23,9 @@ using Umbraco.Web.Models; namespace Umbraco.Web.Editors { - //TODO: We'll need to be careful about the security on this controller, when we start implementing + //TODO: We'll need to be careful about the security on this controller, when we start implementing // methods to modify content types we'll need to enforce security on the individual methods, we - // cannot put security on the whole controller because things like + // cannot put security on the whole controller because things like // GetAllowedChildren, GetPropertyTypeScaffold, GetAllPropertyTypeAliases are required for content editing. /// @@ -129,7 +130,7 @@ namespace Umbraco.Web.Editors } [UmbracoTreeAuthorize( - Constants.Trees.DocumentTypes, Constants.Trees.Content, + Constants.Trees.DocumentTypes, Constants.Trees.Content, Constants.Trees.MediaTypes, Constants.Trees.Media, Constants.Trees.MemberTypes, Constants.Trees.Members)] public ContentPropertyDisplay GetPropertyTypeScaffold(int id) @@ -166,13 +167,13 @@ namespace Umbraco.Web.Editors return Request.CreateResponse(HttpStatusCode.OK); } - + public HttpResponseMessage PostCreateContainer(int parentId, string name) { var result = Services.ContentTypeService.CreateContentTypeContainer(parentId, name, Security.CurrentUser.Id); return result - ? Request.CreateResponse(HttpStatusCode.OK, result.Result) //return the id + ? Request.CreateResponse(HttpStatusCode.OK, result.Result) //return the id : Request.CreateNotificationValidationErrorResponse(result.Exception.Message); } @@ -203,7 +204,7 @@ namespace Umbraco.Web.Editors //make sure the template alias is set on the default and allowed template so we can map it back ctSave.DefaultTemplate = template.Alias; - + } }); @@ -231,7 +232,7 @@ namespace Umbraco.Web.Editors } else ct = new ContentType(parentId); - + ct.Icon = "icon-document"; var dto = Mapper.Map(ct); @@ -302,18 +303,29 @@ namespace Umbraco.Web.Editors } /// - /// Move the media type + /// Move the content type /// /// /// public HttpResponseMessage PostMove(MoveOrCopy move) { - return PerformMove( + return PerformMoveOrCopy( move, getContentType: i => Services.ContentTypeService.GetContentType(i), - doMove: (type, i) => Services.ContentTypeService.MoveContentType(type, i)); + doMoveOrCopy: (type, i) => Services.ContentTypeService.MoveContentType(type, i)); } - + /// + /// Copy the content type + /// + /// + /// + public HttpResponseMessage PostCopy(MoveOrCopy copy) + { + return PerformMoveOrCopy( + copy, + getContentType: i => Services.ContentTypeService.GetContentType(i), + doMoveOrCopy: (type, i) => Services.ContentTypeService.CopyContentType(type, i)); + } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs b/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs index bc49e10f02..361c1133fd 100644 --- a/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs +++ b/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs @@ -25,7 +25,7 @@ namespace Umbraco.Web.Editors /// /// Am abstract API controller providing functionality used for dealing with content and media types /// - [PluginController("UmbracoApi")] + [PluginController("UmbracoApi")] [PrefixlessBodyModelValidator] public abstract class ContentTypeControllerBase : UmbracoAuthorizedJsonController { @@ -36,7 +36,7 @@ namespace Umbraco.Web.Editors /// protected ContentTypeControllerBase() : this(UmbracoContext.Current) - { + { } /// @@ -61,17 +61,17 @@ namespace Umbraco.Web.Editors /// This is required because in the case of creating/modifying a content type because new property types being added to it are not yet persisted so cannot /// be looked up via the db, they need to be passed in. /// - /// + /// /// - protected IEnumerable> PerformGetAvailableCompositeContentTypes(int contentTypeId, - UmbracoObjectTypes type, + protected IEnumerable> PerformGetAvailableCompositeContentTypes(int contentTypeId, + UmbracoObjectTypes type, string[] filterContentTypes, string[] filterPropertyTypes) { IContentTypeComposition source = null; //below is all ported from the old doc type editor and comes with the same weaknesses /insanity / magic - + IContentTypeComposition[] allContentTypes; switch (type) @@ -132,7 +132,7 @@ namespace Umbraco.Web.Editors }) .ToList(); } - + protected string TranslateItem(string text) { @@ -155,7 +155,7 @@ namespace Umbraco.Web.Editors Action beforeCreateNew = null) where TContentType : class, IContentTypeComposition where TContentTypeDisplay : ContentTypeCompositionDisplay - where TContentTypeSave : ContentTypeSave + where TContentTypeSave : ContentTypeSave where TPropertyType : PropertyTypeBasic { var ctId = Convert.ToInt32(contentTypeSave.Id); @@ -187,10 +187,10 @@ namespace Umbraco.Web.Editors { group.Properties = group.Properties.Where(x => x.Alias.IsNullOrWhiteSpace() == false).ToList(); } - + if (ctId > 0) { - //its an update to an existing content type + //its an update to an existing content type //This mapping will cause a lot of content type validation to occur which we need to deal with try @@ -216,7 +216,7 @@ namespace Umbraco.Web.Editors { beforeCreateNew(contentTypeSave); } - + //check if the type is trying to allow type 0 below itself - id zero refers to the currently unsaved type //always filter these 0 types out var allowItselfAsChild = false; @@ -227,12 +227,12 @@ namespace Umbraco.Web.Editors } //save as new - + TContentType newCt = null; try { //This mapping will cause a lot of content type validation to occur which we need to deal with - newCt = Mapper.Map(contentTypeSave); + newCt = Mapper.Map(contentTypeSave); } catch (Exception ex) { @@ -260,18 +260,18 @@ namespace Umbraco.Web.Editors return newCt; } } - + /// - /// Change the sort order for media + /// Move /// /// /// - /// + /// /// - protected HttpResponseMessage PerformMove( + protected HttpResponseMessage PerformMoveOrCopy( MoveOrCopy move, Func getContentType, - Func>> doMove) + Func>> doMoveOrCopy) where TContentType : IContentTypeComposition { var toMove = getContentType(move.Id); @@ -280,7 +280,7 @@ namespace Umbraco.Web.Editors return Request.CreateResponse(HttpStatusCode.NotFound); } - var result = doMove(toMove, move.ParentId); + var result = doMoveOrCopy(toMove, move.ParentId); if (result.Success) { var response = Request.CreateResponse(HttpStatusCode.OK); @@ -293,7 +293,7 @@ namespace Umbraco.Web.Editors case MoveOperationStatusType.FailedParentNotFound: return Request.CreateResponse(HttpStatusCode.NotFound); case MoveOperationStatusType.FailedCancelledByEvent: - //returning an object of INotificationModel will ensure that any pending + //returning an object of INotificationModel will ensure that any pending // notification messages are added to the response. return Request.CreateValidationErrorResponse(new SimpleNotificationModel()); case MoveOperationStatusType.FailedNotAllowedByPath: @@ -312,14 +312,14 @@ namespace Umbraco.Web.Editors /// /// private HttpResponseException CreateCompositionValidationExceptionIfInvalid(TContentTypeSave contentTypeSave, IContentTypeComposition composition) - where TContentTypeSave : ContentTypeSave + where TContentTypeSave : ContentTypeSave where TPropertyType : PropertyTypeBasic where TContentTypeDisplay : ContentTypeCompositionDisplay { var validateAttempt = Services.ContentTypeService.ValidateComposition(composition); if (validateAttempt == false) { - //if it's not successful then we need to return some model state for the property aliases that + //if it's not successful then we need to return some model state for the property aliases that // are duplicated var invalidPropertyAliases = validateAttempt.Result.Distinct(); AddCompositionValidationErrors(contentTypeSave, invalidPropertyAliases); @@ -340,7 +340,7 @@ namespace Umbraco.Web.Editors /// /// private void AddCompositionValidationErrors(TContentTypeSave contentTypeSave, IEnumerable invalidPropertyAliases) - where TContentTypeSave : ContentTypeSave + where TContentTypeSave : ContentTypeSave where TPropertyType : PropertyTypeBasic { foreach (var propertyAlias in invalidPropertyAliases) @@ -369,8 +369,8 @@ namespace Umbraco.Web.Editors private HttpResponseException CreateInvalidCompositionResponseException( Exception ex, TContentTypeSave contentTypeSave, TContentType ct, int ctId) where TContentType : class, IContentTypeComposition - where TContentTypeDisplay : ContentTypeCompositionDisplay - where TContentTypeSave : ContentTypeSave + where TContentTypeDisplay : ContentTypeCompositionDisplay + where TContentTypeSave : ContentTypeSave where TPropertyType : PropertyTypeBasic { InvalidCompositionException invalidCompositionException = null; @@ -431,7 +431,7 @@ namespace Umbraco.Web.Editors (_cultureDictionary = CultureDictionaryFactoryResolver.Current.Factory.CreateDictionary()); } } - + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Editors/MediaTypeController.cs b/src/Umbraco.Web/Editors/MediaTypeController.cs index 43071125de..81b92f1ee4 100644 --- a/src/Umbraco.Web/Editors/MediaTypeController.cs +++ b/src/Umbraco.Web/Editors/MediaTypeController.cs @@ -22,7 +22,7 @@ using Umbraco.Web.Models; namespace Umbraco.Web.Editors { - //TODO: We'll need to be careful about the security on this controller, when we start implementing + //TODO: We'll need to be careful about the security on this controller, when we start implementing // methods to modify content types we'll need to enforce security on the individual methods, we // cannot put security on the whole controller because things like GetAllowedChildren are required for content editing. @@ -49,7 +49,7 @@ namespace Umbraco.Web.Editors public MediaTypeController(UmbracoContext umbracoContext) : base(umbracoContext) { - + } public int GetCount() @@ -112,7 +112,7 @@ namespace Umbraco.Web.Editors contentType = x.Item1, allowed = x.Item2 }); - return Request.CreateResponse(result); + return Request.CreateResponse(result); } public MediaTypeDisplay GetEmpty(int parentId) @@ -129,8 +129,8 @@ namespace Umbraco.Web.Editors /// Returns all member types /// public IEnumerable GetAll() - { - + { + return Services.ContentTypeService.GetAllMediaTypes() .Select(Mapper.Map); } @@ -154,7 +154,7 @@ namespace Umbraco.Web.Editors var result = Services.ContentTypeService.CreateMediaTypeContainer(parentId, name, Security.CurrentUser.Id); return result - ? Request.CreateResponse(HttpStatusCode.OK, result.Result) //return the id + ? Request.CreateResponse(HttpStatusCode.OK, result.Result) //return the id : Request.CreateNotificationValidationErrorResponse(result.Exception.Message); } @@ -227,11 +227,10 @@ namespace Umbraco.Web.Editors /// public HttpResponseMessage PostMove(MoveOrCopy move) { - return PerformMove( - move, - getContentType: i => Services.ContentTypeService.GetMediaType(i), - doMove: (type, i) => Services.ContentTypeService.MoveMediaType(type, i)); + return PerformMoveOrCopy( + move, + getContentType: i => Services.ContentTypeService.GetMediaType(i), + doMoveOrCopy: (type, i) => Services.ContentTypeService.MoveMediaType(type, i)); } - } } \ No newline at end of file diff --git a/src/Umbraco.Web/Trees/ContentTypeTreeController.cs b/src/Umbraco.Web/Trees/ContentTypeTreeController.cs index c45d5d0c1b..1cda4995fa 100644 --- a/src/Umbraco.Web/Trees/ContentTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTypeTreeController.cs @@ -123,6 +123,7 @@ namespace Umbraco.Web.Trees menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionMove.Instance.Alias)), true); } } + menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionCopy.Instance.Alias))); menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionExport.Instance.Alias)), true).ConvertLegacyMenuItem(new UmbracoEntity { Id = int.Parse(id), From dafde545b0547c1e4759e79f92f5417863cca924 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 25 Feb 2016 16:02:04 +0100 Subject: [PATCH 035/132] U4-7641 - bugfix + implement for media types --- src/Umbraco.Core/Models/IMediaType.cs | 8 ++- src/Umbraco.Core/Models/MediaType.cs | 25 ++++++++ .../Repositories/ContentTypeBaseRepository.cs | 4 +- .../Interfaces/IContentTypeRepository.cs | 1 + .../Interfaces/IMediaTypeRepository.cs | 8 +++ .../Services/ContentTypeService.cs | 42 +++++++++++++ .../Services/IContentTypeService.cs | 1 + .../common/resources/mediatype.resource.js | 26 +++++++- .../src/views/mediatypes/copy.controller.js | 63 +++++++++++++++++++ .../src/views/mediatypes/copy.html | 51 +++++++++++++++ .../Editors/MediaTypeController.cs | 13 ++++ .../Trees/MediaTypeTreeController.cs | 1 + 12 files changed, 237 insertions(+), 6 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/views/mediatypes/copy.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/mediatypes/copy.html diff --git a/src/Umbraco.Core/Models/IMediaType.cs b/src/Umbraco.Core/Models/IMediaType.cs index 3934d7a40f..29e4b665ba 100644 --- a/src/Umbraco.Core/Models/IMediaType.cs +++ b/src/Umbraco.Core/Models/IMediaType.cs @@ -7,6 +7,12 @@ namespace Umbraco.Core.Models ///
+ /// Creates a deep clone of the current entity with its identity/alias and it's property identities reset + ///
+ /// + /// + IMediaType DeepCloneWithResetIdentities(string newAlias); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/MediaType.cs b/src/Umbraco.Core/Models/MediaType.cs index c8e2915afd..8596cce910 100644 --- a/src/Umbraco.Core/Models/MediaType.cs +++ b/src/Umbraco.Core/Models/MediaType.cs @@ -38,5 +38,30 @@ namespace Umbraco.Core.Models : base(parent, alias) { } + + /// + /// Creates a deep clone of the current entity with its identity/alias and it's property identities reset + /// + /// + public IMediaType DeepCloneWithResetIdentities(string alias) + { + var clone = (MediaType)DeepClone(); + clone.Alias = alias; + clone.Key = Guid.Empty; + foreach (var propertyGroup in clone.PropertyGroups) + { + propertyGroup.ResetIdentity(); + propertyGroup.ResetDirtyProperties(false); + } + foreach (var propertyType in clone.PropertyTypes) + { + propertyType.ResetIdentity(); + propertyType.ResetDirtyProperties(false); + } + + clone.ResetIdentity(); + clone.ResetDirtyProperties(false); + return clone; + } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs index d3e7182783..9a33d71519 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs @@ -1215,11 +1215,11 @@ AND umbracoNode.id <> @id", public string GetUniqueAlias(string alias) { + // alias is unique accross ALL content types! var aliasColumn = SqlSyntax.GetQuotedColumnName("alias"); var aliases = Database.Fetch(@"SELECT cmsContentType." + aliasColumn + @" FROM cmsContentType INNER JOIN umbracoNode ON cmsContentType.nodeId = umbracoNode.id -WHERE cmsContentType." + aliasColumn + @" LIKE @pattern -AND umbracoNode.nodeObjectType = @objectType", +WHERE cmsContentType." + aliasColumn + @" LIKE @pattern", new { pattern = alias + "%", objectType = NodeObjectTypeId }); var i = 1; string test; diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentTypeRepository.cs index acb1d50a61..61d83645b3 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentTypeRepository.cs @@ -38,6 +38,7 @@ namespace Umbraco.Core.Persistence.Repositories ///
/// The original alias. /// The original alias with a number appended to it, so that it is unique. + /// /// Unique accross all content, media and member types. string GetUniqueAlias(string alias); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaTypeRepository.cs index 1cec8005c9..7f2f76e541 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IMediaTypeRepository.cs @@ -15,5 +15,13 @@ namespace Umbraco.Core.Persistence.Repositories IEnumerable GetByQuery(IQuery query); IEnumerable> Move(IMediaType toMove, EntityContainer container); + + /// + /// Derives a unique alias from an existing alias. + /// + /// The original alias. + /// The original alias with a number appended to it, so that it is unique. + /// Unique accross all content, media and member types. + string GetUniqueAlias(string alias); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/ContentTypeService.cs b/src/Umbraco.Core/Services/ContentTypeService.cs index 141f4a5a97..72e73d61ae 100644 --- a/src/Umbraco.Core/Services/ContentTypeService.cs +++ b/src/Umbraco.Core/Services/ContentTypeService.cs @@ -1067,6 +1067,48 @@ namespace Umbraco.Core.Services new OperationStatus(MoveOperationStatusType.Success, evtMsgs)); } + public Attempt> CopyMediaType(IMediaType toCopy, int containerId) + { + var evtMsgs = EventMessagesFactory.Get(); + + var uow = UowProvider.GetUnitOfWork(); + using (var containerRepository = RepositoryFactory.CreateEntityContainerRepository(uow, Constants.ObjectTypes.MediaTypeContainerGuid)) + using (var repository = RepositoryFactory.CreateMediaTypeRepository(uow)) + { + try + { + if (containerId > 0) + { + var container = containerRepository.Get(containerId); + if (container == null) + throw new DataOperationException(MoveOperationStatusType.FailedParentNotFound); + } + var alias = repository.GetUniqueAlias(toCopy.Alias); + var copy = toCopy.DeepCloneWithResetIdentities(alias); + copy.Name = copy.Name + " (copy)"; // might not be unique + + // if it has a parent, and the parent is a content type, unplug composition + // all other compositions remain in place in the copied content type + if (copy.ParentId > 0) + { + var parent = repository.Get(copy.ParentId); + if (parent != null) + copy.RemoveContentType(parent.Alias); + } + + copy.ParentId = containerId; + repository.AddOrUpdate(copy); + } + catch (DataOperationException ex) + { + return Attempt.Fail(new OperationStatus(ex.Operation, evtMsgs)); + } + uow.Commit(); + } + + return Attempt.Succeed(new OperationStatus(MoveOperationStatusType.Success, evtMsgs)); + } + public Attempt> CopyContentType(IContentType toCopy, int containerId) { var evtMsgs = EventMessagesFactory.Get(); diff --git a/src/Umbraco.Core/Services/IContentTypeService.cs b/src/Umbraco.Core/Services/IContentTypeService.cs index e55430330c..cbb8bcddb4 100644 --- a/src/Umbraco.Core/Services/IContentTypeService.cs +++ b/src/Umbraco.Core/Services/IContentTypeService.cs @@ -291,6 +291,7 @@ namespace Umbraco.Core.Services Attempt> MoveMediaType(IMediaType toMove, int containerId); Attempt> MoveContentType(IContentType toMove, int containerId); + Attempt> CopyMediaType(IMediaType toCopy, int containerId); Attempt> CopyContentType(IContentType toCopy, int containerId); } } \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/mediatype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/mediatype.resource.js index 516b9eb5d1..117edef77f 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/mediatype.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/mediatype.resource.js @@ -53,7 +53,7 @@ function mediaTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { * .then(function(array) { * $scope.type = type; * }); - * + * * @param {Int} mediaId id of the media item to retrive allowed child types for * @returns {Promise} resourcePromise object. * @@ -145,9 +145,9 @@ function mediaTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { * .then(function() { * alert("node was moved"); * }, function(err){ - * alert("node didnt move:" + err.data.Message); + * alert("node didnt move:" + err.data.Message); * }); - * + * * @param {Object} args arguments object * @param {Int} args.idd the ID of the node to move * @param {Int} args.parentId the ID of the parent node to move to @@ -174,6 +174,26 @@ function mediaTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { 'Failed to move content'); }, + copy: function (args) { + if (!args) { + throw "args cannot be null"; + } + if (!args.parentId) { + throw "args.parentId cannot be null"; + } + if (!args.id) { + throw "args.id cannot be null"; + } + + return umbRequestHelper.resourcePromise( + $http.post(umbRequestHelper.getApiUrl("mediaTypeApiBaseUrl", "PostCopy"), + { + parentId: args.parentId, + id: args.id + }), + 'Failed to copy content'); + }, + createContainer: function(parentId, name) { return umbRequestHelper.resourcePromise( diff --git a/src/Umbraco.Web.UI.Client/src/views/mediatypes/copy.controller.js b/src/Umbraco.Web.UI.Client/src/views/mediatypes/copy.controller.js new file mode 100644 index 0000000000..2a1b2463f8 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/mediatypes/copy.controller.js @@ -0,0 +1,63 @@ +angular.module("umbraco") +.controller("Umbraco.Editors.MediaTypes.CopyController", + function ($scope, mediaTypeResource, treeService, navigationService, notificationsService, appState, eventsService) { + var dialogOptions = $scope.dialogOptions; + $scope.dialogTreeEventHandler = $({}); + + function nodeSelectHandler(ev, args) { + args.event.preventDefault(); + args.event.stopPropagation(); + + if ($scope.target) { + //un-select if there's a current one selected + $scope.target.selected = false; + } + + $scope.target = args.node; + $scope.target.selected = true; + } + + $scope.copy = function () { + + $scope.busy = true; + $scope.error = false; + + mediaTypeResource.copy({ parentId: $scope.target.id, id: dialogOptions.currentNode.id }) + .then(function (path) { + $scope.error = false; + $scope.success = true; + $scope.busy = false; + + //get the currently edited node (if any) + var activeNode = appState.getTreeState("selectedNode"); + + //we need to do a double sync here: first sync to the copied content - but don't activate the node, + //then sync to the currenlty edited content (note: this might not be the content that was copied!!) + + navigationService.syncTree({ tree: "mediaTypes", path: path, forceReload: true, activate: false }).then(function (args) { + if (activeNode) { + var activeNodePath = treeService.getPath(activeNode).join(); + //sync to this node now - depending on what was copied this might already be synced but might not be + navigationService.syncTree({ tree: "mediaTypes", path: activeNodePath, forceReload: false, activate: true }); + } + }); + + }, function (err) { + $scope.success = false; + $scope.error = err; + $scope.busy = false; + //show any notifications + if (angular.isArray(err.data.notifications)) { + for (var i = 0; i < err.data.notifications.length; i++) { + notificationsService.showNotification(err.data.notifications[i]); + } + } + }); + }; + + $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler); + + $scope.$on('$destroy', function () { + $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); + }); + }); diff --git a/src/Umbraco.Web.UI.Client/src/views/mediatypes/copy.html b/src/Umbraco.Web.UI.Client/src/views/mediatypes/copy.html new file mode 100644 index 0000000000..319e59c4cc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/mediatypes/copy.html @@ -0,0 +1,51 @@ +
+ +
+
+ +

+ Select the folder to copy {{currentNode.name}} to in the tree structure below +

+ +
+
+
+ +
+
{{error.errorMsg}}
+

{{error.data.message}}

+
+ +
+
+ {{currentNode.name}} was copied underneath {{target.name}}
+ +
+ +
+ +
+ + +
+ +
+
+
+ + +
diff --git a/src/Umbraco.Web/Editors/MediaTypeController.cs b/src/Umbraco.Web/Editors/MediaTypeController.cs index 81b92f1ee4..f736c207b8 100644 --- a/src/Umbraco.Web/Editors/MediaTypeController.cs +++ b/src/Umbraco.Web/Editors/MediaTypeController.cs @@ -232,5 +232,18 @@ namespace Umbraco.Web.Editors getContentType: i => Services.ContentTypeService.GetMediaType(i), doMoveOrCopy: (type, i) => Services.ContentTypeService.MoveMediaType(type, i)); } + + /// + /// Copy the media type + /// + /// + /// + public HttpResponseMessage PostCopy(MoveOrCopy copy) + { + return PerformMoveOrCopy( + copy, + getContentType: i => Services.ContentTypeService.GetMediaType(i), + doMoveOrCopy: (type, i) => Services.ContentTypeService.CopyMediaType(type, i)); + } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Trees/MediaTypeTreeController.cs b/src/Umbraco.Web/Trees/MediaTypeTreeController.cs index a7da33898d..717d53ab96 100644 --- a/src/Umbraco.Web/Trees/MediaTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/MediaTypeTreeController.cs @@ -92,6 +92,7 @@ namespace Umbraco.Web.Trees { menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias))); menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionMove.Instance.Alias)), hasSeparator: true); + menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionCopy.Instance.Alias))); } return menu; From 57a766098de4b422565b45b6ee8b7b0fb4c996b8 Mon Sep 17 00:00:00 2001 From: Lars-Erik Aabech Date: Thu, 25 Feb 2016 16:04:37 +0100 Subject: [PATCH 036/132] Removed nested form and changed buttons to type="button" + action instead of ng-submit on form. --- .../src/views/components/overlays/umb-overlay.html | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/components/overlays/umb-overlay.html b/src/Umbraco.Web.UI.Client/src/views/components/overlays/umb-overlay.html index e3031d6e1f..9bc0839122 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/overlays/umb-overlay.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/overlays/umb-overlay.html @@ -1,5 +1,5 @@
-
+

{{model.title}}

@@ -44,8 +44,9 @@ + type="button" + disabled="!directive.enableConfirmButton" + action="submitForm(model)">
@@ -64,10 +65,11 @@ label-key="{{model.submitButtonLabelKey}}" label="{{model.submitButtonLabel}}" ng-if="model.submit && model.hideSubmitButton !== true" - type="submit"> + type="button" + action="submitForm(model)"> - + From 2719e2c23123c094016ab4ba47d541b1007595dc Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 25 Feb 2016 15:19:42 +0000 Subject: [PATCH 037/132] WIP - ListView that grabs the current nodes permission from the editorState & use a neater object to return to the view rather than the lovely Magic String CHARs. Need help understanding that I don't get all the permissions to be expected --- .../listview/listview.controller.js | 35 +++++++++++++++++-- .../propertyeditors/listview/listview.html | 12 ++++--- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js index 85e43c02ec..b22978526e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js @@ -1,4 +1,4 @@ -function listViewController($rootScope, $scope, $routeParams, $injector, $cookieStore, notificationsService, iconHelper, dialogService, editorState, localizationService, $location, appState, $timeout, $q, mediaResource, listViewHelper) { +function listViewController($rootScope, $scope, $routeParams, $injector, $cookieStore, notificationsService, iconHelper, dialogService, editorState, localizationService, $location, appState, $timeout, $q, mediaResource, listViewHelper, userService) { //this is a quick check to see if we're in create mode, if so just exit - we cannot show children for content // that isn't created yet, if we continue this will use the parent id in the route params which isn't what @@ -59,6 +59,36 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie items: [] }; + + $scope.currentNodePermissions = {} + + //Just ensure we do have an editorState + if(editorState.current){ + + //Get Current User to check if their usertype is an admin (overwrites all permissions!) + userService.getCurrentUser().then(function(data){ + + //Fetch current node allowed actions for the current user + //This is the current node & not each individual child node in the list + var currentUserPermissions = editorState.current.allowedActions; + + //Create a nicer model rather than the funky & hard to remember permissions strings + $scope.currentNodePermissions = { + "isAdminUser": data.userType.toLowerCase() === "admin", + "canCopy": _.contains(currentUserPermissions, 'O'), //Magic Char = O + "canDelete": _.contains(currentUserPermissions, 'D'), //Magic Char = D + "canMove": _.contains(currentUserPermissions, 'M'), //Magic Char = M + "canPublish": _.contains(currentUserPermissions, 'U'), //Magic Char = U + "canUnpublish": _.contains(currentUserPermissions, 'Z'), //Magic Char = Z + "rawPermissions": currentUserPermissions + }; + + }); + } + + + + $scope.options = { displayAtTabNumber: $scope.model.config.displayAtTabNumber ? $scope.model.config.displayAtTabNumber : 1, pageSize: $scope.model.config.pageSize ? $scope.model.config.pageSize : 10, @@ -166,9 +196,8 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie getListResultsCallback(id, $scope.options).then(function(data) { $scope.actionInProgress = false; - $scope.listViewResultSet = data; - + //update all values for display if ($scope.listViewResultSet.items) { _.each($scope.listViewResultSet.items, function(e, index) { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html index 0f8014529e..ee5fa32581 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html @@ -82,7 +82,7 @@ + +
{{ currentNodePermissions | json }}
From f349a9ae3e9dcfb8f0ec28088ed5419a22a9fea0 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 25 Feb 2016 16:26:20 +0100 Subject: [PATCH 038/132] U4-7641 - bugfix --- .../Services/ContentTypeService.cs | 18 ++++--- .../Services/IContentTypeService.cs | 4 +- .../Editors/ContentTypeController.cs | 8 +-- .../Editors/ContentTypeControllerBase.cs | 53 +++++++++++++++++-- .../Editors/MediaTypeController.cs | 8 +-- 5 files changed, 69 insertions(+), 22 deletions(-) diff --git a/src/Umbraco.Core/Services/ContentTypeService.cs b/src/Umbraco.Core/Services/ContentTypeService.cs index 72e73d61ae..34dcdc01a9 100644 --- a/src/Umbraco.Core/Services/ContentTypeService.cs +++ b/src/Umbraco.Core/Services/ContentTypeService.cs @@ -1067,10 +1067,11 @@ namespace Umbraco.Core.Services new OperationStatus(MoveOperationStatusType.Success, evtMsgs)); } - public Attempt> CopyMediaType(IMediaType toCopy, int containerId) + public Attempt> CopyMediaType(IMediaType toCopy, int containerId) { var evtMsgs = EventMessagesFactory.Get(); + IMediaType copy; var uow = UowProvider.GetUnitOfWork(); using (var containerRepository = RepositoryFactory.CreateEntityContainerRepository(uow, Constants.ObjectTypes.MediaTypeContainerGuid)) using (var repository = RepositoryFactory.CreateMediaTypeRepository(uow)) @@ -1084,7 +1085,7 @@ namespace Umbraco.Core.Services throw new DataOperationException(MoveOperationStatusType.FailedParentNotFound); } var alias = repository.GetUniqueAlias(toCopy.Alias); - var copy = toCopy.DeepCloneWithResetIdentities(alias); + copy = toCopy.DeepCloneWithResetIdentities(alias); copy.Name = copy.Name + " (copy)"; // might not be unique // if it has a parent, and the parent is a content type, unplug composition @@ -1101,18 +1102,19 @@ namespace Umbraco.Core.Services } catch (DataOperationException ex) { - return Attempt.Fail(new OperationStatus(ex.Operation, evtMsgs)); + return Attempt.Fail(new OperationStatus(null, ex.Operation, evtMsgs)); } uow.Commit(); } - return Attempt.Succeed(new OperationStatus(MoveOperationStatusType.Success, evtMsgs)); + return Attempt.Succeed(new OperationStatus(copy, MoveOperationStatusType.Success, evtMsgs)); } - public Attempt> CopyContentType(IContentType toCopy, int containerId) + public Attempt> CopyContentType(IContentType toCopy, int containerId) { var evtMsgs = EventMessagesFactory.Get(); + IContentType copy; var uow = UowProvider.GetUnitOfWork(); using (var containerRepository = RepositoryFactory.CreateEntityContainerRepository(uow, Constants.ObjectTypes.DocumentTypeContainerGuid)) using (var repository = RepositoryFactory.CreateContentTypeRepository(uow)) @@ -1126,7 +1128,7 @@ namespace Umbraco.Core.Services throw new DataOperationException(MoveOperationStatusType.FailedParentNotFound); } var alias = repository.GetUniqueAlias(toCopy.Alias); - var copy = toCopy.DeepCloneWithResetIdentities(alias); + copy = toCopy.DeepCloneWithResetIdentities(alias); copy.Name = copy.Name + " (copy)"; // might not be unique // if it has a parent, and the parent is a content type, unplug composition @@ -1143,12 +1145,12 @@ namespace Umbraco.Core.Services } catch (DataOperationException ex) { - return Attempt.Fail(new OperationStatus(ex.Operation, evtMsgs)); + return Attempt.Fail(new OperationStatus(null, ex.Operation, evtMsgs)); } uow.Commit(); } - return Attempt.Succeed(new OperationStatus(MoveOperationStatusType.Success, evtMsgs)); + return Attempt.Succeed(new OperationStatus(copy, MoveOperationStatusType.Success, evtMsgs)); } /// diff --git a/src/Umbraco.Core/Services/IContentTypeService.cs b/src/Umbraco.Core/Services/IContentTypeService.cs index cbb8bcddb4..2dcdf01291 100644 --- a/src/Umbraco.Core/Services/IContentTypeService.cs +++ b/src/Umbraco.Core/Services/IContentTypeService.cs @@ -291,7 +291,7 @@ namespace Umbraco.Core.Services Attempt> MoveMediaType(IMediaType toMove, int containerId); Attempt> MoveContentType(IContentType toMove, int containerId); - Attempt> CopyMediaType(IMediaType toCopy, int containerId); - Attempt> CopyContentType(IContentType toCopy, int containerId); + Attempt> CopyMediaType(IMediaType toCopy, int containerId); + Attempt> CopyContentType(IContentType toCopy, int containerId); } } \ No newline at end of file diff --git a/src/Umbraco.Web/Editors/ContentTypeController.cs b/src/Umbraco.Web/Editors/ContentTypeController.cs index 5099db8cac..45a2821130 100644 --- a/src/Umbraco.Web/Editors/ContentTypeController.cs +++ b/src/Umbraco.Web/Editors/ContentTypeController.cs @@ -309,10 +309,10 @@ namespace Umbraco.Web.Editors /// public HttpResponseMessage PostMove(MoveOrCopy move) { - return PerformMoveOrCopy( + return PerformMove( move, getContentType: i => Services.ContentTypeService.GetContentType(i), - doMoveOrCopy: (type, i) => Services.ContentTypeService.MoveContentType(type, i)); + doMove: (type, i) => Services.ContentTypeService.MoveContentType(type, i)); } /// @@ -322,10 +322,10 @@ namespace Umbraco.Web.Editors /// public HttpResponseMessage PostCopy(MoveOrCopy copy) { - return PerformMoveOrCopy( + return PerformCopy( copy, getContentType: i => Services.ContentTypeService.GetContentType(i), - doMoveOrCopy: (type, i) => Services.ContentTypeService.CopyContentType(type, i)); + doCopy: (type, i) => Services.ContentTypeService.CopyContentType(type, i)); } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs b/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs index 361c1133fd..0e699b591a 100644 --- a/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs +++ b/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs @@ -266,12 +266,12 @@ namespace Umbraco.Web.Editors /// /// /// - /// + /// /// - protected HttpResponseMessage PerformMoveOrCopy( + protected HttpResponseMessage PerformMove( MoveOrCopy move, Func getContentType, - Func>> doMoveOrCopy) + Func>> doMove) where TContentType : IContentTypeComposition { var toMove = getContentType(move.Id); @@ -280,7 +280,7 @@ namespace Umbraco.Web.Editors return Request.CreateResponse(HttpStatusCode.NotFound); } - var result = doMoveOrCopy(toMove, move.ParentId); + var result = doMove(toMove, move.ParentId); if (result.Success) { var response = Request.CreateResponse(HttpStatusCode.OK); @@ -305,6 +305,51 @@ namespace Umbraco.Web.Editors } } + /// + /// Move + /// + /// + /// + /// + /// + protected HttpResponseMessage PerformCopy( + MoveOrCopy move, + Func getContentType, + Func>> doCopy) + where TContentType : IContentTypeComposition + { + var toMove = getContentType(move.Id); + if (toMove == null) + { + return Request.CreateResponse(HttpStatusCode.NotFound); + } + + var result = doCopy(toMove, move.ParentId); + if (result.Success) + { + var copy = result.Result.Entity; + var response = Request.CreateResponse(HttpStatusCode.OK); + response.Content = new StringContent(copy.Path, Encoding.UTF8, "application/json"); + return response; + } + + switch (result.Result.StatusType) + { + case MoveOperationStatusType.FailedParentNotFound: + return Request.CreateResponse(HttpStatusCode.NotFound); + case MoveOperationStatusType.FailedCancelledByEvent: + //returning an object of INotificationModel will ensure that any pending + // notification messages are added to the response. + return Request.CreateValidationErrorResponse(new SimpleNotificationModel()); + case MoveOperationStatusType.FailedNotAllowedByPath: + var notificationModel = new SimpleNotificationModel(); + notificationModel.AddErrorNotification(Services.TextService.Localize("moveOrCopy/notAllowedByPath"), ""); + return Request.CreateValidationErrorResponse(notificationModel); + default: + throw new ArgumentOutOfRangeException(); + } + } + /// /// Validates the composition and adds errors to the model state if any are found then throws an error response if there are errors /// diff --git a/src/Umbraco.Web/Editors/MediaTypeController.cs b/src/Umbraco.Web/Editors/MediaTypeController.cs index f736c207b8..31be4509fb 100644 --- a/src/Umbraco.Web/Editors/MediaTypeController.cs +++ b/src/Umbraco.Web/Editors/MediaTypeController.cs @@ -227,10 +227,10 @@ namespace Umbraco.Web.Editors /// public HttpResponseMessage PostMove(MoveOrCopy move) { - return PerformMoveOrCopy( + return PerformMove( move, getContentType: i => Services.ContentTypeService.GetMediaType(i), - doMoveOrCopy: (type, i) => Services.ContentTypeService.MoveMediaType(type, i)); + doMove: (type, i) => Services.ContentTypeService.MoveMediaType(type, i)); } /// @@ -240,10 +240,10 @@ namespace Umbraco.Web.Editors /// public HttpResponseMessage PostCopy(MoveOrCopy copy) { - return PerformMoveOrCopy( + return PerformCopy( copy, getContentType: i => Services.ContentTypeService.GetMediaType(i), - doMoveOrCopy: (type, i) => Services.ContentTypeService.CopyMediaType(type, i)); + doCopy: (type, i) => Services.ContentTypeService.CopyMediaType(type, i)); } } } \ No newline at end of file From 7d8791ec8618663afee0bdf7ec6813bbd603aef4 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 25 Feb 2016 17:05:44 +0100 Subject: [PATCH 039/132] Fix ActionButtonsResolver (need review) --- .../Models/Mapping/ContentModelMapper.cs | 32 ++----------------- 1 file changed, 3 insertions(+), 29 deletions(-) diff --git a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs index 2c9a6410b4..1f36eb6c5b 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs @@ -292,35 +292,9 @@ namespace Umbraco.Web.Models.Mapping source.HasIdentity ? source.Id : source.ParentId) .FirstOrDefault(); - if (permissions == null) - { - return Enumerable.Empty(); - } - - var result = new List(); - - //can they publish ? - if (permissions.AssignedPermissions.Contains(ActionPublish.Instance.Letter.ToString(CultureInfo.InvariantCulture))) - { - result.Add(ActionPublish.Instance.Letter); - } - //can they send to publish ? - if (permissions.AssignedPermissions.Contains(ActionToPublish.Instance.Letter.ToString(CultureInfo.InvariantCulture))) - { - result.Add(ActionToPublish.Instance.Letter); - } - //can they save ? - if (permissions.AssignedPermissions.Contains(ActionUpdate.Instance.Letter.ToString(CultureInfo.InvariantCulture))) - { - result.Add(ActionUpdate.Instance.Letter); - } - //can they create ? - if (permissions.AssignedPermissions.Contains(ActionNew.Instance.Letter.ToString(CultureInfo.InvariantCulture))) - { - result.Add(ActionNew.Instance.Letter); - } - - return result; + return permissions == null + ? Enumerable.Empty() + : permissions.AssignedPermissions.Where(x => x.Length == 1).Select(x => x.ToUpperInvariant()[0]); } } From 36246b96d49d70dbe9509a69a755d6295b195a41 Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 25 Feb 2016 17:39:34 +0100 Subject: [PATCH 040/132] Fix bug with permissions cache refresh --- src/Umbraco.Web/Cache/UserCacheRefresher.cs | 2 +- src/Umbraco.Web/Cache/UserPermissionsCacheRefresher.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/Cache/UserCacheRefresher.cs b/src/Umbraco.Web/Cache/UserCacheRefresher.cs index 2f8a370cd4..6f9cc7db80 100644 --- a/src/Umbraco.Web/Cache/UserCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/UserCacheRefresher.cs @@ -49,7 +49,7 @@ namespace Umbraco.Web.Cache userCache.Result.ClearCacheItem(RepositoryBase.GetCacheIdKey(id)); if (UserPermissionsCache) - UserPermissionsCache.Result.ClearCacheItem(string.Format("{0}{1}", CacheKeys.UserPermissionsCacheKey, id)); + UserPermissionsCache.Result.ClearCacheByKeySearch(string.Format("{0}{1}", CacheKeys.UserPermissionsCacheKey, id)); base.Remove(id); } diff --git a/src/Umbraco.Web/Cache/UserPermissionsCacheRefresher.cs b/src/Umbraco.Web/Cache/UserPermissionsCacheRefresher.cs index 64744024ed..eed29ff764 100644 --- a/src/Umbraco.Web/Cache/UserPermissionsCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/UserPermissionsCacheRefresher.cs @@ -46,7 +46,7 @@ namespace Umbraco.Web.Cache public override void Remove(int id) { if (UserPermissionsCache) - UserPermissionsCache.Result.ClearCacheItem(string.Format("{0}{1}", CacheKeys.UserPermissionsCacheKey, id)); + UserPermissionsCache.Result.ClearCacheByKeySearch(string.Format("{0}{1}", CacheKeys.UserPermissionsCacheKey, id)); base.Remove(id); } From 17b29357d45177a4f356c9d5e4b1f739f3cee6bf Mon Sep 17 00:00:00 2001 From: Stephan Date: Thu, 25 Feb 2016 17:39:34 +0100 Subject: [PATCH 041/132] U4-7040 - Fix bug with permissions cache refresh --- src/Umbraco.Web/Cache/UserCacheRefresher.cs | 2 +- src/Umbraco.Web/Cache/UserPermissionsCacheRefresher.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web/Cache/UserCacheRefresher.cs b/src/Umbraco.Web/Cache/UserCacheRefresher.cs index 2f8a370cd4..6f9cc7db80 100644 --- a/src/Umbraco.Web/Cache/UserCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/UserCacheRefresher.cs @@ -49,7 +49,7 @@ namespace Umbraco.Web.Cache userCache.Result.ClearCacheItem(RepositoryBase.GetCacheIdKey(id)); if (UserPermissionsCache) - UserPermissionsCache.Result.ClearCacheItem(string.Format("{0}{1}", CacheKeys.UserPermissionsCacheKey, id)); + UserPermissionsCache.Result.ClearCacheByKeySearch(string.Format("{0}{1}", CacheKeys.UserPermissionsCacheKey, id)); base.Remove(id); } diff --git a/src/Umbraco.Web/Cache/UserPermissionsCacheRefresher.cs b/src/Umbraco.Web/Cache/UserPermissionsCacheRefresher.cs index 64744024ed..eed29ff764 100644 --- a/src/Umbraco.Web/Cache/UserPermissionsCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/UserPermissionsCacheRefresher.cs @@ -46,7 +46,7 @@ namespace Umbraco.Web.Cache public override void Remove(int id) { if (UserPermissionsCache) - UserPermissionsCache.Result.ClearCacheItem(string.Format("{0}{1}", CacheKeys.UserPermissionsCacheKey, id)); + UserPermissionsCache.Result.ClearCacheByKeySearch(string.Format("{0}{1}", CacheKeys.UserPermissionsCacheKey, id)); base.Remove(id); } From c616354522d9566ddeadc8705c5e804aab8956bd Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 25 Feb 2016 20:49:56 +0000 Subject: [PATCH 042/132] After reading src code for contentEditorHelper.configureContentEditorButtons it makes the same assumption that if the publish action 'U' is present then the user can also unpublish aka 'Z' --- .../src/views/propertyeditors/listview/listview.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js index b22978526e..b516aad062 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js @@ -79,7 +79,7 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie "canDelete": _.contains(currentUserPermissions, 'D'), //Magic Char = D "canMove": _.contains(currentUserPermissions, 'M'), //Magic Char = M "canPublish": _.contains(currentUserPermissions, 'U'), //Magic Char = U - "canUnpublish": _.contains(currentUserPermissions, 'Z'), //Magic Char = Z + "canUnpublish": _.contains(currentUserPermissions, 'U'), //Magic Char = Z (however UI says it can't be set, so if we can publish 'U' we can unpublish) "rawPermissions": currentUserPermissions }; From 956425dfbf316f0ef9ba0f2b166ce049630bd8d4 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 25 Feb 2016 21:06:25 +0000 Subject: [PATCH 043/132] Now updated so the create button hides if we do not have the Create permission 'C' --- .../src/views/propertyeditors/listview/listview.controller.js | 1 + .../src/views/propertyeditors/listview/listview.html | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js index b516aad062..665ff22cd3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js @@ -76,6 +76,7 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie $scope.currentNodePermissions = { "isAdminUser": data.userType.toLowerCase() === "admin", "canCopy": _.contains(currentUserPermissions, 'O'), //Magic Char = O + "canCreate": _.contains(currentUserPermissions, 'C'), //Magic Char = C "canDelete": _.contains(currentUserPermissions, 'D'), //Magic Char = D "canMove": _.contains(currentUserPermissions, 'M'), //Magic Char = M "canPublish": _.contains(currentUserPermissions, 'U'), //Magic Char = U diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html index ee5fa32581..c2657459a0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html @@ -10,7 +10,7 @@ - +
Create From c7a3efadb12d51eb3f706593d02c2de310749c91 Mon Sep 17 00:00:00 2001 From: Warren Buckley Date: Thu, 25 Feb 2016 21:09:12 +0000 Subject: [PATCH 044/132] Remove debug JSON in the view --- .../src/views/propertyeditors/listview/listview.html | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html index c2657459a0..a7a94ff53a 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html @@ -152,8 +152,6 @@ entity-type="{{entityType}}" on-get-content="reloadView"> - -
{{ currentNodePermissions | json }}
From 0d191fb8bd091b091b2bd5781551f28f0535173b Mon Sep 17 00:00:00 2001 From: Stephan Date: Fri, 26 Feb 2016 11:35:24 +0100 Subject: [PATCH 045/132] U4-7823 - stop flooding the log with errors getting media 0 --- .../XmlPublishedCache/PublishedMediaCache.cs | 70 ++++++++++--------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs index 61dd50e610..79806b3273 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs @@ -87,7 +87,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { throw new NotImplementedException("PublishedMediaCache does not support XPath."); } - + public virtual IEnumerable GetByXPath(UmbracoContext umbracoContext, bool preview, string xpath, XPathVariable[] vars) { throw new NotImplementedException("PublishedMediaCache does not support XPath."); @@ -105,7 +105,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache public bool XPathNavigatorIsNavigable { get { return false; } } - public virtual bool HasContent(UmbracoContext context, bool preview) { throw new NotImplementedException(); } + public virtual bool HasContent(UmbracoContext context, bool preview) { throw new NotImplementedException(); } private ExamineManager GetExamineManagerSafe() { @@ -138,7 +138,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache return indexer; } catch (Exception ex) - { + { LogHelper.Error("Could not retrieve the InternalIndexer", ex); //something didn't work, continue returning null. } @@ -182,6 +182,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache // it is called, but at least it should NOT hit the database // nor Lucene each time, relying on the memory cache instead + if (id <= 0) return null; // fail fast + var cacheValues = GetCacheValues(id, GetUmbracoMediaCacheValues); return cacheValues == null ? null : CreateFromCacheValues(cacheValues); @@ -197,11 +199,11 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { //first check in Examine as this is WAY faster var criteria = searchProvider.CreateSearchCriteria("media"); - + var filter = criteria.Id(id).Not().Field(UmbracoContentIndexer.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()); //the above filter will create a query like this, NOTE: That since the use of the wildcard, it automatically escapes it in Lucene. //+(+__NodeId:3113 -__Path:-1,-21,*) +__IndexType:media - + var results = searchProvider.Search(filter.Compile()); if (results.Any()) { @@ -215,7 +217,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache //Catch the exception here for the time being, and just fallback to GetMedia //TODO: Need to fix examine in LB scenarios! LogHelper.Error("Could not load data from Examine index for media", ex); - } + } } LogHelper.Warn( @@ -231,8 +233,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { if (media != null && media.Current != null) { - return media.Current.Name.InvariantEquals("error") - ? null + return media.Current.Name.InvariantEquals("error") + ? null : ConvertFromXPathNavigator(media.Current); } @@ -245,14 +247,14 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache internal CacheValues ConvertFromSearchResult(SearchResult searchResult) { - //NOTE: Some fields will not be included if the config section for the internal index has been + //NOTE: Some fields will not be included if the config section for the internal index has been //mucked around with. It should index everything and so the index definition should simply be: // - + var values = new Dictionary(searchResult.Fields); //we need to ensure some fields exist, because of the above issue - if (!new []{"template", "templateId"}.Any(values.ContainsKey)) + if (!new []{"template", "templateId"}.Any(values.ContainsKey)) values.Add("template", 0.ToString()); if (!new[] { "sortOrder" }.Any(values.ContainsKey)) values.Add("sortOrder", 0.ToString()); @@ -269,7 +271,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache if (!new[] { "createDate" }.Any(values.ContainsKey)) values.Add("createDate", default(DateTime).ToString("yyyy-MM-dd HH:mm:ss")); if (!new[] { "level" }.Any(values.ContainsKey)) - { + { values.Add("level", values["__Path"].Split(',').Length.ToString()); } @@ -303,7 +305,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { values["nodeTypeAlias"] = xpath.Name; } - + var result = xpath.SelectChildren(XPathNodeType.Element); //add the attributes e.g. id, parentId etc if (result.Current != null && result.Current.HasAttributes) @@ -320,7 +322,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache if (!values.ContainsKey(result.Current.Name)) { values[result.Current.Name] = result.Current.Value; - } + } } result.Current.MoveToParent(); } @@ -351,9 +353,9 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache XPath = forceNav ? xpath : null // outside of tests we do NOT want to cache the navigator! }; - //var content = new DictionaryPublishedContent(values, + //var content = new DictionaryPublishedContent(values, // d => d.ParentId != -1 //parent should be null if -1 - // ? GetUmbracoMedia(d.ParentId) + // ? GetUmbracoMedia(d.ParentId) // : null, // //callback to return the children of the current node based on the xml structure already found // d => GetChildrenMedia(d.Id, xpath), @@ -363,8 +365,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache } /// - /// We will need to first check if the document was loaded by Examine, if so we'll need to check if this property exists - /// in the results, if it does not, then we'll have to revert to looking up in the db. + /// We will need to first check if the document was loaded by Examine, if so we'll need to check if this property exists + /// in the results, if it does not, then we'll have to revert to looking up in the db. /// /// /// @@ -399,7 +401,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache /// /// private IEnumerable GetChildrenMedia(int parentId, XPathNavigator xpath = null) - { + { //if there is no navigator, try examine first, then re-look it up if (xpath == null) @@ -412,7 +414,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { //first check in Examine as this is WAY faster var criteria = searchProvider.CreateSearchCriteria("media"); - + var filter = criteria.ParentId(parentId).Not().Field(UmbracoContentIndexer.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()); //the above filter will create a query like this, NOTE: That since the use of the wildcard, it automatically escapes it in Lucene. //+(+parentId:3113 -__Path:-1,-21,*) +__IndexType:media @@ -433,7 +435,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache { results = searchProvider.Search(filter.Compile()); } - + if (results.Any()) { // var medias = results.Select(ConvertFromSearchResult); @@ -451,7 +453,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache else { //if there's no result then return null. Previously we defaulted back to library.GetMedia below - //but this will always get called for when we are getting descendents since many items won't have + //but this will always get called for when we are getting descendents since many items won't have //children and then we are hitting the database again! //So instead we're going to rely on Examine to have the correct results like it should. return Enumerable.Empty(); @@ -462,7 +464,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache //Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco //See this thread: http://examine.cdodeplex.com/discussions/264341 //Catch the exception here for the time being, and just fallback to GetMedia - } + } } //falling back to get media @@ -514,13 +516,13 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache // // will leave it here as it must have done something! // if (x.Name != "contents") // { - // //make sure it's actually a node, not a property + // //make sure it's actually a node, not a property // if (!string.IsNullOrEmpty(x.GetAttribute("path", "")) && // !string.IsNullOrEmpty(x.GetAttribute("id", ""))) // { // mediaList.Add(ConvertFromXPathNavigator(x)); // } - // } + // } //} return mediaList; @@ -530,7 +532,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache /// An IPublishedContent that is represented all by a dictionary. ///
/// - /// This is a helper class and definitely not intended for public use, it expects that all of the values required + /// This is a helper class and definitely not intended for public use, it expects that all of the values required /// to create an IPublishedContent exist in the dictionary by specific aliases. /// internal class DictionaryPublishedContent : PublishedContentWithKeyBase @@ -544,7 +546,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache private static readonly string[] IgnoredKeys = { "version", "isDoc" }; public DictionaryPublishedContent( - IDictionary valueDictionary, + IDictionary valueDictionary, Func getParent, Func> getChildren, Func getProperty, @@ -585,7 +587,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache if (int.TryParse(val, out pId)) { ParentId = pId; - } + } }, "parentID"); _contentType = PublishedContentType.Get(PublishedItemType.Media, _documentTypeAlias); @@ -600,7 +602,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache string value; const bool isPreviewing = false; // false :: never preview a media var property = valueDictionary.TryGetValue(alias, out value) == false - ? new XmlPublishedProperty(propertyType, isPreviewing) + ? new XmlPublishedProperty(propertyType, isPreviewing) : new XmlPublishedProperty(propertyType, isPreviewing, value); _properties.Add(property); } @@ -648,7 +650,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache internal bool LoadedFromExamine { get; private set; } //private readonly Func _getParent; - private readonly Lazy _getParent; + private readonly Lazy _getParent; //private readonly Func> _getChildren; private readonly Lazy> _getChildren; private readonly Func _getProperty; @@ -788,7 +790,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache IPublishedProperty property; string key = null; var cache = UmbracoContextCache.Current; - + if (cache != null) { key = string.Format("RECURSIVE_PROPERTY::{0}::{1}", Id, alias.ToLowerInvariant()); @@ -911,15 +913,15 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache // we do clear a lot of things... but the cache refresher is somewhat // convoluted and it's hard to tell what to clear exactly ;-( - + // clear the parent - NOT (why?) //var exist = (CacheValues) cache.GetCacheItem(key); //if (exist != null) // cache.ClearCacheItem(PublishedMediaCacheKey + GetValuesValue(exist.Values, "parentID")); - + // clear the item cache.ClearCacheItem(key); - + // clear all children - in case we moved and their path has changed var fid = "/" + sid + "/"; cache.ClearCacheObjectTypes((k, v) => From 4d950fb4d4e4fd945dc43df1a5cb6471ffa09681 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asbj=C3=B8rn=20Riis-Knudsen?= Date: Sun, 28 Feb 2016 17:40:58 +0100 Subject: [PATCH 046/132] Fix transparency for image icons in the tray --- .../components/application/sectionicon.directive.js | 2 +- src/Umbraco.Web.UI.Client/src/less/sections.less | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/sectionicon.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/sectionicon.directive.js index d6a4537262..28b6620aeb 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/sectionicon.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/sectionicon.directive.js @@ -17,7 +17,7 @@ if(convert){ element.html(""); }else{ - element.html(""); + element.html(""); } //it's a file, normally legacy so look in the icon tray images } diff --git a/src/Umbraco.Web.UI.Client/src/less/sections.less b/src/Umbraco.Web.UI.Client/src/less/sections.less index f57feeb2ad..c2db58a99e 100644 --- a/src/Umbraco.Web.UI.Client/src/less/sections.less +++ b/src/Umbraco.Web.UI.Client/src/less/sections.less @@ -19,7 +19,8 @@ ul.sections li { } ul.sections li [class^="icon-"]:before, -ul.sections li [class*=" icon-"]:before{ +ul.sections li [class*=" icon-"]:before, +ul.sections li img.icon-section { font-size: 30px; margin: 1px 0 0 0; opacity: 0.4; @@ -29,7 +30,8 @@ ul.sections li [class*=" icon-"]:before{ } ul.sections:hover li [class^="icon-"]:before, -ul.sections:hover li [class*=" icon-"]:before { +ul.sections:hover li [class*=" icon-"]:before, +ul.sections:hover li img.icon-section { opacity: 1 } From 1a4322eb48071c3412f7fc11e5af85b918efcf2a Mon Sep 17 00:00:00 2001 From: AndyButland Date: Mon, 29 Feb 2016 09:04:50 +0000 Subject: [PATCH 047/132] Fix issue with database field check that led to default template for content type not being set --- .../Persistence/Repositories/ContentTypeBaseRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs index 907f66435e..d157d7affe 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeBaseRepository.cs @@ -914,7 +914,7 @@ AND umbracoNode.id <> @id", //check for default templates bool? isDefaultTemplate = Convert.ToBoolean(ct.dtIsDefault); int? templateId = ct.dtTemplateId; - if (currDefaultTemplate == -1 && isDefaultTemplate.HasValue && templateId.HasValue) + if (currDefaultTemplate == -1 && isDefaultTemplate.HasValue && isDefaultTemplate.Value && templateId.HasValue) { currDefaultTemplate = templateId.Value; } From 83e679ba5ecc54b840135ce5a846cd1828e8378b Mon Sep 17 00:00:00 2001 From: Jeremy Pyne Date: Wed, 2 Mar 2016 16:00:33 -0500 Subject: [PATCH 048/132] Fix for issue causing the entire stylesheets tree to reload when selecting a item. --- .../umbraco/settings/stylesheet/editstylesheet.aspx.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/stylesheet/editstylesheet.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/stylesheet/editstylesheet.aspx.cs index c570e45a8c..63dec9111a 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/settings/stylesheet/editstylesheet.aspx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/settings/stylesheet/editstylesheet.aspx.cs @@ -60,7 +60,7 @@ namespace umbraco.cms.presentation.settings.stylesheet lttPath.Text = "" + stylesheet.VirtualPath + ""; editorSource.Text = stylesheet.Content; - TreeSyncPath = DeepLink.GetTreePathFromFilePath(filename); + TreeSyncPath = DeepLink.GetTreePathFromFilePath(filename).TrimEnd(".css"); // name derives from path, without the .css extension, clean for xss NameTxt.Text = stylesheet.Path.TrimEnd(".css").CleanForXss('\\', '/'); From bba7367ba61037daa8a532b6681637f6fa4c4a04 Mon Sep 17 00:00:00 2001 From: Simon Busborg Date: Mon, 7 Mar 2016 12:12:35 +0100 Subject: [PATCH 049/132] Fixes U4-8058 Labels are changed to block-level elements --- src/Umbraco.Web.UI.Client/src/less/forms.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/forms.less b/src/Umbraco.Web.UI.Client/src/less/forms.less index eb569c7cdf..bf6b01b81c 100644 --- a/src/Umbraco.Web.UI.Client/src/less/forms.less +++ b/src/Umbraco.Web.UI.Client/src/less/forms.less @@ -164,7 +164,7 @@ textarea { // Identify controls by their labels label { - display: block; + display: inline-block; margin-bottom: 5px; } From 35a9272b68f8e91077ca19f59e2bba1f1541c408 Mon Sep 17 00:00:00 2001 From: Simon Busborg Date: Mon, 7 Mar 2016 14:45:09 +0100 Subject: [PATCH 050/132] Fixes U4-8036 Umbraco.MultiNodeTreePicker checkbox doesn't appear after selecting something from list view --- .../tree/umb-tree-search-results.html | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree-search-results.html b/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree-search-results.html index e277ec63a8..97f08fe359 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree-search-results.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/tree/umb-tree-search-results.html @@ -1,19 +1,23 @@ - + From 0929b7d42cb71a9df426b5287335a214f9d14306 Mon Sep 17 00:00:00 2001 From: Simon Busborg Date: Mon, 7 Mar 2016 14:55:18 +0100 Subject: [PATCH 051/132] Fixes U4-8113 Styling for property labels affect package meta data labels --- src/Umbraco.Web.UI.Client/src/less/forms.less | 4 ++-- src/Umbraco.Web.UI.Client/src/less/main.less | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/less/forms.less b/src/Umbraco.Web.UI.Client/src/less/forms.less index bf6b01b81c..f9dedf5902 100644 --- a/src/Umbraco.Web.UI.Client/src/less/forms.less +++ b/src/Umbraco.Web.UI.Client/src/less/forms.less @@ -16,12 +16,12 @@ label small, .guiDialogTiny { font-size: 11px } -label.control-label { +label.control-label, .control-label { padding: 0 10px 0 0 !important; font-weight: bold; - } + .umb-status-label{ color: @gray !important; } diff --git a/src/Umbraco.Web.UI.Client/src/less/main.less b/src/Umbraco.Web.UI.Client/src/less/main.less index 09f916c59f..00cdd4f9b1 100644 --- a/src/Umbraco.Web.UI.Client/src/less/main.less +++ b/src/Umbraco.Web.UI.Client/src/less/main.less @@ -139,7 +139,7 @@ h5.-black { } /* LABELS*/ -.umb-control-group label.control-label { +.umb-control-group label.control-label, .umb-control-group .control-label { text-align: left; } .umb-control-group label.control-label > div > label { From a707d23692ff0bb34d963025bc0d04f328cf4d0f Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 7 Mar 2016 15:13:31 +0100 Subject: [PATCH 052/132] Allows for specifying explicit product names by overriding the property in MigrationStartupHander --- .../Persistence/Migrations/MigrationRunner.cs | 4 + .../MigrationStartupHandlerTests.cs | 100 ++++++++++++++++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + .../Migrations/MigrationStartupHander.cs | 16 ++- 4 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 src/Umbraco.Tests/Persistence/Migrations/MigrationStartupHandlerTests.cs diff --git a/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs b/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs index 36ddb2d4ba..081865b9a1 100644 --- a/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs +++ b/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.IO; using System.Linq; using System.Text; @@ -27,18 +28,21 @@ namespace Umbraco.Core.Persistence.Migrations private readonly IMigration[] _migrations; [Obsolete("Use the ctor that specifies all dependencies instead")] + [EditorBrowsable(EditorBrowsableState.Never)] public MigrationRunner(Version currentVersion, Version targetVersion, string productName) : this(LoggerResolver.Current.Logger, currentVersion, targetVersion, productName) { } [Obsolete("Use the ctor that specifies all dependencies instead")] + [EditorBrowsable(EditorBrowsableState.Never)] public MigrationRunner(ILogger logger, Version currentVersion, Version targetVersion, string productName) : this(logger, currentVersion, targetVersion, productName, null) { } [Obsolete("Use the ctor that specifies all dependencies instead")] + [EditorBrowsable(EditorBrowsableState.Never)] public MigrationRunner(ILogger logger, Version currentVersion, Version targetVersion, string productName, params IMigration[] migrations) : this(ApplicationContext.Current.Services.MigrationEntryService, logger, new SemVersion(currentVersion), new SemVersion(targetVersion), productName, migrations) { diff --git a/src/Umbraco.Tests/Persistence/Migrations/MigrationStartupHandlerTests.cs b/src/Umbraco.Tests/Persistence/Migrations/MigrationStartupHandlerTests.cs new file mode 100644 index 0000000000..dd35f9ad3c --- /dev/null +++ b/src/Umbraco.Tests/Persistence/Migrations/MigrationStartupHandlerTests.cs @@ -0,0 +1,100 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Moq; +using NUnit.Framework; +using Semver; +using Umbraco.Core; +using Umbraco.Core.Events; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Migrations; +using Umbraco.Core.Profiling; +using Umbraco.Core.Services; +using Umbraco.Web.Strategies.Migrations; + +namespace Umbraco.Tests.Persistence.Migrations +{ + [TestFixture] + public class MigrationStartupHandlerTests + { + [Test] + public void Executes_For_Any_Product_Name_When_Not_Specified() + { + var changed1 = new Args { CountExecuted = 0 }; + var testHandler1 = new TestMigrationHandler(changed1); + testHandler1.OnApplicationStarting(Mock.Of(), new ApplicationContext(CacheHelper.CreateDisabledCacheHelper(), new ProfilingLogger(Mock.Of(), Mock.Of()))); + + var conn = new Mock(); + conn.Setup(x => x.BeginTransaction(It.IsAny())).Returns(Mock.Of()); + var db = new Mock(conn.Object); + + var runner1 = new MigrationRunner(Mock.Of(), Mock.Of(), new SemVersion(1), new SemVersion(2), "Test1", + new IMigration[] { Mock.Of() }); + var result1 = runner1.Execute(db.Object, DatabaseProviders.SqlServerCE, false); + Assert.AreEqual(1, changed1.CountExecuted); + } + + [Test] + public void Executes_Only_For_Specified_Product_Name() + { + var changed1 = new Args { CountExecuted = 0}; + var testHandler1 = new TestMigrationHandler("Test1", changed1); + testHandler1.OnApplicationStarting(Mock.Of(), new ApplicationContext(CacheHelper.CreateDisabledCacheHelper(), new ProfilingLogger(Mock.Of(), Mock.Of()))); + var changed2 = new Args { CountExecuted = 0 }; + var testHandler2 = new TestMigrationHandler("Test2", changed2); + testHandler2.OnApplicationStarting(Mock.Of(), new ApplicationContext(CacheHelper.CreateDisabledCacheHelper(), new ProfilingLogger(Mock.Of(), Mock.Of()))); + + var conn = new Mock(); + conn.Setup(x => x.BeginTransaction(It.IsAny())).Returns(Mock.Of()); + var db = new Mock(conn.Object); + + var runner1 = new MigrationRunner(Mock.Of(), Mock.Of(), new SemVersion(1), new SemVersion(2), "Test1", + new IMigration[] { Mock.Of()}); + var result1 = runner1.Execute(db.Object, DatabaseProviders.SqlServerCE, false); + Assert.AreEqual(1, changed1.CountExecuted); + Assert.AreEqual(0, changed2.CountExecuted); + + var runner2 = new MigrationRunner(Mock.Of(), Mock.Of(), new SemVersion(1), new SemVersion(2), "Test2", + new IMigration[] { Mock.Of() }); + var result2 = runner2.Execute(db.Object, DatabaseProviders.SqlServerCE, false); + Assert.AreEqual(1, changed1.CountExecuted); + Assert.AreEqual(1, changed2.CountExecuted); + } + + public class Args + { + public int CountExecuted { get; set; } + } + + public class TestMigrationHandler : MigrationStartupHander + { + private readonly string _prodName; + private readonly Args _changed; + + public TestMigrationHandler(Args changed) + { + _changed = changed; + } + + public TestMigrationHandler(string prodName, Args changed) + { + _prodName = prodName; + _changed = changed; + } + + protected override void AfterMigration(MigrationRunner sender, MigrationEventArgs e) + { + _changed.CountExecuted++; + } + + public override string[] TargetProductNames + { + get { return _prodName.IsNullOrWhiteSpace() ? new string[] {} : new[] {_prodName}; } + } + } + } +} diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index e553be9b49..deda4f5794 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -175,6 +175,7 @@ + diff --git a/src/Umbraco.Web/Strategies/Migrations/MigrationStartupHander.cs b/src/Umbraco.Web/Strategies/Migrations/MigrationStartupHander.cs index 6b2c92f79f..a350911580 100644 --- a/src/Umbraco.Web/Strategies/Migrations/MigrationStartupHander.cs +++ b/src/Umbraco.Web/Strategies/Migrations/MigrationStartupHander.cs @@ -1,4 +1,5 @@ -using Umbraco.Core; +using System.Linq; +using Umbraco.Core; using Umbraco.Core.Persistence.Migrations; namespace Umbraco.Web.Strategies.Migrations @@ -41,9 +42,20 @@ namespace Umbraco.Web.Strategies.Migrations private void MigrationRunner_Migrated(MigrationRunner sender, Core.Events.MigrationEventArgs e) { - AfterMigration(sender, e); + if (TargetProductNames.Length == 0 || TargetProductNames.Contains(e.ProductName)) + { + AfterMigration(sender, e); + } } protected abstract void AfterMigration(MigrationRunner sender, Core.Events.MigrationEventArgs e); + + /// + /// Override to specify explicit target product names + /// + /// + /// Leaving empty will run for all migration products + /// + public virtual string[] TargetProductNames { get { return new string[] {}; } } } } \ No newline at end of file From 9c121f0044b2aab9d59ccc0c384e2e3b290e9e9a Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 7 Mar 2016 16:26:30 +0100 Subject: [PATCH 053/132] Fixes RenderModelBinder to inherit from DefaultModelBinder and use the default logic if the model in the route values is not IRenderModel --- src/Umbraco.Web/Mvc/RenderModelBinder.cs | 35 ++++++++++++++---------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Web/Mvc/RenderModelBinder.cs b/src/Umbraco.Web/Mvc/RenderModelBinder.cs index ed07275378..d219a438e0 100644 --- a/src/Umbraco.Web/Mvc/RenderModelBinder.cs +++ b/src/Umbraco.Web/Mvc/RenderModelBinder.cs @@ -8,7 +8,10 @@ using Umbraco.Web.Models; namespace Umbraco.Web.Mvc { - public class RenderModelBinder : IModelBinder, IModelBinderProvider + /// + /// Allows for Model Binding any IPublishedContent or IRenderModel + /// + public class RenderModelBinder : DefaultModelBinder, IModelBinder, IModelBinderProvider { /// /// Binds the model to a value by using the specified controller context and binding context. @@ -17,27 +20,31 @@ namespace Umbraco.Web.Mvc /// The bound value. /// /// The controller context.The binding context. - public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) + public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { object model; if (controllerContext.RouteData.DataTokens.TryGetValue(Core.Constants.Web.UmbracoDataToken, out model) == false) return null; - // when rendering "special" stuff such as surface controllers, etc, the token does *not* contain - // the model source, but some special strings - and then we have to find the source using the - // "default" MVC way - var modelString = model as string; - if (modelString == "surface" || modelString == "api" || modelString == "backoffice") // fixme - more? + //This model binder deals with IRenderModel and IPublishedContent by extracting the model from the route's + // datatokens. This data token is set in 2 places: RenderRouteHandler, UmbracoVirtualNodeRouteHandler + // and both always set the model to an instance of `RenderModel`. So if this isn't an instance of IRenderModel then + // we need to let the DefaultModelBinder deal with the logic. + var renderModel = model as IRenderModel; + if (renderModel == null) { var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); + if (value == null) return null; + model = value.RawValue; + } - // fixme - should we return here? - // or go with the binding logic below that's nicer with strongly typed models - } + //if for any reason the model is not either IRenderModel or IPublishedContent, then we return since those are the only + // types this binder is dealing with. + if ((model is IRenderModel) == false && (model is IPublishedContent) == false) return null; - //default culture - var culture = CultureInfo.CurrentCulture; + //default culture + var culture = CultureInfo.CurrentCulture; var umbracoContext = controllerContext.GetUmbracoContext() ?? UmbracoContext.Current; @@ -47,8 +54,8 @@ namespace Umbraco.Web.Mvc culture = umbracoContext.PublishedContentRequest.Culture; } - return BindModel(model, bindingContext.ModelType, culture); - } + return BindModel(model, bindingContext.ModelType, culture); + } // source is the model that we have // modelType is the type of the model that we need to bind to From 5f365241a8410a7692517cff155f71347e78537b Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 7 Mar 2016 16:29:40 +0100 Subject: [PATCH 054/132] updates RenderModelBinder to simply check for IPublishedContent or IRenderModel --- src/Umbraco.Web/Mvc/RenderModelBinder.cs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/Umbraco.Web/Mvc/RenderModelBinder.cs b/src/Umbraco.Web/Mvc/RenderModelBinder.cs index d219a438e0..49bb4e3026 100644 --- a/src/Umbraco.Web/Mvc/RenderModelBinder.cs +++ b/src/Umbraco.Web/Mvc/RenderModelBinder.cs @@ -160,15 +160,9 @@ namespace Umbraco.Web.Mvc public IModelBinder GetBinder(Type modelType) { - // can bind to RenderModel - if (modelType == typeof(RenderModel)) return this; - - // can bind to RenderModel - if (modelType.IsGenericType && modelType.GetGenericTypeDefinition() == typeof(RenderModel<>)) return this; - - // can bind to TContent where TContent : IPublishedContent - if (typeof(IPublishedContent).IsAssignableFrom(modelType)) return this; - return null; + return TypeHelper.IsTypeAssignableFrom(modelType) || TypeHelper.IsTypeAssignableFrom(modelType) + ? this + : null; } } } \ No newline at end of file From d5a57c505cfcc2f2b5fb2f122c9e3fd0eccae59e Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 7 Mar 2016 16:53:57 +0100 Subject: [PATCH 055/132] adds RenderModelBinderTests --- src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + .../Web/Mvc/RenderModelBinderTests.cs | 84 +++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 src/Umbraco.Tests/Web/Mvc/RenderModelBinderTests.cs diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index e553be9b49..83a7ec903b 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -244,6 +244,7 @@ + diff --git a/src/Umbraco.Tests/Web/Mvc/RenderModelBinderTests.cs b/src/Umbraco.Tests/Web/Mvc/RenderModelBinderTests.cs new file mode 100644 index 0000000000..5c2c1390fa --- /dev/null +++ b/src/Umbraco.Tests/Web/Mvc/RenderModelBinderTests.cs @@ -0,0 +1,84 @@ +using System.Globalization; +using Moq; +using NUnit.Framework; +using Umbraco.Core.Models; +using Umbraco.Core.Models.PublishedContent; +using Umbraco.Web.Models; +using Umbraco.Web.Mvc; + +namespace Umbraco.Tests.Web.Mvc +{ + [TestFixture] + public class RenderModelBinderTests + { + [Test] + public void Returns_Binder_For_IPublishedContent_And_IRenderModel() + { + var binder = new RenderModelBinder(); + var found = binder.GetBinder(typeof (IPublishedContent)); + Assert.IsNotNull(found); + found = binder.GetBinder(typeof(IRenderModel)); + Assert.IsNotNull(found); + found = binder.GetBinder(typeof(RenderModel)); + Assert.IsNotNull(found); + found = binder.GetBinder(typeof(DynamicPublishedContent)); + Assert.IsNotNull(found); + found = binder.GetBinder(typeof(MyContent)); + Assert.IsNotNull(found); + + found = binder.GetBinder(typeof(MyOtherContent)); + Assert.IsNull(found); + } + + [Test] + public void BindModel_Null_Source_Returns_Null() + { + Assert.IsNull(RenderModelBinder.BindModel(null, typeof(MyContent), CultureInfo.CurrentCulture)); + } + + [Test] + public void BindModel_Returns_If_Same_Type() + { + var content = new MyContent(Mock.Of()); + var bound = RenderModelBinder.BindModel(content, typeof (IPublishedContent), CultureInfo.CurrentCulture); + Assert.AreSame(content, bound); + } + + [Test] + public void BindModel_RenderModel_To_IPublishedContent() + { + var content = new MyContent(Mock.Of()); + var renderModel = new RenderModel(content, CultureInfo.CurrentCulture); + var bound = RenderModelBinder.BindModel(renderModel, typeof(IPublishedContent), CultureInfo.CurrentCulture); + Assert.AreSame(content, bound); + } + + [Test] + public void BindModel_IPublishedContent_To_RenderModel() + { + var content = new MyContent(Mock.Of()); + var bound = (IRenderModel)RenderModelBinder.BindModel(content, typeof(RenderModel), CultureInfo.CurrentCulture); + Assert.AreSame(content, bound.Content); + } + + [Test] + public void BindModel_IPublishedContent_To_Generic_RenderModel() + { + var content = new MyContent(Mock.Of()); + var bound = (IRenderModel)RenderModelBinder.BindModel(content, typeof(RenderModel), CultureInfo.CurrentCulture); + Assert.AreSame(content, bound.Content); + } + + public class MyOtherContent + { + + } + + public class MyContent : PublishedContentWrapped + { + public MyContent(IPublishedContent content) : base(content) + { + } + } + } +} \ No newline at end of file From b9765c3bec173c4431a45ffa7bd8a68ba9684b40 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 7 Mar 2016 17:52:28 +0100 Subject: [PATCH 056/132] fixes RenderModelBinder to use base implementation,adds unit tests --- .../Web/Mvc/RenderModelBinderTests.cs | 73 +++++++++++++++++++ src/Umbraco.Web/Mvc/RenderModelBinder.cs | 6 +- 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Tests/Web/Mvc/RenderModelBinderTests.cs b/src/Umbraco.Tests/Web/Mvc/RenderModelBinderTests.cs index 5c2c1390fa..5ec039a4dd 100644 --- a/src/Umbraco.Tests/Web/Mvc/RenderModelBinderTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/RenderModelBinderTests.cs @@ -1,4 +1,8 @@ +using System.Collections.Generic; using System.Globalization; +using System.Web; +using System.Web.Mvc; +using System.Web.Routing; using Moq; using NUnit.Framework; using Umbraco.Core.Models; @@ -69,6 +73,75 @@ namespace Umbraco.Tests.Web.Mvc Assert.AreSame(content, bound.Content); } + [Test] + public void No_DataToken_Returns_Null() + { + var binder = new RenderModelBinder(); + var routeData = new RouteData(); + var result = binder.BindModel(new ControllerContext(Mock.Of(), routeData, Mock.Of()), + new ModelBindingContext()); + + Assert.IsNull(result); + } + + [Test] + public void Invalid_DataToken_Model_Type_Returns_Null() + { + var binder = new RenderModelBinder(); + var routeData = new RouteData(); + routeData.DataTokens[Core.Constants.Web.UmbracoDataToken] = "hello"; + + //the value provider is the default implementation + var valueProvider = new Mock(); + //also IUnvalidatedValueProvider + var invalidatedValueProvider = valueProvider.As(); + invalidatedValueProvider.Setup(x => x.GetValue(It.IsAny(), It.IsAny())).Returns(() => + new ValueProviderResult(null, "", CultureInfo.CurrentCulture)); + + var controllerCtx = new ControllerContext( + Mock.Of(http => http.Items == new Dictionary()), + routeData, + Mock.Of()); + + var result = binder.BindModel(controllerCtx, + new ModelBindingContext + { + ValueProvider = valueProvider.Object, + ModelMetadata = new ModelMetadata(new EmptyModelMetadataProvider(), null, () => null, typeof(IPublishedContent), "content") + }); + + Assert.IsNull(result); + } + + [Test] + public void IPublishedContent_DataToken_Model_Type_Uses_DefaultImplementation() + { + var content = new MyContent(Mock.Of()); + var binder = new RenderModelBinder(); + var routeData = new RouteData(); + routeData.DataTokens[Core.Constants.Web.UmbracoDataToken] = content; + + //the value provider is the default implementation + var valueProvider = new Mock(); + //also IUnvalidatedValueProvider + var invalidatedValueProvider = valueProvider.As(); + invalidatedValueProvider.Setup(x => x.GetValue(It.IsAny(), It.IsAny())).Returns(() => + new ValueProviderResult(content, "content", CultureInfo.CurrentCulture)); + + var controllerCtx = new ControllerContext( + Mock.Of(http => http.Items == new Dictionary()), + routeData, + Mock.Of()); + var result = binder.BindModel(controllerCtx, + new ModelBindingContext + { + ValueProvider = valueProvider.Object, + ModelMetadata = new ModelMetadata(new EmptyModelMetadataProvider(), null, () => null, typeof(IPublishedContent), "content") + }); + + Assert.AreEqual(content, result); + } + public class MyOtherContent { diff --git a/src/Umbraco.Web/Mvc/RenderModelBinder.cs b/src/Umbraco.Web/Mvc/RenderModelBinder.cs index 49bb4e3026..dd9af16140 100644 --- a/src/Umbraco.Web/Mvc/RenderModelBinder.cs +++ b/src/Umbraco.Web/Mvc/RenderModelBinder.cs @@ -33,10 +33,8 @@ namespace Umbraco.Web.Mvc var renderModel = model as IRenderModel; if (renderModel == null) { - var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); - if (value == null) return null; - - model = value.RawValue; + model = base.BindModel(controllerContext, bindingContext); + if (model == null) return null; } //if for any reason the model is not either IRenderModel or IPublishedContent, then we return since those are the only From 390a94e323709f71a96f9a389c880fcecb49030e Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 8 Mar 2016 10:29:36 +0100 Subject: [PATCH 057/132] U4-8054 Issue with NuGet Uninstall.ps1 (running on updates) #U4-8054 Fixed --- build/NuSpecs/tools/uninstall.core.ps1 | 48 -------------------------- build/NuSpecs/tools/uninstall.ps1 | 39 --------------------- 2 files changed, 87 deletions(-) delete mode 100644 build/NuSpecs/tools/uninstall.core.ps1 delete mode 100644 build/NuSpecs/tools/uninstall.ps1 diff --git a/build/NuSpecs/tools/uninstall.core.ps1 b/build/NuSpecs/tools/uninstall.core.ps1 deleted file mode 100644 index 3daa6a1ba7..0000000000 --- a/build/NuSpecs/tools/uninstall.core.ps1 +++ /dev/null @@ -1,48 +0,0 @@ -param($installPath, $toolsPath, $package, $project) - -Write-Host "installPath:" "${installPath}" -Write-Host "toolsPath:" "${toolsPath}" - -Write-Host " " - -if ($project) { - - # Create paths and list them - $projectPath = (Get-Item $project.Properties.Item("FullPath").Value).FullName - Write-Host "projectPath:" "${projectPath}" - $backupPath = Join-Path $projectPath "App_Data\NuGetBackup" - Write-Host "backupPath:" "${backupPath}" - $umbracoBinFolder = Join-Path $projectPath "bin" - Write-Host "umbracoBinFolder:" "${umbracoBinFolder}" - - # Remove backups - Write-Host "removing backups:" "${backupPath}" - if(Test-Path $backupPath) { Remove-Item -Recurse -Force $backupPath -Confirm:$false } - - # Delete files Umbraco ships with - - Write-Host "removing dlls:" "${umbracoBinFolder}" - if(Test-Path $umbracoBinFolder\businesslogic.dll) { Remove-Item $umbracoBinFolder\businesslogic.dll -Force -Confirm:$false } - if(Test-Path $umbracoBinFolder\cms.dll) { Remove-Item $umbracoBinFolder\cms.dll -Force -Confirm:$false } - if(Test-Path $umbracoBinFolder\controls.dll) { Remove-Item $umbracoBinFolder\controls.dll -Force -Confirm:$false } - if(Test-Path $umbracoBinFolder\interfaces.dll) { Remove-Item $umbracoBinFolder\interfaces.dll -Force -Confirm:$false } - if(Test-Path $umbracoBinFolder\log4net.dll) { Remove-Item $umbracoBinFolder\log4net.dll -Force -Confirm:$false } - if(Test-Path $umbracoBinFolder\Microsoft.ApplicationBlocks.Data.dll) { Remove-Item $umbracoBinFolder\Microsoft.ApplicationBlocks.Data.dll -Force -Confirm:$false } - if(Test-Path $umbracoBinFolder\SQLCE4Umbraco.dll) { Remove-Item $umbracoBinFolder\SQLCE4Umbraco.dll -Force -Confirm:$false } - if(Test-Path $umbracoBinFolder\System.Data.SqlServerCe.dll) { Remove-Item $umbracoBinFolder\System.Data.SqlServerCe.dll -Force -Confirm:$false } - if(Test-Path $umbracoBinFolder\System.Data.SqlServerCe.Entity.dll) { Remove-Item $umbracoBinFolder\System.Data.SqlServerCe.Entity.dll -Force -Confirm:$false } - if(Test-Path $umbracoBinFolder\TidyNet.dll) { Remove-Item $umbracoBinFolder\TidyNet.dll -Force -Confirm:$false } - if(Test-Path $umbracoBinFolder\umbraco.dll) { Remove-Item $umbracoBinFolder\umbraco.dll -Force -Confirm:$false } - if(Test-Path $umbracoBinFolder\Umbraco.Core.dll) { Remove-Item $umbracoBinFolder\Umbraco.Core.dll -Force -Confirm:$false } - if(Test-Path $umbracoBinFolder\umbraco.DataLayer.dll) { Remove-Item $umbracoBinFolder\umbraco.DataLayer.dll -Force -Confirm:$false } - if(Test-Path $umbracoBinFolder\umbraco.editorControls.dll) { Remove-Item $umbracoBinFolder\umbraco.editorControls.dll -Force -Confirm:$false } - if(Test-Path $umbracoBinFolder\umbraco.MacroEngines.dll) { Remove-Item $umbracoBinFolder\umbraco.MacroEngines.dll -Force -Confirm:$false } - if(Test-Path $umbracoBinFolder\umbraco.providers.dll) { Remove-Item $umbracoBinFolder\umbraco.providers.dll -Force -Confirm:$false } - if(Test-Path $umbracoBinFolder\Umbraco.Web.UI.dll) { Remove-Item $umbracoBinFolder\Umbraco.Web.UI.dll -Force -Confirm:$false } - if(Test-Path $umbracoBinFolder\UmbracoExamine.dll) { Remove-Item $umbracoBinFolder\UmbracoExamine.dll -Force -Confirm:$false } - - $amd64Folder = Join-Path $umbracoBinFolder "amd64" - if(Test-Path $amd64Folder) { Remove-Item $amd64Folder -Force -Recurse -Confirm:$false } - $x86Folder = Join-Path $umbracoBinFolder "x86" - if(Test-Path $x86Folder) { Remove-Item $x86Folder -Force -Recurse -Confirm:$false } -} \ No newline at end of file diff --git a/build/NuSpecs/tools/uninstall.ps1 b/build/NuSpecs/tools/uninstall.ps1 deleted file mode 100644 index 4f7dd35384..0000000000 --- a/build/NuSpecs/tools/uninstall.ps1 +++ /dev/null @@ -1,39 +0,0 @@ -param($installPath, $toolsPath, $package, $project) - -Write-Host "installPath:" "${installPath}" -Write-Host "toolsPath:" "${toolsPath}" - -Write-Host " " - -if ($project) { - - # Create paths and list them - $projectPath = (Get-Item $project.Properties.Item("FullPath").Value).FullName - Write-Host "projectPath:" "${projectPath}" - $backupPath = Join-Path $projectPath "App_Data\NuGetBackup" - Write-Host "backupPath:" "${backupPath}" - $appBrowsers = Join-Path $projectPath "App_Browsers" - Write-Host "appBrowsers:" "${appBrowsers}" - $appData = Join-Path $projectPath "App_Data" - Write-Host "appData:" "${appData}" - - # Remove backups - Write-Host "removing backups:" "${backupPath}" - if(Test-Path $backupPath) { Remove-Item -Recurse -Force $backupPath -Confirm:$false } - - # Remove app_data files - Write-Host "removing app_data files:" "${appData}" - if(Test-Path $appData\packages) { Remove-Item $appData\packages -Recurse -Force -Confirm:$false } - - Write-Host "removing app_browsers:" "${appBrowsers}" - if(Test-Path $appBrowsers\Form.browser) { Remove-Item $appBrowsers\Form.browser -Force -Confirm:$false } - if(Test-Path $appBrowsers\w3cvalidator.browser) { Remove-Item $appBrowsers\w3cvalidator.browser -Force -Confirm:$false } - - # Remove umbraco and umbraco_files - $umbracoFolder = Join-Path $projectPath "Umbraco" - Write-Host "removing umbraco folder:" "${umbracoFolder}" - if(Test-Path $umbracoFolder) { Remove-Item $umbracoFolder -Recurse -Force -Confirm:$false } - $umbracoClientFolder = Join-Path $projectPath "Umbraco_Client" - Write-Host "removing umbraco client folder:" "${umbracoClientFolder}" - if(Test-Path $umbracoClientFolder) { Remove-Item $umbracoClientFolder -Recurse -Force -Confirm:$false } -} From 55e7159f7d343ccc2eb4f97230bba4c58be285fa Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 8 Mar 2016 10:35:57 +0100 Subject: [PATCH 058/132] Forgot to remove them from the nuspec files --- build/NuSpecs/UmbracoCms.Core.nuspec | 1 - build/NuSpecs/UmbracoCms.nuspec | 1 - 2 files changed, 2 deletions(-) diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index ae2c45e329..72733c5800 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -72,7 +72,6 @@ - diff --git a/build/NuSpecs/UmbracoCms.nuspec b/build/NuSpecs/UmbracoCms.nuspec index 46703a0c39..08e57d1832 100644 --- a/build/NuSpecs/UmbracoCms.nuspec +++ b/build/NuSpecs/UmbracoCms.nuspec @@ -33,7 +33,6 @@ - From f6e48243a6576e89b817e9fbc8804e0eba2bafa7 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 8 Mar 2016 13:37:26 +0100 Subject: [PATCH 059/132] U4-8088 NuGet upgrade of UmbracoCms pacakage doesn't add ModelsBuilder keys to web.config --- build/NuSpecs/tools/Web.config.install.xdt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/NuSpecs/tools/Web.config.install.xdt b/build/NuSpecs/tools/Web.config.install.xdt index f8ec871700..dfb9925840 100644 --- a/build/NuSpecs/tools/Web.config.install.xdt +++ b/build/NuSpecs/tools/Web.config.install.xdt @@ -21,6 +21,8 @@ + + From 372a768dca24e2eb8e579b8f63b61f3736dc83ca Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 8 Mar 2016 15:53:22 +0100 Subject: [PATCH 060/132] U4-4700 List view doesn't respect user permissions - makes the buttons show/hide based on the selected items instead of the current item. --- .../src/common/resources/content.resource.js | 10 + .../common/services/listviewhelper.service.js | 493 ++++++++++-------- .../src/common/services/util.service.js | 2 +- .../listview/listview.controller.js | 72 ++- .../propertyeditors/listview/listview.html | 10 +- .../test/config/app.unit.js | 3 +- .../test/config/karma.conf.js | 2 +- .../common/services/list-view-helper.spec.js | 48 ++ src/Umbraco.Web/Editors/ContentController.cs | 16 +- 9 files changed, 387 insertions(+), 269 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/test/unit/common/services/list-view-helper.spec.js diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js index 5aa56a80af..2cf7127707 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js @@ -509,6 +509,16 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { 'Failed to check permission for item ' + id); }, + getPermissions: function (nodeIds) { + return umbRequestHelper.resourcePromise( + $http.post( + umbRequestHelper.getApiUrl( + "contentApiBaseUrl", + "GetPermissions"), + nodeIds), + 'Failed to get permissions'); + }, + /** * @ngdoc method * @name umbraco.resources.contentResource#save diff --git a/src/Umbraco.Web.UI.Client/src/common/services/listviewhelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/listviewhelper.service.js index 428b9f4323..e211218ce1 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/listviewhelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/listviewhelper.service.js @@ -1,288 +1,319 @@ -(function() { - 'use strict'; +(function () { + 'use strict'; - function listViewHelper(localStorageService) { + function listViewHelper(localStorageService) { - var firstSelectedIndex = 0; - var localStorageKey = "umblistViewLayout"; + var firstSelectedIndex = 0; + var localStorageKey = "umblistViewLayout"; - function getLayout(nodeId, availableLayouts) { + function getLayout(nodeId, availableLayouts) { - var storedLayouts = []; + var storedLayouts = []; - if(localStorageService.get(localStorageKey)) { - storedLayouts = localStorageService.get(localStorageKey); - } - - if (storedLayouts && storedLayouts.length > 0) { - for (var i = 0; storedLayouts.length > i; i++) { - var layout = storedLayouts[i]; - if (layout.nodeId === nodeId) { - return setLayout(nodeId, layout, availableLayouts); - } - } - - } - - return getFirstAllowedLayout(availableLayouts); - - } - - function setLayout(nodeId, selectedLayout, availableLayouts) { - - var activeLayout = {}; - var layoutFound = false; - - for (var i = 0; availableLayouts.length > i; i++) { - var layout = availableLayouts[i]; - if (layout.path === selectedLayout.path) { - activeLayout = layout; - layout.active = true; - layoutFound = true; - } else { - layout.active = false; - } - } - - if(!layoutFound) { - activeLayout = getFirstAllowedLayout(availableLayouts); - } - - saveLayoutInLocalStorage(nodeId, activeLayout); - - return activeLayout; - - } - - function saveLayoutInLocalStorage(nodeId, selectedLayout) { - var layoutFound = false; - var storedLayouts = []; - - if(localStorageService.get(localStorageKey)) { - storedLayouts = localStorageService.get(localStorageKey); - } - - if(storedLayouts.length > 0) { - for(var i = 0; storedLayouts.length > i; i++) { - var layout = storedLayouts[i]; - if(layout.nodeId === nodeId) { - layout.path = selectedLayout.path; - layoutFound = true; - } - } - } - - if(!layoutFound) { - var storageObject = { - "nodeId": nodeId, - "path": selectedLayout.path - }; - storedLayouts.push(storageObject); - } - - localStorageService.set(localStorageKey, storedLayouts); - - } - - function getFirstAllowedLayout(layouts) { - - var firstAllowedLayout = {}; - - for (var i = 0; layouts.length > i; i++) { - var layout = layouts[i]; - if (layout.selected === true) { - firstAllowedLayout = layout; - break; + if (localStorageService.get(localStorageKey)) { + storedLayouts = localStorageService.get(localStorageKey); } - } - return firstAllowedLayout; - } + if (storedLayouts && storedLayouts.length > 0) { + for (var i = 0; storedLayouts.length > i; i++) { + var layout = storedLayouts[i]; + if (layout.nodeId === nodeId) { + return setLayout(nodeId, layout, availableLayouts); + } + } - function selectHandler(selectedItem, selectedIndex, items, selection, $event) { + } - var start = 0; - var end = 0; - var item = null; + return getFirstAllowedLayout(availableLayouts); - if ($event.shiftKey === true) { + } - if(selectedIndex > firstSelectedIndex) { + function setLayout(nodeId, selectedLayout, availableLayouts) { - start = firstSelectedIndex; - end = selectedIndex; + var activeLayout = {}; + var layoutFound = false; - for (; end >= start; start++) { - item = items[start]; - selectItem(item, selection); - } + for (var i = 0; availableLayouts.length > i; i++) { + var layout = availableLayouts[i]; + if (layout.path === selectedLayout.path) { + activeLayout = layout; + layout.active = true; + layoutFound = true; + } else { + layout.active = false; + } + } + + if (!layoutFound) { + activeLayout = getFirstAllowedLayout(availableLayouts); + } + + saveLayoutInLocalStorage(nodeId, activeLayout); + + return activeLayout; + + } + + function saveLayoutInLocalStorage(nodeId, selectedLayout) { + var layoutFound = false; + var storedLayouts = []; + + if (localStorageService.get(localStorageKey)) { + storedLayouts = localStorageService.get(localStorageKey); + } + + if (storedLayouts.length > 0) { + for (var i = 0; storedLayouts.length > i; i++) { + var layout = storedLayouts[i]; + if (layout.nodeId === nodeId) { + layout.path = selectedLayout.path; + layoutFound = true; + } + } + } + + if (!layoutFound) { + var storageObject = { + "nodeId": nodeId, + "path": selectedLayout.path + }; + storedLayouts.push(storageObject); + } + + localStorageService.set(localStorageKey, storedLayouts); + + } + + function getFirstAllowedLayout(layouts) { + + var firstAllowedLayout = {}; + + for (var i = 0; layouts.length > i; i++) { + var layout = layouts[i]; + if (layout.selected === true) { + firstAllowedLayout = layout; + break; + } + } + + return firstAllowedLayout; + } + + function selectHandler(selectedItem, selectedIndex, items, selection, $event) { + + var start = 0; + var end = 0; + var item = null; + + if ($event.shiftKey === true) { + + if (selectedIndex > firstSelectedIndex) { + + start = firstSelectedIndex; + end = selectedIndex; + + for (; end >= start; start++) { + item = items[start]; + selectItem(item, selection); + } + + } else { + + start = firstSelectedIndex; + end = selectedIndex; + + for (; end <= start; start--) { + item = items[start]; + selectItem(item, selection); + } + + } } else { - start = firstSelectedIndex; - end = selectedIndex; + if (selectedItem.selected) { + deselectItem(selectedItem, selection); + } else { + selectItem(selectedItem, selection); + } - for (; end <= start; start--) { - item = items[start]; - selectItem(item, selection); - } + firstSelectedIndex = selectedIndex; } - } else { + } - if(selectedItem.selected) { - deselectItem(selectedItem, selection); - } else { - selectItem(selectedItem, selection); + function selectItem(item, selection) { + var isSelected = false; + for (var i = 0; selection.length > i; i++) { + var selectedItem = selection[i]; + if (item.id === selectedItem.id) { + isSelected = true; + } + } + if (!isSelected) { + selection.push({ id: item.id }); + item.selected = true; + } + } + + function deselectItem(item, selection) { + for (var i = 0; selection.length > i; i++) { + var selectedItem = selection[i]; + if (item.id === selectedItem.id) { + selection.splice(i, 1); + item.selected = false; + } + } + } + + function clearSelection(items, folders, selection) { + + var i = 0; + + selection.length = 0; + + if (angular.isArray(items)) { + for (i = 0; items.length > i; i++) { + var item = items[i]; + item.selected = false; + } } - firstSelectedIndex = selectedIndex; - - } - - } - - function selectItem(item, selection) { - var isSelected = false; - for (var i = 0; selection.length > i; i++) { - var selectedItem = selection[i]; - if (item.id === selectedItem.id) { - isSelected = true; + if (angular.isArray(items)) { + for (i = 0; folders.length > i; i++) { + var folder = folders[i]; + folder.selected = false; + } } - } - if(!isSelected) { - selection.push({id: item.id}); - item.selected = true; - } - } + } - function deselectItem(item, selection) { - for (var i = 0; selection.length > i; i++) { - var selectedItem = selection[i]; - if (item.id === selectedItem.id) { - selection.splice(i, 1); - item.selected = false; + function selectAllItems(items, selection, $event) { + + var checkbox = $event.target; + var clearSelection = false; + + if (!angular.isArray(items)) { + return; } - } - } - function clearSelection(items, folders, selection) { + selection.length = 0; - var i = 0; + for (var i = 0; i < items.length; i++) { - selection.length = 0; + var item = items[i]; + + if (checkbox.checked) { + selection.push({ id: item.id }); + } else { + clearSelection = true; + } + + item.selected = checkbox.checked; - if(angular.isArray(items)) { - for(i = 0; items.length > i; i++) { - var item = items[i]; - item.selected = false; } - } - if(angular.isArray(items)) { - for(i = 0; folders.length > i; i++) { - var folder = folders[i]; - folder.selected = false; + if (clearSelection) { + selection.length = 0; } - } - } - function selectAllItems(items, selection, $event) { + } - var checkbox = $event.target; - var clearSelection = false; + function isSelectedAll(items, selection) { - if (!angular.isArray(items)) { - return; - } + var numberOfSelectedItem = 0; - selection.length = 0; + for (var itemIndex = 0; items.length > itemIndex; itemIndex++) { + var item = items[itemIndex]; - for (var i = 0; i < items.length; i++) { + for (var selectedIndex = 0; selection.length > selectedIndex; selectedIndex++) { + var selectedItem = selection[selectedIndex]; - var item = items[i]; + if (item.id === selectedItem.id) { + numberOfSelectedItem++; + } + } - if (checkbox.checked) { - selection.push({id: item.id}); - } else { - clearSelection = true; - } + } - item.selected = checkbox.checked; + if (numberOfSelectedItem === items.length) { + return true; + } - } + } - if (clearSelection) { - selection.length = 0; - } + function setSortingDirection(col, direction, options) { + return options.orderBy.toUpperCase() === col.toUpperCase() && options.orderDirection === direction; + } - } + function setSorting(field, allow, options) { + if (allow) { + options.orderBy = field; - function isSelectedAll(items, selection) { + if (options.orderDirection === "desc") { + options.orderDirection = "asc"; + } else { + options.orderDirection = "desc"; + } + } + } + + //This takes in a dictionary of Ids with Permissions and determines + // the intersect of all permissions to return an object representing the + // listview button permissions + function getButtonPermissions(unmergedPermissions, currentIdsWithPermissions) { + + if (currentIdsWithPermissions == null) { + currentIdsWithPermissions = {}; + } - var numberOfSelectedItem = 0; + //merge the newly retrieved permissions to the main dictionary + _.each(unmergedPermissions, function (value, key, list) { + currentIdsWithPermissions[key] = value; + }); - for(var itemIndex = 0; items.length > itemIndex; itemIndex++) { - var item = items[itemIndex]; + //get the intersect permissions + var arr = []; + _.each(currentIdsWithPermissions, function (value, key, list) { + arr.push(value); + }); - for(var selectedIndex = 0; selection.length > selectedIndex; selectedIndex++) { - var selectedItem = selection[selectedIndex]; + //we need to use 'apply' to call intersection with an array of arrays, + //see: http://stackoverflow.com/a/16229480/694494 + var intersectPermissions = _.intersection.apply(_, arr); - if(item.id === selectedItem.id) { - numberOfSelectedItem++; - } - } + return { + canCopy: _.contains(intersectPermissions, 'O'), //Magic Char = O + canCreate: _.contains(intersectPermissions, 'C'), //Magic Char = C + canDelete: _.contains(intersectPermissions, 'D'), //Magic Char = D + canMove: _.contains(intersectPermissions, 'M'), //Magic Char = M + canPublish: _.contains(intersectPermissions, 'U'), //Magic Char = U + canUnpublish: _.contains(intersectPermissions, 'U'), //Magic Char = Z (however UI says it can't be set, so if we can publish 'U' we can unpublish) + }; + } - } + var service = { + getLayout: getLayout, + getFirstAllowedLayout: getFirstAllowedLayout, + setLayout: setLayout, + saveLayoutInLocalStorage: saveLayoutInLocalStorage, + selectHandler: selectHandler, + selectItem: selectItem, + deselectItem: deselectItem, + clearSelection: clearSelection, + selectAllItems: selectAllItems, + isSelectedAll: isSelectedAll, + setSortingDirection: setSortingDirection, + setSorting: setSorting, + getButtonPermissions: getButtonPermissions + }; - if(numberOfSelectedItem === items.length) { - return true; - } + return service; - } + } - function setSortingDirection(col, direction, options) { - return options.orderBy.toUpperCase() === col.toUpperCase() && options.orderDirection === direction; - } - - - function setSorting(field, allow, options) { - if (allow) { - options.orderBy = field; - - if (options.orderDirection === "desc") { - options.orderDirection = "asc"; - } else { - options.orderDirection = "desc"; - } - } - } - - - - var service = { - getLayout: getLayout, - getFirstAllowedLayout: getFirstAllowedLayout, - setLayout: setLayout, - saveLayoutInLocalStorage: saveLayoutInLocalStorage, - selectHandler: selectHandler, - selectItem: selectItem, - deselectItem: deselectItem, - clearSelection: clearSelection, - selectAllItems: selectAllItems, - isSelectedAll: isSelectedAll, - setSortingDirection: setSortingDirection, - setSorting: setSorting - }; - - return service; - - } - - - angular.module('umbraco.services').factory('listViewHelper', listViewHelper); + angular.module('umbraco.services').factory('listViewHelper', listViewHelper); })(); diff --git a/src/Umbraco.Web.UI.Client/src/common/services/util.service.js b/src/Umbraco.Web.UI.Client/src/common/services/util.service.js index 554790b6d0..9fbf2947af 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/util.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/util.service.js @@ -21,6 +21,7 @@ function packageHelper(assetsService, treeService, eventsService, $templateCache } angular.module('umbraco.services').factory('packageHelper', packageHelper); +//TODO: I believe this is obsolete function umbPhotoFolderHelper($compile, $log, $timeout, $filter, imageHelper, mediaHelper, umbRequestHelper) { return { /** sets the image's url, thumbnail and if its a folder */ @@ -319,7 +320,6 @@ function umbPhotoFolderHelper($compile, $log, $timeout, $filter, imageHelper, me } }; } - angular.module("umbraco.services").factory("umbPhotoFolderHelper", umbPhotoFolderHelper); /** diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js index 665ff22cd3..572969f753 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js @@ -58,37 +58,53 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie totalPages: 0, items: [] }; - - - $scope.currentNodePermissions = {} - //Just ensure we do have an editorState - if(editorState.current){ - - //Get Current User to check if their usertype is an admin (overwrites all permissions!) - userService.getCurrentUser().then(function(data){ - - //Fetch current node allowed actions for the current user - //This is the current node & not each individual child node in the list - var currentUserPermissions = editorState.current.allowedActions; - - //Create a nicer model rather than the funky & hard to remember permissions strings - $scope.currentNodePermissions = { - "isAdminUser": data.userType.toLowerCase() === "admin", - "canCopy": _.contains(currentUserPermissions, 'O'), //Magic Char = O - "canCreate": _.contains(currentUserPermissions, 'C'), //Magic Char = C - "canDelete": _.contains(currentUserPermissions, 'D'), //Magic Char = D - "canMove": _.contains(currentUserPermissions, 'M'), //Magic Char = M - "canPublish": _.contains(currentUserPermissions, 'U'), //Magic Char = U - "canUnpublish": _.contains(currentUserPermissions, 'U'), //Magic Char = Z (however UI says it can't be set, so if we can publish 'U' we can unpublish) - "rawPermissions": currentUserPermissions - }; - + //when this is null, we don't check permissions + $scope.buttonPermissions = null; + + //When we are dealing with 'content', we need to deal with permissions on child nodes. + // Currently there is no real good way to + if ($scope.entityType = "content") { + + var idsWithPermissions = null; + var intersectPermissions = null; + + $scope.buttonPermissions = { + canCopy: true, + canCreate: true, + canDelete: true, + canMove: true, + canPublish: true, + canUnpublish: true + }; + + $scope.$watch(function() { + return $scope.selection.length; + }, function(newVal, oldVal) { + + if ((idsWithPermissions == null && newVal > 0) || (idsWithPermissions != null)) { + + //go get the permissions for the selected items + var ids = _.map($scope.selection, function(i) { + return i.id; + }); + + //remove the dictionary items that don't have matching ids + var filtered = {}; + _.each(idsWithPermissions, function (value, key, list) { + if (_.contains(ids, Number(key))) { + filtered[key] = value; + } + }); + idsWithPermissions = filtered; + + contentResource.getPermissions(ids).then(function (p) { + $scope.buttonPermissions = listViewHelper.getButtonPermissions(p, idsWithPermissions); + }); + } }); - } - - + } $scope.options = { displayAtTabNumber: $scope.model.config.displayAtTabNumber ? $scope.model.config.displayAtTabNumber : 1, diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html index a7a94ff53a..904939b75f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.html @@ -82,7 +82,7 @@ + /// Returns permissions for all nodes passed in for the current user + /// + /// + /// + [HttpPost] + public Dictionary GetPermissions(int[] nodeIds) + { + return Services.UserService + .GetPermissions(Security.CurrentUser, nodeIds) + .ToDictionary(x => x.EntityId, x => x.AssignedPermissions); + } + [HttpGet] public bool HasPermission(string permissionToCheck, int nodeId) { @@ -352,9 +366,7 @@ namespace Umbraco.Web.Editors return display; } - - /// /// Publishes a document with a given ID /// From 2a2f22c7d90e07015e6724c60591d9e12e00c49f Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 8 Mar 2016 15:53:45 +0100 Subject: [PATCH 061/132] fixes js null issue with boolean.controller --- .../src/views/propertyeditors/boolean/boolean.controller.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/boolean/boolean.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/boolean/boolean.controller.js index 59bc155bf5..aba7fddd1f 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/boolean/boolean.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/boolean/boolean.controller.js @@ -4,8 +4,8 @@ function booleanEditorController($scope, $rootScope, assetsService) { $scope.renderModel = { value: false }; - - if($scope.model.config.default.toString() === "1" && $scope.model && !$scope.model.value) { + + if ($scope.model.config.default && $scope.model.config.default.toString() === "1" && $scope.model && !$scope.model.value) { $scope.renderModel.value = true; } From 402a5c9d5606b3ec2592a8e339f7688032c795a5 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 8 Mar 2016 15:57:59 +0100 Subject: [PATCH 062/132] fixes bool check --- .../src/views/propertyeditors/listview/listview.controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js index 572969f753..1b25fc5339 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js @@ -64,7 +64,7 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie //When we are dealing with 'content', we need to deal with permissions on child nodes. // Currently there is no real good way to - if ($scope.entityType = "content") { + if ($scope.entityType === "content") { var idsWithPermissions = null; var intersectPermissions = null; From a2564bd0c3fb435ef4e25e81957dd9964dd56e58 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 8 Mar 2016 16:18:33 +0100 Subject: [PATCH 063/132] ensures that requests are only made for ids that haven't already been queried. --- .../listview/listview.controller.js | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js index 1b25fc5339..5aade0cac0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js @@ -67,7 +67,6 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie if ($scope.entityType === "content") { var idsWithPermissions = null; - var intersectPermissions = null; $scope.buttonPermissions = { canCopy: true, @@ -83,24 +82,35 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie }, function(newVal, oldVal) { if ((idsWithPermissions == null && newVal > 0) || (idsWithPermissions != null)) { - - //go get the permissions for the selected items + + //get all of the selected ids var ids = _.map($scope.selection, function(i) { - return i.id; + return i.id.toString(); }); //remove the dictionary items that don't have matching ids var filtered = {}; _.each(idsWithPermissions, function (value, key, list) { - if (_.contains(ids, Number(key))) { + if (_.contains(ids, key)) { filtered[key] = value; - } + } }); idsWithPermissions = filtered; - contentResource.getPermissions(ids).then(function (p) { - $scope.buttonPermissions = listViewHelper.getButtonPermissions(p, idsWithPermissions); + //find all ids that we haven't looked up permissions for + var existingIds = _.keys(idsWithPermissions); + var missingLookup = _.map(_.difference(ids, existingIds), function (i) { + return Number(i); }); + + if (missingLookup.length > 0) { + contentResource.getPermissions(missingLookup).then(function(p) { + $scope.buttonPermissions = listViewHelper.getButtonPermissions(p, idsWithPermissions); + }); + } + else { + $scope.buttonPermissions = listViewHelper.getButtonPermissions({}, idsWithPermissions); + } } }); From be0bacd895314500fd5c0a8e4a88e35c4451cd05 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 8 Mar 2016 17:23:54 +0100 Subject: [PATCH 064/132] Ensures all angular views are cache busted so browser cache is not an issue after upgrading. --- src/Umbraco.Web.UI.Client/src/app.js | 34 +++++++++++++++---- .../HtmlHelperBackOfficeExtensions.cs | 8 ++++- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/app.js b/src/Umbraco.Web.UI.Client/src/app.js index e2a12bc876..64f468712a 100644 --- a/src/Umbraco.Web.UI.Client/src/app.js +++ b/src/Umbraco.Web.UI.Client/src/app.js @@ -22,15 +22,35 @@ var packages = angular.module("umbraco.packages", []); //module is initilized. angular.module("umbraco.views", ["umbraco.viewcache"]); angular.module("umbraco.viewcache", []) - .run(function($rootScope, $templateCache){ - /** For debug mode, always clear template cache to cut down on + .run(function($rootScope, $templateCache) { + /** For debug mode, always clear template cache to cut down on dev frustration and chrome cache on templates */ - if(Umbraco.Sys.ServerVariables.isDebuggingEnabled){ - //$rootScope.$on('$viewContentLoaded', function() { - $templateCache.removeAll(); - //}); + if (Umbraco.Sys.ServerVariables.isDebuggingEnabled) { + $templateCache.removeAll(); } - }) + }) + .config([ + //This ensures that all of our angular views are cache busted, if the path starts with views/ and ends with .html, then + // we will append the cache busting value to it. This way all upgraded sites will not have to worry about browser cache. + "$provide", function($provide) { + return $provide.decorator("$http", [ + "$delegate", function($delegate) { + var get = $delegate.get; + $delegate.get = function (url, config) { + + if (Umbraco.Sys.ServerVariables.application && url.startsWith("views/") && url.endsWith(".html")) { + var rnd = Umbraco.Sys.ServerVariables.application.version + "." + Umbraco.Sys.ServerVariables.application.cdf; + var _op = (url.indexOf("?") > 0) ? "&" : "?"; + url += _op + "umb__rnd=" + rnd; + } + + return get(url, config); + }; + return $delegate; + } + ]); + } + ]); //Call a document callback if defined, this is sort of a dodgy hack to // be able to configure angular values in the Default.cshtml diff --git a/src/Umbraco.Web/HtmlHelperBackOfficeExtensions.cs b/src/Umbraco.Web/HtmlHelperBackOfficeExtensions.cs index 75e628851e..3f38ae35ec 100644 --- a/src/Umbraco.Web/HtmlHelperBackOfficeExtensions.cs +++ b/src/Umbraco.Web/HtmlHelperBackOfficeExtensions.cs @@ -3,8 +3,11 @@ using System.Linq; using System.Text; using System.Web; using System.Web.Mvc; +using ClientDependency.Core.Config; using Microsoft.Owin.Security; using Newtonsoft.Json; +using Umbraco.Core; +using Umbraco.Core.Configuration; using Umbraco.Web.Editors; namespace Umbraco.Web @@ -30,6 +33,7 @@ namespace Umbraco.Web /// public static IHtmlString BareMinimumServerVariablesScript(this HtmlHelper html, UrlHelper uri, string externalLoginsUrl) { + var version = UmbracoVersion.GetSemanticVersion().ToSemanticString(); var str = @"