using System; using System.Collections.Generic; using System.Linq; using System.Web; using Umbraco.Core.Auditing; using Umbraco.Core.Models; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Persistence.UnitOfWork; namespace Umbraco.Core.Services { /// /// Represents the Media Service, which is an easy access to operations involving /// public class MediaService : IMediaService { private readonly IUnitOfWork _unitOfWork; private readonly IMediaRepository _mediaRepository; private readonly IMediaTypeRepository _mediaTypeRepository; private readonly IUserService _userService; private HttpContextBase _httpContext; public MediaService() : this(new PetaPocoUnitOfWorkProvider()) { } public MediaService(IUnitOfWorkProvider provider) { _unitOfWork = provider.GetUnitOfWork(); _mediaRepository = RepositoryResolver.Current.Factory.CreateMediaRepository(_unitOfWork); _mediaTypeRepository = RepositoryResolver.Current.Factory.CreateMediaTypeRepository(_unitOfWork); } internal MediaService(IUnitOfWorkProvider provider, IUserService userService) { _unitOfWork = provider.GetUnitOfWork(); _mediaRepository = RepositoryResolver.Current.Factory.CreateMediaRepository(_unitOfWork); _mediaTypeRepository = RepositoryResolver.Current.Factory.CreateMediaTypeRepository(_unitOfWork); _userService = userService; } /// /// Creates an object using the alias of the /// that this Media is based on. /// /// Id of Parent for the new Media item /// Alias of the /// Optional id of the user creating the media item /// public IMedia CreateContent(int parentId, string mediaTypeAlias, int userId = -1) { var repository = _mediaTypeRepository; var query = Query.Builder.Where(x => x.Alias == mediaTypeAlias); var mediaTypes = repository.GetByQuery(query); if (!mediaTypes.Any()) throw new Exception(string.Format("No ContentType matching the passed in Alias: '{0}' was found", mediaTypeAlias)); var contentType = mediaTypes.First(); if (contentType == null) throw new Exception(string.Format("ContentType matching the passed in Alias: '{0}' was null", mediaTypeAlias)); IMedia media = null; var e = new NewEventArgs { Alias = mediaTypeAlias, ParentId = parentId }; if (Creating != null) Creating(media, e); if (!e.Cancel) { media = new Models.Media(parentId, contentType); SetUser(media, userId); if (Created != null) Created(media, e); Audit.Add(AuditTypes.New, "", media.CreatorId, media.Id); } return media; } /// /// Gets an object by Id /// /// Id of the Content to retrieve /// public IMedia GetById(int id) { var repository = _mediaRepository; return repository.Get(id); } /// /// Gets a collection of objects by Parent Id /// /// Id of the Parent to retrieve Children from /// An Enumerable list of objects public IEnumerable GetChildren(int id) { var repository = _mediaRepository; var query = Query.Builder.Where(x => x.ParentId == id); var medias = repository.GetByQuery(query); return medias; } /// /// Gets descendants of a object by its Id /// /// Id of the Parent to retrieve descendants from /// An Enumerable flat list of objects public IEnumerable GetDescendants(int id) { var repository = _mediaRepository; var media = repository.Get(id); var query = Query.Builder.Where(x => x.Path.StartsWith(media.Path)); var medias = repository.GetByQuery(query); return medias; } /// /// Gets a collection of objects by the Id of the /// /// Id of the /// An Enumerable list of objects public IEnumerable GetMediaOfMediaType(int id) { var repository = _mediaRepository; var query = Query.Builder.Where(x => x.ContentTypeId == id); var medias = repository.GetByQuery(query); return medias; } /// /// Gets a collection of objects, which reside at the first level / root /// /// An Enumerable list of objects public IEnumerable GetRootMedia() { var repository = _mediaRepository; var query = Query.Builder.Where(x => x.ParentId == -1); var medias = repository.GetByQuery(query); return medias; } /// /// Gets a collection of an objects, which resides in the Recycle Bin /// /// An Enumerable list of objects public IEnumerable GetMediaInRecycleBin() { var repository = _mediaRepository; var query = Query.Builder.Where(x => x.ParentId == -20); var medias = repository.GetByQuery(query); return medias; } /// /// Moves an object to a new location /// /// The to move /// Id of the Media's new Parent /// Id of the User moving the Media public void Move(IMedia media, int parentId, int userId = -1) { var e = new MoveEventArgs { ParentId = parentId }; if (Moving != null) Moving(media, e); if (!e.Cancel) { media.ParentId = parentId; Save(media, userId); if (Moved != null) Moved(media, e); Audit.Add(AuditTypes.Move, "Move Media performed by user", userId == -1 ? 0 : userId, media.Id); } } /// /// Deletes an object by moving it to the Recycle Bin /// /// The to delete /// Id of the User deleting the Media public void MoveToRecycleBin(IMedia media, int userId = -1) { //TODO If media item has children those should also be moved to the recycle bin as well var e = new MoveEventArgs { ParentId = -20 }; if (Trashing != null) Trashing(media, e); if (!e.Cancel) { var repository = _mediaRepository; ((Core.Models.Media) media).ChangeTrashedState(true); repository.AddOrUpdate(media); _unitOfWork.Commit(); if (Trashed != null) Trashed(media, e); Audit.Add(AuditTypes.Move, "Move Media to Recycle Bin performed by user", userId == -1 ? 0 : userId, media.Id); } } /// /// Empties the Recycle Bin by deleting all that resides in the bin /// public void EmptyRecycleBin() { var repository = _mediaRepository; var query = Query.Builder.Where(x => x.ParentId == -20); var contents = repository.GetByQuery(query); foreach (var content in contents) { repository.Delete(content); } _unitOfWork.Commit(); Audit.Add(AuditTypes.Delete, "Empty Recycle Bin performed by user", 0, -20); } /// /// Deletes all media of specified type. All children of deleted media is moved to Recycle Bin. /// /// This needs extra care and attention as its potentially a dangerous and extensive operation /// Id of the /// Optional id of the user deleting the media public void DeleteMediaOfType(int mediaTypeId, int userId = -1) { var repository = _mediaRepository; //NOTE What about media that has the contenttype as part of its composition? //The ContentType has to be removed from the composition somehow as it would otherwise break //Dbl.check+test that the ContentType's Id is removed from the ContentType2ContentType table var query = Query.Builder.Where(x => x.ContentTypeId == mediaTypeId); var contents = repository.GetByQuery(query); var e = new DeleteEventArgs { Id = mediaTypeId }; if (Deleting != null) Deleting(contents, e); if (!e.Cancel) { foreach (var content in contents) { ((Core.Models.Media) content).ChangeTrashedState(true); repository.AddOrUpdate(content); } _unitOfWork.Commit(); if (Deleted != null) Deleted(contents, e); Audit.Add(AuditTypes.Delete, "Delete Media items by Type performed by user", userId == -1 ? 0 : userId, -1); } } /// /// Permanently deletes an object /// /// /// Please note that this method will completely remove the Media from the database, /// but current not from the file system. /// /// The to delete /// Id of the User deleting the Media public void Delete(IMedia media, int userId = -1) { var e = new DeleteEventArgs { Id = media.Id }; if (Deleting != null) Deleting(media, e); if (!e.Cancel) { _mediaRepository.Delete(media); _unitOfWork.Commit(); if (Deleted != null) Deleted(media, e); Audit.Add(AuditTypes.Delete, "Delete Media performed by user", userId == -1 ? 0 : userId, media.Id); } } /// /// Saves a single object /// /// The to save /// Id of the User saving the Content public void Save(IMedia media, int userId = -1) { var e = new SaveEventArgs(); if (Saving != null) Saving(media, e); if (!e.Cancel) { SetUser(media, userId); _mediaRepository.AddOrUpdate(media); _unitOfWork.Commit(); if (Saved != null) Saved(media, e); Audit.Add(AuditTypes.Save, "Save Media performed by user", media.CreatorId, media.Id); } } /// /// Saves a collection of objects /// /// Collection of to save /// Id of the User saving the Content public void Save(IEnumerable medias, int userId = -1) { var e = new SaveEventArgs(); if (Saving != null) Saving(medias, e); if (!e.Cancel) { foreach (var media in medias) { SetUser(media, userId); _mediaRepository.AddOrUpdate(media); } _unitOfWork.Commit(); if (Saved != null) Saved(medias, e); Audit.Add(AuditTypes.Save, "Save Media items performed by user", userId == -1 ? 0 : userId, -1); } } /// /// Internal method to set the HttpContextBase for testing. /// /// internal void SetHttpContext(HttpContextBase httpContext) { _httpContext = httpContext; } /// /// Updates a media object with the User (id), who created the content. /// /// object to update /// Optional Id of the User private void SetUser(IMedia media, int userId) { if (userId > -1) { //If a user id was passed in we use that media.CreatorId = userId; } else if (UserServiceOrContext()) { var profile = _httpContext == null ? _userService.GetCurrentBackOfficeUser() : _userService.GetCurrentBackOfficeUser(_httpContext); media.CreatorId = profile.Id.SafeCast(); } else { //Otherwise we default to Admin user, which should always exist (almost always) media.CreatorId = 0; } } private bool UserServiceOrContext() { return _userService != null && (HttpContext.Current != null || _httpContext != null); } #region Event Handlers /// /// Occurs before Delete /// public static event EventHandler Deleting; /// /// Occurs after Delete /// public static event EventHandler Deleted; /// /// Occurs before Save /// public static event EventHandler Saving; /// /// Occurs after Save /// public static event EventHandler Saved; /// /// Occurs before Create /// public static event EventHandler Creating; /// /// Occurs after Create /// public static event EventHandler Created; /// /// Occurs before Content is moved to Recycle Bin /// public static event EventHandler Trashing; /// /// Occurs after Content is moved to Recycle Bin /// public static event EventHandler Trashed; /// /// Occurs before Move /// public static event EventHandler Moving; /// /// Occurs after Move /// public static event EventHandler Moved; #endregion } }