U4-6003 List View - Order By Custom Property Fix

Original work done on https://github.com/umbraco/Umbraco-CMS/pull/711 but ported to the latest version

Content below for reference
With the current implementation of the list view you can only sort by system columns (Name, SortOrder etc.) and not custom columns you have added to your document types. This PR allows that.

The crux of it is a sub-query added to the ORDER BY clause when we are ordering by a custom field. This looks up the field's value from the most recent content version.

Provided here and not in the previous pull request is:

MySQL support
Have done some performance testing. On a local laptop with 1000 nodes in a list view, it's sorting in around 220-250ms. It's a little slower that sorting on native properties like node name, but still perfectly usable - there's no significant delay you see in use.
Please note also:

GetPagedResultsByQuery() in VersionableRepositoryBase was previously doing an ORDER BY in SQL and then repeating this via LINQ to Objects. I couldn't see that this second ordering was necessary so removed it, but wanted to flag here in case I've missed something around why this was necessary.
The PR also includes small amends to fix or hide sorting on a couple of the default columns for the member amd media list views.
This commit is contained in:
André Ferreira
2016-02-26 14:30:32 +00:00
parent ac2ebc4071
commit bd2a40d214
31 changed files with 2526 additions and 2457 deletions

View File

@@ -1,71 +1,71 @@
(function() {
'use strict';
function TableDirective() {
function link(scope, el, attr, ctrl) {
scope.clickItem = function(item, $event) {
if(scope.onClick) {
scope.onClick(item);
$event.stopPropagation();
}
};
scope.selectItem = function(item, $index, $event) {
if(scope.onSelect) {
scope.onSelect(item, $index, $event);
$event.stopPropagation();
}
};
scope.selectAll = function($event) {
if(scope.onSelectAll) {
scope.onSelectAll($event);
}
};
scope.isSelectedAll = function() {
if(scope.onSelectedAll && scope.items && scope.items.length > 0) {
return scope.onSelectedAll();
}
};
scope.isSortDirection = function (col, direction) {
if (scope.onSortingDirection) {
return scope.onSortingDirection(col, direction);
}
};
scope.sort = function(field, allow) {
if(scope.onSort) {
scope.onSort(field, allow);
}
};
}
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/umb-table.html',
scope: {
items: '=',
itemProperties: '=',
allowSelectAll: '=',
onSelect: '=',
onClick: '=',
onSelectAll: '=',
onSelectedAll: '=',
onSortingDirection: '=',
onSort: '='
},
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('umbTable', TableDirective);
})();
(function () {
'use strict';
function TableDirective() {
function link(scope, el, attr, ctrl) {
scope.clickItem = function (item, $event) {
if (scope.onClick) {
scope.onClick(item);
$event.stopPropagation();
}
};
scope.selectItem = function (item, $index, $event) {
if (scope.onSelect) {
scope.onSelect(item, $index, $event);
$event.stopPropagation();
}
};
scope.selectAll = function ($event) {
if (scope.onSelectAll) {
scope.onSelectAll($event);
}
};
scope.isSelectedAll = function () {
if (scope.onSelectedAll && scope.items && scope.items.length > 0) {
return scope.onSelectedAll();
}
};
scope.isSortDirection = function (col, direction) {
if (scope.onSortingDirection) {
return scope.onSortingDirection(col, direction);
}
};
scope.sort = function (field, allow, isSystem) {
if (scope.onSort) {
scope.onSort(field, allow, isSystem);
}
};
}
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/umb-table.html',
scope: {
items: '=',
itemProperties: '=',
allowSelectAll: '=',
onSelect: '=',
onClick: '=',
onSelectAll: '=',
onSelectedAll: '=',
onSortingDirection: '=',
onSort: '='
},
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('umbTable', TableDirective);
})();

View File

@@ -4,470 +4,472 @@
* @description Loads in data for media
**/
function mediaResource($q, $http, umbDataFormatter, umbRequestHelper) {
/** internal method process the saving of data and post processing the result */
function saveMediaItem(content, action, files) {
return umbRequestHelper.postSaveContent({
restApiUrl: umbRequestHelper.getApiUrl(
/** internal method process the saving of data and post processing the result */
function saveMediaItem(content, action, files) {
return umbRequestHelper.postSaveContent({
restApiUrl: umbRequestHelper.getApiUrl(
"mediaApiBaseUrl",
"PostSave"),
content: content,
action: action,
files: files,
dataFormatter: function (c, a) {
return umbDataFormatter.formatMediaPostData(c, a);
}
});
}
content: content,
action: action,
files: files,
dataFormatter: function (c, a) {
return umbDataFormatter.formatMediaPostData(c, a);
}
});
}
return {
getRecycleBin: function () {
return umbRequestHelper.resourcePromise(
return {
getRecycleBin: function () {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"mediaApiBaseUrl",
"GetRecycleBin")),
umbRequestHelper.getApiUrl(
"mediaApiBaseUrl",
"GetRecycleBin")),
'Failed to retrieve data for media recycle bin');
},
},
/**
* @ngdoc method
* @name umbraco.resources.mediaResource#sort
* @methodOf umbraco.resources.mediaResource
*
* @description
* Sorts all children below a given parent node id, based on a collection of node-ids
*
* ##usage
* <pre>
* var ids = [123,34533,2334,23434];
* mediaResource.sort({ sortedIds: ids })
* .then(function() {
* $scope.complete = true;
* });
* </pre>
* @param {Object} args arguments object
* @param {Int} args.parentId the ID of the parent node
* @param {Array} options.sortedIds array of node IDs as they should be sorted
* @returns {Promise} resourcePromise object.
*
*/
sort: function (args) {
if (!args) {
throw "args cannot be null";
}
if (!args.parentId) {
throw "args.parentId cannot be null";
}
if (!args.sortedIds) {
throw "args.sortedIds cannot be null";
}
/**
* @ngdoc method
* @name umbraco.resources.mediaResource#sort
* @methodOf umbraco.resources.mediaResource
*
* @description
* Sorts all children below a given parent node id, based on a collection of node-ids
*
* ##usage
* <pre>
* var ids = [123,34533,2334,23434];
* mediaResource.sort({ sortedIds: ids })
* .then(function() {
* $scope.complete = true;
* });
* </pre>
* @param {Object} args arguments object
* @param {Int} args.parentId the ID of the parent node
* @param {Array} options.sortedIds array of node IDs as they should be sorted
* @returns {Promise} resourcePromise object.
*
*/
sort: function (args) {
if (!args) {
throw "args cannot be null";
}
if (!args.parentId) {
throw "args.parentId cannot be null";
}
if (!args.sortedIds) {
throw "args.sortedIds cannot be null";
}
return umbRequestHelper.resourcePromise(
return umbRequestHelper.resourcePromise(
$http.post(umbRequestHelper.getApiUrl("mediaApiBaseUrl", "PostSort"),
{
parentId: args.parentId,
idSortOrder: args.sortedIds
}),
{
parentId: args.parentId,
idSortOrder: args.sortedIds
}),
'Failed to sort media');
},
},
/**
* @ngdoc method
* @name umbraco.resources.mediaResource#move
* @methodOf umbraco.resources.mediaResource
*
* @description
* Moves a node underneath a new parentId
*
* ##usage
* <pre>
* mediaResource.move({ parentId: 1244, id: 123 })
* .then(function() {
* alert("node was moved");
* }, function(err){
* alert("node didnt move:" + err.data.Message);
* });
* </pre>
* @param {Object} args arguments object
* @param {Int} args.idd the ID of the node to move
* @param {Int} args.parentId the ID of the parent node to move to
* @returns {Promise} resourcePromise object.
*
*/
move: function (args) {
if (!args) {
throw "args cannot be null";
}
if (!args.parentId) {
throw "args.parentId cannot be null";
}
if (!args.id) {
throw "args.id cannot be null";
}
/**
* @ngdoc method
* @name umbraco.resources.mediaResource#move
* @methodOf umbraco.resources.mediaResource
*
* @description
* Moves a node underneath a new parentId
*
* ##usage
* <pre>
* mediaResource.move({ parentId: 1244, id: 123 })
* .then(function() {
* alert("node was moved");
* }, function(err){
* alert("node didnt move:" + err.data.Message);
* });
* </pre>
* @param {Object} args arguments object
* @param {Int} args.idd the ID of the node to move
* @param {Int} args.parentId the ID of the parent node to move to
* @returns {Promise} resourcePromise object.
*
*/
move: function (args) {
if (!args) {
throw "args cannot be null";
}
if (!args.parentId) {
throw "args.parentId cannot be null";
}
if (!args.id) {
throw "args.id cannot be null";
}
return umbRequestHelper.resourcePromise(
return umbRequestHelper.resourcePromise(
$http.post(umbRequestHelper.getApiUrl("mediaApiBaseUrl", "PostMove"),
{
parentId: args.parentId,
id: args.id
}),
{
parentId: args.parentId,
id: args.id
}),
'Failed to move media');
},
},
/**
* @ngdoc method
* @name umbraco.resources.mediaResource#getById
* @methodOf umbraco.resources.mediaResource
*
* @description
* Gets a media item with a given id
*
* ##usage
* <pre>
* mediaResource.getById(1234)
* .then(function(media) {
* var myMedia = media;
* alert('its here!');
* });
* </pre>
*
* @param {Int} id id of media item to return
* @returns {Promise} resourcePromise object containing the media item.
*
*/
getById: function (id) {
return umbRequestHelper.resourcePromise(
/**
* @ngdoc method
* @name umbraco.resources.mediaResource#getById
* @methodOf umbraco.resources.mediaResource
*
* @description
* Gets a media item with a given id
*
* ##usage
* <pre>
* mediaResource.getById(1234)
* .then(function(media) {
* var myMedia = media;
* alert('its here!');
* });
* </pre>
*
* @param {Int} id id of media item to return
* @returns {Promise} resourcePromise object containing the media item.
*
*/
getById: function (id) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"mediaApiBaseUrl",
"GetById",
[{ id: id }])),
umbRequestHelper.getApiUrl(
"mediaApiBaseUrl",
"GetById",
[{ id: id }])),
'Failed to retrieve data for media id ' + id);
},
},
/**
* @ngdoc method
* @name umbraco.resources.mediaResource#deleteById
* @methodOf umbraco.resources.mediaResource
*
* @description
* Deletes a media item with a given id
*
* ##usage
* <pre>
* mediaResource.deleteById(1234)
* .then(function() {
* alert('its gone!');
* });
* </pre>
*
* @param {Int} id id of media item to delete
* @returns {Promise} resourcePromise object.
*
*/
deleteById: function(id) {
return umbRequestHelper.resourcePromise(
/**
* @ngdoc method
* @name umbraco.resources.mediaResource#deleteById
* @methodOf umbraco.resources.mediaResource
*
* @description
* Deletes a media item with a given id
*
* ##usage
* <pre>
* mediaResource.deleteById(1234)
* .then(function() {
* alert('its gone!');
* });
* </pre>
*
* @param {Int} id id of media item to delete
* @returns {Promise} resourcePromise object.
*
*/
deleteById: function (id) {
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"mediaApiBaseUrl",
"DeleteById",
[{ id: id }])),
umbRequestHelper.getApiUrl(
"mediaApiBaseUrl",
"DeleteById",
[{ id: id }])),
'Failed to delete item ' + id);
},
},
/**
* @ngdoc method
* @name umbraco.resources.mediaResource#getByIds
* @methodOf umbraco.resources.mediaResource
*
* @description
* Gets an array of media items, given a collection of ids
*
* ##usage
* <pre>
* mediaResource.getByIds( [1234,2526,28262])
* .then(function(mediaArray) {
* var myDoc = contentArray;
* alert('they are here!');
* });
* </pre>
*
* @param {Array} ids ids of media items to return as an array
* @returns {Promise} resourcePromise object containing the media items array.
*
*/
getByIds: function (ids) {
var idQuery = "";
_.each(ids, function(item) {
idQuery += "ids=" + item + "&";
});
/**
* @ngdoc method
* @name umbraco.resources.mediaResource#getByIds
* @methodOf umbraco.resources.mediaResource
*
* @description
* Gets an array of media items, given a collection of ids
*
* ##usage
* <pre>
* mediaResource.getByIds( [1234,2526,28262])
* .then(function(mediaArray) {
* var myDoc = contentArray;
* alert('they are here!');
* });
* </pre>
*
* @param {Array} ids ids of media items to return as an array
* @returns {Promise} resourcePromise object containing the media items array.
*
*/
getByIds: function (ids) {
return umbRequestHelper.resourcePromise(
var idQuery = "";
_.each(ids, function (item) {
idQuery += "ids=" + item + "&";
});
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"mediaApiBaseUrl",
"GetByIds",
idQuery)),
umbRequestHelper.getApiUrl(
"mediaApiBaseUrl",
"GetByIds",
idQuery)),
'Failed to retrieve data for media ids ' + ids);
},
},
/**
* @ngdoc method
* @name umbraco.resources.mediaResource#getScaffold
* @methodOf umbraco.resources.mediaResource
*
* @description
* Returns a scaffold of an empty media item, given the id of the media item to place it underneath and the media type alias.
*
* - Parent Id must be provided so umbraco knows where to store the media
* - Media Type alias must be provided so umbraco knows which properties to put on the media scaffold
*
* The scaffold is used to build editors for media that has not yet been populated with data.
*
* ##usage
* <pre>
* mediaResource.getScaffold(1234, 'folder')
* .then(function(scaffold) {
* var myDoc = scaffold;
* myDoc.name = "My new media item";
*
* mediaResource.save(myDoc, true)
* .then(function(media){
* alert("Retrieved, updated and saved again");
* });
* });
* </pre>
*
* @param {Int} parentId id of media item to return
* @param {String} alias mediatype alias to base the scaffold on
* @returns {Promise} resourcePromise object containing the media scaffold.
*
*/
getScaffold: function (parentId, alias) {
return umbRequestHelper.resourcePromise(
/**
* @ngdoc method
* @name umbraco.resources.mediaResource#getScaffold
* @methodOf umbraco.resources.mediaResource
*
* @description
* Returns a scaffold of an empty media item, given the id of the media item to place it underneath and the media type alias.
*
* - Parent Id must be provided so umbraco knows where to store the media
* - Media Type alias must be provided so umbraco knows which properties to put on the media scaffold
*
* The scaffold is used to build editors for media that has not yet been populated with data.
*
* ##usage
* <pre>
* mediaResource.getScaffold(1234, 'folder')
* .then(function(scaffold) {
* var myDoc = scaffold;
* myDoc.name = "My new media item";
*
* mediaResource.save(myDoc, true)
* .then(function(media){
* alert("Retrieved, updated and saved again");
* });
* });
* </pre>
*
* @param {Int} parentId id of media item to return
* @param {String} alias mediatype alias to base the scaffold on
* @returns {Promise} resourcePromise object containing the media scaffold.
*
*/
getScaffold: function (parentId, alias) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"mediaApiBaseUrl",
"GetEmpty",
[{ contentTypeAlias: alias }, { parentId: parentId }])),
umbRequestHelper.getApiUrl(
"mediaApiBaseUrl",
"GetEmpty",
[{ contentTypeAlias: alias }, { parentId: parentId }])),
'Failed to retrieve data for empty media item type ' + alias);
},
},
rootMedia: function () {
return umbRequestHelper.resourcePromise(
rootMedia: function () {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"mediaApiBaseUrl",
"GetRootMedia")),
umbRequestHelper.getApiUrl(
"mediaApiBaseUrl",
"GetRootMedia")),
'Failed to retrieve data for root media');
},
},
/**
* @ngdoc method
* @name umbraco.resources.mediaResource#getChildren
* @methodOf umbraco.resources.mediaResource
*
* @description
* Gets children of a media item with a given id
*
* ##usage
* <pre>
* mediaResource.getChildren(1234, {pageSize: 10, pageNumber: 2})
* .then(function(contentArray) {
* var children = contentArray;
* alert('they are here!');
* });
* </pre>
*
* @param {Int} parentid id of content item to return children of
* @param {Object} options optional options object
* @param {Int} options.pageSize if paging data, number of nodes per page, default = 0
* @param {Int} options.pageNumber if paging data, current page index, default = 0
* @param {String} options.filter if provided, query will only return those with names matching the filter
* @param {String} options.orderDirection can be `Ascending` or `Descending` - Default: `Ascending`
* @param {String} options.orderBy property to order items by, default: `SortOrder`
* @returns {Promise} resourcePromise object containing an array of content items.
*
*/
getChildren: function (parentId, options) {
/**
* @ngdoc method
* @name umbraco.resources.mediaResource#getChildren
* @methodOf umbraco.resources.mediaResource
*
* @description
* Gets children of a media item with a given id
*
* ##usage
* <pre>
* mediaResource.getChildren(1234, {pageSize: 10, pageNumber: 2})
* .then(function(contentArray) {
* var children = contentArray;
* alert('they are here!');
* });
* </pre>
*
* @param {Int} parentid id of content item to return children of
* @param {Object} options optional options object
* @param {Int} options.pageSize if paging data, number of nodes per page, default = 0
* @param {Int} options.pageNumber if paging data, current page index, default = 0
* @param {String} options.filter if provided, query will only return those with names matching the filter
* @param {String} options.orderDirection can be `Ascending` or `Descending` - Default: `Ascending`
* @param {String} options.orderBy property to order items by, default: `SortOrder`
* @returns {Promise} resourcePromise object containing an array of content items.
*
*/
getChildren: function (parentId, options) {
var defaults = {
pageSize: 0,
pageNumber: 0,
filter: '',
orderDirection: "Ascending",
orderBy: "SortOrder"
};
if (options === undefined) {
options = {};
}
//overwrite the defaults if there are any specified
angular.extend(defaults, options);
//now copy back to the options we will use
options = defaults;
//change asc/desct
if (options.orderDirection === "asc") {
options.orderDirection = "Ascending";
}
else if (options.orderDirection === "desc") {
options.orderDirection = "Descending";
}
var defaults = {
pageSize: 0,
pageNumber: 0,
filter: '',
orderDirection: "Ascending",
orderBy: "SortOrder",
orderBySystemField: true
};
if (options === undefined) {
options = {};
}
//overwrite the defaults if there are any specified
angular.extend(defaults, options);
//now copy back to the options we will use
options = defaults;
//change asc/desct
if (options.orderDirection === "asc") {
options.orderDirection = "Ascending";
}
else if (options.orderDirection === "desc") {
options.orderDirection = "Descending";
}
return umbRequestHelper.resourcePromise(
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"mediaApiBaseUrl",
"GetChildren",
[
{ id: parentId },
{ pageNumber: options.pageNumber },
{ pageSize: options.pageSize },
{ orderBy: options.orderBy },
{ orderDirection: options.orderDirection },
{ filter: options.filter }
])),
umbRequestHelper.getApiUrl(
"mediaApiBaseUrl",
"GetChildren",
[
{ id: parentId },
{ pageNumber: options.pageNumber },
{ pageSize: options.pageSize },
{ orderBy: options.orderBy },
{ orderDirection: options.orderDirection },
{ orderBySystemField: options.orderBySystemField },
{ filter: options.filter }
])),
'Failed to retrieve children for media item ' + parentId);
},
/**
* @ngdoc method
* @name umbraco.resources.mediaResource#save
* @methodOf umbraco.resources.mediaResource
*
* @description
* Saves changes made to a media item, if the media item is new, the isNew paramater must be passed to force creation
* if the media item needs to have files attached, they must be provided as the files param and passed separately
*
*
* ##usage
* <pre>
* mediaResource.getById(1234)
* .then(function(media) {
* media.name = "I want a new name!";
* mediaResource.save(media, false)
* .then(function(media){
* alert("Retrieved, updated and saved again");
* });
* });
* </pre>
*
* @param {Object} media The media item object with changes applied
* @param {Bool} isNew set to true to create a new item or to update an existing
* @param {Array} files collection of files for the media item
* @returns {Promise} resourcePromise object containing the saved media item.
*
*/
save: function (media, isNew, files) {
return saveMediaItem(media, "save" + (isNew ? "New" : ""), files);
},
},
/**
* @ngdoc method
* @name umbraco.resources.mediaResource#addFolder
* @methodOf umbraco.resources.mediaResource
*
* @description
* Shorthand for adding a media item of the type "Folder" under a given parent ID
*
* ##usage
* <pre>
* mediaResource.addFolder("My gallery", 1234)
* .then(function(folder) {
* alert('New folder');
* });
* </pre>
*
* @param {string} name Name of the folder to create
* @param {int} parentId Id of the media item to create the folder underneath
* @returns {Promise} resourcePromise object.
*
*/
addFolder: function(name, parentId){
return umbRequestHelper.resourcePromise(
/**
* @ngdoc method
* @name umbraco.resources.mediaResource#save
* @methodOf umbraco.resources.mediaResource
*
* @description
* Saves changes made to a media item, if the media item is new, the isNew paramater must be passed to force creation
* if the media item needs to have files attached, they must be provided as the files param and passed separately
*
*
* ##usage
* <pre>
* mediaResource.getById(1234)
* .then(function(media) {
* media.name = "I want a new name!";
* mediaResource.save(media, false)
* .then(function(media){
* alert("Retrieved, updated and saved again");
* });
* });
* </pre>
*
* @param {Object} media The media item object with changes applied
* @param {Bool} isNew set to true to create a new item or to update an existing
* @param {Array} files collection of files for the media item
* @returns {Promise} resourcePromise object containing the saved media item.
*
*/
save: function (media, isNew, files) {
return saveMediaItem(media, "save" + (isNew ? "New" : ""), files);
},
/**
* @ngdoc method
* @name umbraco.resources.mediaResource#addFolder
* @methodOf umbraco.resources.mediaResource
*
* @description
* Shorthand for adding a media item of the type "Folder" under a given parent ID
*
* ##usage
* <pre>
* mediaResource.addFolder("My gallery", 1234)
* .then(function(folder) {
* alert('New folder');
* });
* </pre>
*
* @param {string} name Name of the folder to create
* @param {int} parentId Id of the media item to create the folder underneath
* @returns {Promise} resourcePromise object.
*
*/
addFolder: function (name, parentId) {
return umbRequestHelper.resourcePromise(
$http.post(umbRequestHelper
.getApiUrl("mediaApiBaseUrl", "PostAddFolder"),
{
name: name,
parentId: parentId
}),
.getApiUrl("mediaApiBaseUrl", "PostAddFolder"),
{
name: name,
parentId: parentId
}),
'Failed to add folder');
},
/**
* @ngdoc method
* @name umbraco.resources.mediaResource#getChildFolders
* @methodOf umbraco.resources.mediaResource
*
* @description
* Retrieves all media children with types used as folders.
* Uses the convention of looking for media items with mediaTypes ending in
* *Folder so will match "Folder", "bannerFolder", "secureFolder" etc,
*
* ##usage
* <pre>
* mediaResource.getChildFolders(1234)
* .then(function(data) {
* alert('folders');
* });
* </pre>
*
* @param {int} parentId Id of the media item to query for child folders
* @returns {Promise} resourcePromise object.
*
*/
getChildFolders: function(parentId){
if(!parentId){
parentId = -1;
}
return umbRequestHelper.resourcePromise(
},
/**
* @ngdoc method
* @name umbraco.resources.mediaResource#getChildFolders
* @methodOf umbraco.resources.mediaResource
*
* @description
* Retrieves all media children with types used as folders.
* Uses the convention of looking for media items with mediaTypes ending in
* *Folder so will match "Folder", "bannerFolder", "secureFolder" etc,
*
* ##usage
* <pre>
* mediaResource.getChildFolders(1234)
* .then(function(data) {
* alert('folders');
* });
* </pre>
*
* @param {int} parentId Id of the media item to query for child folders
* @returns {Promise} resourcePromise object.
*
*/
getChildFolders: function (parentId) {
if (!parentId) {
parentId = -1;
}
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"mediaApiBaseUrl",
"GetChildFolders",
[
{ id: parentId }
])),
umbRequestHelper.getApiUrl(
"mediaApiBaseUrl",
"GetChildFolders",
[
{ id: parentId }
])),
'Failed to retrieve child folders for media item ' + parentId);
},
/**
* @ngdoc method
* @name umbraco.resources.mediaResource#emptyRecycleBin
* @methodOf umbraco.resources.mediaResource
*
* @description
* Empties the media recycle bin
*
* ##usage
* <pre>
* mediaResource.emptyRecycleBin()
* .then(function() {
* alert('its empty!');
* });
* </pre>
*
* @returns {Promise} resourcePromise object.
*
*/
emptyRecycleBin: function() {
return umbRequestHelper.resourcePromise(
},
/**
* @ngdoc method
* @name umbraco.resources.mediaResource#emptyRecycleBin
* @methodOf umbraco.resources.mediaResource
*
* @description
* Empties the media recycle bin
*
* ##usage
* <pre>
* mediaResource.emptyRecycleBin()
* .then(function() {
* alert('its empty!');
* });
* </pre>
*
* @returns {Promise} resourcePromise object.
*
*/
emptyRecycleBin: function () {
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"mediaApiBaseUrl",
"EmptyRecycleBin")),
umbRequestHelper.getApiUrl(
"mediaApiBaseUrl",
"EmptyRecycleBin")),
'Failed to empty the recycle bin');
}
};
}
};
}
angular.module('umbraco.resources').factory('mediaResource', mediaResource);

