diff --git a/src/Umbraco.Core/Models/UmbracoEntity.cs b/src/Umbraco.Core/Models/UmbracoEntity.cs index 4791606d2c..d87ec768f9 100644 --- a/src/Umbraco.Core/Models/UmbracoEntity.cs +++ b/src/Umbraco.Core/Models/UmbracoEntity.cs @@ -20,6 +20,8 @@ namespace Umbraco.Core.Models private bool _isPublished; private bool _isDraft; private bool _hasPendingChanges; + private string _contentTypeAlias; + private string _umbracoFile; private Guid _nodeObjectTypeId; private static readonly PropertyInfo CreatorIdSelector = ExpressionHelper.GetPropertyInfo(x => x.CreatorId); @@ -33,7 +35,11 @@ namespace Umbraco.Core.Models private static readonly PropertyInfo IsPublishedSelector = ExpressionHelper.GetPropertyInfo(x => x.IsPublished); private static readonly PropertyInfo IsDraftSelector = ExpressionHelper.GetPropertyInfo(x => x.IsDraft); private static readonly PropertyInfo HasPendingChangesSelector = ExpressionHelper.GetPropertyInfo(x => x.HasPendingChanges); + private static readonly PropertyInfo ContentTypeAliasSelector = ExpressionHelper.GetPropertyInfo(x => x.ContentTypeAlias); + private static readonly PropertyInfo ContentTypeIconUrlSelector = ExpressionHelper.GetPropertyInfo(x => x.ContentTypeIcon); + private static readonly PropertyInfo UmbracoFileSelector = ExpressionHelper.GetPropertyInfo(x => x.UmbracoFile); private static readonly PropertyInfo NodeObjectTypeIdSelector = ExpressionHelper.GetPropertyInfo(x => x.NodeObjectTypeId); + private string _contentTypeIcon; public UmbracoEntity() { @@ -187,6 +193,45 @@ namespace Umbraco.Core.Models } } + public string ContentTypeAlias + { + get { return _contentTypeAlias; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _contentTypeAlias = value; + return _contentTypeAlias; + }, _contentTypeAlias, ContentTypeAliasSelector); + } + } + + public string ContentTypeIcon + { + get { return _contentTypeIcon; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _contentTypeIcon = value; + return _contentTypeIcon; + }, _contentTypeIcon, ContentTypeIconUrlSelector); + } + } + + public string UmbracoFile + { + get { return _umbracoFile; } + set + { + SetPropertyValueAndDetectChanges(o => + { + _umbracoFile = value; + return _umbracoFile; + }, _umbracoFile, UmbracoFileSelector); + } + } + public Guid NodeObjectTypeId { get { return _nodeObjectTypeId; } diff --git a/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs b/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs index e3fec16cf4..e2e319829b 100644 --- a/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs @@ -21,7 +21,10 @@ namespace Umbraco.Core.Persistence.Factories ParentId = dto.ParentId, Path = dto.Path, SortOrder = dto.SortOrder, - HasChildren = dto.Children > 0 + HasChildren = dto.Children > 0, + ContentTypeAlias = dto.Alias ?? string.Empty, + ContentTypeIcon = dto.IconUrl ?? string.Empty, + UmbracoFile = dto.UmbracoFile ?? string.Empty }; entity.IsPublished = dto.PublishedVersion != default(Guid) || diff --git a/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs b/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs index a4a0e08e5d..7fdec8702f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs @@ -58,8 +58,8 @@ namespace Umbraco.Core.Persistence.Repositories public virtual IUmbracoEntity Get(int id, Guid objectTypeId) { - bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document); - var sql = GetBaseWhere(GetBase, isContent, objectTypeId, id).Append(GetGroupBy(isContent)); + bool isContentOrMedia = objectTypeId == new Guid(Constants.ObjectTypes.Document) || objectTypeId == new Guid(Constants.ObjectTypes.Media); + var sql = GetBaseWhere(GetBase, isContentOrMedia, objectTypeId, id).Append(GetGroupBy(isContentOrMedia)); var nodeDto = _work.Database.FirstOrDefault(sql); if (nodeDto == null) return null; @@ -81,8 +81,8 @@ namespace Umbraco.Core.Persistence.Repositories } else { - bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document); - var sql = GetBaseWhere(GetBase, isContent, objectTypeId).Append(GetGroupBy(isContent)); + bool isContentOrMedia = objectTypeId == new Guid(Constants.ObjectTypes.Document) || objectTypeId == new Guid(Constants.ObjectTypes.Media); + var sql = GetBaseWhere(GetBase, isContentOrMedia, objectTypeId).Append(GetGroupBy(isContentOrMedia)); var dtos = _work.Database.Fetch(sql); var factory = new UmbracoEntityFactory(); @@ -111,10 +111,10 @@ namespace Umbraco.Core.Persistence.Repositories public virtual IEnumerable GetByQuery(IQuery query, Guid objectTypeId) { - bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document); - var sqlClause = GetBaseWhere(GetBase, isContent, objectTypeId); + bool isContentOrMedia = objectTypeId == new Guid(Constants.ObjectTypes.Document) || objectTypeId == new Guid(Constants.ObjectTypes.Media); + var sqlClause = GetBaseWhere(GetBase, isContentOrMedia, objectTypeId); var translator = new SqlTranslator(sqlClause, query); - var sql = translator.Translate().Append(GetGroupBy(isContent)); + var sql = translator.Translate().Append(GetGroupBy(isContentOrMedia)); var dtos = _work.Database.Fetch(sql); @@ -128,94 +128,110 @@ namespace Umbraco.Core.Persistence.Repositories #region Sql Statements - protected virtual Sql GetBase(bool isContent) + protected virtual Sql GetBase(bool isContentOrMedia) { var columns = new List { - "main.id", - "main.trashed", - "main.parentID", - "main.nodeUser", - "main.level", - "main.path", - "main.sortOrder", - "main.uniqueID", - "main.text", - "main.nodeObjectType", - "main.createDate", + "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) + if (isContentOrMedia) { columns.Add("published.versionId as publishedVerison"); columns.Add("latest.versionId as newestVersion"); + columns.Add("contenttype.alias"); + columns.Add("contenttype.icon"); + columns.Add("property.dataNvarchar as umbracoFile"); } var sql = new Sql() .Select(columns.ToArray()) - .From("umbracoNode main") - .LeftJoin("umbracoNode parent").On("parent.parentID = main.id"); + .From("umbracoNode umbracoNode") + .LeftJoin("umbracoNode parent").On("parent.parentID = umbracoNode.id"); - if (isContent) + if (isContentOrMedia) { - sql.LeftJoin("(SELECT nodeId, versionId FROM cmsDocument WHERE published = 1 GROUP BY nodeId, versionId) as published").On("main.id = published.nodeId"); - sql.LeftJoin("(SELECT nodeId, versionId FROM cmsDocument WHERE newest = 1 GROUP BY nodeId, versionId) as latest").On("main.id = latest.nodeId"); + sql.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") + .LeftJoin( + "(SELECT contentNodeId, dataNvarchar FROM cmsPropertyData INNER JOIN cmsPropertyType ON cmsPropertyType.id = cmsPropertyData.propertytypeid"+ + " INNER JOIN cmsDataType ON cmsPropertyType.dataTypeId = cmsDataType.nodeId WHERE cmsDataType.controlId = '"+ Constants.PropertyEditors.UploadField +"') as property") + .On("umbracoNode.id = property.contentNodeId"); } return sql; } - protected virtual Sql GetBaseWhere(Func baseQuery, bool isContent, Guid id) + protected virtual Sql GetBaseWhere(Func baseQuery, bool isContentOrMedia, Guid id) { - var sql = baseQuery(isContent) - .Where("main.nodeObjectType = @NodeObjectType", new {NodeObjectType = id}); + var sql = baseQuery(isContentOrMedia) + .Where("umbracoNode.nodeObjectType = @NodeObjectType", new { NodeObjectType = id }); return sql; } - protected virtual Sql GetBaseWhere(Func baseQuery, bool isContent, int id) + protected virtual Sql GetBaseWhere(Func baseQuery, bool isContentOrMedia, int id) { - var sql = baseQuery(isContent) - .Where("main.id = @Id", new {Id = id}) - .Append(GetGroupBy(isContent)); + var sql = baseQuery(isContentOrMedia) + .Where("umbracoNode.id = @Id", new { Id = id }) + .Append(GetGroupBy(isContentOrMedia)); return sql; } - protected virtual Sql GetBaseWhere(Func baseQuery, bool isContent, Guid objectId, int id) + protected virtual Sql GetBaseWhere(Func baseQuery, bool isContentOrMedia, Guid objectId, int id) { - var sql = baseQuery(isContent) - .Where("main.id = @Id AND main.nodeObjectType = @NodeObjectType", + var sql = baseQuery(isContentOrMedia) + .Where("umbracoNode.id = @Id AND umbracoNode.nodeObjectType = @NodeObjectType", new {Id = id, NodeObjectType = objectId}); return sql; } - protected virtual Sql GetGroupBy(bool isContent) + protected virtual Sql GetGroupBy(bool isContentOrMedia) { var columns = new List { - "main.id", - "main.trashed", - "main.parentID", - "main.nodeUser", - "main.level", - "main.path", - "main.sortOrder", - "main.uniqueID", - "main.text", - "main.nodeObjectType", - "main.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) + if (isContentOrMedia) { columns.Add("published.versionId"); columns.Add("latest.versionId"); + columns.Add("contenttype.alias"); + columns.Add("contenttype.icon"); + columns.Add("property.dataNvarchar"); } var sql = new Sql() .GroupBy(columns.ToArray()) - .OrderBy("main.sortOrder"); + .OrderBy("umbracoNode.sortOrder"); return sql; } @@ -246,6 +262,15 @@ namespace Umbraco.Core.Persistence.Repositories [Column("newestVerison")] public Guid NewestVersion { get; set; } + + [Column("alias")] + public string Alias { get; set; } + + [Column("icon")] + public string IconUrl { get; set; } + + [Column("umbracoFile")] + public string UmbracoFile { get; set; } } #endregion } diff --git a/src/Umbraco.Core/Services/EntityService.cs b/src/Umbraco.Core/Services/EntityService.cs index 5e0b68b239..e8bd53e9c6 100644 --- a/src/Umbraco.Core/Services/EntityService.cs +++ b/src/Umbraco.Core/Services/EntityService.cs @@ -164,13 +164,13 @@ namespace Umbraco.Core.Services /// /// Gets a collection of children by the parents Id /// - /// Id of the parent to retrieve children for + /// Id of the parent to retrieve children for /// An enumerable list of objects - public virtual IEnumerable GetChildren(int id) + public virtual IEnumerable GetChildren(int parentId) { using (var repository = _repositoryFactory.CreateEntityRepository(_uowProvider.GetUnitOfWork())) { - var query = Query.Builder.Where(x => x.ParentId == id); + var query = Query.Builder.Where(x => x.ParentId == parentId); var contents = repository.GetByQuery(query); return contents; @@ -180,15 +180,15 @@ namespace Umbraco.Core.Services /// /// Gets a collection of children by the parents Id and UmbracoObjectType /// - /// Id of the parent to retrieve children for + /// Id of the parent to retrieve children for /// UmbracoObjectType of the children to retrieve /// An enumerable list of objects - public virtual IEnumerable GetChildren(int id, UmbracoObjectTypes umbracoObjectType) + public virtual IEnumerable GetChildren(int parentId, UmbracoObjectTypes umbracoObjectType) { var objectTypeId = umbracoObjectType.GetGuid(); using (var repository = _repositoryFactory.CreateEntityRepository(_uowProvider.GetUnitOfWork())) { - var query = Query.Builder.Where(x => x.ParentId == id); + var query = Query.Builder.Where(x => x.ParentId == parentId); var contents = repository.GetByQuery(query, objectTypeId); return contents; diff --git a/src/Umbraco.Tests/Services/BaseServiceTest.cs b/src/Umbraco.Tests/Services/BaseServiceTest.cs index 56b94dcbf1..cd4318b5a6 100644 --- a/src/Umbraco.Tests/Services/BaseServiceTest.cs +++ b/src/Umbraco.Tests/Services/BaseServiceTest.cs @@ -1,11 +1,8 @@ using System; using NUnit.Framework; -using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.TestHelpers.Entities; -using umbraco.editorControls.tinyMCE3; -using umbraco.interfaces; namespace Umbraco.Tests.Services { @@ -26,7 +23,7 @@ namespace Umbraco.Tests.Services base.TearDown(); } - public void CreateTestData() + public virtual void CreateTestData() { //NOTE Maybe not the best way to create/save test data as we are using the services, which are being tested. diff --git a/src/Umbraco.Tests/Services/EntityServiceTests.cs b/src/Umbraco.Tests/Services/EntityServiceTests.cs index abf541883f..dce19cb80a 100644 --- a/src/Umbraco.Tests/Services/EntityServiceTests.cs +++ b/src/Umbraco.Tests/Services/EntityServiceTests.cs @@ -3,6 +3,7 @@ using System.Linq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Models; +using Umbraco.Tests.TestHelpers.Entities; namespace Umbraco.Tests.Services { @@ -61,6 +62,18 @@ namespace Umbraco.Tests.Services Assert.That(entities.Any(x => x.Trashed), Is.True); } + [Test] + public void EntityService_Can_Get_Child_Content_By_ParentId_And_UmbracoObjectType() + { + var service = ServiceContext.EntityService; + + var entities = service.GetChildren(-1, UmbracoObjectTypes.Document); + + Assert.That(entities.Any(), Is.True); + Assert.That(entities.Count(), Is.EqualTo(1)); + Assert.That(entities.Any(x => x.Trashed), Is.False); + } + [Test] public void EntityService_Throws_When_Getting_All_With_Invalid_Type() { @@ -105,5 +118,37 @@ namespace Umbraco.Tests.Services Assert.That(entities.Any(), Is.True); Assert.That(entities.Count(), Is.EqualTo(1)); } + + [Test] + public void EntityService_Can_Find_All_Media_By_UmbracoObjectTypes() + { + var service = ServiceContext.EntityService; + + var entities = service.GetAll(UmbracoObjectTypes.Media); + + Assert.That(entities.Any(), Is.True); + Assert.That(entities.Count(), Is.EqualTo(3)); + Assert.That(entities.Any(x => ((UmbracoEntity)x).UmbracoFile != string.Empty), Is.True); + } + + public override void CreateTestData() + { + base.CreateTestData(); + + //Create and Save folder-Media -> 1050 + var folderMediaType = ServiceContext.ContentTypeService.GetMediaType(1031); + var folder = MockedMedia.CreateMediaFolder(folderMediaType, -1); + ServiceContext.MediaService.Save(folder, 0); + + //Create and Save image-Media -> 1051 + var imageMediaType = ServiceContext.ContentTypeService.GetMediaType(1032); + var image = MockedMedia.CreateMediaImage(imageMediaType, folder.Id); + ServiceContext.MediaService.Save(image, 0); + + //Create and Save file-Media -> 1052 + var fileMediaType = ServiceContext.ContentTypeService.GetMediaType(1033); + var file = MockedMedia.CreateMediaFile(fileMediaType, folder.Id); + ServiceContext.MediaService.Save(file, 0); + } } } \ No newline at end of file diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseMediaTree.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseMediaTree.cs index be85585f8e..98569162a3 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseMediaTree.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseMediaTree.cs @@ -1,26 +1,30 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Text; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; using umbraco.BasePages; using umbraco.BusinessLogic; using umbraco.BusinessLogic.Actions; -using umbraco.cms.businesslogic.media; -using umbraco.cms.businesslogic.property; using umbraco.interfaces; using Umbraco.Core; +using Media = umbraco.cms.businesslogic.media.Media; +using Property = umbraco.cms.businesslogic.property.Property; namespace umbraco.cms.presentation.Trees { public abstract class BaseMediaTree : BaseTree { + private DisposableTimer _timer; + private User _user; + public BaseMediaTree(string application) : base(application) { } - private User m_user; - /// /// Returns the current User. This ensures that we don't instantiate a new User object /// each time. @@ -29,11 +33,10 @@ namespace umbraco.cms.presentation.Trees { get { - return (m_user == null ? (m_user = UmbracoEnsuredPage.CurrentUser) : m_user); + return (_user == null ? (_user = UmbracoEnsuredPage.CurrentUser) : _user); } } - public override void RenderJS(ref StringBuilder Javascript) { if (!string.IsNullOrEmpty(this.FunctionToCall)) @@ -53,8 +56,83 @@ function openMedia(id) { } } + //Updated Render method for improved performance, but currently not usable because of backwards compatibility + //with the OnBeforeTreeRender/OnAfterTreeRender events, which sends an array for legacy Media items. + /*public override void Render(ref XmlTree tree) + { + _timer = DisposableTimer.Start(x => LogHelper.Info("Media tree loaded" + " (took " + x + "ms)")); + + var service = base.Services.EntityService; + var entities = service.GetChildren(m_id, UmbracoObjectTypes.Media); + + var args = new TreeEventArgs(tree); + OnBeforeTreeRender(entities, args); + + foreach (UmbracoEntity entity in entities) + { + XmlTreeNode xNode = XmlTreeNode.Create(this); + xNode.NodeID = entity.Id.ToString(CultureInfo.InvariantCulture); + xNode.Text = entity.Name; + + xNode.HasChildren = entity.HasChildren; + xNode.Source = this.IsDialog ? GetTreeDialogUrl(entity.Id) : GetTreeServiceUrl(entity.Id); + + xNode.Icon = entity.ContentTypeIconUrl; + xNode.OpenIcon = entity.ContentTypeIconUrl; + + xNode.Menu = this.ShowContextMenu ? new List(new IAction[] { ActionRefresh.Instance }) : null; + + if (IsDialog == false) + { + xNode.Action = "javascript:openMedia(" + entity.Id + ");"; + } + else + { + if (this.DialogMode == TreeDialogModes.fulllink) + { + if (string.IsNullOrEmpty(entity.UmbracoFile) == false) + { + xNode.Action = "javascript:openMedia('" + entity.UmbracoFile + "');"; + } + else + { + if (string.Equals(entity.ContentTypeAlias, Constants.Conventions.MediaTypes.Folder, StringComparison.OrdinalIgnoreCase)) + { + xNode.Action = "javascript:jQuery('.umbTree #" + entity.Id.ToString(CultureInfo.InvariantCulture) + "').click();"; + } + else + { + xNode.Action = null; + xNode.Style.DimNode(); + } + } + } + else + { + xNode.Action = "javascript:openMedia('" + entity.Id.ToString(CultureInfo.InvariantCulture) + "');"; + } + } + + OnBeforeNodeRender(ref tree, ref xNode, EventArgs.Empty); + if (xNode != null) + { + tree.Add(xNode); + OnAfterNodeRender(ref tree, ref xNode, EventArgs.Empty); + } + } + + //stop the timer and log the output + _timer.Dispose(); + + OnAfterTreeRender(entities, args); + }*/ + public override void Render(ref XmlTree tree) { +#if Debug + _timer = DisposableTimer.Start(x => LogHelper.Info("Media tree loaded" + " (took " + x + "ms)")); +#endif + Media[] docs = new Media(m_id).Children; var args = new TreeEventArgs(tree); @@ -124,10 +202,13 @@ function openMedia(id) { OnAfterNodeRender(ref tree, ref xNode, EventArgs.Empty); } } +#if Debug + _timer.Dispose(); +#endif OnAfterTreeRender(docs, args); } - /// + /// /// Returns the value for a link in WYSIWYG mode, by default only media items that have a /// DataTypeUploadField are linkable, however, a custom tree can be created which overrides /// this method, or another GUID for a custom data type can be added to the LinkableMediaDataTypes diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadMedia.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadMedia.cs index 90135d9d15..80a1e4d307 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadMedia.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/Trees/loadMedia.cs @@ -1,28 +1,9 @@ using System; -using System.Collections; using System.Collections.Generic; -using System.Data; -using System.IO; -using System.Text; -using System.Web; -using System.Xml; -using System.Configuration; using umbraco.BasePages; -using umbraco.BusinessLogic; using umbraco.businesslogic; -using umbraco.cms.businesslogic; -using umbraco.cms.businesslogic.cache; -using umbraco.cms.businesslogic.contentitem; -using umbraco.cms.businesslogic.datatype; -using umbraco.cms.businesslogic.language; -using umbraco.cms.businesslogic.media; -using umbraco.cms.businesslogic.member; -using umbraco.cms.businesslogic.property; -using umbraco.cms.businesslogic.web; using umbraco.interfaces; -using umbraco.DataLayer; using umbraco.BusinessLogic.Actions; -using umbraco.BusinessLogic.Utils; using umbraco.cms.presentation.Trees; using Umbraco.Core;