Compare commits

...

32 Commits

Author SHA1 Message Date
mikkelhm 32ad98cde2 Bump version to alpha070 2017-03-02 13:33:12 +01:00
Mikkel Holck Madsen f4b1df1ede U4-9575 - Merge pull request #1770 from umbraco/temp-cloud-env-title
Dev Night - Display Deploy Cloud Env in <title>
2017-03-02 12:55:08 +01:00
Warren Buckley 2b0f2577b5 Uppercases the Section name in the page <title> & if Deploy installed grab the CurrentWorkspace from the global JS obj 2017-02-28 21:56:30 +00:00
mikkelhm a39557a3a7 Bump version to alpha068 2017-02-24 12:31:59 +01:00
Stephan 2239316889 Fix RelationRepository, debugging 2017-02-23 19:00:42 +01:00
Per Ploug d3582e2296 An ID is required on all items edited in backoffice editors
The ID is required when an item is saved, as the routing will
automatically redirect based on this ID - files, does not have an ID -
so the filepath is used in the same as it is used for the tree
2017-02-22 15:47:36 +00:00
Stephan 1a37d88855 ShadowFS in App_Data/TEMP 2017-02-22 10:05:53 +01:00
Claus 2d5ffabe1e adding missing files from last commits. 2017-02-21 18:15:11 +01:00
Stephan f61b7579a2 Version 7.6-alpha066 2017-02-21 17:13:34 +01:00
Stephan 86a3780c61 Cleanup IFileSource for Deploy/IFileSource 2017-02-21 16:01:01 +01:00
Emil Wangaa 552909fde2 Merge pull request #1753 from umbraco/temp-U4-9293
Fixes: U4-9293 Search the entire media section in Media Picker when inserting an image into content
2017-02-21 12:00:26 +01:00
Emil Wangaa 7adae4d345 Merge pull request #1755 from umbraco/temp-U4-9470
U4-9470 Mini list view: only show expand option for nodes with children
2017-02-21 09:00:52 +01:00
Emil Wangaa d747cf69c3 Fixes typo in comment 2017-02-21 08:23:21 +01:00
Shannon bc5e3b4d66 Fixes issue of moving an item to the root, it will throw a JS error because target.nodeType is null 2017-02-21 15:44:49 +11:00
Shannon febeb82d10 Fixes the EntityController.GetPagedDescendants to not return items in the recycle bin, added some tests. 2017-02-21 15:43:46 +11:00
Shannon 1c2b2c1409 Merge branch 'refs/heads/temp-U4-9470' into temp-U4-9293 2017-02-21 13:54:21 +11:00
Shannon b4eb9f333e Gets the member picker working with mini list views, falls back to Examine search for when searching on member types - since the EntityService doesn't support that currently, gets the icon mapped correctly for members, 2017-02-21 13:49:58 +11:00
Shannon af00a421cd Updates EntityController to deal with special Members root node id 2017-02-20 21:23:09 +11:00
Emil Wangaa 4dabb606e7 Fixes issue with the mediapicker search getting 404 and loading failing because of missing property values for folders 2017-02-17 09:53:23 +01:00
Mads Rasmussen d5f66ebe02 update image properties to match media grid component 2017-02-16 19:59:45 +01:00
Emil Wangaa bd38dde1fc Adds GetPagedDescendants to EntityController
Implements GetPagedDescendants in the mediapicker Search
Removes duplicate ExamineSearch and Search endpoint code from MediaController
2017-02-16 14:41:50 +01:00
Emil Wangaa cab1536a30 Merge remote-tracking branch 'origin/temp-U4-9470' into temp-U4-9293 2017-02-16 12:46:35 +01:00
Mads Rasmussen 83f75fdaff nested mini list views: use visibility hidden instead of ng-if to better align list items 2017-02-15 11:00:35 +01:00
Mads Rasmussen 487792920b only show published state for content 2017-02-15 10:40:24 +01:00
Shannon 6ae06ad2e0 Merge remote-tracking branch 'refs/remotes/origin/dev-v7.6' into temp-U4-9470
# Conflicts:
#	src/Umbraco.Web/Editors/EntityController.cs
2017-02-15 17:41:12 +11:00
Shannon 8387c435d4 Gets mini list view working so that only nodes with children show the arrow, added GetPagedChildren to EntityController 2017-02-15 17:40:10 +11:00
Shannon 5807bbccc4 revert change to petapoco, we will calc the total manually for this due to an outer join/aggregate, adds GetDescendents + tests for EntityService 2017-02-15 16:38:43 +11:00
Shannon 1bfc041bbe Adds Paged query to entity repo/service + tests, had to fix paged queries in petapoco to strip ending GROUP BY otherwise the COUNT is incorrect 2017-02-15 15:13:23 +11:00
Mads Rasmussen 7c049b55a8 media picker - search media library instead of filter current folder 2017-02-13 14:36:39 +01:00
Emil Wangaa 051b3f2e6a Allows get for search media endpoint 2017-02-13 11:23:21 +01:00
Emil Wangaa 21f947efbd Moves media search to MediaController and implements paging for the endpoint 2017-02-13 11:21:09 +01:00
Emil Wangaa 9461e35028 Adds endpoint for searching media for the mediapicker 2017-02-13 09:54:50 +01:00
40 changed files with 1623 additions and 248 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
7.6.0
alpha065
alpha070
+1 -1
View File
@@ -12,4 +12,4 @@ using System.Resources;
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("7.6.0")]
[assembly: AssemblyInformationalVersion("7.6.0-alpha065")]
[assembly: AssemblyInformationalVersion("7.6.0-alpha070")]
@@ -24,7 +24,7 @@ namespace Umbraco.Core.Configuration
/// Gets the version comment (like beta or RC).
/// </summary>
/// <value>The version comment.</value>
public static string CurrentComment { get { return "alpha065"; } }
public static string CurrentComment { get { return "alpha070"; } }
// Get the version of the umbraco.dll by looking at a class in that dll
// Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx
@@ -109,6 +109,11 @@ namespace Umbraco.Core
/// </summary>
public const string Member = "39EB0F98-B348-42A1-8662-E7EB18487560";
/// <summary>
/// Guid for a Media Type object.
/// </summary>
public static readonly Guid MemberGuid = new Guid(Member);
/// <summary>
/// Guid for a Member Group object.
/// </summary>
+13 -3
View File
@@ -66,10 +66,20 @@ namespace Umbraco.Core.Deploy
/// <returns>The length of the file, or -1 if the file does not exist.</returns>
Task<long> GetFileLengthAsync(StringUdi udi, CancellationToken token);
// fixme - doc
void GetFiles(IEnumerable<StringUdi> udis, IFileStore fileStore);
/// <summary>
/// Gets files and store them using a file store.
/// </summary>
/// <param name="udis">The udis of the files to get.</param>
/// <param name="fileTypes">A collection of file types which can store the files.</param>
void GetFiles(IEnumerable<StringUdi> udis, IFileTypeCollection fileTypes);
Task GetFilesAsync(IEnumerable<StringUdi> udis, IFileStore fileStore, CancellationToken token);
/// <summary>
/// Gets files and store them using a file store.
/// </summary>
/// <param name="udis">The udis of the files to get.</param>
/// <param name="fileTypes">A collection of file types which can store the files.</param>
/// <param name="token">A cancellation token.</param>
Task GetFilesAsync(IEnumerable<StringUdi> udis, IFileTypeCollection fileTypes, CancellationToken token);
///// <summary>
///// Gets the content of a file as a bytes array.
-9
View File
@@ -1,9 +0,0 @@
using System.IO;
namespace Umbraco.Core.Deploy
{
public interface IFileStore
{
void SaveStream(StringUdi udi, Stream stream);
}
}
+32
View File
@@ -0,0 +1,32 @@
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace Umbraco.Core.Deploy
{
public interface IFileType
{
Stream GetStream(StringUdi udi);
Task<Stream> GetStreamAsync(StringUdi udi, CancellationToken token);
Stream GetChecksumStream(StringUdi udi);
long GetLength(StringUdi udi);
void SetStream(StringUdi udi, Stream stream);
Task SetStreamAsync(StringUdi udi, Stream stream, CancellationToken token);
bool CanSetPhysical { get; }
void Set(StringUdi udi, string physicalPath, bool copy = false);
// this is not pretty as *everywhere* in Deploy we take care of ignoring
// the physical path and always rely on Core's virtual IFileSystem but
// Cloud wants to add some of these files to Git and needs the path...
string GetPhysicalPath(StringUdi udi);
string GetVirtualPath(StringUdi udi);
}
}
@@ -0,0 +1,9 @@
namespace Umbraco.Core.Deploy
{
public interface IFileTypeCollection
{
IFileType this[string entityType] { get; }
bool Contains(string entityType);
}
}
+6 -11
View File
@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
@@ -67,22 +68,15 @@ namespace Umbraco.Core
}
}
/// <summary>The for each.</summary>
/// <param name="items">The items.</param>
/// <param name="func">The func.</param>
/// <typeparam name="TItem">item type</typeparam>
/// <typeparam name="TResult">Result type</typeparam>
/// <returns>the Results</returns>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Use a normal foreach loop instead, this adds more allocations than necessary")]
public static TResult[] ForEach<TItem, TResult>(this IEnumerable<TItem> items, Func<TItem, TResult> func)
{
return items.Select(func).ToArray();
}
/// <summary>The for each.</summary>
/// <param name="items">The items.</param>
/// <param name="action">The action.</param>
/// <typeparam name="TItem">Item type</typeparam>
/// <returns>list of TItem</returns>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Use a normal foreach loop instead, this adds more allocations than necessary")]
public static IEnumerable<TItem> ForEach<TItem>(this IEnumerable<TItem> items, Action<TItem> action)
{
if (items != null)
@@ -101,6 +95,7 @@ namespace Umbraco.Core
/// <param name="f">The select child.</param>
/// <typeparam name="T">Item type</typeparam>
/// <returns>list of TItem</returns>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Do not use, use SelectRecursive instead which has far less potential of re-iterating an iterator which may cause significantly more SQL queries")]
public static IEnumerable<T> FlattenList<T>(this IEnumerable<T> e, Func<T, IEnumerable<T>> f)
{
+1 -1
View File
@@ -27,7 +27,7 @@ namespace Umbraco.Core.IO
// on ShadowFileSystemsScope.None - and if None is false then we should be running
// in a single thread anyways
var virt = "~/App_Data/Shadow/" + id + "/" + _shadowPath;
var virt = "~/App_Data/TEMP/ShadowFs/" + id + "/" + _shadowPath;
_shadowDir = IOHelper.MapPath(virt);
Directory.CreateDirectory(_shadowDir);
var tempfs = new PhysicalFileSystem(virt);
@@ -30,7 +30,8 @@ namespace Umbraco.Core.Models
return descendants;
}
throw new NotSupportedException("The content type must be " + typeof(IContentType) + " or " + typeof(IMediaType));
//No other content types have children (i.e. member types)
return Enumerable.Empty<IContentTypeBase>();
}
/// <summary>
@@ -113,6 +113,11 @@ namespace Umbraco.Core.Models
}
}
/// <summary>
/// When resolved from EntityService this checks if the entity has the HasChildren flag
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
public static bool HasChildren(this IUmbracoEntity entity)
{
if (entity.AdditionalData.ContainsKey("HasChildren"))
@@ -133,6 +138,11 @@ namespace Umbraco.Core.Models
return entity.AdditionalData.GetValueIgnoreCase(key, defaultVal);
}
/// <summary>
/// When resolved from EntityService this checks if the entity has the IsContainer flag
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
public static bool IsContainer(this IUmbracoEntity entity)
{
if (entity.AdditionalData.ContainsKeyIgnoreCase("IsContainer") == false) return false;
@@ -50,7 +50,7 @@ namespace Umbraco.Core.Persistence.Factories
entity.ContentTypeThumbnail = asDictionary.ContainsKey("thumbnail") ? (d.thumbnail ?? string.Empty) : string.Empty;
var publishedVersion = default(Guid);
//some content items don't have a published version
//some content items don't have a published/newest version
if (asDictionary.ContainsKey("publishedVersion") && asDictionary["publishedVersion"] != null)
{
Guid.TryParse(d.publishedVersion.ToString(), out publishedVersion);
+6 -8
View File
@@ -703,7 +703,7 @@ namespace Umbraco.Core.Persistence
static Regex rxColumns = new Regex(@"\A\s*SELECT\s+((?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|.)*?)(?<!,\s+)\bFROM\b", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);
static Regex rxOrderBy = new Regex(@"\bORDER\s+BY\s+(?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|[\w\(\)\.])+(?:\s+(?:ASC|DESC))?(?:\s*,\s*(?:\((?>\((?<depth>)|\)(?<-depth>)|.?)*(?(depth)(?!))\)|[\w\(\)\.])+(?:\s+(?:ASC|DESC))?)*", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);
static Regex rxDistinct = new Regex(@"\ADISTINCT\s", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);
static Regex rxDistinct = new Regex(@"\ADISTINCT\s", RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.Singleline | RegexOptions.Compiled);
public static bool SplitSqlForPaging(string sql, out string sqlCount, out string sqlSelectRemoved, out string sqlOrderBy)
{
sqlSelectRemoved = null;
@@ -723,10 +723,9 @@ namespace Umbraco.Core.Persistence
sqlCount = sql.Substring(0, g.Index) + "COUNT(" + m.Groups[1].ToString().Trim() + ") " + sql.Substring(g.Index + g.Length);
else
sqlCount = sql.Substring(0, g.Index) + "COUNT(*) " + sql.Substring(g.Index + g.Length);
// Look for an "ORDER BY <whatever>" clause
m = rxOrderBy.Match(sqlCount);
// Look for an "ORDER BY <whatever>" clause
m = rxOrderBy.Match(sqlCount);
if (!m.Success)
{
sqlOrderBy = null;
@@ -737,8 +736,7 @@ namespace Umbraco.Core.Persistence
sqlOrderBy = g.ToString();
sqlCount = sqlCount.Substring(0, g.Index) + sqlCount.Substring(g.Index + g.Length);
}
return true;
return true;
}
/// <summary>
@@ -820,7 +818,7 @@ namespace Umbraco.Core.Persistence
result.CurrentPage = page;
result.ItemsPerPage = itemsPerPage;
result.TotalItems = ExecuteScalar<long>(sqlCount, args);
result.TotalPages = result.TotalItems / itemsPerPage;
result.TotalPages = result.TotalItems / itemsPerPage;
if ((result.TotalItems % itemsPerPage) != 0)
result.TotalPages++;
@@ -9,6 +9,7 @@ using Umbraco.Core.Models;
using Umbraco.Core;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Factories;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.UnitOfWork;
@@ -49,6 +50,124 @@ namespace Umbraco.Core.Persistence.Repositories
#region Query Methods
public IEnumerable<IUmbracoEntity> GetPagedResultsByQuery(IQuery<IUmbracoEntity> query, Guid objectTypeId, long pageIndex, int pageSize, out long totalRecords,
string orderBy, Direction orderDirection, IQuery<IUmbracoEntity> filter = null)
{
bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document);
bool isMedia = objectTypeId == new Guid(Constants.ObjectTypes.Media);
var factory = new UmbracoEntityFactory();
var sqlClause = GetBaseWhere(GetBase, isContent, isMedia, sql =>
{
if (filter != null)
{
foreach (var filterClaus in filter.GetWhereClauses())
{
sql.Where(filterClaus.Item1, filterClaus.Item2);
}
}
}, objectTypeId);
var translator = new SqlTranslator<IUmbracoEntity>(sqlClause, query);
var entitySql = translator.Translate();
var pagedSql = entitySql.Append(GetGroupBy(isContent, isMedia, false)).OrderBy("umbracoNode.id");
IEnumerable<IUmbracoEntity> result;
if (isMedia)
{
//Treat media differently for now, as an Entity it will be returned with ALL of it's properties in the AdditionalData bag!
var pagedResult = _work.Database.Page<dynamic>(pageIndex + 1, pageSize, pagedSql);
var ids = pagedResult.Items.Select(x => (int) x.id).InGroupsOf(2000);
var entities = pagedResult.Items.Select(factory.BuildEntityFromDynamic).Cast<IUmbracoEntity>().ToList();
//Now we need to merge in the property data since we need paging and we can't do this the way that the big media query was working before
foreach (var idGroup in ids)
{
var propSql = GetPropertySql(Constants.ObjectTypes.Media)
.Where("contentNodeId IN (@ids)", new {ids = idGroup})
.OrderBy("contentNodeId");
//This does NOT fetch all data into memory in a list, this will read
// over the records as a data reader, this is much better for performance and memory,
// but it means that during the reading of this data set, nothing else can be read
// from SQL server otherwise we'll get an exception.
var allPropertyData = _work.Database.Query<dynamic>(propSql);
//keep track of the current property data item being enumerated
var propertyDataSetEnumerator = allPropertyData.GetEnumerator();
var hasCurrent = false; // initially there is no enumerator.Current
try
{
//This must be sorted by node id (which is done by SQL) because this is how we are sorting the query to lookup property types above,
// which allows us to more efficiently iterate over the large data set of property values.
foreach (var entity in entities)
{
// assemble the dtos for this def
// use the available enumerator.Current if any else move to next
while (hasCurrent || propertyDataSetEnumerator.MoveNext())
{
if (propertyDataSetEnumerator.Current.contentNodeId == entity.Id)
{
hasCurrent = false; // enumerator.Current is not available
//the property data goes into the additional data
entity.AdditionalData[propertyDataSetEnumerator.Current.propertyTypeAlias] = new UmbracoEntity.EntityProperty
{
PropertyEditorAlias = propertyDataSetEnumerator.Current.propertyEditorAlias,
Value = StringExtensions.IsNullOrWhiteSpace(propertyDataSetEnumerator.Current.dataNtext)
? propertyDataSetEnumerator.Current.dataNvarchar
: StringExtensions.ConvertToJsonIfPossible(propertyDataSetEnumerator.Current.dataNtext)
};
}
else
{
hasCurrent = true; // enumerator.Current is available for another def
break; // no more propertyDataDto for this def
}
}
}
}
finally
{
propertyDataSetEnumerator.Dispose();
}
}
result = entities;
}
else
{
var pagedResult = _work.Database.Page<dynamic>(pageIndex + 1, pageSize, pagedSql);
result = pagedResult.Items.Select(factory.BuildEntityFromDynamic).Cast<IUmbracoEntity>().ToList();
}
//The total items from the PetaPoco page query will be wrong due to the Outer join used on parent, depending on the search this will
//return duplicate results when the COUNT is used in conjuction with it, so we need to get the total on our own.
//generate a query that does not contain the LEFT Join for parent, this would cause
//the COUNT(*) query to return the wrong
var sqlCountClause = GetBaseWhere(
(isC, isM, f) => GetBase(isC, isM, f, true), //true == is a count query
isContent, isMedia, sql =>
{
if (filter != null)
{
foreach (var filterClaus in filter.GetWhereClauses())
{
sql.Where(filterClaus.Item1, filterClaus.Item2);
}
}
}, objectTypeId);
var translatorCount = new SqlTranslator<IUmbracoEntity>(sqlCountClause, query);
var countSql = translatorCount.Translate();
totalRecords = _work.Database.ExecuteScalar<int>(countSql);
return result;
}
public IUmbracoEntity GetByKey(Guid key)
{
var sql = GetBaseWhere(GetBase, false, false, key);
@@ -71,8 +190,7 @@ namespace Umbraco.Core.Persistence.Repositories
if (isMedia)
{
//for now treat media differently
//TODO: We should really use this methodology for Content/Members too!! since it includes properties and ALL of the dynamic db fields
//Treat media differently for now, as an Entity it will be returned with ALL of it's properties in the AdditionalData bag!
var entities = _work.Database.Fetch<dynamic, UmbracoPropertyDto, UmbracoEntity>(
new UmbracoEntityRelator().Map, sql);
@@ -115,8 +233,7 @@ namespace Umbraco.Core.Persistence.Repositories
if (isMedia)
{
//for now treat media differently
//TODO: We should really use this methodology for Content/Members too!! since it includes properties and ALL of the dynamic db fields
//Treat media differently for now, as an Entity it will be returned with ALL of it's properties in the AdditionalData bag!
var entities = _work.Database.Fetch<dynamic, UmbracoPropertyDto, UmbracoEntity>(
new UmbracoEntityRelator().Map, sql);
@@ -171,8 +288,7 @@ namespace Umbraco.Core.Persistence.Repositories
if (isMedia)
{
//for now treat media differently
//TODO: We should really use this methodology for Content/Members too!! since it includes properties and ALL of the dynamic db fields
//Treat media differently for now, as an Entity it will be returned with ALL of it's properties in the AdditionalData bag!
var entities = _work.Database.Fetch<dynamic, UmbracoPropertyDto, UmbracoEntity>(
new UmbracoEntityRelator().Map, sql);
foreach (var entity in entities)
@@ -231,8 +347,7 @@ namespace Umbraco.Core.Persistence.Repositories
}
});
//treat media differently for now
//TODO: We should really use this methodology for Content/Members too!! since it includes properties and ALL of the dynamic db fields
//Treat media differently for now, as an Entity it will be returned with ALL of it's properties in the AdditionalData bag!
var entities = _work.Database.Fetch<dynamic, UmbracoPropertyDto, UmbracoEntity>(
new UmbracoEntityRelator().Map, mediaSql);
return entities;
@@ -278,11 +393,9 @@ namespace Umbraco.Core.Persistence.Repositories
return GetFullSqlForMedia(entitySql.Append(GetGroupBy(isContent, true, false)), filter);
}
private Sql GetFullSqlForMedia(Sql entitySql, Action<Sql> filter = null)
private Sql GetPropertySql(string nodeObjectType)
{
//this will add any dataNvarchar property to the output which can be added to the additional properties
var joinSql = new Sql()
var sql = new Sql()
.Select("contentNodeId, versionId, dataNvarchar, dataNtext, propertyEditorAlias, alias as propertyTypeAlias")
.From<PropertyDataDto>()
.InnerJoin<NodeDto>()
@@ -291,7 +404,16 @@ namespace Umbraco.Core.Persistence.Repositories
.On<PropertyTypeDto, PropertyDataDto>(dto => dto.Id, dto => dto.PropertyTypeId)
.InnerJoin<DataTypeDto>()
.On<PropertyTypeDto, DataTypeDto>(dto => dto.DataTypeId, dto => dto.DataTypeId)
.Where("umbracoNode.nodeObjectType = @nodeObjectType", new {nodeObjectType = Constants.ObjectTypes.Media});
.Where("umbracoNode.nodeObjectType = @nodeObjectType", new { nodeObjectType = nodeObjectType });
return sql;
}
private Sql GetFullSqlForMedia(Sql entitySql, Action<Sql> filter = null)
{
//this will add any dataNvarchar property to the output which can be added to the additional properties
var joinSql = GetPropertySql(Constants.ObjectTypes.Media);
if (filter != null)
{
@@ -310,34 +432,52 @@ namespace Umbraco.Core.Persistence.Repositories
.OrderBy("sortOrder, id");
return wrappedSql;
}
}
protected virtual Sql GetBase(bool isContent, bool isMedia, Action<Sql> customFilter)
{
var columns = new List<object>
{
"umbracoNode.id",
"umbracoNode.trashed",
"umbracoNode.parentID",
"umbracoNode.nodeUser",
"umbracoNode.level",
"umbracoNode.path",
"umbracoNode.sortOrder",
"umbracoNode.uniqueID",
"umbracoNode.text",
"umbracoNode.nodeObjectType",
"umbracoNode.createDate",
"COUNT(parent.parentID) as children"
};
return GetBase(isContent, isMedia, customFilter, false);
}
if (isContent || isMedia)
protected virtual Sql GetBase(bool isContent, bool isMedia, Action<Sql> customFilter, bool isCount)
{
var columns = new List<object>();
if (isCount)
{
columns.Add("published.versionId as publishedVersion");
columns.Add("latest.versionId as newestVersion");
columns.Add("contenttype.alias");
columns.Add("contenttype.icon");
columns.Add("contenttype.thumbnail");
columns.Add("contenttype.isContainer");
columns.Add("COUNT(*)");
}
else
{
columns.AddRange(new List<object>
{
"umbracoNode.id",
"umbracoNode.trashed",
"umbracoNode.parentID",
"umbracoNode.nodeUser",
"umbracoNode.level",
"umbracoNode.path",
"umbracoNode.sortOrder",
"umbracoNode.uniqueID",
"umbracoNode.text",
"umbracoNode.nodeObjectType",
"umbracoNode.createDate",
"COUNT(parent.parentID) as children"
});
if (isContent || isMedia)
{
if (isContent)
{
//only content has this info
columns.Add("published.versionId as publishedVersion");
columns.Add("document.versionId as newestVersion");
}
columns.Add("contenttype.alias");
columns.Add("contenttype.icon");
columns.Add("contenttype.thumbnail");
columns.Add("contenttype.isContainer");
}
}
//Creates an SQL query to return a single row for the entity
@@ -348,17 +488,24 @@ namespace Umbraco.Core.Persistence.Repositories
if (isContent || isMedia)
{
entitySql.InnerJoin("cmsContent content").On("content.nodeId = umbracoNode.id")
.LeftJoin("cmsContentType contenttype").On("contenttype.nodeId = content.contentType")
.LeftJoin(
"(SELECT nodeId, versionId FROM cmsDocument WHERE published = 1 GROUP BY nodeId, versionId) as published")
.On("umbracoNode.id = published.nodeId")
.LeftJoin(
"(SELECT nodeId, versionId FROM cmsDocument WHERE newest = 1 GROUP BY nodeId, versionId) as latest")
.On("umbracoNode.id = latest.nodeId");
entitySql.InnerJoin("cmsContent content").On("content.nodeId = umbracoNode.id");
if (isContent)
{
//only content has this info
entitySql
.InnerJoin("cmsDocument document").On("document.nodeId = umbracoNode.id")
.LeftJoin("(SELECT nodeId, versionId FROM cmsDocument WHERE published = 1) as published")
.On("umbracoNode.id = published.nodeId");
}
entitySql.LeftJoin("cmsContentType contenttype").On("contenttype.nodeId = content.contentType");
}
entitySql.LeftJoin("umbracoNode parent").On("parent.parentID = umbracoNode.id");
if (isCount == false)
{
entitySql.LeftJoin("umbracoNode parent").On("parent.parentID = umbracoNode.id");
}
if (customFilter != null)
{
@@ -372,22 +519,42 @@ namespace Umbraco.Core.Persistence.Repositories
{
var sql = baseQuery(isContent, isMedia, filter)
.Where("umbracoNode.nodeObjectType = @NodeObjectType", new { NodeObjectType = nodeObjectType });
if (isContent)
{
sql.Where("document.newest = 1");
}
return sql;
}
protected virtual Sql GetBaseWhere(Func<bool, bool, Action<Sql>, Sql> baseQuery, bool isContent, bool isMedia, int id)
{
var sql = baseQuery(isContent, isMedia, null)
.Where("umbracoNode.id = @Id", new { Id = id })
.Append(GetGroupBy(isContent, isMedia));
.Where("umbracoNode.id = @Id", new { Id = id });
if (isContent)
{
sql.Where("document.newest = 1");
}
sql.Append(GetGroupBy(isContent, isMedia));
return sql;
}
protected virtual Sql GetBaseWhere(Func<bool, bool, Action<Sql>, Sql> baseQuery, bool isContent, bool isMedia, Guid key)
{
var sql = baseQuery(isContent, isMedia, null)
.Where("umbracoNode.uniqueID = @UniqueID", new { UniqueID = key })
.Append(GetGroupBy(isContent, isMedia));
.Where("umbracoNode.uniqueID = @UniqueID", new { UniqueID = key });
if (isContent)
{
sql.Where("document.newest = 1");
}
sql.Append(GetGroupBy(isContent, isMedia));
return sql;
}
@@ -396,6 +563,12 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = baseQuery(isContent, isMedia, null)
.Where("umbracoNode.id = @Id AND umbracoNode.nodeObjectType = @NodeObjectType",
new {Id = id, NodeObjectType = nodeObjectType});
if (isContent)
{
sql.Where("document.newest = 1");
}
return sql;
}
@@ -404,30 +577,39 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = baseQuery(isContent, isMedia, null)
.Where("umbracoNode.uniqueID = @UniqueID AND umbracoNode.nodeObjectType = @NodeObjectType",
new { UniqueID = key, NodeObjectType = nodeObjectType });
if (isContent)
{
sql.Where("document.newest = 1");
}
return sql;
}
protected virtual Sql GetGroupBy(bool isContent, bool isMedia, bool includeSort = true)
{
var columns = new List<object>
{
"umbracoNode.id",
"umbracoNode.trashed",
"umbracoNode.parentID",
"umbracoNode.nodeUser",
"umbracoNode.level",
"umbracoNode.path",
"umbracoNode.sortOrder",
"umbracoNode.uniqueID",
"umbracoNode.text",
"umbracoNode.nodeObjectType",
"umbracoNode.createDate"
};
{
"umbracoNode.id",
"umbracoNode.trashed",
"umbracoNode.parentID",
"umbracoNode.nodeUser",
"umbracoNode.level",
"umbracoNode.path",
"umbracoNode.sortOrder",
"umbracoNode.uniqueID",
"umbracoNode.text",
"umbracoNode.nodeObjectType",
"umbracoNode.createDate"
};
if (isContent || isMedia)
{
columns.Add("published.versionId");
columns.Add("latest.versionId");
if (isContent)
{
columns.Add("published.versionId");
columns.Add("document.versionId");
}
columns.Add("contenttype.alias");
columns.Add("contenttype.icon");
columns.Add("contenttype.thumbnail");
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
namespace Umbraco.Core.Persistence.Repositories
@@ -16,6 +17,21 @@ namespace Umbraco.Core.Persistence.Repositories
IEnumerable<IUmbracoEntity> GetByQuery(IQuery<IUmbracoEntity> query);
IEnumerable<IUmbracoEntity> GetByQuery(IQuery<IUmbracoEntity> query, Guid objectTypeId);
/// <summary>
/// Gets paged results
/// </summary>
/// <param name="query">Query to excute</param>
/// <param name="objectTypeId"></param>
/// <param name="pageIndex">Page number</param>
/// <param name="pageSize">Page size</param>
/// <param name="totalRecords">Total records query would return without paging</param>
/// <param name="orderBy">Field to order by</param>
/// <param name="orderDirection">Direction to order by</param>
/// <param name="filter"></param>
/// <returns>An Enumerable list of <see cref="IUmbracoEntity"/> objects</returns>
IEnumerable<IUmbracoEntity> GetPagedResultsByQuery(IQuery<IUmbracoEntity> query, Guid objectTypeId, long pageIndex, int pageSize, out long totalRecords,
string orderBy, Direction orderDirection, IQuery<IUmbracoEntity> filter = null);
/// <summary>
/// Returns true if the entity exists
/// </summary>
@@ -73,12 +73,16 @@ namespace Umbraco.Core.Persistence.Repositories
RelationFactory factory = null;
var relationTypeId = -1;
// the ToList() here is important because we are using _relationTypeRepository and we
// cannot wait until the result is actually enumerated to do so, because that would
// mean we kinda capture the current unit of work and reuse it after it's been disposed
return dtos.Select(x =>
{
if (relationTypeId != x.RelationType)
factory = new RelationFactory(_relationTypeRepository.Get(relationTypeId = x.RelationType));
return DtoToEntity(x, factory);
});
}).ToList();
}
private static IRelation DtoToEntity(RelationDto dto, RelationFactory factory)
+3 -3
View File
@@ -368,13 +368,13 @@ namespace Umbraco.Core.Scoping
#endif
}
var parent = ParentScope;
_scopeProvider.AmbientScope = parent;
#if DEBUG_SCOPES
_scopeProvider.Disposed(this);
#endif
var parent = ParentScope;
_scopeProvider.AmbientScope = parent;
if (parent != null)
parent.ChildCompleted(_completed);
else
+38 -9
View File
@@ -3,6 +3,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Web;
using Umbraco.Core.Events;
using Umbraco.Core.Persistence;
@@ -99,18 +100,23 @@ namespace Umbraco.Core.Scoping
where T : class
{
var objectKey = CallContext.LogicalGetData(key).AsGuid();
if (objectKey == Guid.Empty) return null;
lock (StaticCallContextObjectsLock)
{
object callContextObject;
if (StaticCallContextObjects.TryGetValue(objectKey, out callContextObject))
{
#if DEBUG_SCOPES
//Logging.LogHelper.Debug<ScopeProvider>("GotObject " + objectKey.ToString("N").Substring(0, 8));
Logging.LogHelper.Debug<ScopeProvider>("Got " + typeof(T).Name + " Object " + objectKey.ToString("N").Substring(0, 8));
//Logging.LogHelper.Debug<ScopeProvider>("At:\r\n" + Head(Environment.StackTrace, 24));
#endif
return (T) callContextObject;
}
Logging.LogHelper.Warn<ScopeProvider>("Missed " + typeof(T).Name + " Object " + objectKey.ToString("N").Substring(0, 8));
#if DEBUG_SCOPES
//Logging.LogHelper.Debug<ScopeProvider>("MissedObject " + objectKey.ToString("N").Substring(0, 8));
//Logging.LogHelper.Debug<ScopeProvider>("At:\r\n" + Head(Environment.StackTrace, 24));
#endif
return null;
}
@@ -146,7 +152,8 @@ namespace Umbraco.Core.Scoping
lock (StaticCallContextObjectsLock)
{
#if DEBUG_SCOPES
//Logging.LogHelper.Debug<ScopeProvider>("RemoveObject " + objectKey.ToString("N").Substring(0, 8));
Logging.LogHelper.Debug<ScopeProvider>("Remove Object " + objectKey.ToString("N").Substring(0, 8));
//Logging.LogHelper.Debug<ScopeProvider>("At:\r\n" + Head(Environment.StackTrace, 24));
#endif
StaticCallContextObjects.Remove(objectKey);
}
@@ -160,7 +167,8 @@ namespace Umbraco.Core.Scoping
lock (StaticCallContextObjectsLock)
{
#if DEBUG_SCOPES
//Logging.LogHelper.Debug<ScopeProvider>("AddObject " + objectKey.ToString("N").Substring(0, 8));
Logging.LogHelper.Debug<ScopeProvider>("AddObject " + objectKey.ToString("N").Substring(0, 8));
//Logging.LogHelper.Debug<ScopeProvider>("At:\r\n" + Head(Environment.StackTrace, 24));
#endif
StaticCallContextObjects.Add(objectKey, value);
}
@@ -506,7 +514,7 @@ namespace Umbraco.Core.Scoping
lock (StaticScopeInfosLock)
{
if (StaticScopeInfos.ContainsKey(scope)) throw new Exception("oops: already registered.");
//Logging.LogHelper.Debug<ScopeProvider>("Register " + scope.InstanceId.ToString("N").Substring(0, 8));
Logging.LogHelper.Debug<ScopeProvider>("Register " + scope.InstanceId.ToString("N").Substring(0, 8));
StaticScopeInfos[scope] = new ScopeInfo(scope, Environment.StackTrace);
}
}
@@ -524,14 +532,35 @@ namespace Umbraco.Core.Scoping
if (context == null) return;
throw new Exception("oops: unregistered scope.");
}
//Logging.LogHelper.Debug<ScopeProvider>("Register context " + (context ?? "null") + " for " + scope.InstanceId.ToString("N").Substring(0, 8));
var sb = new StringBuilder();
var s = scope;
while (s != null)
{
if (sb.Length > 0) sb.Append(" < ");
sb.Append(s.InstanceId.ToString("N").Substring(0, 8));
var ss = s as IScopeInternal;
s = ss == null ? null : ss.ParentScope;
}
Logging.LogHelper.Debug<ScopeProvider>("Register " + (context ?? "null") + " context " + sb);
if (context == null) info.NullStack = Environment.StackTrace;
//if (context == null)
// Logging.LogHelper.Debug<ScopeProvider>("STACK\r\n" + info.NullStack);
//Logging.LogHelper.Debug<ScopeProvider>("At:\r\n" + Head(Environment.StackTrace, 16));
info.Context = context;
}
}
private static string Head(string s, int count)
{
var pos = 0;
var i = 0;
while (i < count && pos >= 0)
{
pos = s.IndexOf("\r\n", pos + 1, StringComparison.OrdinalIgnoreCase);
i++;
}
if (pos < 0) return s;
return s.Substring(0, pos);
}
public void Disposed(IScope scope)
{
lock (StaticScopeInfosLock)
@@ -541,7 +570,7 @@ namespace Umbraco.Core.Scoping
// enable this by default
//Console.WriteLine("unregister " + scope.InstanceId.ToString("N").Substring(0, 8));
StaticScopeInfos.Remove(scope);
//Logging.LogHelper.Debug<ScopeProvider>("Remove " + scope.InstanceId.ToString("N").Substring(0, 8));
Logging.LogHelper.Debug<ScopeProvider>("Remove " + scope.InstanceId.ToString("N").Substring(0, 8));
// instead, enable this to keep *all* scopes
// beware, there can be a lot of scopes!
+110
View File
@@ -9,6 +9,7 @@ using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.UnitOfWork;
@@ -376,6 +377,115 @@ namespace Umbraco.Core.Services
}
}
/// <summary>
/// Returns a paged collection of children
/// </summary>
/// <param name="parentId">The parent id to return children for</param>
/// <param name="umbracoObjectType"></param>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="totalRecords"></param>
/// <param name="orderBy"></param>
/// <param name="orderDirection"></param>
/// <param name="filter"></param>
/// <returns></returns>
public IEnumerable<IUmbracoEntity> GetPagedChildren(int parentId, UmbracoObjectTypes umbracoObjectType, long pageIndex, int pageSize, out long totalRecords,
string orderBy = "SortOrder", Direction orderDirection = Direction.Ascending, string filter = "")
{
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var query = Query<IUmbracoEntity>.Builder.Where(x => x.ParentId == parentId && x.Trashed == false);
IQuery<IUmbracoEntity> filterQuery = null;
if (filter.IsNullOrWhiteSpace() == false)
{
filterQuery = Query<IUmbracoEntity>.Builder.Where(x => x.Name.Contains(filter));
}
var contents = repository.GetPagedResultsByQuery(query, objectTypeId, pageIndex, pageSize, out totalRecords, orderBy, orderDirection, filterQuery);
uow.Commit();
return contents;
}
}
/// <summary>
/// Returns a paged collection of descendants
/// </summary>
/// <param name="id"></param>
/// <param name="umbracoObjectType"></param>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="totalRecords"></param>
/// <param name="orderBy"></param>
/// <param name="orderDirection"></param>
/// <param name="filter"></param>
/// <returns></returns>
public IEnumerable<IUmbracoEntity> GetPagedDescendants(int id, UmbracoObjectTypes umbracoObjectType, long pageIndex, int pageSize, out long totalRecords,
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "")
{
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var query = Query<IUmbracoEntity>.Builder;
//if the id is System Root, then just get all
if (id != Constants.System.Root)
query.Where(x => x.Path.SqlContains(string.Format(",{0},", id), TextColumnType.NVarchar));
IQuery<IUmbracoEntity> filterQuery = null;
if (filter.IsNullOrWhiteSpace() == false)
{
filterQuery = Query<IUmbracoEntity>.Builder.Where(x => x.Name.Contains(filter));
}
var contents = repository.GetPagedResultsByQuery(query, objectTypeId, pageIndex, pageSize, out totalRecords, orderBy, orderDirection, filterQuery);
uow.Commit();
return contents;
}
}
/// <summary>
/// Returns a paged collection of descendants from the root
/// </summary>
/// <param name="umbracoObjectType"></param>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="totalRecords"></param>
/// <param name="orderBy"></param>
/// <param name="orderDirection"></param>
/// <param name="filter"></param>
/// <param name="includeTrashed">true/false to include trashed objects</param>
/// <returns></returns>
public IEnumerable<IUmbracoEntity> GetPagedDescendantsFromRoot(UmbracoObjectTypes umbracoObjectType, long pageIndex, int pageSize, out long totalRecords,
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "", bool includeTrashed = true)
{
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork())
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var query = Query<IUmbracoEntity>.Builder;
//don't include trashed if specfied
if (includeTrashed == false)
{
query.Where(x => x.Trashed == false);
}
IQuery<IUmbracoEntity> filterQuery = null;
if (filter.IsNullOrWhiteSpace() == false)
{
filterQuery = Query<IUmbracoEntity>.Builder.Where(x => x.Name.Contains(filter));
}
var contents = repository.GetPagedResultsByQuery(query, objectTypeId, pageIndex, pageSize, out totalRecords, orderBy, orderDirection, filterQuery);
uow.Commit();
return contents;
}
}
/// <summary>
/// Gets a collection of descendents by the parents Id
/// </summary>
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
namespace Umbraco.Core.Services
{
@@ -137,6 +138,51 @@ namespace Umbraco.Core.Services
/// <returns>An enumerable list of <see cref="IUmbracoEntity"/> objects</returns>
IEnumerable<IUmbracoEntity> GetChildren(int parentId, UmbracoObjectTypes umbracoObjectType);
/// <summary>
/// Returns a paged collection of children
/// </summary>
/// <param name="parentId">The parent id to return children for</param>
/// <param name="umbracoObjectType"></param>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="totalRecords"></param>
/// <param name="orderBy"></param>
/// <param name="orderDirection"></param>
/// <param name="filter"></param>
/// <returns></returns>
IEnumerable<IUmbracoEntity> GetPagedChildren(int parentId, UmbracoObjectTypes umbracoObjectType, long pageIndex, int pageSize, out long totalRecords,
string orderBy = "SortOrder", Direction orderDirection = Direction.Ascending, string filter = "");
/// <summary>
/// Returns a paged collection of descendants
/// </summary>
/// <param name="id"></param>
/// <param name="umbracoObjectType"></param>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="totalRecords"></param>
/// <param name="orderBy"></param>
/// <param name="orderDirection"></param>
/// <param name="filter"></param>
/// <returns></returns>
IEnumerable<IUmbracoEntity> GetPagedDescendants(int id, UmbracoObjectTypes umbracoObjectType, long pageIndex, int pageSize, out long totalRecords,
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "");
/// <summary>
/// Returns a paged collection of descendants from the root
/// </summary>
/// <param name="umbracoObjectType"></param>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <param name="totalRecords"></param>
/// <param name="orderBy"></param>
/// <param name="orderDirection"></param>
/// <param name="filter"></param>
/// <param name="includeTrashed">true/false to include trashed objects</param>
/// <returns></returns>
IEnumerable<IUmbracoEntity> GetPagedDescendantsFromRoot(UmbracoObjectTypes umbracoObjectType, long pageIndex, int pageSize, out long totalRecords,
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "", bool includeTrashed = true);
/// <summary>
/// Gets a collection of descendents by the parents Id
/// </summary>
+2 -1
View File
@@ -311,7 +311,8 @@
<Compile Include="Deploy\ArtifactSignature.cs" />
<Compile Include="Deploy\Difference.cs" />
<Compile Include="Deploy\Direction.cs" />
<Compile Include="Deploy\IFileStore.cs" />
<Compile Include="Deploy\IFileType.cs" />
<Compile Include="Deploy\IFileTypeCollection.cs" />
<Compile Include="Events\EventDefinitionFilter.cs" />
<Compile Include="Events\IDeletingMediaFilesEventArgs.cs" />
<Compile Include="Events\ScopeEventDispatcherBase.cs" />
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Umbraco.Core;
@@ -25,8 +26,368 @@ namespace Umbraco.Tests.Services
public override void TearDown()
{
base.TearDown();
}
[Test]
public void EntityService_Can_Get_Paged_Content_Children()
{
var contentType = ServiceContext.ContentTypeService.GetContentType("umbTextpage");
var root = MockedContent.CreateSimpleContent(contentType);
ServiceContext.ContentService.Save(root);
for (int i = 0; i < 10; i++)
{
var c1 = MockedContent.CreateSimpleContent(contentType, Guid.NewGuid().ToString(), root);
ServiceContext.ContentService.Save(c1);
}
var service = ServiceContext.EntityService;
long total;
var entities = service.GetPagedChildren(root.Id, UmbracoObjectTypes.Document, 0, 6, out total).ToArray();
Assert.That(entities.Length, Is.EqualTo(6));
Assert.That(total, Is.EqualTo(10));
entities = service.GetPagedChildren(root.Id, UmbracoObjectTypes.Document, 1, 6, out total).ToArray();
Assert.That(entities.Length, Is.EqualTo(4));
Assert.That(total, Is.EqualTo(10));
}
[Test]
public void EntityService_Can_Get_Paged_Content_Descendants()
{
var contentType = ServiceContext.ContentTypeService.GetContentType("umbTextpage");
var root = MockedContent.CreateSimpleContent(contentType);
ServiceContext.ContentService.Save(root);
var count = 0;
for (int i = 0; i < 10; i++)
{
var c1 = MockedContent.CreateSimpleContent(contentType, Guid.NewGuid().ToString(), root);
ServiceContext.ContentService.Save(c1);
count++;
for (int j = 0; j < 5; j++)
{
var c2 = MockedContent.CreateSimpleContent(contentType, Guid.NewGuid().ToString(), c1);
ServiceContext.ContentService.Save(c2);
count++;
}
}
var service = ServiceContext.EntityService;
long total;
var entities = service.GetPagedDescendants(root.Id, UmbracoObjectTypes.Document, 0, 31, out total).ToArray();
Assert.That(entities.Length, Is.EqualTo(31));
Assert.That(total, Is.EqualTo(60));
entities = service.GetPagedDescendants(root.Id, UmbracoObjectTypes.Document, 1, 31, out total).ToArray();
Assert.That(entities.Length, Is.EqualTo(29));
Assert.That(total, Is.EqualTo(60));
}
[Test]
public void EntityService_Can_Get_Paged_Content_Descendants_Including_Recycled()
{
var contentType = ServiceContext.ContentTypeService.GetContentType("umbTextpage");
var root = MockedContent.CreateSimpleContent(contentType);
ServiceContext.ContentService.Save(root);
var toDelete = new List<IContent>();
for (int i = 0; i < 10; i++)
{
var c1 = MockedContent.CreateSimpleContent(contentType, Guid.NewGuid().ToString(), root);
ServiceContext.ContentService.Save(c1);
if (i % 2 == 0)
{
toDelete.Add(c1);
}
for (int j = 0; j < 5; j++)
{
var c2 = MockedContent.CreateSimpleContent(contentType, Guid.NewGuid().ToString(), c1);
ServiceContext.ContentService.Save(c2);
}
}
foreach (var content in toDelete)
{
ServiceContext.ContentService.MoveToRecycleBin(content);
}
var service = ServiceContext.EntityService;
long total;
//search at root to see if it returns recycled
var entities = service.GetPagedDescendants(-1, UmbracoObjectTypes.Document, 0, 1000, out total)
.Select(x => x.Id)
.ToArray();
foreach (var c in toDelete)
{
Assert.True(entities.Contains(c.Id));
}
}
[Test]
public void EntityService_Can_Get_Paged_Content_Descendants_Without_Recycled()
{
var contentType = ServiceContext.ContentTypeService.GetContentType("umbTextpage");
var root = MockedContent.CreateSimpleContent(contentType);
ServiceContext.ContentService.Save(root);
var toDelete = new List<IContent>();
for (int i = 0; i < 10; i++)
{
var c1 = MockedContent.CreateSimpleContent(contentType, Guid.NewGuid().ToString(), root);
ServiceContext.ContentService.Save(c1);
if (i % 2 == 0)
{
toDelete.Add(c1);
}
for (int j = 0; j < 5; j++)
{
var c2 = MockedContent.CreateSimpleContent(contentType, Guid.NewGuid().ToString(), c1);
ServiceContext.ContentService.Save(c2);
}
}
foreach (var content in toDelete)
{
ServiceContext.ContentService.MoveToRecycleBin(content);
}
var service = ServiceContext.EntityService;
long total;
//search at root to see if it returns recycled
var entities = service.GetPagedDescendantsFromRoot(UmbracoObjectTypes.Document, 0, 1000, out total, includeTrashed:false)
.Select(x => x.Id)
.ToArray();
foreach (var c in toDelete)
{
Assert.IsFalse(entities.Contains(c.Id));
}
}
[Test]
public void EntityService_Can_Get_Paged_Content_Descendants_With_Search()
{
var contentType = ServiceContext.ContentTypeService.GetContentType("umbTextpage");
var root = MockedContent.CreateSimpleContent(contentType);
ServiceContext.ContentService.Save(root);
for (int i = 0; i < 10; i++)
{
var c1 = MockedContent.CreateSimpleContent(contentType, "ssss" + Guid.NewGuid(), root);
ServiceContext.ContentService.Save(c1);
for (int j = 0; j < 5; j++)
{
var c2 = MockedContent.CreateSimpleContent(contentType, "tttt" + Guid.NewGuid(), c1);
ServiceContext.ContentService.Save(c2);
}
}
var service = ServiceContext.EntityService;
long total;
var entities = service.GetPagedDescendants(root.Id, UmbracoObjectTypes.Document, 0, 10, out total, filter: "ssss").ToArray();
Assert.That(entities.Length, Is.EqualTo(10));
Assert.That(total, Is.EqualTo(10));
entities = service.GetPagedDescendants(root.Id, UmbracoObjectTypes.Document, 0, 50, out total, filter: "tttt").ToArray();
Assert.That(entities.Length, Is.EqualTo(50));
Assert.That(total, Is.EqualTo(50));
}
[Test]
public void EntityService_Can_Get_Paged_Media_Children()
{
var folderType = ServiceContext.ContentTypeService.GetMediaType(1031);
var imageMediaType = ServiceContext.ContentTypeService.GetMediaType(1032);
var root = MockedMedia.CreateMediaFolder(folderType, -1);
ServiceContext.MediaService.Save(root);
for (int i = 0; i < 10; i++)
{
var c1 = MockedMedia.CreateMediaImage(imageMediaType, root.Id);
ServiceContext.MediaService.Save(c1);
}
var service = ServiceContext.EntityService;
long total;
var entities = service.GetPagedChildren(root.Id, UmbracoObjectTypes.Media, 0, 6, out total).ToArray();
Assert.That(entities.Length, Is.EqualTo(6));
Assert.That(total, Is.EqualTo(10));
entities = service.GetPagedChildren(root.Id, UmbracoObjectTypes.Media, 1, 6, out total).ToArray();
Assert.That(entities.Length, Is.EqualTo(4));
Assert.That(total, Is.EqualTo(10));
}
[Test]
public void EntityService_Can_Get_Paged_Media_Descendants()
{
var folderType = ServiceContext.ContentTypeService.GetMediaType(1031);
var imageMediaType = ServiceContext.ContentTypeService.GetMediaType(1032);
var root = MockedMedia.CreateMediaFolder(folderType, -1);
ServiceContext.MediaService.Save(root);
var count = 0;
for (int i = 0; i < 10; i++)
{
var c1 = MockedMedia.CreateMediaImage(imageMediaType, root.Id);
ServiceContext.MediaService.Save(c1);
count++;
for (int j = 0; j < 5; j++)
{
var c2 = MockedMedia.CreateMediaImage(imageMediaType, c1.Id);
ServiceContext.MediaService.Save(c2);
count++;
}
}
var service = ServiceContext.EntityService;
long total;
var entities = service.GetPagedDescendants(root.Id, UmbracoObjectTypes.Media, 0, 31, out total).ToArray();
Assert.That(entities.Length, Is.EqualTo(31));
Assert.That(total, Is.EqualTo(60));
entities = service.GetPagedDescendants(root.Id, UmbracoObjectTypes.Media, 1, 31, out total).ToArray();
Assert.That(entities.Length, Is.EqualTo(29));
Assert.That(total, Is.EqualTo(60));
}
[Test]
public void EntityService_Can_Get_Paged_Media_Descendants_Including_Recycled()
{
var folderType = ServiceContext.ContentTypeService.GetMediaType(1031);
var imageMediaType = ServiceContext.ContentTypeService.GetMediaType(1032);
var root = MockedMedia.CreateMediaFolder(folderType, -1);
ServiceContext.MediaService.Save(root);
var toDelete = new List<IMedia>();
for (int i = 0; i < 10; i++)
{
var c1 = MockedMedia.CreateMediaImage(imageMediaType, root.Id);
ServiceContext.MediaService.Save(c1);
if (i % 2 == 0)
{
toDelete.Add(c1);
}
for (int j = 0; j < 5; j++)
{
var c2 = MockedMedia.CreateMediaImage(imageMediaType, c1.Id);
ServiceContext.MediaService.Save(c2);
}
}
foreach (var content in toDelete)
{
ServiceContext.MediaService.MoveToRecycleBin(content);
}
var service = ServiceContext.EntityService;
long total;
//search at root to see if it returns recycled
var entities = service.GetPagedDescendants(-1, UmbracoObjectTypes.Media, 0, 1000, out total)
.Select(x => x.Id)
.ToArray();
foreach (var media in toDelete)
{
Assert.IsTrue(entities.Contains(media.Id));
}
}
[Test]
public void EntityService_Can_Get_Paged_Media_Descendants_Without_Recycled()
{
var folderType = ServiceContext.ContentTypeService.GetMediaType(1031);
var imageMediaType = ServiceContext.ContentTypeService.GetMediaType(1032);
var root = MockedMedia.CreateMediaFolder(folderType, -1);
ServiceContext.MediaService.Save(root);
var toDelete = new List<IMedia>();
for (int i = 0; i < 10; i++)
{
var c1 = MockedMedia.CreateMediaImage(imageMediaType, root.Id);
ServiceContext.MediaService.Save(c1);
if (i % 2 == 0)
{
toDelete.Add(c1);
}
for (int j = 0; j < 5; j++)
{
var c2 = MockedMedia.CreateMediaImage(imageMediaType, c1.Id);
ServiceContext.MediaService.Save(c2);
}
}
foreach (var content in toDelete)
{
ServiceContext.MediaService.MoveToRecycleBin(content);
}
var service = ServiceContext.EntityService;
long total;
//search at root to see if it returns recycled
var entities = service.GetPagedDescendantsFromRoot(UmbracoObjectTypes.Media, 0, 1000, out total, includeTrashed:false)
.Select(x => x.Id)
.ToArray();
foreach (var media in toDelete)
{
Assert.IsFalse(entities.Contains(media.Id));
}
}
[Test]
public void EntityService_Can_Get_Paged_Media_Descendants_With_Search()
{
var folderType = ServiceContext.ContentTypeService.GetMediaType(1031);
var imageMediaType = ServiceContext.ContentTypeService.GetMediaType(1032);
var root = MockedMedia.CreateMediaFolder(folderType, -1);
ServiceContext.MediaService.Save(root);
for (int i = 0; i < 10; i++)
{
var c1 = MockedMedia.CreateMediaImage(imageMediaType, root.Id);
c1.Name = "ssss" + Guid.NewGuid();
ServiceContext.MediaService.Save(c1);
for (int j = 0; j < 5; j++)
{
var c2 = MockedMedia.CreateMediaImage(imageMediaType, c1.Id);
c2.Name = "tttt" + Guid.NewGuid();
ServiceContext.MediaService.Save(c2);
}
}
var service = ServiceContext.EntityService;
long total;
var entities = service.GetPagedDescendants(root.Id, UmbracoObjectTypes.Media, 0, 10, out total, filter: "ssss").ToArray();
Assert.That(entities.Length, Is.EqualTo(10));
Assert.That(total, Is.EqualTo(10));
entities = service.GetPagedDescendants(root.Id, UmbracoObjectTypes.Media, 0, 50, out total, filter: "tttt").ToArray();
Assert.That(entities.Length, Is.EqualTo(50));
Assert.That(total, Is.EqualTo(50));
}
[Test]
public void EntityService_Can_Find_All_Content_By_UmbracoObjectTypes()
{
@@ -1,7 +1,7 @@
(function () {
'use strict';
function MiniListViewDirective(contentResource, memberResource, mediaResource) {
function MiniListViewDirective(entityResource) {
function link(scope, el, attr, ctrl) {
@@ -52,22 +52,19 @@
// start loading animation list view
miniListView.loading = true;
// setup the correct resource depending on section
var resource = "";
if (scope.entityType === "Member") {
resource = memberResource.getPagedResults;
} else if (scope.entityType === "Media") {
resource = mediaResource.getChildren;
} else {
resource = contentResource.getChildren;
}
resource(miniListView.node.id, miniListView.pagination)
entityResource.getPagedChildren(miniListView.node.id, scope.entityType, miniListView.pagination)
.then(function (data) {
// update children
miniListView.children = data.items;
_.each(miniListView.children, function(c) {
if (c.metaData) {
c.hasChildren = c.metaData.HasChildren;
if(scope.entityType === "Document") {
c.published = c.metaData.IsPublished;
}
}
});
// update pagination
miniListView.pagination.totalItems = data.totalItems;
miniListView.pagination.totalPages = data.totalPages;
@@ -477,20 +477,20 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
}
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"contentApiBaseUrl",
"GetChildren",
[
{ id: parentId },
{ pageNumber: options.pageNumber },
{ pageSize: options.pageSize },
{ orderBy: options.orderBy },
{ orderDirection: options.orderDirection },
{ orderBySystemField: toBool(options.orderBySystemField) },
{ filter: options.filter }
])),
'Failed to retrieve children for content item ' + parentId);
$http.get(
umbRequestHelper.getApiUrl(
"contentApiBaseUrl",
"GetChildren",
{
id: parentId,
pageNumber: options.pageNumber,
pageSize: options.pageSize,
orderBy: options.orderBy,
orderDirection: options.orderDirection,
orderBySystemField: toBool(options.orderBySystemField),
filter: options.filter
})),
'Failed to retrieve children for content item ' + parentId);
},
/**
@@ -38,6 +38,10 @@ function entityResource($q, $http, umbRequestHelper) {
getSafeAlias: function (value, camelCase) {
if (!value) {
return "";
}
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
@@ -68,6 +72,11 @@ function entityResource($q, $http, umbRequestHelper) {
*
*/
getPath: function (id, type) {
if (id === -1 || id === "-1") {
return "-1";
}
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
@@ -98,7 +107,12 @@ function entityResource($q, $http, umbRequestHelper) {
* @returns {Promise} resourcePromise object containing the url.
*
*/
getUrl: function(id, type) {
getUrl: function (id, type) {
if (id === -1 || id === "-1") {
return "";
}
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
@@ -131,7 +145,12 @@ function entityResource($q, $http, umbRequestHelper) {
* @returns {Promise} resourcePromise object containing the entity.
*
*/
getById: function (id, type) {
getById: function (id, type) {
if (id === -1 || id === "-1") {
return null;
}
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
@@ -285,18 +304,19 @@ function entityResource($q, $http, umbRequestHelper) {
/**
* @ngdoc method
* @name umbraco.resources.entityResource#getAncestors
* @name umbraco.resources.entityResource#getChildren
* @methodOf umbraco.resources.entityResource
*
* @description
* Gets children entities for a given item
*
*
* @param {Int} parentid id of content item to return children of
* @param {string} type Object type name
* @returns {Promise} resourcePromise object containing the entity.
*
*/
getChildren: function (id, type) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
@@ -305,6 +325,146 @@ function entityResource($q, $http, umbRequestHelper) {
[{ id: id }, { type: type }])),
'Failed to retrieve child data for id ' + id);
},
/**
* @ngdoc method
* @name umbraco.resources.entityResource#getPagedChildren
* @methodOf umbraco.resources.entityResource
*
* @description
* Gets paged children of a content item with a given id
*
* ##usage
* <pre>
* entityResource.getPagedChildren(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 children 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 = 1
* @param {Int} options.pageNumber if paging data, current page index, default = 100
* @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.
*
*/
getPagedChildren: function (parentId, type, options) {
var defaults = {
pageSize: 1,
pageNumber: 100,
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",
"GetPagedChildren",
{
id: parentId,
type: type,
pageNumber: options.pageNumber,
pageSize: options.pageSize,
orderBy: options.orderBy,
orderDirection: options.orderDirection,
filter: encodeURIComponent(options.filter)
}
)),
'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 = 1
* @param {Int} options.pageNumber if paging data, current page index, default = 100
* @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: 1,
pageNumber: 100,
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: encodeURIComponent(options.filter)
}
)),
'Failed to retrieve child data for id ' + parentId);
},
/**
* @ngdoc method
@@ -482,7 +482,49 @@ function mediaResource($q, $http, umbDataFormatter, umbRequestHelper) {
"mediaApiBaseUrl",
"EmptyRecycleBin")),
'Failed to empty the recycle bin');
},
/**
* @ngdoc method
* @name umbraco.resources.mediaResource#search
* @methodOf umbraco.resources.mediaResource
*
* @description
* Empties the media recycle bin
*
* ##usage
* <pre>
* mediaResource.search("my search", 1, 100, -1)
* .then(function(searchResult) {
* alert('it's here!');
* });
* </pre>
*
* @param {string} query The search query
* @param {int} pageNumber The page number
* @param {int} pageSize The number of media items on a page
* @param {int} searchFrom Id to search from
* @returns {Promise} resourcePromise object.
*
*/
search: function (query, pageNumber, pageSize, searchFrom) {
var args = [
{ "query": query },
{ "pageNumber": pageNumber },
{ "pageSize": pageSize },
{ "searchFrom": searchFrom }
];
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"mediaApiBaseUrl",
"Search",
args)),
'Failed to retrieve media items for search: ' + query);
}
};
}
+27 -2
View File
@@ -26,10 +26,35 @@ app.run(['userService', '$log', '$rootScope', '$location', 'navigationService',
/** execute code on each successful route */
$rootScope.$on('$routeChangeSuccess', function(event, current, previous) {
if(current.params.section){
$rootScope.locationTitle = current.params.section + " - " + $location.$$host;
var deployConfig = Umbraco.Sys.ServerVariables.deploy;
var deployEnv, deployEnvTitle;
if (deployConfig) {
deployEnv = Umbraco.Sys.ServerVariables.deploy.CurrentWorkspace;
deployEnvTitle = "(" + deployEnv + ") ";
}
if(current.params.section) {
//Uppercase the current section, content, media, settings, developer, forms
var currentSection = current.params.section.charAt(0).toUpperCase() + current.params.section.slice(1);
var baseTitle = currentSection + " - " + $location.$$host;
//Check deploy for Global Umbraco.Sys obj workspace
if(deployEnv){
$rootScope.locationTitle = deployEnvTitle + baseTitle;
}
else {
$rootScope.locationTitle = baseTitle;
}
}
else {
if(deployEnv) {
$rootScope.locationTitle = deployEnvTitle + "Umbraco - " + $location.$$host;
}
$rootScope.locationTitle = "Umbraco - " + $location.$$host;
}
@@ -288,6 +288,10 @@ input.umb-table__input {
color: @black;
}
.umb-table__row-expand--hidden {
visibility: hidden;
}
.umb-table--condensed {
.umb-table-cell:first-of-type:not(.not-fixed) {
@@ -33,6 +33,14 @@ angular.module("umbraco")
$scope.acceptedMediatypes = types;
});
$scope.searchOptions = {
pageNumber: 1,
pageSize: 100,
totalItems: 0,
totalPages: 0,
filter: '',
};
//preload selected item
$scope.target = undefined;
if (dialogOptions.currentTarget) {
@@ -106,35 +114,9 @@ angular.module("umbraco")
$scope.path = [];
}
//mediaResource.rootMedia()
mediaResource.getChildren(folder.id)
.then(function(data) {
$scope.searchTerm = "";
$scope.images = data.items ? data.items : [];
// set already selected images to selected
for (var folderImageIndex = 0; folderImageIndex < $scope.images.length; folderImageIndex++) {
var folderImage = $scope.images[folderImageIndex];
var imageIsSelected = false;
for (var selectedImageIndex = 0;
selectedImageIndex < $scope.model.selectedImages.length;
selectedImageIndex++) {
var selectedImage = $scope.model.selectedImages[selectedImageIndex];
if (folderImage.key === selectedImage.key) {
imageIsSelected = true;
}
}
if (imageIsSelected) {
folderImage.selected = true;
}
}
});
getChildren(folder.id);
$scope.currentFolder = folder;
localStorageService.set("umbLastOpenedMediaNodeId", folder.id);
};
$scope.clickHandler = function(image, event, index) {
@@ -238,4 +220,106 @@ angular.module("umbraco")
$scope.mediaPickerDetailsOverlay = null;
};
};
$scope.changeSearch = function() {
$scope.loading = true;
debounceSearchMedia();
};
$scope.changePagination = function(pageNumber) {
$scope.loading = true;
$scope.searchOptions.pageNumber = pageNumber;
searchMedia();
};
var debounceSearchMedia = _.debounce(function () {
$scope.$apply(function () {
if ($scope.searchOptions.filter) {
searchMedia();
} else {
// reset pagination
$scope.searchOptions = {
pageNumber: 1,
pageSize: 100,
totalItems: 0,
totalPages: 0,
filter: ''
};
getChildren($scope.currentFolder.id);
}
});
}, 500);
function searchMedia() {
$scope.loading = true;
entityResource.getPagedDescendants($scope.startNodeId, "Media", $scope.searchOptions)
.then(function (data) {
// update image data to work with image grid
angular.forEach(data.items, function(mediaItem){
// set thumbnail and src
mediaItem.thumbnail = mediaHelper.resolveFileFromEntity(mediaItem, true);
mediaItem.image = mediaHelper.resolveFileFromEntity(mediaItem, false);
// set properties to match a media object
if (mediaItem.metaData &&
mediaItem.metaData.umbracoWidth &&
mediaItem.metaData.umbracoHeight) {
mediaItem.properties = [
{
alias: "umbracoWidth",
value: mediaItem.metaData.umbracoWidth.Value
},
{
alias: "umbracoHeight",
value: mediaItem.metaData.umbracoHeight.Value
}
];
}
});
// update images
$scope.images = data.items ? data.items : [];
// update pagination
if (data.pageNumber > 0)
$scope.searchOptions.pageNumber = data.pageNumber;
if (data.pageSize > 0)
$scope.searchOptions.pageSize = data.pageSize;
$scope.searchOptions.totalItems = data.totalItems;
$scope.searchOptions.totalPages = data.totalPages;
// set already selected images to selected
preSelectImages();
$scope.loading = false;
});
}
function getChildren(id) {
$scope.loading = true;
mediaResource.getChildren(id)
.then(function(data) {
$scope.searchOptions.filter = "";
$scope.images = data.items ? data.items : [];
// set already selected images to selected
preSelectImages();
$scope.loading = false;
});
}
function preSelectImages() {
for (var folderImageIndex = 0; folderImageIndex < $scope.images.length; folderImageIndex++) {
var folderImage = $scope.images[folderImageIndex];
var imageIsSelected = false;
for (var selectedImageIndex = 0;
selectedImageIndex < $scope.model.selectedImages.length;
selectedImageIndex++) {
var selectedImage = $scope.model.selectedImages[selectedImageIndex];
if (folderImage.key === selectedImage.key) {
imageIsSelected = true;
}
}
if (imageIsSelected) {
folderImage.selected = true;
}
}
}
});
@@ -10,13 +10,18 @@
<div class="umb-control-group umb-mediapicker-upload">
<umb-load-indicator
ng-if="loading">
</umb-load-indicator>
<div class="form-search">
<i class="icon-search"></i>
<input
class="umb-search-field search-query"
ng-model="searchTerm"
class="umb-search-field search-query -full-width-input"
ng-model="searchOptions.filter"
localize="placeholder"
placeholder="@placeholders_filter"
placeholder="@placeholders_search"
ng-change="changeSearch()"
type="text">
</div>
@@ -32,7 +37,7 @@
</div>
<div class="row umb-control-group">
<div class="row umb-control-group" ng-if="!searchOptions.filter">
<ul class="umb-breadcrumbs">
<li ng-hide="startNodeId != -1" class="umb-breadcrumbs__ancestor">
<a href ng-click="gotoFolder()" prevent-default>Media</a>
@@ -63,20 +68,20 @@
</div>
<umb-file-dropzone
ng-if="acceptedMediatypes.length > 0"
ng-if="acceptedMediatypes.length > 0 && !loading"
accepted-mediatypes="acceptedMediatypes"
parent-id="{{currentFolder.id}}"
files-uploaded="onUploadComplete"
files-queued="onFilesQueue"
accept="{{acceptedFileTypes}}"
max-file-size="{{maxFileSize}}"
hide-dropzone="{{!activeDrag && images.length > 0 }}"
hide-dropzone="{{!activeDrag && images.length > 0 || searchOptions.filter }}"
compact="{{ images.length > 0 }}">
</umb-file-dropzone>
<umb-media-grid
ng-if="!loading"
items="images"
filter-by="searchTerm"
on-click="clickHandler"
on-click-name="clickItemName"
item-max-width="150"
@@ -86,6 +91,21 @@
only-images={{onlyImages}}>
</umb-media-grid>
<div class="flex justify-center">
<umb-pagination
ng-if="searchOptions.totalPages > 0 && !loading"
page-number="searchOptions.pageNumber"
total-pages="searchOptions.totalPages"
on-change="changePagination(pageNumber)">
</umb-pagination>
</div>
<umb-empty-state
ng-if="searchOptions.filter && images.length === 0 && !loading"
position="center">
<localize key="general_searchNoResult"></localize>
</umb-empty-state>
</div>
<umb-overlay
@@ -72,7 +72,7 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
$scope.enableSearh = true;
//if a alternative startnode is used, we need to check if it is a container
if (dialogOptions.startNodeId && dialogOptions.startNodeId !== -1) {
if (dialogOptions.startNodeId && dialogOptions.startNodeId !== -1 && dialogOptions.startNodeId !== "-1") {
entityResource.getById(dialogOptions.startNodeId, $scope.entityType).then(function (node) {
if (node.metaData.IsContainer) {
openMiniListView(node);
@@ -62,14 +62,14 @@
ng-repeat="child in miniListView.children"
ng-click="selectNode(child)"
ng-class="{'-selected':child.selected}">
<div class="umb-table-cell umb-table-cell--auto-width" ng-class="{'umb-table-cell--faded':!child.published && entityType === 'Document'}">
<div class="umb-table-cell umb-table-cell--auto-width" ng-class="{'umb-table-cell--faded':child.published === false}">
<div class="flex items-center">
<ins class="icon-navigation-right umb-table__row-expand" ng-click="openNode($event, child)">&nbsp;</ins>
<ins class="icon-navigation-right umb-table__row-expand" ng-click="openNode($event, child)" ng-class="{'umb-table__row-expand--hidden': child.hasChildren !== true}">&nbsp;</ins>
<i class="umb-table-body__icon umb-table-body__fileicon {{child.icon}}"></i>
<i class="umb-table-body__icon umb-table-body__checkicon icon-check"></i>
</div>
</div>
<div class="umb-table-cell black" ng-class="{'umb-table-cell--faded':!child.published && entityType === 'Document'}">{{ child.name }}</div>
<div class="umb-table-cell black" ng-class="{'umb-table-cell--faded':child.published === false}">{{ child.name }}</div>
</div>
<!-- Load indicator when the list doesn't have items -->
@@ -483,7 +483,7 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie
//we need to do a double sync here: first refresh the node where the content was moved,
// then refresh the node where the content was moved from
navigationService.syncTree({
tree: target.nodeType,
tree: target.nodeType ? target.nodeType : (target.metaData.treeAlias),
path: newPath,
forceReload: true,
activate: false
@@ -136,6 +136,7 @@ namespace Umbraco.Web.Editors
var display = Mapper.Map<IPartialView, CodeFileDisplay>(view);
display.FileType = Core.Constants.Trees.PartialViews;
display.Path = Url.GetTreePathFromFilePath(view.Path);
display.Id = System.Web.HttpUtility.UrlEncode(view.Path);
return display;
}
return null;
@@ -147,6 +148,7 @@ namespace Umbraco.Web.Editors
var display = Mapper.Map<IPartialView, CodeFileDisplay>(viewMacro);
display.FileType = Core.Constants.Trees.PartialViewMacros;
display.Path = Url.GetTreePathFromFilePath(viewMacro.Path);
display.Id = System.Web.HttpUtility.UrlEncode(viewMacro.Path);
return display;
}
return null;
@@ -158,6 +160,7 @@ namespace Umbraco.Web.Editors
var display = Mapper.Map<Script, CodeFileDisplay>(script);
display.FileType = Core.Constants.Trees.Scripts;
display.Path = Url.GetTreePathFromFilePath(script.Path);
display.Id = System.Web.HttpUtility.UrlEncode(script.Path);
return display;
}
return null;
@@ -341,6 +344,7 @@ namespace Umbraco.Web.Editors
{
display = Mapper.Map(partialViewResult.Result, display);
display.Path = Url.GetTreePathFromFilePath(partialViewResult.Result.Path);
display.Id = System.Web.HttpUtility.UrlEncode(partialViewResult.Result.Path);
return display;
}
@@ -355,6 +359,7 @@ namespace Umbraco.Web.Editors
{
display = Mapper.Map(partialViewMacroResult.Result, display);
display.Path = Url.GetTreePathFromFilePath(partialViewMacroResult.Result.Path);
display.Id = System.Web.HttpUtility.UrlEncode(partialViewMacroResult.Result.Path);
return display;
}
@@ -371,6 +376,7 @@ namespace Umbraco.Web.Editors
script.Path = display.Name;
display = Mapper.Map(script, display);
display.Path = Url.GetTreePathFromFilePath(script.Path);
display.Id = System.Web.HttpUtility.UrlEncode(script.Path);
return display;
}
+272 -56
View File
@@ -30,6 +30,7 @@ using Examine.SearchCriteria;
using Umbraco.Web.Dynamics;
using umbraco;
using System.Text.RegularExpressions;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using System.Web.Http.Controllers;
using Umbraco.Core.Xml;
@@ -54,6 +55,11 @@ namespace Umbraco.Web.Editors
public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
{
controllerSettings.Services.Replace(typeof(IHttpActionSelector), new ParameterSwapControllerActionSelector(
//This is a special case, we'll accept a String here so that we can get page members when the special "all-members"
//id is passed in eventually we'll probably want to support GUID + Udi too
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetPagedChildren", "id", typeof(int), typeof(string)),
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetById", "id", typeof(int), typeof(Guid), typeof(Udi)),
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetByIds", "ids", typeof(int[]), typeof(Guid[]), typeof(Udi[]))));
}
@@ -216,7 +222,7 @@ namespace Umbraco.Web.Editors
/// <returns></returns>
public EntityBasic GetByQuery(string query, int nodeContextId, UmbracoEntityTypes type)
{
//TODO: Rename this!!! It's a bit misleading, it should be GetByXPath
//TODO: Rename this!!! It's misleading, it should be GetByXPath
if (type != UmbracoEntityTypes.Document)
@@ -382,6 +388,176 @@ namespace Umbraco.Web.Editors
return GetResultForChildren(id, type);
}
/// <summary>
/// Get paged child entities by id
/// </summary>
/// <param name="id"></param>
/// <param name="type"></param>
/// <param name="pageNumber"></param>
/// <param name="pageSize"></param>
/// <param name="orderBy"></param>
/// <param name="orderDirection"></param>
/// <param name="filter"></param>
/// <returns></returns>
public PagedResult<EntityBasic> GetPagedChildren(
string id,
UmbracoEntityTypes type,
int pageNumber,
int pageSize,
string orderBy = "SortOrder",
Direction orderDirection = Direction.Ascending,
string filter = "")
{
int intId;
if (int.TryParse(id, out intId))
{
return GetPagedChildren(intId, type, pageNumber, pageSize, orderBy, orderDirection, filter);
}
Guid guidId;
if (Guid.TryParse(id, out guidId))
{
//Not supported currently
throw new HttpResponseException(HttpStatusCode.NotFound);
}
Udi udiId;
if (Udi.TryParse(id, out udiId))
{
//Not supported currently
throw new HttpResponseException(HttpStatusCode.NotFound);
}
//so we don't have an INT, GUID or UDI, it's just a string, so now need to check if it's a special id or a member type
if (id == Constants.Conventions.MemberTypes.AllMembersListId)
{
//the EntityService can search paged members from the root
intId = -1;
return GetPagedChildren(intId, type, pageNumber, pageSize, orderBy, orderDirection, filter);
}
//the EntityService cannot search members of a certain type, this is currently not supported and would require
//quite a bit of plumbing to do in the Services/Repository, we'll revert to a paged search
int total;
var searchResult = ExamineSearch(filter ?? "", type, pageSize, pageNumber - 1, out total, id);
return new PagedResult<EntityBasic>(total, pageNumber, pageSize)
{
Items = searchResult
};
}
/// <summary>
/// Get paged child entities by id
/// </summary>
/// <param name="id"></param>
/// <param name="type"></param>
/// <param name="pageNumber"></param>
/// <param name="pageSize"></param>
/// <param name="orderBy"></param>
/// <param name="orderDirection"></param>
/// <param name="filter"></param>
/// <returns></returns>
public PagedResult<EntityBasic> GetPagedChildren(
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.GetPagedChildren(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 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;
//if it's from root, don't return recycled
var entities = id == Constants.System.Root
? Services.EntityService.GetPagedDescendantsFromRoot(objectType.Value, pageNumber - 1, pageSize, out totalRecords, orderBy, orderDirection, filter, includeTrashed:false)
: 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);
@@ -397,12 +573,30 @@ namespace Umbraco.Web.Editors
/// </summary>
/// <param name="query"></param>
/// <param name="entityType"></param>
/// <param name="searchFrom">
/// A starting point for the search, generally a node id, but for members this is a member type alias
/// </param>
/// <param name="searchFrom"></param>
/// <returns></returns>
private IEnumerable<EntityBasic> ExamineSearch(string query, UmbracoEntityTypes entityType, string searchFrom = null)
{
int total;
return ExamineSearch(query, entityType, 200, 0, out total, searchFrom);
}
/// <summary>
/// Searches for results based on the entity type
/// </summary>
/// <param name="query"></param>
/// <param name="entityType"></param>
/// <param name="totalFound"></param>
/// <param name="searchFrom">
/// A starting point for the search, generally a node id, but for members this is a member type alias
/// </param>
/// <param name="pageSize"></param>
/// <param name="pageIndex"></param>
/// <returns></returns>
private IEnumerable<EntityBasic> ExamineSearch(string query, UmbracoEntityTypes entityType, int pageSize, int pageIndex, out int totalFound, string searchFrom = null)
{
//TODO: We need to update this to support paging
var sb = new StringBuilder();
string type;
@@ -478,91 +672,113 @@ namespace Umbraco.Web.Editors
query = Lucene.Net.QueryParsers.QueryParser.Escape(query);
if (query.IsNullOrWhiteSpace())
//nothing to search
if (searchFrom.IsNullOrWhiteSpace() && query.IsNullOrWhiteSpace())
{
totalFound = 0;
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)
//update the query with the query term
if (query.IsNullOrWhiteSpace() == false)
{
//additional fields normally
sb.Append(f);
sb.Append(": (");
sb.Append(query);
//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(") ");
}
sb.Append(") ");
}
}
else
{
if (query.Trim(new[] { '\"', '\'' }).IsNullOrWhiteSpace())
var trimmed = query.Trim(new[] {'\"', '\''});
//nothing to search
if (searchFrom.IsNullOrWhiteSpace() && trimmed.IsNullOrWhiteSpace())
{
totalFound = 0;
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)
//update the query with the query term
if (trimmed.IsNullOrWhiteSpace() == false)
{
sb.Append(w.ToLower());
sb.Append("* ");
}
sb.Append(") ");
query = Lucene.Net.QueryParsers.QueryParser.Escape(query);
var querywords = query.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var f in fields)
{
//additional fields normally
sb.Append(f);
sb.Append(":");
//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(")");
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(" ");
}
sb.Append(") ");
}
}
//must match index type
sb.Append(") +__IndexType:");
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 result = internalSearcher
//only return the number of items specified to read up to the amount of records to fill from 0 -> the number of items on the page requested
.Search(raw, pageSize * (pageIndex + 1));
totalFound = result.TotalItemCount;
var pagedResult = result.Skip(pageIndex);
switch (entityType)
{
case UmbracoEntityTypes.Member:
return MemberFromSearchResults(result);
return MemberFromSearchResults(pagedResult.ToArray());
case UmbracoEntityTypes.Media:
return MediaFromSearchResults(result);
return MediaFromSearchResults(pagedResult);
case UmbracoEntityTypes.Document:
return ContentFromSearchResults(result);
return ContentFromSearchResults(pagedResult);
default:
throw new NotSupportedException("The " + typeof(EntityController) + " currently does not support searching against object type " + entityType);
}
@@ -573,7 +789,7 @@ namespace Umbraco.Web.Editors
/// </summary>
/// <param name="results"></param>
/// <returns></returns>
private IEnumerable<EntityBasic> MemberFromSearchResults(ISearchResults results)
private IEnumerable<EntityBasic> MemberFromSearchResults(SearchResult[] results)
{
var mapped = Mapper.Map<IEnumerable<EntityBasic>>(results).ToArray();
//add additional data
@@ -607,7 +823,7 @@ namespace Umbraco.Web.Editors
/// </summary>
/// <param name="results"></param>
/// <returns></returns>
private IEnumerable<EntityBasic> MediaFromSearchResults(ISearchResults results)
private IEnumerable<EntityBasic> MediaFromSearchResults(IEnumerable<SearchResult> results)
{
var mapped = Mapper.Map<IEnumerable<EntityBasic>>(results).ToArray();
//add additional data
@@ -627,9 +843,9 @@ namespace Umbraco.Web.Editors
/// </summary>
/// <param name="results"></param>
/// <returns></returns>
private IEnumerable<EntityBasic> ContentFromSearchResults(ISearchResults results)
private IEnumerable<EntityBasic> ContentFromSearchResults(IEnumerable<SearchResult> results)
{
var mapped = Mapper.Map<ISearchResults, IEnumerable<EntityBasic>>(results).ToArray();
var mapped = Mapper.Map<IEnumerable<EntityBasic>>(results).ToArray();
//add additional data
foreach (var m in mapped)
{
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Net;
using System.Net.Http;
@@ -28,7 +29,9 @@ using Umbraco.Web.Mvc;
using Umbraco.Web.WebApi;
using System.Linq;
using System.Runtime.Serialization;
using System.Text.RegularExpressions;
using System.Web.Http.Controllers;
using Examine;
using Umbraco.Web.WebApi.Binders;
using Umbraco.Web.WebApi.Filters;
using umbraco;
@@ -42,6 +42,10 @@ namespace Umbraco.Web.Models.ContentEditing
[ReadOnly(true)]
public string Snippet { get; set; }
[DataMember(Name = "id")]
[ReadOnly(true)]
public string Id { get; set; }
public List<Notification> Notifications { get; private set; }
}
}
@@ -84,17 +84,21 @@ namespace Umbraco.Web.Models.ContentEditing
}
/// <summary>
/// The real persisted content object
/// The real persisted content object - used during inbound model binding
/// </summary>
/// <remarks>
/// This is not used for outgoing model information.
/// </remarks>
[JsonIgnore]
internal TPersisted PersistedContent { get; set; }
/// <summary>
/// The DTO object used to gather all required content data including data type information etc... for use with validation
/// The DTO object used to gather all required content data including data type information etc... for use with validation - used during inbound model binding
/// </summary>
/// <remarks>
/// We basically use this object to hydrate all required data from the database into one object so we can validate everything we need
/// instead of having to look up all the data individually.
/// This is not used for outgoing model information.
/// </remarks>
[JsonIgnore]
internal ContentItemDto<TPersisted> ContentDto { get; set; }
@@ -21,7 +21,14 @@ namespace Umbraco.Web.Models.Mapping
.ForMember(x => x.Udi, expression => expression.MapFrom(x => Udi.Create(UmbracoObjectTypesExtensions.GetUdiType(x.NodeObjectTypeId), x.Key)))
.ForMember(basic => basic.Icon, expression => expression.MapFrom(entity => entity.ContentTypeIcon))
.ForMember(dto => dto.Trashed, expression => expression.Ignore())
.ForMember(x => x.Alias, expression => expression.Ignore());
.ForMember(x => x.Alias, expression => expression.Ignore())
.AfterMap((entity, basic) =>
{
if (entity.NodeObjectTypeId == Constants.ObjectTypes.MemberGuid && basic.Icon.IsNullOrWhiteSpace())
{
basic.Icon = "icon-user";
}
});
config.CreateMap<PropertyType, EntityBasic>()
.ForMember(x => x.Udi, expression => expression.Ignore())
@@ -151,6 +158,9 @@ namespace Umbraco.Web.Models.Mapping
config.CreateMap<ISearchResults, IEnumerable<EntityBasic>>()
.ConvertUsing(results => results.Select(Mapper.Map<EntityBasic>).ToList());
config.CreateMap<IEnumerable<SearchResult>, IEnumerable<EntityBasic>>()
.ConvertUsing(results => results.Select(Mapper.Map<EntityBasic>).ToList());
}
}
}