View File

@@ -4,230 +4,232 @@
* @description Loads in data for members
**/
function memberResource($q, $http, umbDataFormatter, umbRequestHelper) {
/** internal method process the saving of data and post processing the result */
function saveMember(content, action, files) {
return umbRequestHelper.postSaveContent({
restApiUrl: umbRequestHelper.getApiUrl(
/** internal method process the saving of data and post processing the result */
function saveMember(content, action, files) {
return umbRequestHelper.postSaveContent({
restApiUrl: umbRequestHelper.getApiUrl(
"memberApiBaseUrl",
"PostSave"),
content: content,
action: action,
files: files,
dataFormatter: function(c, a) {
return umbDataFormatter.formatMemberPostData(c, a);
}
});
}
content: content,
action: action,
files: files,
dataFormatter: function (c, a) {
return umbDataFormatter.formatMemberPostData(c, a);
}
});
}
return {
getPagedResults: function (memberTypeAlias, options) {
return {
if (memberTypeAlias === 'all-members') {
memberTypeAlias = null;
}
getPagedResults: function (memberTypeAlias, options) {
var defaults = {
pageSize: 25,
pageNumber: 1,
filter: '',
orderDirection: "Ascending",
orderBy: "LoginName"
};
if (options === undefined) {
options = {};
}
//overwrite the defaults if there are any specified
angular.extend(defaults, options);
//now copy back to the options we will use
options = defaults;
//change asc/desct
if (options.orderDirection === "asc") {
options.orderDirection = "Ascending";
}
else if (options.orderDirection === "desc") {
options.orderDirection = "Descending";
}
if (memberTypeAlias === 'all-members') {
memberTypeAlias = null;
}
var params = [
var defaults = {
pageSize: 25,
pageNumber: 1,
filter: '',
orderDirection: "Ascending",
orderBy: "LoginName",
orderBySystemField: true
};
if (options === undefined) {
options = {};
}
//overwrite the defaults if there are any specified
angular.extend(defaults, options);
//now copy back to the options we will use
options = defaults;
//change asc/desct
if (options.orderDirection === "asc") {
options.orderDirection = "Ascending";
}
else if (options.orderDirection === "desc") {
options.orderDirection = "Descending";
}
var params = [
{ pageNumber: options.pageNumber },
{ pageSize: options.pageSize },
{ orderBy: options.orderBy },
{ orderDirection: options.orderDirection },
{ orderBySystemField: options.orderBySystemField },
{ filter: options.filter }
];
if (memberTypeAlias != null) {
params.push({ memberTypeAlias: memberTypeAlias });
}
];
if (memberTypeAlias != null) {
params.push({ memberTypeAlias: memberTypeAlias });
}
return umbRequestHelper.resourcePromise(
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"memberApiBaseUrl",
"GetPagedResults",
params)),
umbRequestHelper.getApiUrl(
"memberApiBaseUrl",
"GetPagedResults",
params)),
'Failed to retrieve member paged result');
},
getListNode: function (listName) {
},
return umbRequestHelper.resourcePromise(
getListNode: function (listName) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"memberApiBaseUrl",
"GetListNodeDisplay",
[{ listName: listName }])),
umbRequestHelper.getApiUrl(
"memberApiBaseUrl",
"GetListNodeDisplay",
[{ listName: listName }])),
'Failed to retrieve data for member list ' + listName);
},
},
/**
* @ngdoc method
* @name umbraco.resources.memberResource#getByKey
* @methodOf umbraco.resources.memberResource
*
* @description
* Gets a member item with a given key
*
* ##usage
* <pre>
* memberResource.getByKey("0000-0000-000-00000-000")
* .then(function(member) {
* var mymember = member;
* alert('its here!');
* });
* </pre>
*
* @param {Guid} key key of member item to return
* @returns {Promise} resourcePromise object containing the member item.
*
*/
getByKey: function (key) {
return umbRequestHelper.resourcePromise(
/**
* @ngdoc method
* @name umbraco.resources.memberResource#getByKey
* @methodOf umbraco.resources.memberResource
*
* @description
* Gets a member item with a given key
*
* ##usage
* <pre>
* memberResource.getByKey("0000-0000-000-00000-000")
* .then(function(member) {
* var mymember = member;
* alert('its here!');
* });
* </pre>
*
* @param {Guid} key key of member item to return
* @returns {Promise} resourcePromise object containing the member item.
*
*/
getByKey: function (key) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"memberApiBaseUrl",
"GetByKey",
[{ key: key }])),
umbRequestHelper.getApiUrl(
"memberApiBaseUrl",
"GetByKey",
[{ key: key }])),
'Failed to retrieve data for member id ' + key);
},
},
/**
* @ngdoc method
* @name umbraco.resources.memberResource#deleteByKey
* @methodOf umbraco.resources.memberResource
*
* @description
* Deletes a member item with a given key
*
* ##usage
* <pre>
* memberResource.deleteByKey("0000-0000-000-00000-000")
* .then(function() {
* alert('its gone!');
* });
* </pre>
*
* @param {Guid} key id of member item to delete
* @returns {Promise} resourcePromise object.
*
*/
deleteByKey: function (key) {
return umbRequestHelper.resourcePromise(
/**
* @ngdoc method
* @name umbraco.resources.memberResource#deleteByKey
* @methodOf umbraco.resources.memberResource
*
* @description
* Deletes a member item with a given key
*
* ##usage
* <pre>
* memberResource.deleteByKey("0000-0000-000-00000-000")
* .then(function() {
* alert('its gone!');
* });
* </pre>
*
* @param {Guid} key id of member item to delete
* @returns {Promise} resourcePromise object.
*
*/
deleteByKey: function (key) {
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"memberApiBaseUrl",
"DeleteByKey",
[{ key: key }])),
umbRequestHelper.getApiUrl(
"memberApiBaseUrl",
"DeleteByKey",
[{ key: key }])),
'Failed to delete item ' + key);
},
},
/**
* @ngdoc method
* @name umbraco.resources.memberResource#getScaffold
* @methodOf umbraco.resources.memberResource
*
* @description
* Returns a scaffold of an empty member item, given the id of the member item to place it underneath and the member type alias.
*
* - Member Type alias must be provided so umbraco knows which properties to put on the member scaffold
*
* The scaffold is used to build editors for member that has not yet been populated with data.
*
* ##usage
* <pre>
* memberResource.getScaffold('client')
* .then(function(scaffold) {
* var myDoc = scaffold;
* myDoc.name = "My new member item";
*
* memberResource.save(myDoc, true)
* .then(function(member){
* alert("Retrieved, updated and saved again");
* });
* });
* </pre>
*
* @param {String} alias membertype alias to base the scaffold on
* @returns {Promise} resourcePromise object containing the member scaffold.
*
*/
getScaffold: function (alias) {
if (alias) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"memberApiBaseUrl",
"GetEmpty",
[{ contentTypeAlias: alias }])),
'Failed to retrieve data for empty member item type ' + alias);
}
else {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"memberApiBaseUrl",
"GetEmpty")),
'Failed to retrieve data for empty member item type ' + alias);
}
/**
* @ngdoc method
* @name umbraco.resources.memberResource#getScaffold
* @methodOf umbraco.resources.memberResource
*
* @description
* Returns a scaffold of an empty member item, given the id of the member item to place it underneath and the member type alias.
*
* - Member Type alias must be provided so umbraco knows which properties to put on the member scaffold
*
* The scaffold is used to build editors for member that has not yet been populated with data.
*
* ##usage
* <pre>
* memberResource.getScaffold('client')
* .then(function(scaffold) {
* var myDoc = scaffold;
* myDoc.name = "My new member item";
*
* memberResource.save(myDoc, true)
* .then(function(member){
* alert("Retrieved, updated and saved again");
* });
* });
* </pre>
*
* @param {String} alias membertype alias to base the scaffold on
* @returns {Promise} resourcePromise object containing the member scaffold.
*
*/
getScaffold: function (alias) {
},
/**
* @ngdoc method
* @name umbraco.resources.memberResource#save
* @methodOf umbraco.resources.memberResource
*
* @description
* Saves changes made to a member, if the member is new, the isNew paramater must be passed to force creation
* if the member needs to have files attached, they must be provided as the files param and passed separately
*
*
* ##usage
* <pre>
* memberResource.getBykey("23234-sd8djsd-3h8d3j-sdh8d")
* .then(function(member) {
* member.name = "Bob";
* memberResource.save(member, false)
* .then(function(member){
* alert("Retrieved, updated and saved again");
* });
* });
* </pre>
*
* @param {Object} media The member item object with changes applied
* @param {Bool} isNew set to true to create a new item or to update an existing
* @param {Array} files collection of files for the media item
* @returns {Promise} resourcePromise object containing the saved media item.
*
*/
save: function (member, isNew, files) {
return saveMember(member, "save" + (isNew ? "New" : ""), files);
}
};
if (alias) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"memberApiBaseUrl",
"GetEmpty",
[{ contentTypeAlias: alias }])),
'Failed to retrieve data for empty member item type ' + alias);
}
else {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"memberApiBaseUrl",
"GetEmpty")),
'Failed to retrieve data for empty member item type ' + alias);
}
},
/**
* @ngdoc method
* @name umbraco.resources.memberResource#save
* @methodOf umbraco.resources.memberResource
*
* @description
* Saves changes made to a member, if the member is new, the isNew paramater must be passed to force creation
* if the member needs to have files attached, they must be provided as the files param and passed separately
*
*
* ##usage
* <pre>
* memberResource.getBykey("23234-sd8djsd-3h8d3j-sdh8d")
* .then(function(member) {
* member.name = "Bob";
* memberResource.save(member, false)
* .then(function(member){
* alert("Retrieved, updated and saved again");
* });
* });
* </pre>
*
* @param {Object} media The member item object with changes applied
* @param {Bool} isNew set to true to create a new item or to update an existing
* @param {Array} files collection of files for the media item
* @returns {Promise} resourcePromise object containing the saved media item.
*
*/
save: function (member, isNew, files) {
return saveMember(member, "save" + (isNew ? "New" : ""), files);
}
};
}
angular.module('umbraco.resources').factory('memberResource', memberResource);

