Gets delete and empty recycle bin working, adds unit tests for tree service, ensures that the recycle bin shows children whenever something is deleted (though, i now realize i need to refresh the recycle bin node and expand it when something is deleted). Renames some namespaces for content/media/etc... controllers and makes the file names more readable.

This commit is contained in:
Shannon
2013-08-01 14:51:35 +10:00
parent f63988639f
commit 2ebf6d09c6
28 changed files with 419 additions and 125 deletions

2
.gitignore vendored
View File

@@ -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

View File

@@ -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'));
}
};
}]);

View File

@@ -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;

View File

@@ -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(

View File

@@ -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

View File

@@ -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';
}
})

View File

@@ -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)

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -1,4 +1,4 @@
<div class="umb-dialog" ng-controller="Umbraco.Editors.ContentCreateController">
<div class="umb-dialog" ng-controller="Umbraco.Editors.Content.CreateController">
<div class="umb-dialog-body" auto-scale="90">
<p class="umb-abstract">Create a page under {{currentNode.name}}</p>

View File

@@ -1,4 +1,4 @@
<div class="umb-dialog" ng-controller="Umbraco.Editors.ContentDeleteController">
<div class="umb-dialog" ng-controller="Umbraco.Editors.Content.DeleteController">
<div class="umb-dialog-body" auto-scale="90">
<umb-confirm on-confirm="performDelete" on-cancel="cancel">

View File

@@ -1,5 +1,5 @@
<ng-form name="contentForm" ng-show="contentLoaded">
<umb-panel ng-controller="Umbraco.Editors.ContentEditController" val-show-validation>
<umb-panel ng-controller="Umbraco.Editors.Content.EditController" val-show-validation>
<umb-header tabs="content.tabs">
<umb-content-name ng-model="content.name" placeholder="Enter a page title"></umb-content-name>

View File

@@ -0,0 +1,8 @@
<div class="umb-dialog" ng-controller="Umbraco.Editors.Content.EmptyRecycleBinController">
<div class="umb-dialog-body" auto-scale="90">
<umb-confirm on-confirm="performDelete" on-cancel="cancel">
</umb-confirm>
</div>
</div>

View File

@@ -0,0 +1,9 @@
<umb-panel>
<div class="umb-panel-header">
<div class="row-fluid">
<span class="span4 umb-panel-header-meta">
<h1>Recycle bin</h1>
</span>
</div>
</div>
</umb-panel>

View File

@@ -1,4 +1,4 @@
<div class="umb-dialog" ng-controller="Umbraco.Editors.MediaCreateController">
<div class="umb-dialog" ng-controller="Umbraco.Editors.Media.CreateController">
<div class="umb-dialog-body" auto-scale="90">
<p class="umb-abstract">Create a page under {{currentNode.name}}</p>

View File

@@ -1,5 +1,5 @@
<ng-form name="contentForm" ng-show="contentLoaded">
<umb-panel ng-controller="Umbraco.Editors.MediaEditController" val-show-validation>
<umb-panel ng-controller="Umbraco.Editors.Media.EditController" val-show-validation>
<umb-header tabs="content.tabs">
<umb-content-name ng-model="content.name" placeholder="Enter a page title"></umb-content-name>

View File

@@ -1,6 +1,6 @@
function mediaCreateController ($scope, $routeParams,mediaTypeResource) {
$scope.allowedTypes = mediaTypeResource.getAllowedTypes($scope.currentNode.id);
}
angular.module('umbraco')
.controller("Umbraco.Editors.MediaCreateController", mediaCreateController);
function mediaCreateController ($scope, $routeParams,mediaTypeResource) {
$scope.allowedTypes = mediaTypeResource.getAllowedTypes($scope.currentNode.id);
}
angular.module('umbraco')
.controller("Umbraco.Editors.Media.CreateController", mediaCreateController);

View File

