From ef0ad41c8e1af5f5ea87833b3e01f5c47b57701b Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 9 Jun 2014 17:53:07 +0100 Subject: [PATCH 01/11] New relation type: "Relate Parent Document On Delete" --- .../Persistence/Migrations/Initial/BaseDataCreation.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs index b15f491587..8c9a933c7b 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs @@ -268,7 +268,8 @@ namespace Umbraco.Core.Persistence.Migrations.Initial private void CreateUmbracoRelationTypeData() { - _database.Insert("umbracoRelationType", "id", false, new RelationTypeDto { Id = 1, Alias = Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias, ChildObjectType = new Guid(Constants.ObjectTypes.Document), ParentObjectType = new Guid("C66BA18E-EAF3-4CFF-8A22-41B16D66A972"), Dual = true, Name = Constants.Conventions.RelationTypes.RelateDocumentOnCopyName }); + _database.Insert("umbracoRelationType", "id", false, new RelationTypeDto { Id = 1, Alias = "relateDocumentOnCopy", ChildObjectType = new Guid(Constants.ObjectTypes.Document), ParentObjectType = new Guid(Constants.ObjectTypes.Document), Dual = true, Name = "Relate Document On Copy" }); + _database.Insert("umbracoRelationType", "id", false, new RelationTypeDto { Id = 2, Alias = "relateParentDocumentOnDelete", ChildObjectType = new Guid(Constants.ObjectTypes.Document), ParentObjectType = new Guid(Constants.ObjectTypes.Document), Dual = false, Name = "Relate Parent Document On Delete" }); } private void CreateCmsTaskTypeData() From 0c7826b2503cb67dd0334b7a7ed3aa27499cd08f Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 9 Jun 2014 17:56:33 +0100 Subject: [PATCH 02/11] Added 'Restore' action class + menu option for items in the recycle bin. --- .../Trees/ContentTreeController.cs | 1 + src/umbraco.cms/Actions/ActionRestore.cs | 92 +++++++++++++++++++ src/umbraco.cms/umbraco.cms.csproj | 1 + 3 files changed, 94 insertions(+) create mode 100644 src/umbraco.cms/Actions/ActionRestore.cs diff --git a/src/Umbraco.Web/Trees/ContentTreeController.cs b/src/Umbraco.Web/Trees/ContentTreeController.cs index ef17840edf..9ed37b29d6 100644 --- a/src/Umbraco.Web/Trees/ContentTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTreeController.cs @@ -177,6 +177,7 @@ namespace Umbraco.Web.Trees if (item.Path.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).Contains(RecycleBinId.ToInvariantString())) { nodeMenu.DefaultMenuAlias = null; + nodeMenu.Items.Insert(2, new MenuItem(ActionRestore.Instance, ui.Text("actions", ActionRestore.Instance.Alias))); } else { diff --git a/src/umbraco.cms/Actions/ActionRestore.cs b/src/umbraco.cms/Actions/ActionRestore.cs new file mode 100644 index 0000000000..106dc61763 --- /dev/null +++ b/src/umbraco.cms/Actions/ActionRestore.cs @@ -0,0 +1,92 @@ +using System; +using umbraco.interfaces; +using umbraco.BasePages; + +namespace umbraco.BusinessLogic.Actions +{ + /// + /// This action is invoked when the content item is to be restored from the recycle bin + /// + public class ActionRestore : IAction + { + private static readonly ActionRestore m_instance = new ActionRestore(); + + /// + /// A public constructor exists ONLY for backwards compatibility in regards to 3rd party add-ons. + /// All Umbraco assemblies should use the singleton instantiation (this.Instance) + /// When this applicatio is refactored, this constuctor should be made private. + /// + [Obsolete("Use the singleton instantiation instead of a constructor")] + public ActionRestore() { } + + public static ActionRestore Instance + { + get { return m_instance; } + } + + #region IAction Members + + public char Letter + { + get + { + + return '8'; // TODO: Check if this char is available? Is it still used? + } + } + + public string JsFunctionName + { + get + { + return null; + } + } + + public string JsSource + { + get + { + + return null; + } + } + + public string Alias + { + get + { + + return "restore"; + } + } + + public string Icon + { + get + { + + return "undo"; + } + } + + public bool ShowInNotifier + { + get + { + + return true; + } + } + + public bool CanBePermissionAssigned + { + get + { + + return true; + } + } + #endregion + } +} \ No newline at end of file diff --git a/src/umbraco.cms/umbraco.cms.csproj b/src/umbraco.cms/umbraco.cms.csproj index b50979975e..ea1d27ce09 100644 --- a/src/umbraco.cms/umbraco.cms.csproj +++ b/src/umbraco.cms/umbraco.cms.csproj @@ -211,6 +211,7 @@ Code + From 458e7dde949148b819cbf5d87f39f6ad39d77a7f Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 9 Jun 2014 17:58:47 +0100 Subject: [PATCH 03/11] Created "relationResource" to expose methods from RelationService to angular controllers. Added view for the "restore" menu option. --- .../common/mocks/umbraco.servervariables.js | 1 + .../src/common/resources/relation.resource.js | 35 ++++++++++++ .../content/content.restore.controller.js | 53 +++++++++++++++++++ .../src/views/content/restore.html | 26 +++++++++ .../Editors/BackOfficeController.cs | 4 ++ src/Umbraco.Web/Editors/RelationController.cs | 52 ++++++++++++++++++ src/Umbraco.Web/Umbraco.Web.csproj | 1 + 7 files changed, 172 insertions(+) create mode 100644 src/Umbraco.Web.UI.Client/src/common/resources/relation.resource.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/content/content.restore.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/content/restore.html create mode 100644 src/Umbraco.Web/Editors/RelationController.cs diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/umbraco.servervariables.js b/src/Umbraco.Web.UI.Client/src/common/mocks/umbraco.servervariables.js index 8049b452d3..a68606efa7 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/umbraco.servervariables.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/umbraco.servervariables.js @@ -20,6 +20,7 @@ Umbraco.Sys.ServerVariables = { "entityApiBaseUrl": "/umbraco/UmbracoApi/Entity/", "dashboardApiBaseUrl": "/umbraco/UmbracoApi/Dashboard/", "updateCheckApiBaseUrl": "/umbraco/Api/UpdateCheck/", + "relationApiBaseUrl": "/umbraco/UmbracoApi/Relation/", "rteApiBaseUrl": "/umbraco/UmbracoApi/RichTextPreValue/" }, umbracoSettings: { diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/relation.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/relation.resource.js new file mode 100644 index 0000000000..9ebf73e3fd --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/resources/relation.resource.js @@ -0,0 +1,35 @@ +/** + * @ngdoc service + * @name umbraco.resources.relationResource + * @description Handles loading of relation data + **/ +function relationResource($q, $http, umbRequestHelper) { + return { + + /** + * @ngdoc method + * @name umbraco.resources.relationResource#getByChildId + * @methodOf umbraco.resources.relationResource + * + * @description + * Retrieves the relation data for a given child ID + * + * @param {int} id of the child item + * @param {string} alias of the relation type + * @returns {Promise} resourcePromise object containing the relations array. + * + */ + getByChildId: function (id, alias) { + + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "relationApiBaseUrl", + "GetByChildId", + [{ childId: id, relationTypeAlias: alias }])), + "Failed to get relation by child ID " + id + " and type of " + alias); + } + }; +} + +angular.module('umbraco.resources').factory('relationResource', relationResource); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.restore.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.restore.controller.js new file mode 100644 index 0000000000..a5094a26fc --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.restore.controller.js @@ -0,0 +1,53 @@ +angular.module("umbraco").controller("Umbraco.Editors.Content.RestoreController", + function ($scope, relationResource, contentResource, navigationService, appState, treeService) { + var dialogOptions = $scope.dialogOptions; + + var node = dialogOptions.currentNode; + + relationResource.getByChildId(node.id, "relateParentDocumentOnDelete").then(function (data) { + var relation = data[0]; + + contentResource.getById(relation.ParentId).then(function (data) { + $scope.target = data; + + }, function (err) { + $scope.success = false; + $scope.error = err; + }); + + }, function (err) { + $scope.success = false; + $scope.error = err; + }); + + $scope.restore = function () { + contentResource.move({ parentId: $scope.target.id, id: node.id }) + .then(function (path) { + $scope.error = false; + $scope.success = true; + + //first we need to remove the node that launched the dialog + treeService.removeNode($scope.currentNode); + + //get the currently edited node (if any) + var activeNode = appState.getTreeState("selectedNode"); + + //we need to do a double sync here: first sync to the moved content - but don't activate the node, + //then sync to the currenlty edited content (note: this might not be the content that was moved!!) + + navigationService.syncTree({ tree: "content", 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: "content", path: activeNodePath, forceReload: false, activate: true }); + } + }); + + // TODO: [LK] Call the relationResource to remove the relation (post-delete) + + }, function (err) { + $scope.success = false; + $scope.error = err; + }); + }; + }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/content/restore.html b/src/Umbraco.Web.UI.Client/src/views/content/restore.html new file mode 100644 index 0000000000..4652e20402 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/content/restore.html @@ -0,0 +1,26 @@ +
+
+
+ +

+ Restore {{currentNode.name}} to under {{target.name}}? +

+ +
+

{{error.errorMsg}}

+

{{error.data.Message}}

+
+ +
+

{{currentNode.name}} was moved underneath {{target.name}}

+ +
+ +
+
+ + +
\ No newline at end of file diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs index 91c26c603a..c757dedc34 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -188,6 +188,10 @@ namespace Umbraco.Web.Editors "packageInstallApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( controller => controller.Fetch(string.Empty)) }, + { + "relationApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetById(0)) + }, { "rteApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( controller => controller.GetConfiguration()) diff --git a/src/Umbraco.Web/Editors/RelationController.cs b/src/Umbraco.Web/Editors/RelationController.cs new file mode 100644 index 0000000000..225d554a0e --- /dev/null +++ b/src/Umbraco.Web/Editors/RelationController.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Web.Http; +using Umbraco.Core; +using Umbraco.Core.Models; +using Umbraco.Web.Mvc; +using Umbraco.Web.WebApi.Filters; +using Constants = Umbraco.Core.Constants; + +namespace Umbraco.Web.Editors +{ + [PluginController("UmbracoApi")] + [UmbracoApplicationAuthorizeAttribute(Constants.Applications.Content)] + public class RelationController : ContentControllerBase + { + public RelationController() + : this(UmbracoContext.Current) + { + } + + public RelationController(UmbracoContext umbracoContext) + : base(umbracoContext) + { + } + + public IRelation GetById(int id) + { + return Services.RelationService.GetById(id); + } + + [EnsureUserPermissionForContent("childId")] + public IEnumerable GetByChildId(int childId, string relationTypeAlias = "") + { + var relations = Services.RelationService.GetByChildId(childId); + + if (relations == null) + { + throw new HttpResponseException(HttpStatusCode.NotFound); + } + + if (!string.IsNullOrWhiteSpace(relationTypeAlias)) + { + return relations.Where(x => x.RelationType.Alias.InvariantEquals(relationTypeAlias)); + } + + return relations; + } + + // TODO: [LK] Add method for deleting/removing the relation + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 7e649e4246..24895c7bd7 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -308,6 +308,7 @@ + From 911e81e4af5ee717d27801111f003a5a53413757 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 9 Jun 2014 18:22:36 +0100 Subject: [PATCH 04/11] Added constants for the relation-type aliases. --- src/Umbraco.Core/Constants-Conventions.cs | 16 ++++++++++++++++ .../Migrations/Initial/BaseDataCreation.cs | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Constants-Conventions.cs b/src/Umbraco.Core/Constants-Conventions.cs index 59cec787f6..5f5201a049 100644 --- a/src/Umbraco.Core/Constants-Conventions.cs +++ b/src/Umbraco.Core/Constants-Conventions.cs @@ -281,6 +281,22 @@ namespace Umbraco.Core public const string AllMembersListId = "all-members"; } + /// + /// Defines the alias identifiers for the built-in Umbraco relation types + /// + public static class Relations + { + /// + /// Relation type alias for relating a document on the copy event + /// + public const string RelateDocumentOnCopy = "relateDocumentOnCopy"; + + /// + /// Relation type alias for relating a document to its parent document on delete + /// + public const string RelateParentDocumentOnDelete = "relateParentDocumentOnDelete"; + } + /// /// Constants for Umbraco URLs/Querystrings. /// diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs index 8c9a933c7b..14903b841e 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs @@ -268,8 +268,8 @@ namespace Umbraco.Core.Persistence.Migrations.Initial private void CreateUmbracoRelationTypeData() { - _database.Insert("umbracoRelationType", "id", false, new RelationTypeDto { Id = 1, Alias = "relateDocumentOnCopy", ChildObjectType = new Guid(Constants.ObjectTypes.Document), ParentObjectType = new Guid(Constants.ObjectTypes.Document), Dual = true, Name = "Relate Document On Copy" }); - _database.Insert("umbracoRelationType", "id", false, new RelationTypeDto { Id = 2, Alias = "relateParentDocumentOnDelete", ChildObjectType = new Guid(Constants.ObjectTypes.Document), ParentObjectType = new Guid(Constants.ObjectTypes.Document), Dual = false, Name = "Relate Parent Document On Delete" }); + _database.Insert("umbracoRelationType", "id", false, new RelationTypeDto { Id = 1, Alias = Constants.Conventions.Relations.RelateDocumentOnCopy, ChildObjectType = new Guid(Constants.ObjectTypes.Document), ParentObjectType = new Guid(Constants.ObjectTypes.Document), Dual = true, Name = "Relate Document On Copy" }); + _database.Insert("umbracoRelationType", "id", false, new RelationTypeDto { Id = 2, Alias = Constants.Conventions.Relations.RelateParentDocumentOnDelete, ChildObjectType = new Guid(Constants.ObjectTypes.Document), ParentObjectType = new Guid(Constants.ObjectTypes.Document), Dual = false, Name = "Relate Parent Document On Delete" }); } private void CreateCmsTaskTypeData() From b4499734039f9586af5f53b87f3afb9cb4f7379b Mon Sep 17 00:00:00 2001 From: leekelleher Date: Mon, 9 Jun 2014 18:23:07 +0100 Subject: [PATCH 05/11] Added the relationship between the trashed node and its former parent node --- src/Umbraco.Core/Services/ContentService.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 421beacbf7..c21c4f6871 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -1131,6 +1131,9 @@ namespace Umbraco.Core.Services return; } + // Add a relation for the item being deleted, so that we can know the original parent for if we need to restore later + ApplicationContext.Current.Services.RelationService.Relate(content.Parent(), content, Constants.Conventions.Relations.RelateParentDocumentOnDelete); + var moveInfo = new List> { new MoveEventInfo(content, originalPath, Constants.System.RecycleBinContent) From 0a6a41e4c2bd5df9f4dc90194772b643138c5b94 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Sat, 3 Jan 2015 22:19:03 +0000 Subject: [PATCH 06/11] Swapped the Action letter to be 'V', as it appeared to be available. --- src/umbraco.cms/Actions/ActionRestore.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/umbraco.cms/Actions/ActionRestore.cs b/src/umbraco.cms/Actions/ActionRestore.cs index 106dc61763..21079861ae 100644 --- a/src/umbraco.cms/Actions/ActionRestore.cs +++ b/src/umbraco.cms/Actions/ActionRestore.cs @@ -9,7 +9,10 @@ namespace umbraco.BusinessLogic.Actions /// public class ActionRestore : IAction { + //create singleton +#pragma warning disable 612,618 private static readonly ActionRestore m_instance = new ActionRestore(); +#pragma warning restore 612,618 /// /// A public constructor exists ONLY for backwards compatibility in regards to 3rd party add-ons. @@ -30,8 +33,7 @@ namespace umbraco.BusinessLogic.Actions { get { - - return '8'; // TODO: Check if this char is available? Is it still used? + return 'V'; } } @@ -87,6 +89,7 @@ namespace umbraco.BusinessLogic.Actions return true; } } + #endregion } } \ No newline at end of file From eace046342ea83fe4e81286f487e02772f947ca0 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Sat, 3 Jan 2015 22:20:19 +0000 Subject: [PATCH 07/11] Moved the SQL insert (to create the new relation type) to a separate DB migration for v7.3.0. (This may need to be reviewed by Core team?) --- .../Migrations/Initial/BaseDataCreation.cs | 1 - .../AddRelationTypeForDocumentOnDelete.cs | 26 +++++++++++++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 1 + 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddRelationTypeForDocumentOnDelete.cs diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs index 14903b841e..0b2e360467 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs @@ -269,7 +269,6 @@ namespace Umbraco.Core.Persistence.Migrations.Initial private void CreateUmbracoRelationTypeData() { _database.Insert("umbracoRelationType", "id", false, new RelationTypeDto { Id = 1, Alias = Constants.Conventions.Relations.RelateDocumentOnCopy, ChildObjectType = new Guid(Constants.ObjectTypes.Document), ParentObjectType = new Guid(Constants.ObjectTypes.Document), Dual = true, Name = "Relate Document On Copy" }); - _database.Insert("umbracoRelationType", "id", false, new RelationTypeDto { Id = 2, Alias = Constants.Conventions.Relations.RelateParentDocumentOnDelete, ChildObjectType = new Guid(Constants.ObjectTypes.Document), ParentObjectType = new Guid(Constants.ObjectTypes.Document), Dual = false, Name = "Relate Parent Document On Delete" }); } private void CreateCmsTaskTypeData() diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddRelationTypeForDocumentOnDelete.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddRelationTypeForDocumentOnDelete.cs new file mode 100644 index 0000000000..9268ff4af3 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddRelationTypeForDocumentOnDelete.cs @@ -0,0 +1,26 @@ +using System; +using Umbraco.Core.Configuration; +using Umbraco.Core.Models.Rdbms; + +namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero +{ + [Migration("7.3.0", 0, GlobalSettings.UmbracoMigrationName)] + public class AddRelationTypeForDocumentOnDelete : MigrationBase + { + public override void Up() + { + Execute.Code(AddRelationType); + } + + public static string AddRelationType(Database database) + { + database.Insert("umbracoRelationType", "id", false, new RelationTypeDto { Id = 2, Alias = Constants.Conventions.Relations.RelateParentDocumentOnDelete, ChildObjectType = new Guid(Constants.ObjectTypes.Document), ParentObjectType = new Guid(Constants.ObjectTypes.Document), Dual = false, Name = "Relate Parent Document On Delete" }); + + return string.Empty; + } + + public override void Down() + { + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index da47e24a80..c2aff8259e 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -390,6 +390,7 @@ + From f21265c7aefd990fd6a590578524da2c29190f76 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Sat, 3 Jan 2015 22:22:54 +0000 Subject: [PATCH 08/11] Added "DeleteById" method to the Relation Resource/Service. --- .../src/common/resources/relation.resource.js | 30 +++++++++++++++++++ .../content/content.restore.controller.js | 7 +++-- src/Umbraco.Web/Editors/RelationController.cs | 17 ++++++++++- 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/relation.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/relation.resource.js index 9ebf73e3fd..19bfded340 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/relation.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/relation.resource.js @@ -28,6 +28,36 @@ function relationResource($q, $http, umbRequestHelper) { "GetByChildId", [{ childId: id, relationTypeAlias: alias }])), "Failed to get relation by child ID " + id + " and type of " + alias); + }, + + /** + * @ngdoc method + * @name umbraco.resources.relationResource#deleteById + * @methodOf umbraco.resources.relationResource + * + * @description + * Deletes a relation item with a given id + * + * ##usage + *
+         * relationResource.deleteById(1234)
+         *    .then(function() {
+         *        alert('its gone!');
+         *    });
+         * 
+ * + * @param {Int} id id of relation item to delete + * @returns {Promise} resourcePromise object. + * + */ + deleteById: function (id) { + return umbRequestHelper.resourcePromise( + $http.post( + umbRequestHelper.getApiUrl( + "relationApiBaseUrl", + "DeleteById", + [{ id: id }])), + 'Failed to delete item ' + id); } }; } diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.restore.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.restore.controller.js index a5094a26fc..59800afe09 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.restore.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.restore.controller.js @@ -5,9 +5,9 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.RestoreController" var node = dialogOptions.currentNode; relationResource.getByChildId(node.id, "relateParentDocumentOnDelete").then(function (data) { - var relation = data[0]; + $scope.relation = data[0]; - contentResource.getById(relation.ParentId).then(function (data) { + contentResource.getById($scope.relation.ParentId).then(function (data) { $scope.target = data; }, function (err) { @@ -43,7 +43,8 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.RestoreController" } }); - // TODO: [LK] Call the relationResource to remove the relation (post-delete) + // remove the relation, as the item has been restored + relationResource.deleteById($scope.relation.Id); }, function (err) { $scope.success = false; diff --git a/src/Umbraco.Web/Editors/RelationController.cs b/src/Umbraco.Web/Editors/RelationController.cs index 225d554a0e..5457abbe9a 100644 --- a/src/Umbraco.Web/Editors/RelationController.cs +++ b/src/Umbraco.Web/Editors/RelationController.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Net; +using System.Net.Http; using System.Web.Http; using Umbraco.Core; using Umbraco.Core.Models; @@ -47,6 +48,20 @@ namespace Umbraco.Web.Editors return relations; } - // TODO: [LK] Add method for deleting/removing the relation + [HttpDelete] + [HttpPost] + public HttpResponseMessage DeleteById(int id) + { + var foundRelation = GetObjectFromRequest(() => Services.RelationService.GetById(id)); + + if (foundRelation == null) + { + return HandleContentNotFound(id, false); + } + + Services.RelationService.Delete(foundRelation); + + return Request.CreateResponse(HttpStatusCode.OK); + } } } \ No newline at end of file From 56f578997694888bbf6d10cb5aeb692546c34988 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Sat, 3 Jan 2015 22:24:10 +0000 Subject: [PATCH 09/11] Added "restore" language key/value for English (+US). Will aim to do other languages in due course. --- src/Umbraco.Web.UI/umbraco/config/lang/en.xml | 1 + src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml index e43af2752f..8acd5e3933 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en.xml @@ -27,6 +27,7 @@ Unpublish Reload nodes Republish entire site + Restore Permissions Rollback Send To Publish 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 0d700fa50f..26df92a44b 100644 --- a/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml @@ -27,6 +27,7 @@ Unpublish Reload nodes Republish entire site + Restore Permissions Rollback Send To Publish From 573192c33fa9cdf760aa938b9e968506b7b932c3 Mon Sep 17 00:00:00 2001 From: leekelleher Date: Sun, 4 Jan 2015 17:09:05 +0000 Subject: [PATCH 10/11] Amends based @sitereactor's feedback. --- src/Umbraco.Core/Constants-Conventions.cs | 28 ++++++++----------- .../Models/Rdbms/RelationTypeDto.cs | 2 +- .../Migrations/Initial/BaseDataCreation.cs | 3 +- .../AddRelationTypeForDocumentOnDelete.cs | 2 +- 4 files changed, 15 insertions(+), 20 deletions(-) diff --git a/src/Umbraco.Core/Constants-Conventions.cs b/src/Umbraco.Core/Constants-Conventions.cs index 5f5201a049..bdbd46e0cb 100644 --- a/src/Umbraco.Core/Constants-Conventions.cs +++ b/src/Umbraco.Core/Constants-Conventions.cs @@ -281,22 +281,6 @@ namespace Umbraco.Core public const string AllMembersListId = "all-members"; } - /// - /// Defines the alias identifiers for the built-in Umbraco relation types - /// - public static class Relations - { - /// - /// Relation type alias for relating a document on the copy event - /// - public const string RelateDocumentOnCopy = "relateDocumentOnCopy"; - - /// - /// Relation type alias for relating a document to its parent document on delete - /// - public const string RelateParentDocumentOnDelete = "relateParentDocumentOnDelete"; - } - /// /// Constants for Umbraco URLs/Querystrings. /// @@ -309,7 +293,7 @@ namespace Umbraco.Core } /// - /// Defines the alias identifiers for Umbraco relation types. + /// Defines the alias identifiers for built-in Umbraco relation types. /// public static class RelationTypes { @@ -322,6 +306,16 @@ namespace Umbraco.Core /// ContentType alias for default relation type "Relate Document On Copy". ///
public const string RelateDocumentOnCopyAlias = "relateDocumentOnCopy"; + + /// + /// ContentType name for default relation type "Relate Parent Document On Delete". + /// + public const string RelateParentDocumentOnDeleteName = "Relate Parent Document On Delete"; + + /// + /// ContentType alias for default relation type "Relate Parent Document On Delete". + /// + public const string RelateParentDocumentOnDeleteAlias = "relateParentDocumentOnDelete"; } } } diff --git a/src/Umbraco.Core/Models/Rdbms/RelationTypeDto.cs b/src/Umbraco.Core/Models/Rdbms/RelationTypeDto.cs index 62a27e9506..5748fb7eff 100644 --- a/src/Umbraco.Core/Models/Rdbms/RelationTypeDto.cs +++ b/src/Umbraco.Core/Models/Rdbms/RelationTypeDto.cs @@ -10,7 +10,7 @@ namespace Umbraco.Core.Models.Rdbms internal class RelationTypeDto { [Column("id")] - [PrimaryKeyColumn(IdentitySeed = 2)] + [PrimaryKeyColumn(IdentitySeed = 3)] public int Id { get; set; } [Column("dual")] diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs index 0b2e360467..cb9408b218 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs @@ -268,7 +268,8 @@ namespace Umbraco.Core.Persistence.Migrations.Initial private void CreateUmbracoRelationTypeData() { - _database.Insert("umbracoRelationType", "id", false, new RelationTypeDto { Id = 1, Alias = Constants.Conventions.Relations.RelateDocumentOnCopy, ChildObjectType = new Guid(Constants.ObjectTypes.Document), ParentObjectType = new Guid(Constants.ObjectTypes.Document), Dual = true, Name = "Relate Document On Copy" }); + _database.Insert("umbracoRelationType", "id", false, new RelationTypeDto { Id = 1, Alias = Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias, ChildObjectType = new Guid(Constants.ObjectTypes.Document), ParentObjectType = new Guid(Constants.ObjectTypes.Document), Dual = true, Name = Constants.Conventions.RelationTypes.RelateDocumentOnCopyName }); + _database.Insert("umbracoRelationType", "id", false, new RelationTypeDto { Id = 2, Alias = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias, ChildObjectType = new Guid(Constants.ObjectTypes.Document), ParentObjectType = new Guid(Constants.ObjectTypes.Document), Dual = false, Name = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteName }); } private void CreateCmsTaskTypeData() diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddRelationTypeForDocumentOnDelete.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddRelationTypeForDocumentOnDelete.cs index 9268ff4af3..e46085897b 100644 --- a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddRelationTypeForDocumentOnDelete.cs +++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/AddRelationTypeForDocumentOnDelete.cs @@ -14,7 +14,7 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZe public static string AddRelationType(Database database) { - database.Insert("umbracoRelationType", "id", false, new RelationTypeDto { Id = 2, Alias = Constants.Conventions.Relations.RelateParentDocumentOnDelete, ChildObjectType = new Guid(Constants.ObjectTypes.Document), ParentObjectType = new Guid(Constants.ObjectTypes.Document), Dual = false, Name = "Relate Parent Document On Delete" }); + database.Insert("umbracoRelationType", "id", false, new RelationTypeDto { Alias = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias, ChildObjectType = new Guid(Constants.ObjectTypes.Document), ParentObjectType = new Guid(Constants.ObjectTypes.Document), Dual = false, Name = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteName }); return string.Empty; } From 8a32f02781a67d4b9b7f7603c6f07759921d680e Mon Sep 17 00:00:00 2001 From: leekelleher Date: Sun, 4 Jan 2015 20:46:00 +0000 Subject: [PATCH 11/11] Added custom event handler to create the relation for trashed nodes. Removed the additional (AJAX) resource call from the Angular controller, as the relation removal is handled in server-side code during the move. --- src/Umbraco.Core/Services/ContentService.cs | 3 - .../content/content.restore.controller.js | 21 +++--- .../Strategies/RelateOnTrashHandler.cs | 65 +++++++++++++++++++ src/Umbraco.Web/Umbraco.Web.csproj | 1 + 4 files changed, 78 insertions(+), 12 deletions(-) create mode 100644 src/Umbraco.Web/Strategies/RelateOnTrashHandler.cs diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index c21c4f6871..421beacbf7 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -1131,9 +1131,6 @@ namespace Umbraco.Core.Services return; } - // Add a relation for the item being deleted, so that we can know the original parent for if we need to restore later - ApplicationContext.Current.Services.RelationService.Relate(content.Parent(), content, Constants.Conventions.Relations.RelateParentDocumentOnDelete); - var moveInfo = new List> { new MoveEventInfo(content, originalPath, Constants.System.RecycleBinContent) diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.restore.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.restore.controller.js index 59800afe09..0f492e74be 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.restore.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.restore.controller.js @@ -7,13 +7,18 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.RestoreController" relationResource.getByChildId(node.id, "relateParentDocumentOnDelete").then(function (data) { $scope.relation = data[0]; - contentResource.getById($scope.relation.ParentId).then(function (data) { - $scope.target = data; + if ($scope.relation.ParentId == -1) { + $scope.target = { id: -1, name: "Root" }; - }, function (err) { - $scope.success = false; - $scope.error = err; - }); + } else { + contentResource.getById($scope.relation.ParentId).then(function (data) { + $scope.target = data; + + }, function (err) { + $scope.success = false; + $scope.error = err; + }); + } }, function (err) { $scope.success = false; @@ -21,6 +26,7 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.RestoreController" }); $scope.restore = function () { + // this code was copied from `content.move.controller.js` contentResource.move({ parentId: $scope.target.id, id: node.id }) .then(function (path) { $scope.error = false; @@ -43,9 +49,6 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.RestoreController" } }); - // remove the relation, as the item has been restored - relationResource.deleteById($scope.relation.Id); - }, function (err) { $scope.success = false; $scope.error = err; diff --git a/src/Umbraco.Web/Strategies/RelateOnTrashHandler.cs b/src/Umbraco.Web/Strategies/RelateOnTrashHandler.cs new file mode 100644 index 0000000000..8b3cd598a2 --- /dev/null +++ b/src/Umbraco.Web/Strategies/RelateOnTrashHandler.cs @@ -0,0 +1,65 @@ +using System; +using System.Linq; +using Umbraco.Core; +using Umbraco.Core.Auditing; +using Umbraco.Core.Events; +using Umbraco.Core.Models; +using Umbraco.Core.Services; + +namespace Umbraco.Web.Strategies +{ + public sealed class RelateOnTrashHandler : ApplicationEventHandler + { + protected override void ApplicationStarting(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext) + { + ContentService.Moved += ContentService_Moved; + ContentService.Trashed += ContentService_Trashed; + } + + private void ContentService_Moved(IContentService sender, MoveEventArgs e) + { + foreach (var item in e.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Constants.System.RecycleBinContent.ToInvariantString()))) + { + var relationService = ApplicationContext.Current.Services.RelationService; + var relationTypeAlias = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias; + var relations = relationService.GetByChildId(item.Entity.Id); + + foreach (var relation in relations.Where(x => x.RelationType.Alias.InvariantEquals(relationTypeAlias))) + { + relationService.Delete(relation); + } + } + } + + private void ContentService_Trashed(IContentService sender, MoveEventArgs e) + { + var relationService = ApplicationContext.Current.Services.RelationService; + var relationTypeAlias = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias; + var relationType = relationService.GetRelationTypeByAlias(relationTypeAlias); + + // check that the relation-type exists, if not, then recreate it + if (relationType == null) + { + var documentObjectType = new Guid(Constants.ObjectTypes.Document); + var relationTypeName = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteName; + + relationType = new RelationType(documentObjectType, documentObjectType, relationTypeAlias, relationTypeName); + relationService.Save(relationType); + } + + foreach (var item in e.MoveInfoCollection) + { + var originalPath = item.OriginalPath.ToDelimitedList(); + var originalParentId = originalPath.Count > 2 + ? int.Parse(originalPath[originalPath.Count - 2]) + : Constants.System.Root; + + // Add a relation for the item being deleted, so that we can know the original parent for if we need to restore later + var relation = new Relation(originalParentId, item.Entity.Id, relationType); + relationService.Save(relation); + + Audit.Add(AuditTypes.Delete, string.Format("Trashed content with Id: '{0}' related to original parent content with Id: '{1}'", item.Entity.Id, originalParentId), item.Entity.WriterId, item.Entity.Id); + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 24895c7bd7..5f24703a14 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -494,6 +494,7 @@ +