View File

@@ -1,76 +1,60 @@
<div>
<div class="umb-table" ng-if="items">
<!-- Listviews head section -->
<div class="umb-table-head">
<div class="umb-table-row">
<div class="umb-table-cell">
<input class="umb-table__input" type="checkbox"
ng-if="allowSelectAll"
ng-click="selectAll($event)"
ng-checked="isSelectedAll()">
</div>
<div class="umb-table-cell umb-table__name">
<a class="umb-table-head__link sortable" href="#" ng-click="sort('Name', true)" prevent-default>
<localize key="general_name">Name</localize>
<i class="umb-table-head__icon icon" ng-class="{'icon-navigation-up': isSortDirection('Name', 'asc'), 'icon-navigation-down': isSortDirection('Name', 'desc')}"></i>
</a>
</div>
<div class="umb-table-cell" ng-repeat="column in itemProperties">
<a class="umb-table-head__link" title="Sort by {{ column.header }}" href="#"
ng-click="sort(column.alias, column.allowSorting)"
ng-class="{'sortable':column.allowSorting}" prevent-default>
<span ng-bind="column.header"></span>
<i class="umb-table-head__icon icon" ng-class="{'icon-navigation-up': isSortDirection(column.alias, 'asc'), 'icon-navigation-down': isSortDirection(column.alias, 'desc')}"></i>
</a>
</div>
</div>
</div>
<!-- Listview body section -->
<div class="umb-table-body">
<div class="umb-table-row"
ng-repeat="item in items"
ng-class="{
'-selected':item.selected,
'-published':item.published,
'-unpublished':!item.published
}"
ng-click="selectItem(item, $index, $event)">
<div class="umb-table-cell">
<i class="umb-table-body__icon umb-table-body__fileicon {{item.icon}}" ng-class="getIcon(item)"></i>
<i class="umb-table-body__icon umb-table-body__checkicon icon-check"></i>
</div>
<div class="umb-table-cell umb-table__name">
<a title="{{ item.name }}" class="umb-table-body__link" href=""
ng-click="clickItem(item, $event)"
ng-bind="item.name">
</a>
</div>
<div class="umb-table-cell" ng-repeat="column in itemProperties">
<span title="{{column.header}}: {{item[column.alias]}}">{{item[column.alias]}}</span>
</div>
</div>
</div>
</div>
<!-- If list is empty, then display -->
<umb-empty-state
ng-if="!items"
position="center">
<localize key="content_listViewNoItems">There are no items show in the list.</localize>
</umb-empty-state>
</div>
<div>
<div class="umb-table" ng-if="items">
<!-- Listviews head section -->
<div class="umb-table-head">
<div class="umb-table-row">
<div class="umb-table-cell">
<input class="umb-table__input" type="checkbox"
ng-if="allowSelectAll"
ng-click="selectAll($event)"
ng-checked="isSelectedAll()">
</div>
<div class="umb-table-cell umb-table__name">
<a class="umb-table-head__link sortable" href="#" ng-click="sort('Name', true)" prevent-default>
<localize key="general_name">Name</localize>
<i class="umb-table-head__icon icon" ng-class="{'icon-navigation-up': isSortDirection('Name', 'asc'), 'icon-navigation-down': isSortDirection('Name', 'desc')}"></i>
</a>
</div>
<div class="umb-table-cell" ng-repeat="column in itemProperties">
<a class="umb-table-head__link" title="Sort by {{ column.header }}" href="#"
ng-click="sort(column.alias, column.allowSorting, column.isSystem)"
ng-class="{'sortable':column.allowSorting}" prevent-default>
<span ng-bind="column.header"></span>
<i class="umb-table-head__icon icon" ng-class="{'icon-navigation-up': isSortDirection(column.alias, 'asc'), 'icon-navigation-down': isSortDirection(column.alias, 'desc')}"></i>
</a>
</div>
</div>
</div>
<!-- Listview body section -->
<div class="umb-table-body">
<div class="umb-table-row"
ng-repeat="item in items"
ng-class="{
'-selected':item.selected,
'-published':item.published,
'-unpublished':!item.published
}"
ng-click="selectItem(item, $index, $event)">
<div class="umb-table-cell">
<i class="umb-table-body__icon umb-table-body__fileicon {{item.icon}}" ng-class="getIcon(item)"></i>
<i class="umb-table-body__icon umb-table-body__checkicon icon-check"></i>
</div>
<div class="umb-table-cell umb-table__name">
<a title="{{ item.name }}" class="umb-table-body__link" href=""
ng-click="clickItem(item, $event)"
ng-bind="item.name">
</a>
</div>
<div class="umb-table-cell" ng-repeat="column in itemProperties">
<span title="{{column.header}}: {{item[column.alias]}}">{{item[column.alias]}}</span>
</div>
</div>
</div>
</div>
<!-- If list is empty, then display -->
<umb-empty-state ng-if="!items"
position="center">
<localize key="content_listViewNoItems">There are no items show in the list.</localize>
</umb-empty-state>
</div>