@@ -1,63 +1,63 @@
function mediaEditController($scope, $routeParams, mediaResource, notificationsService, angularHelper, serverValidationManager, contentEditingHelper) {
if ($routeParams.create) {
mediaResource.getScaffold($routeParams.id, $routeParams.doctype)
.then(function (data) {
$scope.contentLoaded = true;
$scope.content = data;
});
}
else {
mediaResource.getById($routeParams.id)
.then(function (data) {
$scope.contentLoaded = true;
$scope.content = data;
//in one particular special case, after we've created a new item we redirect back to the edit
// route but there might be server validation errors in the collection which we need to display
// after the redirect, so we will bind all subscriptions which will show the server validation errors
// if there are any and then clear them so the collection no longer persists them.
serverValidationManager.executeAndClearAllSubscriptions();
});
}
$scope.files = [];
$scope.addFiles = function (propertyId, files) {
//this will clear the files for the current property and then add the new ones for the current property
$scope.files = _.reject($scope.files, function (item) {
return item.id == propertyId;
});
for (var i = 0; i < files.length; i++) {
//save the file object to the scope's files collection
$scope.files.push({ id: propertyId, file: files[i] });
}
};
//ensure there is a form object assigned.
var currentForm = angularHelper.getRequiredCurrentForm($scope);
$scope.save = function (cnt) {
$scope.$broadcast("saving", { scope: $scope });
//don't continue if the form is invalid
if (currentForm.$invalid) return;
serverValidationManager.reset();
mediaResource.saveMedia(cnt, $routeParams.create, $scope.files)
.then(function (data) {
contentEditingHelper.handleSuccessfulSave({
scope: $scope,
newContent: data
});
}, function (err) {
contentEditingHelper.handleSaveError(err, $scope);
});
};
}
angular.module("umbraco")
.controller("Umbraco.Editors.MediaEditController", mediaEditController);
function mediaEditController($scope, $routeParams, mediaResource, notificationsService, angularHelper, serverValidationManager, contentEditingHelper) {
if ($routeParams.create) {
mediaResource.getScaffold($routeParams.id, $routeParams.doctype)
.then(function (data) {
$scope.contentLoaded = true;
$scope.content = data;
});
}
else {
mediaResource.getById($routeParams.id)
.then(function (data) {
$scope.contentLoaded = true;
$scope.content = data;
//in one particular special case, after we've created a new item we redirect back to the edit
// route but there might be server validation errors in the collection which we need to display
// after the redirect, so we will bind all subscriptions which will show the server validation errors
// if there are any and then clear them so the collection no longer persists them.
serverValidationManager.executeAndClearAllSubscriptions();
});
}
$scope.files = [];
$scope.addFiles = function (propertyId, files) {
//this will clear the files for the current property and then add the new ones for the current property
$scope.files = _.reject($scope.files, function (item) {
return item.id == propertyId;
});
for (var i = 0; i < files.length; i++) {
//save the file object to the scope's files collection
$scope.files.push({ id: propertyId, file: files[i] });
}
};
//ensure there is a form object assigned.
var currentForm = angularHelper.getRequiredCurrentForm($scope);
$scope.save = function (cnt) {
$scope.$broadcast("saving", { scope: $scope });
//don't continue if the form is invalid
if (currentForm.$invalid) return;
serverValidationManager.reset();
mediaResource.saveMedia(cnt, $routeParams.create, $scope.files)
.then(function (data) {
contentEditingHelper.handleSuccessfulSave({
scope: $scope,
newContent: data
});
}, function (err) {
contentEditingHelper.handleSaveError(err, $scope);
});
};
}
angular.module("umbraco")
.controller("Umbraco.Editors.Media.EditController", mediaEditController);

View File

