diff --git a/.gitignore b/.gitignore index 2d5a5bbf79..0776fc2050 100644 --- a/.gitignore +++ b/.gitignore @@ -88,3 +88,5 @@ src/Umbraco.Web.UI.Client/src/[Ll]ess/*.css tools/NDepend/ src/Umbraco.Web.UI/App_Plugins/* +src/*.psess +src/*.vspx diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/content.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/content.mocks.js index 57f46bec99..5ffb4929fe 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/content.mocks.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/content.mocks.js @@ -51,24 +51,26 @@ angular.module('umbraco.mocks'). return { register: function() { - $httpBackend - .whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Content/GetById')) - .respond(returnNodebyId); + $httpBackend + .whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Content/GetById')) + .respond(returnNodebyId); - - $httpBackend - .whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Content/GetEmpty')) - .respond(returnEmptyNode); + $httpBackend + .whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Content/GetEmpty')) + .respond(returnEmptyNode); $httpBackend .whenDELETE(mocksUtils.urlRegex('/umbraco/UmbracoApi/Content/DeleteById')) .respond(returnDeletedNode); + + $httpBackend + .whenDELETE(mocksUtils.urlRegex('/umbraco/UmbracoApi/Content/EmptyRecycleBin')) + .respond(returnDeletedNode); }, - expectGetById: function() { - $httpBackend - .expectGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Content/GetById')); + $httpBackend + .expectGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Content/GetById')); } }; }]); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/tree.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/tree.mocks.js index 7f1ae94031..9b5eb5908f 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/resources/tree.mocks.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/resources/tree.mocks.js @@ -26,7 +26,9 @@ angular.module('umbraco.mocks'). { seperator: true, name: "Hostnames", cssclass: "home", alias: "hostnames", metaData: {} }, { name: "Public Access", cssclass: "group", alias: "publicaccess", metaData: {} }, - { seperator: true, name: "Reload", cssclass: "refresh", alias: "users", metaData: {} } + { seperator: true, name: "Reload", cssclass: "refresh", alias: "users", metaData: {} }, + + { seperator: true, name: "Empty Recycle Bin", cssclass: "trash", alias: "emptyrecyclebin", metaData: {} } ]; return [200, menu, null]; @@ -52,10 +54,10 @@ angular.module('umbraco.mocks'). } var children = [ - { name: "child-of-" + section, childNodesUrl: url, id: level + "" + 1234, icon: "icon-file-alt", view: section + "/edit/" + level + "" + 1234, children: [], expanded: false, hasChildren: true, level: level, defaultAction: action, menuUrl: menuUrl }, - { name: "random-name-" + section, childNodesUrl: url, id: level + "" + 1235, icon: "icon-file-alt", view: section + "/edit/" + level + "" + 1235, children: [], expanded: false, hasChildren: true, level: level, defaultAction: action, menuUrl: menuUrl }, - { name: "random-name-" + section, childNodesUrl: url, id: level + "" + 1236, icon: "icon-file-alt", view: section + "/edit/" + level + "" + 1236, children: [], expanded: false, hasChildren: true, level: level, defaultAction: action, menuUrl: menuUrl }, - { name: "random-name-" + section, childNodesUrl: url, id: level + "" + 1237, icon: "icon-file-alt", view: "common/legacy/1237?p=" + encodeURI("developer/contentType.aspx?idequal1234"), children: [], expanded: false, hasChildren: true, level: level, defaultAction: action, menuUrl: menuUrl } + { name: "child-of-" + section, childNodesUrl: url, id: level + "" + 1234, icon: "icon-file-alt", children: [], expanded: false, hasChildren: true, level: level, defaultAction: action, menuUrl: menuUrl }, + { name: "random-name-" + section, childNodesUrl: url, id: level + "" + 1235, icon: "icon-file-alt", children: [], expanded: false, hasChildren: true, level: level, defaultAction: action, menuUrl: menuUrl }, + { name: "random-name-" + section, childNodesUrl: url, id: level + "" + 1236, icon: "icon-file-alt", children: [], expanded: false, hasChildren: true, level: level, defaultAction: action, menuUrl: menuUrl }, + { name: "random-name-" + section, childNodesUrl: url, id: level + "" + 1237, icon: "icon-file-alt", routePath: "common/legacy/1237?p=" + encodeURI("developer/contentType.aspx?idequal1234"), children: [], expanded: false, hasChildren: true, level: level, defaultAction: action, menuUrl: menuUrl } ]; return [200, children, null]; @@ -78,15 +80,16 @@ angular.module('umbraco.mocks'). name: "content", id: -1, children: [ - { name: "My website", id: 1234, childNodesUrl: url, icon: "icon-home", view: section + "/edit/" + 1234, children: [], expanded: false, hasChildren: true, level: 1, defaultAction: "create", menuUrl: menuUrl }, - { name: "Components", id: 1235, childNodesUrl: url, icon: "icon-cogs", view: section + "/edit/" + 1235, children: [], expanded: false, hasChildren: true, level: 1, defaultAction: "create", menuUrl: menuUrl }, - { name: "Archieve", id: 1236, childNodesUrl: url, icon: "icon-folder-close", view: section + "/edit/" + 1236, children: [], expanded: false, hasChildren: true, level: 1, defaultAction: "create", menuUrl: menuUrl }, - { name: "Recycle Bin", id: 1237, childNodesUrl: url, icon: "icon-trash", view: section + "/trash/view/", children: [], expanded: false, hasChildren: true, level: 1, defaultAction: "create", menuUrl: menuUrl } + { name: "My website", id: 1234, childNodesUrl: url, icon: "icon-home", children: [], expanded: false, hasChildren: true, level: 1, defaultAction: "create", menuUrl: menuUrl }, + { name: "Components", id: 1235, childNodesUrl: url, icon: "icon-cogs", children: [], expanded: false, hasChildren: true, level: 1, defaultAction: "create", menuUrl: menuUrl }, + { name: "Archieve", id: 1236, childNodesUrl: url, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, defaultAction: "create", menuUrl: menuUrl }, + { name: "Recycle Bin", id: -20, childNodesUrl: url, icon: "icon-trash", routePath: section + "/recyclebin", children: [], expanded: false, hasChildren: true, level: 1, defaultAction: "create", menuUrl: menuUrl } ], expanded: true, hasChildren: true, level: 0, - menuUrl: menuUrl + menuUrl: menuUrl, + metaData: { treeType: "Umbraco.Web.Trees.ContentTreeController" } }; break; @@ -96,16 +99,16 @@ angular.module('umbraco.mocks'). name: "developer", id: -1, children: [ - { name: "Data types", childNodesUrl: url, id: -1, icon: "icon-folder-close", view: section + "/edit/" + 1234, children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl }, - { name: "Macros", childNodesUrl: url, id: -1, icon: "icon-folder-close", view: section + "/edit/" + 1235, children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl }, - { name: "Pacakges", childNodesUrl: url, id: -1, icon: "icon-folder-close", view: section + "/edit/" + 1236, children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl }, - { name: "XSLT Files", childNodesUrl: url, id: -1, icon: "icon-folder-close", view: section + "/edit/" + 1237, children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl }, - { name: "Razor Files", childNodesUrl: url, id: -1, icon: "icon-folder-close", view: section + "/edit/" + 1237, children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl } + { name: "Data types", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeType: "Umbraco.Web.Trees.DataTypeTreeController" } }, + { name: "Macros", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeType: "Umbraco.Web.Trees.MacrosTreeController" } }, + { name: "Pacakges", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeType: "Umbraco.Web.Trees.PackagesTreeController" } }, + { name: "XSLT Files", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeType: "Umbraco.Web.Trees.XsltTreeController" } }, + { name: "Razor Files", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeType: "Umbraco.Web.Trees.RazorTreeController" } } ], expanded: true, hasChildren: true, level: 0, - isContainer: true + isContainer: true }; break; @@ -114,11 +117,11 @@ angular.module('umbraco.mocks'). name: "settings", id: -1, children: [ - { name: "Stylesheets", childNodesUrl: url, id: -1, icon: "icon-folder-close", view: section + "/edit/" + 1234, children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl }, - { name: "Templates", childNodesUrl: url, id: -1, icon: "icon-folder-close", view: section + "/edit/" + 1235, children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl }, - { name: "Dictionary", childNodesUrl: url, id: -1, icon: "icon-folder-close", view: section + "/edit/" + 1236, children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl }, - { name: "Media types", childNodesUrl: url, id: -1, icon: "icon-folder-close", view: section + "/edit/" + 1237, children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl }, - { name: "Document types", childNodesUrl: url, id: -1, icon: "icon-folder-close", view: section + "/edit/" + 1237, children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl } + { name: "Stylesheets", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeType: "Umbraco.Web.Trees.StylesheetTreeController" } }, + { name: "Templates", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeType: "Umbraco.Web.Trees.TemplatesTreeController" } }, + { name: "Dictionary", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeType: "Umbraco.Web.Trees.DictionaryTreeController" } }, + { name: "Media types", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeType: "Umbraco.Web.Trees.MediaTypesTreeController" } }, + { name: "Document types", childNodesUrl: url, id: -1, icon: "icon-folder-close", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl, metaData: { treeType: "Umbraco.Web.Trees.ContentTypesTreeController" } } ], expanded: true, hasChildren: true, @@ -133,15 +136,16 @@ angular.module('umbraco.mocks'). name: "randomTree", id: -1, children: [ - { name: "random-name-" + section, childNodesUrl: url, id: 1234, icon: "icon-home", defaultAction: "create", view: section + "/edit/" + 1234, children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl }, - { name: "random-name-" + section, childNodesUrl: url, id: 1235, icon: "icon-folder-close", defaultAction: "create", view: section + "/edit/" + 1235, children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl }, - { name: "random-name-" + section, childNodesUrl: url, id: 1236, icon: "icon-folder-close", defaultAction: "create", view: section + "/edit/" + 1236, children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl }, - { name: "random-name-" + section, childNodesUrl: url, id: 1237, icon: "icon-folder-close", defaultAction: "create", view: section + "/edit/" + 1237, children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl } + { name: "random-name-" + section, childNodesUrl: url, id: 1234, icon: "icon-home", defaultAction: "create", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl }, + { name: "random-name-" + section, childNodesUrl: url, id: 1235, icon: "icon-folder-close", defaultAction: "create", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl }, + { name: "random-name-" + section, childNodesUrl: url, id: 1236, icon: "icon-folder-close", defaultAction: "create", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl }, + { name: "random-name-" + section, childNodesUrl: url, id: 1237, icon: "icon-folder-close", defaultAction: "create", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl } ], expanded: true, hasChildren: true, level: 0, - menuUrl: menuUrl + menuUrl: menuUrl, + metaData: { treeType: "Umbraco.Web.Trees.RandomTreeController" } }; break; 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 409f22cc55..dda52c5a98 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 @@ -16,6 +16,15 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { return { + emptyRecycleBin: function() { + return umbRequestHelper.resourcePromise( + $http.delete( + umbRequestHelper.getApiUrl( + "contentApiBaseUrl", + "EmptyRecycleBin")), + 'Failed to empty the recycle bin'); + }, + deleteById: function(id) { return umbRequestHelper.resourcePromise( $http.delete( diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js index fde4494d1e..1b1393979c 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js @@ -12,7 +12,7 @@ function treeService($q, treeResource, iconHelper) { var treeArray = []; var currentSection = "content"; - /** ensures there's a view and level property on each tree node */ + /** ensures there's a routePath, parent and level property on each tree node */ function ensureParentLevelAndView(parentNode, treeNodes, section, level) { //if no level is set, then we make it 1 var childLevel = (level ? level : 1); @@ -33,7 +33,60 @@ function treeService($q, treeResource, iconHelper) { throw "Cannot remove a node that doesn't have a parent"; } //remove the current item from it's siblings - treeNode.parent.children.splice(treeNode.parent.children.indexOf(treeNode), 1); + treeNode.parent.children.splice(treeNode.parent.children.indexOf(treeNode), 1); + }, + + removeChildNodes : function(treeNode) { + treeNode.children = []; + treeNode.hasChildren = false; + }, + + /** Gets a child node by id */ + getChildNode: function(treeNode, id) { + var found = _.find(treeNode.children, function (child) { + return child.id === id; + }); + return found === undefined ? null : found; + }, + + /** Gets a descendant node by id */ + getDescendantNode: function(treeNode, id) { + //check the first level + var found = this.getChildNode(treeNode, id); + if (found) { + return found; + } + + //check each child of this node + for (var i = 0; i < treeNode.children.length; i++) { + if (treeNode.children[i].hasChildren) { + //recurse + found = this.getDescendantNode(treeNode.children[i], id); + if (found) { + return found; + } + } + } + + //not found + return found === undefined ? null : found; + }, + + /** Gets the root node of the current tree type for a given tree node */ + getTreeRoot: function(treeNode) { + //all root nodes have metadata key 'treeType' + var root = null; + var current = treeNode; + while (root === null && current !== undefined) { + + if (current.metaData && current.metaData["treeType"]) { + root = current; + } + else { + current = current.parent; + } + } + return root; }, getTree: function (options) { @@ -97,7 +150,6 @@ function treeService($q, treeResource, iconHelper) { * * @description * Attempts to return a tree node's menu item based on the alias supplied, otherwise returns null. - * @param {object} args An arguments object * @param {object} args.treeNode The tree node to get the menu item for * @param {object} args.menuItemAlias The menu item alias to attempt to find diff --git a/src/Umbraco.Web.UI.Client/src/routes.js b/src/Umbraco.Web.UI.Client/src/routes.js index 84bd4ce84f..4fa91a1321 100644 --- a/src/Umbraco.Web.UI.Client/src/routes.js +++ b/src/Umbraco.Web.UI.Client/src/routes.js @@ -33,6 +33,11 @@ app.config(function ($routeProvider) { if (!rp.method) return "views/common/dashboard.html"; + ////here we detect recycle bins, all recycle bins start with -2* (i.e. -20, -21) + //if (rp.id.startsWith("-2")) { + // return 'views/' + rp.section + '/recyclebin.html'; + //} + return 'views/' + rp.section + '/' + rp.method + '.html'; } }) diff --git a/src/Umbraco.Web.UI.Client/src/views/content/contentcreate.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.create.controller.js similarity index 87% rename from src/Umbraco.Web.UI.Client/src/views/content/contentcreate.controller.js rename to src/Umbraco.Web.UI.Client/src/views/content/content.create.controller.js index 3fa2ea8c8d..de85970d71 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/contentcreate.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.create.controller.js @@ -1,5 +1,5 @@ angular.module('umbraco') -.controller("Umbraco.Editors.ContentCreateController", +.controller("Umbraco.Editors.Content.CreateController", function ($scope, $routeParams, contentTypeResource, iconHelper) { contentTypeResource.getAllowedTypes($scope.currentNode.id) diff --git a/src/Umbraco.Web.UI.Client/src/views/content/contentdelete.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.delete.controller.js similarity index 67% rename from src/Umbraco.Web.UI.Client/src/views/content/contentdelete.controller.js rename to src/Umbraco.Web.UI.Client/src/views/content/content.delete.controller.js index 2a2deee7b3..1ee3114a49 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/contentdelete.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.delete.controller.js @@ -17,6 +17,12 @@ function ContentDeleteController($scope, contentResource, treeService, navigatio $scope.currentNode.loading = false; //TODO: Need to sync tree, etc... treeService.removeNode($scope.currentNode); + + //ensure the recycle bin has child nodes now + var rootNode = treeService.getTreeRoot($scope.currentNode); + var recycleBin = treeService.getDescendantNode(rootNode, -20); + recycleBin.hasChildren = true; + navigationService.hideMenu(); }); @@ -27,4 +33,4 @@ function ContentDeleteController($scope, contentResource, treeService, navigatio }; } -angular.module("umbraco").controller("Umbraco.Editors.ContentDeleteController", ContentDeleteController); +angular.module("umbraco").controller("Umbraco.Editors.Content.DeleteController", ContentDeleteController); diff --git a/src/Umbraco.Web.UI.Client/src/views/content/contentedit.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js similarity index 97% rename from src/Umbraco.Web.UI.Client/src/views/content/contentedit.controller.js rename to src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js index 3f047288ca..62035c79f1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/contentedit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js @@ -91,4 +91,4 @@ function ContentEditController($scope, $routeParams, $location, contentResource, }; } -angular.module("umbraco").controller("Umbraco.Editors.ContentEditController", ContentEditController); +angular.module("umbraco").controller("Umbraco.Editors.Content.EditController", ContentEditController); diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.emptyrecyclebin.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.emptyrecyclebin.controller.js new file mode 100644 index 0000000000..860d65d301 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.emptyrecyclebin.controller.js @@ -0,0 +1,30 @@ +/** + * @ngdoc controller + * @name Umbraco.Editors.Content.EmptyRecycleBinController + * @function + * + * @description + * The controller for deleting content + */ +function ContentEmptyRecycleBinController($scope, contentResource, treeService, navigationService) { + + $scope.performDelete = function() { + + //(used in the UI) + $scope.currentNode.loading = true; + + contentResource.emptyRecycleBin($scope.currentNode.id).then(function () { + $scope.currentNode.loading = false; + //TODO: Need to sync tree, etc... + treeService.removeChildNodes($scope.currentNode); + navigationService.hideMenu(); + }); + + }; + + $scope.cancel = function() { + navigationService.hideDialog(); + }; +} + +angular.module("umbraco").controller("Umbraco.Editors.Content.EmptyRecycleBinController", ContentEmptyRecycleBinController); diff --git a/src/Umbraco.Web.UI.Client/src/views/content/create.html b/src/Umbraco.Web.UI.Client/src/views/content/create.html index 98f9e549da..a91b4dc4c2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/create.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/create.html @@ -1,4 +1,4 @@ -
Create a page under {{currentNode.name}}
diff --git a/src/Umbraco.Web.UI.Client/src/views/content/delete.html b/src/Umbraco.Web.UI.Client/src/views/content/delete.html index f79eb7032a..ed065eeaba 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/delete.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/delete.html @@ -1,4 +1,4 @@ -Create a page under {{currentNode.name}}
diff --git a/src/Umbraco.Web.UI.Client/src/views/media/edit.html b/src/Umbraco.Web.UI.Client/src/views/media/edit.html index dc4fe83b0c..a5d5335cde 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/media/edit.html @@ -1,5 +1,5 @@