View File

@@ -1,75 +1,76 @@
(function() {
"use strict";
function ListViewListLayoutController($scope, listViewHelper, $location, mediaHelper) {
var vm = this;
vm.nodeId = $scope.contentId;
//vm.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes);
//instead of passing in a whitelist, we pass in a blacklist by adding ! to the ext
vm.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.disallowedUploadFiles).replace(/./g, "!.");
vm.maxFileSize = Umbraco.Sys.ServerVariables.umbracoSettings.maxFileSize + "KB";
vm.activeDrag = false;
vm.isRecycleBin = $scope.contentId === '-21' || $scope.contentId === '-20';
vm.selectItem = selectItem;
vm.clickItem = clickItem;
vm.selectAll = selectAll;
vm.isSelectedAll = isSelectedAll;
vm.isSortDirection = isSortDirection;
vm.sort = sort;
vm.dragEnter = dragEnter;
vm.dragLeave = dragLeave;
vm.onFilesQueue = onFilesQueue;
vm.onUploadComplete = onUploadComplete;
function selectAll($event) {
listViewHelper.selectAllItems($scope.items, $scope.selection, $event);
}
function isSelectedAll() {
return listViewHelper.isSelectedAll($scope.items, $scope.selection);
}
function selectItem(selectedItem, $index, $event) {
listViewHelper.selectHandler(selectedItem, $index, $scope.items, $scope.selection, $event);
}
function clickItem(item) {
$location.path($scope.entityType + '/' + $scope.entityType + '/edit/' + item.id);
}
function isSortDirection(col, direction) {
return listViewHelper.setSortingDirection(col, direction, $scope.options);
}
function sort(field, allow) {
if (allow) {
listViewHelper.setSorting(field, allow, $scope.options);
$scope.getContent($scope.contentId);
}
}
// Dropzone upload functions
function dragEnter(el, event) {
vm.activeDrag = true;
}
function dragLeave(el, event) {
vm.activeDrag = false;
}
function onFilesQueue() {
vm.activeDrag = false;
}
function onUploadComplete() {
$scope.getContent($scope.contentId);
}
}
angular.module("umbraco").controller("Umbraco.PropertyEditors.ListView.ListLayoutController", ListViewListLayoutController);
})();
(function () {
"use strict";
function ListViewListLayoutController($scope, listViewHelper, $location, mediaHelper) {
var vm = this;
vm.nodeId = $scope.contentId;
//vm.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes);
//instead of passing in a whitelist, we pass in a blacklist by adding ! to the ext
vm.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.disallowedUploadFiles).replace(/./g, "!.");
vm.maxFileSize = Umbraco.Sys.ServerVariables.umbracoSettings.maxFileSize + "KB";
vm.activeDrag = false;
vm.isRecycleBin = $scope.contentId === '-21' || $scope.contentId === '-20';
vm.selectItem = selectItem;
vm.clickItem = clickItem;
vm.selectAll = selectAll;
vm.isSelectedAll = isSelectedAll;
vm.isSortDirection = isSortDirection;
vm.sort = sort;
vm.dragEnter = dragEnter;
vm.dragLeave = dragLeave;
vm.onFilesQueue = onFilesQueue;
vm.onUploadComplete = onUploadComplete;
function selectAll($event) {
listViewHelper.selectAllItems($scope.items, $scope.selection, $event);
}
function isSelectedAll() {
return listViewHelper.isSelectedAll($scope.items, $scope.selection);
}
function selectItem(selectedItem, $index, $event) {
listViewHelper.selectHandler(selectedItem, $index, $scope.items, $scope.selection, $event);
}
function clickItem(item) {
$location.path($scope.entityType + '/' +$scope.entityType + '/edit/' +item.id);
}
function isSortDirection(col, direction) {
return listViewHelper.setSortingDirection(col, direction, $scope.options);
}
function sort(field, allow, isSystem) {
if (allow) {
$scope.options.orderBySystemField = isSystem;
listViewHelper.setSorting(field, allow, $scope.options);
$scope.getContent($scope.contentId);
}
}
// Dropzone upload functions
function dragEnter(el, event) {
vm.activeDrag = true;
}
function dragLeave(el, event) {
vm.activeDrag = false;
}
function onFilesQueue() {
vm.activeDrag = false;
}
function onUploadComplete() {
$scope.getContent($scope.contentId);
}
}
angular.module("umbraco").controller("Umbraco.PropertyEditors.ListView.ListLayoutController", ListViewListLayoutController);
}) ();