@@ -20,7 +20,7 @@ describe('edit content controller tests', function () {
//this controller requires an angular form controller applied to it
scope.contentForm = angularHelper.getNullForm("contentForm");
controller = $controller('Umbraco.Editors.ContentEditController', {
controller = $controller('Umbraco.Editors.Content.EditController', {
$scope: scope,
$routeParams: routeParams
});

View File

@@ -0,0 +1,130 @@
describe('tree service tests', function () {
var treeService;
function ensureParentLevelAndView(parentNode, treeNodes, section, level) {
//if no level is set, then we make it 1
var childLevel = (level ? level : 1);
for (var i = 0; i < treeNodes.length; i++) {
treeNodes[i].level = childLevel;
//if there is not route path specified, then set it automatically
if (!treeNodes[i].routePath) {
treeNodes[i].routePath = section + "/edit/" + treeNodes[i].id;
}
treeNodes[i].parent = parentNode;
}
}
function getContentTree() {
var url = "/umbraco/UmbracoTrees/ApplicationTreeApi/GetChildren?treeType=content&id=1234&level=1";
var menuUrl = "/umbraco/UmbracoTrees/ApplicationTreeApi/GetMenu?treeType=content&id=1234&parentId=456";
var t = {
name: "content",
id: -1,
children: [
{
name: "My website", id: 1234, childNodesUrl: url, icon: "icon-home", expanded: false, hasChildren: true, level: 1, defaultAction: "create", menuUrl: menuUrl,
children: [
{ name: "random-name-1", childNodesUrl: url, id: 11, icon: "icon-home", defaultAction: "create", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
{ name: "random-name-2", childNodesUrl: url, id: 12, icon: "icon-folder-close", defaultAction: "create", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
{ name: "random-name-3", childNodesUrl: url, id: 13, icon: "icon-folder-close", defaultAction: "create", children: [], expanded: false, hasChildren: true, level: 1, menuUrl: menuUrl },
{ name: "random-name-4", childNodesUrl: url, id: 14, icon: "icon-folder-close", defaultAction: "create", children: [], expanded: false, hasChildren: true, level: 1, 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: "content/recyclebin", children: [], expanded: false, hasChildren: true, level: 1, defaultAction: "create", menuUrl: menuUrl }
],
expanded: true,
hasChildren: true,
level: 0,
menuUrl: menuUrl,
metaData: { treeType: "Umbraco.Web.Trees.ContentTreeController" }
};
ensureParentLevelAndView(t, t.children, "content", 0);
ensureParentLevelAndView(t.children[0], t.children[0].children, "content", 1);
return t;
}
beforeEach(module('umbraco.services'));
beforeEach(inject(function ($injector) {
treeService = $injector.get('treeService');
}));
describe('query existing node structure of the tree', function () {
it('can get a descendant node', function() {
var tree = getContentTree();
var found = treeService.getDescendantNode(tree, 13);
expect(found).toBeDefined();
expect(found).not.toBeNull();
expect(found.id).toBe(13);
expect(found.name).toBe("random-name-3");
});
it('returns null for a descendant node that doesnt exist', function () {
var tree = getContentTree();
var found = treeService.getDescendantNode(tree, 123456);
expect(found).toBeNull();
});
it('can get a child node', function () {
var tree = getContentTree();
var found = treeService.getChildNode(tree, 1235);
expect(found).toBeDefined();
expect(found).not.toBeNull();
expect(found.id).toBe(1235);
expect(found.name).toBe("Components");
});
it('returns null for a child node that doesnt exist', function () {
var tree = getContentTree();
var found = treeService.getChildNode(tree, 123456);
expect(found).toBeNull();
});
it('returns null for a descendant node that doesnt exist', function () {
var tree = getContentTree();
var found = treeService.getDescendantNode(tree, 123456);
expect(found).toBeNull();
});
it('can get the root node from a child node', function () {
var tree = getContentTree();
var testNode = tree.children[0].children[3];
var root = treeService.getTreeRoot(testNode);
expect(root).toBeDefined();
expect(root).not.toBeNull();
expect(root.id).toBe(-1);
expect(root.name).toBe("content");
});
it('can get the root node from the root node', function () {
var tree = getContentTree();
var root = treeService.getTreeRoot(tree);
expect(root).toBeDefined();
expect(root).not.toBeNull();
expect(root.id).toBe(-1);
expect(root.name).toBe("content");
});
});
});

View File

@@ -176,6 +176,33 @@ namespace Umbraco.Web.Editors
return display;
}
/// <summary>
/// Deletes an item
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public HttpResponseMessage DeleteById(int id)
{
var foundContent = Services.ContentService.GetById(id);
if (foundContent == null)
{
HandleContentNotFound(id);
}
Services.ContentService.Delete(foundContent, UmbracoUser.Id);
return Request.CreateResponse(HttpStatusCode.OK);
}
/// <summary>
/// Empties the recycle bin
/// </summary>
/// <returns></returns>
[HttpDelete]
public HttpResponseMessage EmptyRecycleBin()
{
Services.ContentService.EmptyRecycleBin();
return Request.CreateResponse(HttpStatusCode.OK);
}
private void ShowMessageForStatus(PublishStatus status, ContentItemDisplay display)
{
switch (status.StatusType)

View File

@@ -43,7 +43,8 @@ namespace Umbraco.Web.Trees
queryStrings,
ui.GetText("general", "recycleBin"),
"icon-trash",
RecycleBinSmells));
RecycleBinSmells,
queryStrings.GetValue<string>("application") + "/recyclebin"));
return nodes;
}

View File

@@ -280,8 +280,11 @@ namespace Umbraco.Web.Trees
Icon = xmlTreeNode.Icon,
Title = xmlTreeNode.Text,
NodeType = xmlTreeNode.NodeType
};
if (isRoot)
{
node.AdditionalData.Add("treeType", xmlTreeNode.TreeType);
}
//This is a special case scenario, we know that content/media works based on the normal Belle routing/editing so we'll ensure we don't
// pass in the legacy JS handler so we do it the new way, for all other trees (Currently, this is a WIP), we'll render

View File

@@ -10,9 +10,7 @@ namespace umbraco.BusinessLogic.Actions
public class ActionEmptyTranscan : IAction
{
//create singleton
#pragma warning disable 612,618
private static readonly ActionEmptyTranscan m_instance = new ActionEmptyTranscan();
#pragma warning restore 612,618
private static readonly ActionEmptyTranscan InnerInstance = new ActionEmptyTranscan();
/// <summary>
/// A public constructor exists ONLY for backwards compatibility in regards to 3rd party add-ons.
@@ -24,7 +22,7 @@ namespace umbraco.BusinessLogic.Actions
public static ActionEmptyTranscan Instance
{
get { return m_instance; }
get { return InnerInstance; }
}
#region IAction Members
@@ -57,7 +55,7 @@ namespace umbraco.BusinessLogic.Actions
{
get
{
return "emptyTrashcan";
return "emptyRecycleBin";
}
}

View File

@@ -203,7 +203,6 @@
<Compile Include="Actions\ActionSort.cs" />
<Compile Include="Actions\ActionToPublish.cs" />
<Compile Include="Actions\ActionTranslate.cs" />
<Compile Include="Actions\ActionTreeEditMode.cs" />
<Compile Include="Actions\ActionUpdate.cs" />
<Compile Include="Actions\ContextMenuSeperator.cs" />
<Compile Include="Actions\ActionUnPublish.cs">

View File

@@ -89,6 +89,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuSpecs", "NuSpecs", "{227C
..\build\NuSpecs\UmbracoCms.nuspec = ..\build\NuSpecs\UmbracoCms.nuspec
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{245872FE-BA7D-494A-AFFF-B364BB8FF170}"
ProjectSection(SolutionItems) = preProject
Performance1.psess = Performance1.psess
Performance2.psess = Performance2.psess
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -181,4 +187,7 @@ Global
{73529637-28F5-419C-A6BB-D094E39DE614} = {DD32977B-EF54-475B-9A1B-B97A502C6E58}
{B555AAE6-0F56-442F-AC9F-EF497DB38DE7} = {DD32977B-EF54-475B-9A1B-B97A502C6E58}
EndGlobalSection
GlobalSection(Performance) = preSolution
HasPerformanceSessions = true
EndGlobalSection
EndGlobal