Adds GetPagedDescendants to EntityController

Implements GetPagedDescendants in the mediapicker Search
Removes duplicate ExamineSearch and Search endpoint code from MediaController
This commit is contained in:
Emil Wangaa
2017-02-16 14:41:50 +01:00
parent cab1536a30
commit bd38dde1fc
5 changed files with 124 additions and 169 deletions

View File

@@ -395,6 +395,76 @@ function entityResource($q, $http, umbRequestHelper) {
)),
'Failed to retrieve child data for id ' + parentId);
},
/**
* @ngdoc method
* @name umbraco.resources.entityResource#getPagedDescendants
* @methodOf umbraco.resources.entityResource
*
* @description
* Gets paged descendants of a content item with a given id
*
* ##usage
* <pre>
* entityResource.getPagedDescendants(1234, "Content", {pageSize: 10, pageNumber: 2})
* .then(function(contentArray) {
* var children = contentArray;
* alert('they are here!');
* });
* </pre>
*
* @param {Int} parentid id of content item to return descendants of
* @param {string} type Object type name
* @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.
*
*/
getPagedDescendants: function (parentId, type, 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";
}
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"entityApiBaseUrl",
"GetPagedDescendants",
{
id: parentId,
type: type,
pageNumber: options.pageNumber,
pageSize: options.pageSize,
orderBy: options.orderBy,
orderDirection: options.orderDirection,
filter: options.filter
}
)),
'Failed to retrieve child data for id ' + parentId);
},
/**
* @ngdoc method

View File

@@ -37,7 +37,8 @@ angular.module("umbraco")
pageNumber: 1,
pageSize: 100,
totalItems: 0,
totalPages: 0
totalPages: 0,
filter: '',
};
//preload selected item
@@ -233,7 +234,7 @@ angular.module("umbraco")
var debounceSearchMedia = _.debounce(function () {
$scope.$apply(function () {
if ($scope.searchTerm) {
if ($scope.searchOptions.filter) {
searchMedia();
} else {
// reset pagination
@@ -241,7 +242,8 @@ angular.module("umbraco")
pageNumber: 1,
pageSize: 100,
totalItems: 0,
totalPages: 0
totalPages: 0,
filter: ''
};
getChildren($scope.currentFolder.id);
}
@@ -250,7 +252,7 @@ angular.module("umbraco")
function searchMedia() {
$scope.loading = true;
mediaResource.search($scope.searchTerm, $scope.searchOptions.pageNumber, $scope.searchOptions.pageSize, $scope.startNodeId)
entityResource.getPagedDescendants($scope.startNodeId, "Media", $scope.searchOptions)
.then(function (data) {
// update images
$scope.images = data.items ? data.items : [];

View File

@@ -18,7 +18,7 @@
<i class="icon-search"></i>
<input
class="umb-search-field search-query"
ng-model="searchTerm"
ng-model="searchOptions.filter"
localize="placeholder"
placeholder="@placeholders_search"
ng-change="changeSearch()"

View File

@@ -430,6 +430,53 @@ namespace Umbraco.Web.Editors
}
}
public PagedResult<EntityBasic> GetPagedDescendants(
int id,
UmbracoEntityTypes type,
int pageNumber,
int pageSize,
string orderBy = "SortOrder",
Direction orderDirection = Direction.Ascending,
string filter = "")
{
if (pageNumber <= 0)
throw new HttpResponseException(HttpStatusCode.NotFound);
if (pageSize <= 0)
throw new HttpResponseException(HttpStatusCode.NotFound);
var objectType = ConvertToObjectType(type);
if (objectType.HasValue)
{
long totalRecords;
var entities = Services.EntityService.GetPagedDescendants(id, objectType.Value, pageNumber - 1, pageSize, out totalRecords, orderBy, orderDirection, filter);
if (totalRecords == 0)
{
return new PagedResult<EntityBasic>(0, 0, 0);
}
var pagedResult = new PagedResult<EntityBasic>(totalRecords, pageNumber, pageSize)
{
Items = entities.Select(Mapper.Map<EntityBasic>)
};
return pagedResult;
}
//now we need to convert the unknown ones
switch (type)
{
case UmbracoEntityTypes.PropertyType:
case UmbracoEntityTypes.PropertyGroup:
case UmbracoEntityTypes.Domain:
case UmbracoEntityTypes.Language:
case UmbracoEntityTypes.User:
case UmbracoEntityTypes.Macro:
default:
throw new NotSupportedException("The " + typeof(EntityController) + " does not currently support data for the type " + type);
}
}
public IEnumerable<EntityBasic> GetAncestors(int id, UmbracoEntityTypes type)
{
return GetResultForAncestors(id, type);

View File

@@ -315,31 +315,6 @@ namespace Umbraco.Web.Editors
}
#endregion
/// <summary>
/// Searches media and returns a paged result
/// </summary>
/// <param name="query"></param>
/// <param name="pageNumber"></param>
/// <param name="pageSize"></param>
/// <param name="searchFrom"></param>
/// <returns></returns>
[HttpGet]
[FilterAllowedOutgoingMedia(typeof(IEnumerable<ContentItemBasic<ContentPropertyBasic, IMedia>>), "Items")]
public PagedResult<ContentItemBasic<ContentPropertyBasic, IMedia>> Search(string query, int pageNumber = 0,
int pageSize = 0, string searchFrom = null)
{
if (string.IsNullOrEmpty(query))
return new PagedResult<ContentItemBasic<ContentPropertyBasic, IMedia>>(0, 0, 0);
var searchResult = ExamineSearch(query, searchFrom).ToArray();
var items = searchResult.Skip(pageSize * (pageNumber - 1)).Take(pageSize);
var pagedResult = new PagedResult<ContentItemBasic<ContentPropertyBasic, IMedia>>(searchResult.Length, pageNumber, pageSize);
pagedResult.Items = Services.MediaService.GetByIds(items.Select(x => Convert.ToInt32(x.Id)))
.Select(Mapper.Map<IMedia, ContentItemBasic<ContentPropertyBasic, IMedia>>);
return pagedResult;
}
/// <summary>
/// Moves an item to the recycle bin, if it is already there then it will permanently delete it
/// </summary>
@@ -858,144 +833,5 @@ namespace Umbraco.Web.Editors
return hasPathAccess;
}
/// <summary>
/// Searches for results based on the entity type
/// </summary>
/// <param name="query"></param>
/// <param name="searchFrom">
/// A starting point for the search, generally a node id, but for members this is a member type alias
/// </param>
/// <returns></returns>
private IEnumerable<EntityBasic> ExamineSearch(string query, string searchFrom = null)
{
var sb = new StringBuilder();
string type;
var searcher = Constants.Examine.InternalSearcher;
var fields = new[] { "id", "__NodeId" };
type = "media";
var mediaSearchFrom = int.MinValue;
if (Security.CurrentUser.StartMediaId > 0 ||
//if searchFrom is specified and it is greater than 0
(searchFrom != null && int.TryParse(searchFrom, out mediaSearchFrom) && mediaSearchFrom > 0))
{
sb.Append("+__Path: \\-1*\\,");
sb.Append(mediaSearchFrom > 0
? mediaSearchFrom.ToString(CultureInfo.InvariantCulture)
: Security.CurrentUser.StartMediaId.ToString(CultureInfo.InvariantCulture));
sb.Append("\\,* ");
}
var internalSearcher = ExamineManager.Instance.SearchProviderCollection[searcher];
//build a lucene query:
// the __nodeName will be boosted 10x without wildcards
// then __nodeName will be matched normally with wildcards
// the rest will be normal without wildcards
//check if text is surrounded by single or double quotes, if so, then exact match
var surroundedByQuotes = Regex.IsMatch(query, "^\".*?\"$")
|| Regex.IsMatch(query, "^\'.*?\'$");
if (surroundedByQuotes)
{
//strip quotes, escape string, the replace again
query = query.Trim(new[] { '\"', '\'' });
query = Lucene.Net.QueryParsers.QueryParser.Escape(query);
if (query.IsNullOrWhiteSpace())
{
return new List<EntityBasic>();
}
//add back the surrounding quotes
query = string.Format("{0}{1}{0}", "\"", query);
//node name exactly boost x 10
sb.Append("+(__nodeName: (");
sb.Append(query.ToLower());
sb.Append(")^10.0 ");
foreach (var f in fields)
{
//additional fields normally
sb.Append(f);
sb.Append(": (");
sb.Append(query);
sb.Append(") ");
}
}
else
{
if (query.Trim(new[] { '\"', '\'' }).IsNullOrWhiteSpace())
{
return new List<EntityBasic>();
}
query = Lucene.Net.QueryParsers.QueryParser.Escape(query);
var querywords = query.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
//node name exactly boost x 10
sb.Append("+(__nodeName:");
sb.Append("\"");
sb.Append(query.ToLower());
sb.Append("\"");
sb.Append("^10.0 ");
//node name normally with wildcards
sb.Append(" __nodeName:");
sb.Append("(");
foreach (var w in querywords)
{
sb.Append(w.ToLower());
sb.Append("* ");
}
sb.Append(") ");
foreach (var f in fields)
{
//additional fields normally
sb.Append(f);
sb.Append(":");
sb.Append("(");
foreach (var w in querywords)
{
sb.Append(w.ToLower());
sb.Append("* ");
}
sb.Append(")");
sb.Append(" ");
}
}
//must match index type
sb.Append(") +__IndexType:");
sb.Append(type);
var raw = internalSearcher.CreateSearchCriteria().RawQuery(sb.ToString());
//limit results to 200 to avoid huge over processing (CPU)
var result = internalSearcher.Search(raw, 200);
var mapped = Mapper.Map<IEnumerable<EntityBasic>>(result).ToArray();
//add additional data
foreach (var m in mapped)
{
//if no icon could be mapped, it will be set to document, so change it to picture
if (m.Icon == "icon-document")
{
m.Icon = "icon-picture";
}
}
return mapped;
}
}
}