Merge remote-tracking branch 'refs/remotes/umbraco/dev-v7' into dev-v7

This commit is contained in:
bjarnef
2016-01-27 18:55:38 +01:00
49 changed files with 779 additions and 417 deletions

View File

@@ -11,6 +11,10 @@ namespace Umbraco.Core.Cache
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <typeparam name="TId"></typeparam>
/// <remarks>
/// This cache policy uses sliding expiration and caches instances for 5 minutes. However if allow zero count is true, then we use the
/// default policy with no expiry.
/// </remarks>
internal class DefaultRepositoryCachePolicy<TEntity, TId> : DisposableObject, IRepositoryCachePolicy<TEntity, TId>
where TEntity : class, IAggregateRoot
{
@@ -54,7 +58,9 @@ namespace Umbraco.Core.Cache
//just to be safe, we cannot cache an item without an identity
if (entity.HasIdentity)
{
Cache.InsertCacheItem(GetCacheIdKey(entity.Id), () => entity);
Cache.InsertCacheItem(GetCacheIdKey(entity.Id), () => entity,
timeout: TimeSpan.FromMinutes(5),
isSliding: true);
}
//If there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
@@ -225,7 +231,9 @@ namespace Umbraco.Core.Cache
//just to be safe, we cannot cache an item without an identity
if (entity.HasIdentity)
{
Cache.InsertCacheItem(cacheKey, () => entity);
Cache.InsertCacheItem(cacheKey, () => entity,
timeout: TimeSpan.FromMinutes(5),
isSliding: true);
}
});
}
@@ -244,6 +252,7 @@ namespace Umbraco.Core.Cache
{
//there was nothing returned but we want to cache a zero count result so add an TEntity[] to the cache
// to signify that there is a zero count cache
//NOTE: Don't set expiry/sliding for a zero count
Cache.InsertCacheItem(GetCacheTypeKey(), () => new TEntity[] {});
}
else
@@ -256,7 +265,9 @@ namespace Umbraco.Core.Cache
//just to be safe, we cannot cache an item without an identity
if (localCopy.HasIdentity)
{
Cache.InsertCacheItem(GetCacheIdKey(entity.Id), () => localCopy);
Cache.InsertCacheItem(GetCacheIdKey(entity.Id), () => localCopy,
timeout: TimeSpan.FromMinutes(5),
isSliding: true);
}
}
}

View File

@@ -11,6 +11,10 @@ namespace Umbraco.Core.Cache
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <typeparam name="TId"></typeparam>
/// <remarks>
/// This caching policy has no sliding expiration but uses the default ObjectCache.InfiniteAbsoluteExpiration as it's timeout, so it
/// should not leave the cache unless the cache memory is exceeded and it gets thrown out.
/// </remarks>
internal class FullDataSetRepositoryCachePolicy<TEntity, TId> : DefaultRepositoryCachePolicy<TEntity, TId>
where TEntity : class, IAggregateRoot
{

View File

@@ -98,45 +98,10 @@ namespace Umbraco.Core.Models.PublishedContent
#endregion
#region Cache
// these methods are called by ContentTypeCacheRefresher and DataTypeCacheRefresher
internal static void ClearAll()
{
Logging.LogHelper.Debug<PublishedContentType>("Clear all.");
// ok and faster to do it by types, assuming noone else caches PublishedContentType instances
//ApplicationContext.Current.ApplicationCache.ClearStaticCacheByKeySearch("PublishedContentType_");
ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheObjectTypes<PublishedContentType>();
}
internal static void ClearContentType(int id)
{
Logging.LogHelper.Debug<PublishedContentType>("Clear content type w/id {0}.", () => id);
// requires a predicate because the key does not contain the ID
// faster than key strings comparisons anyway
ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheObjectTypes<PublishedContentType>(
(key, value) => value.Id == id);
}
internal static void ClearDataType(int id)
{
Logging.LogHelper.Debug<PublishedContentType>("Clear data type w/id {0}.", () => id);
// there is no recursion to handle here because a PublishedContentType contains *all* its
// properties ie both its own properties and those that were inherited (it's based upon an
// IContentTypeComposition) and so every PublishedContentType having a property based upon
// the cleared data type, be it local or inherited, will be cleared.
ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheObjectTypes<PublishedContentType>(
(key, value) => value.PropertyTypes.Any(x => x.DataTypeId == id));
}
public static PublishedContentType Get(PublishedItemType itemType, string alias)
{
var key = string.Format("PublishedContentType_{0}_{1}",
itemType.ToString().ToLowerInvariant(), alias.ToLowerInvariant());
var type = ApplicationContext.Current.ApplicationCache.StaticCache.GetCacheItem<PublishedContentType>(key,
() => CreatePublishedContentType(itemType, alias));
var type = CreatePublishedContentType(itemType, alias);
return type;
}
@@ -169,21 +134,8 @@ namespace Umbraco.Core.Models.PublishedContent
return new PublishedContentType(contentType);
}
// for unit tests - changing the callback must reset the cache obviously
private static Func<string, PublishedContentType> _getPublishedContentTypeCallBack;
internal static Func<string, PublishedContentType> GetPublishedContentTypeCallback
{
get { return _getPublishedContentTypeCallBack; }
set
{
// see note above
//ClearAll();
ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheByKeySearch("PublishedContentType_");
_getPublishedContentTypeCallBack = value;
}
}
#endregion
// for unit tests
internal static Func<string, PublishedContentType> GetPublishedContentTypeCallback { get; set; }
}
}

View File

@@ -18,8 +18,11 @@ namespace Umbraco.Core.Persistence.Relators
// Is this the same DictionaryItem as the current one we're processing
if (Current != null && Current.Id == a.Id)
{
// Yes, just add this User2AppDto to the current item's collection
Current.User2AppDtos.Add(p);
if (p.AppAlias.IsNullOrWhiteSpace() == false)
{
// Yes, just add this User2AppDto to the current item's collection
Current.User2AppDtos.Add(p);
}
// Return null to indicate we're not done with this User yet
return null;
@@ -35,7 +38,7 @@ namespace Umbraco.Core.Persistence.Relators
Current = a;
Current.User2AppDtos = new List<User2AppDto>();
//this can be null since we are doing a left join
if (p.AppAlias != null)
if (p.AppAlias.IsNullOrWhiteSpace() == false)
{
Current.User2AppDtos.Add(p);
}

View File

@@ -156,7 +156,7 @@ namespace Umbraco.Core.Persistence.Repositories
"DELETE FROM cmsContentXml WHERE nodeId = @Id",
"DELETE FROM cmsContent WHERE nodeId = @Id",
"DELETE FROM umbracoAccess WHERE nodeId = @Id",
"DELETE FROM umbracoNode WHERE id = @Id"
"DELETE FROM umbracoNode WHERE id = @Id"
};
return list;
}

View File

@@ -85,7 +85,6 @@ namespace Umbraco.Core.Persistence.Repositories
return moveInfo;
}
/// <summary>
/// Returns the content type ids that match the query
/// </summary>

View File

@@ -475,7 +475,7 @@ namespace Umbraco.Core.Persistence.Repositories
if (aliases.Any() == false) return base.GetAll();
//return from base.GetAll, this is all cached
return base.GetAll().Where(x => aliases.Contains(x.Alias));
return base.GetAll().Where(x => aliases.InvariantContains(x.Alias));
}
public IEnumerable<ITemplate> GetChildren(int masterTemplateId)

View File

@@ -910,7 +910,7 @@ namespace Umbraco.Core.Services
new MoveEventArgs<IContent>(evtMsgs, new MoveEventInfo<IContent>(content, originalPath, Constants.System.RecycleBinContent)),
this))
{
return Attempt.Fail(OperationStatus.Cancelled(evtMsgs));
return OperationStatus.Cancelled(evtMsgs);
}
var moveInfo = new List<MoveEventInfo<IContent>>
@@ -957,7 +957,7 @@ namespace Umbraco.Core.Services
Audit(AuditType.Move, "Move Content to Recycle Bin performed by user", userId, content.Id);
return Attempt.Succeed(OperationStatus.Success(evtMsgs));
return OperationStatus.Success(evtMsgs);
}
}
@@ -1080,7 +1080,7 @@ namespace Umbraco.Core.Services
new SaveEventArgs<IContent>(asArray, evtMsgs),
this))
{
return Attempt.Fail(OperationStatus.Cancelled(evtMsgs));
return OperationStatus.Cancelled(evtMsgs);
}
}
using (new WriteLock(Locker))
@@ -1124,7 +1124,7 @@ namespace Umbraco.Core.Services
Audit(AuditType.Save, "Bulk Save content performed by user", userId == -1 ? 0 : userId, Constants.System.Root);
return Attempt.Succeed(OperationStatus.Success(evtMsgs));
return OperationStatus.Success(evtMsgs);
}
}
@@ -1147,7 +1147,7 @@ namespace Umbraco.Core.Services
new DeleteEventArgs<IContent>(content, evtMsgs),
this))
{
return Attempt.Fail(OperationStatus.Cancelled(evtMsgs));
return OperationStatus.Cancelled(evtMsgs);
}
//Make sure that published content is unpublished before being deleted
@@ -1178,7 +1178,7 @@ namespace Umbraco.Core.Services
Audit(AuditType.Delete, "Delete Content performed by user", userId, content.Id);
return Attempt.Succeed(OperationStatus.Success(evtMsgs));
return OperationStatus.Success(evtMsgs);
}
}
@@ -2043,7 +2043,7 @@ namespace Umbraco.Core.Services
new SaveEventArgs<IContent>(content, evtMsgs),
this))
{
return Attempt.Fail(OperationStatus.Cancelled(evtMsgs));
return OperationStatus.Cancelled(evtMsgs);
}
}
@@ -2075,7 +2075,7 @@ namespace Umbraco.Core.Services
Audit(AuditType.Save, "Save Content performed by user", userId, content.Id);
return Attempt.Succeed(OperationStatus.Success(evtMsgs));
return OperationStatus.Success(evtMsgs);
}
}

View File

@@ -44,8 +44,9 @@ namespace Umbraco.Core.Services
#region Containers
public Attempt<int> CreateContentTypeContainer(int parentId, string name, int userId = 0)
public Attempt<OperationStatus<EntityContainer, OperationStatusType>> CreateContentTypeContainer(int parentId, string name, int userId = 0)
{
var evtMsgs = EventMessagesFactory.Get();
var uow = UowProvider.GetUnitOfWork();
using (var repo = RepositoryFactory.CreateEntityContainerRepository(uow, Constants.ObjectTypes.DocumentTypeContainerGuid))
{
@@ -57,20 +58,32 @@ namespace Umbraco.Core.Services
ParentId = parentId,
CreatorId = userId
};
if (SavingContentTypeContainer.IsRaisedEventCancelled(
new SaveEventArgs<EntityContainer>(container, evtMsgs),
this))
{
return Attempt.Fail(new OperationStatus<EntityContainer, OperationStatusType>(container, OperationStatusType.FailedCancelledByEvent, evtMsgs));
}
repo.AddOrUpdate(container);
uow.Commit();
return Attempt.Succeed(container.Id);
SavedContentTypeContainer.RaiseEvent(new SaveEventArgs<EntityContainer>(container, evtMsgs), this);
//TODO: Audit trail ?
return Attempt.Succeed(new OperationStatus<EntityContainer, OperationStatusType>(container, OperationStatusType.Success, evtMsgs));
}
catch (Exception ex)
{
return Attempt<int>.Fail(ex);
return Attempt.Fail(new OperationStatus<EntityContainer, OperationStatusType>(null, OperationStatusType.FailedExceptionThrown, evtMsgs), ex);
}
//TODO: Audit trail ?
}
}
public Attempt<int> CreateMediaTypeContainer(int parentId, string name, int userId = 0)
public Attempt<OperationStatus<EntityContainer, OperationStatusType>> CreateMediaTypeContainer(int parentId, string name, int userId = 0)
{
var evtMsgs = EventMessagesFactory.Get();
var uow = UowProvider.GetUnitOfWork();
using (var repo = RepositoryFactory.CreateEntityContainerRepository(uow, Constants.ObjectTypes.MediaTypeContainerGuid))
{
@@ -82,42 +95,83 @@ namespace Umbraco.Core.Services
ParentId = parentId,
CreatorId = userId
};
if (SavingMediaTypeContainer.IsRaisedEventCancelled(
new SaveEventArgs<EntityContainer>(container, evtMsgs),
this))
{
return Attempt.Fail(new OperationStatus<EntityContainer, OperationStatusType>(container, OperationStatusType.FailedCancelledByEvent, evtMsgs));
}
repo.AddOrUpdate(container);
uow.Commit();
return Attempt.Succeed(container.Id);
SavedMediaTypeContainer.RaiseEvent(new SaveEventArgs<EntityContainer>(container, evtMsgs), this);
//TODO: Audit trail ?
return Attempt.Succeed(new OperationStatus<EntityContainer, OperationStatusType>(container, OperationStatusType.Success, evtMsgs));
}
catch (Exception ex)
{
return Attempt<int>.Fail(ex);
return Attempt.Fail(new OperationStatus<EntityContainer, OperationStatusType>(null, OperationStatusType.FailedExceptionThrown, evtMsgs), ex);
}
//TODO: Audit trail ?
}
}
public void SaveContentTypeContainer(EntityContainer container, int userId = 0)
public Attempt<OperationStatus> SaveContentTypeContainer(EntityContainer container, int userId = 0)
{
SaveContainer(container, Constants.ObjectTypes.DocumentTypeContainerGuid, "document type", userId);
return SaveContainer(
SavingContentTypeContainer, SavedContentTypeContainer,
container, Constants.ObjectTypes.DocumentTypeContainerGuid, "document type", userId);
}
public void SaveMediaTypeContainer(EntityContainer container, int userId = 0)
public Attempt<OperationStatus> SaveMediaTypeContainer(EntityContainer container, int userId = 0)
{
SaveContainer(container, Constants.ObjectTypes.MediaTypeContainerGuid, "media type", userId);
return SaveContainer(
SavingMediaTypeContainer, SavedMediaTypeContainer,
container, Constants.ObjectTypes.MediaTypeContainerGuid, "media type", userId);
}
private void SaveContainer(EntityContainer container, Guid containerObjectType, string objectTypeName, int userId)
private Attempt<OperationStatus> SaveContainer(
TypedEventHandler<IContentTypeService, SaveEventArgs<EntityContainer>> savingEvent,
TypedEventHandler<IContentTypeService, SaveEventArgs<EntityContainer>> savedEvent,
EntityContainer container,
Guid containerObjectType,
string objectTypeName, int userId)
{
var evtMsgs = EventMessagesFactory.Get();
if (container.ContainedObjectType != containerObjectType)
throw new InvalidOperationException("Not a " + objectTypeName + " container.");
{
var ex = new InvalidOperationException("Not a " + objectTypeName + " container.");
return OperationStatus.Exception(evtMsgs, ex);
}
if (container.HasIdentity && container.IsPropertyDirty("ParentId"))
throw new InvalidOperationException("Cannot save a container with a modified parent, move the container instead.");
{
var ex = new InvalidOperationException("Cannot save a container with a modified parent, move the container instead.");
return OperationStatus.Exception(evtMsgs, ex);
}
if (savingEvent.IsRaisedEventCancelled(
new SaveEventArgs<EntityContainer>(container, evtMsgs),
this))
{
return OperationStatus.Cancelled(evtMsgs);
}
var uow = UowProvider.GetUnitOfWork();
using (var repo = RepositoryFactory.CreateEntityContainerRepository(uow, containerObjectType))
{
repo.AddOrUpdate(container);
uow.Commit();
//TODO: Audit trail ?
uow.Commit();
}
savedEvent.RaiseEvent(new SaveEventArgs<EntityContainer>(container, evtMsgs), this);
//TODO: Audit trail ?
return OperationStatus.Success(evtMsgs);
}
public EntityContainer GetContentTypeContainer(int containerId)
@@ -226,28 +280,54 @@ namespace Umbraco.Core.Services
}
}
public void DeleteContentTypeContainer(int containerId, int userId = 0)
public Attempt<OperationStatus> DeleteContentTypeContainer(int containerId, int userId = 0)
{
var evtMsgs = EventMessagesFactory.Get();
var uow = UowProvider.GetUnitOfWork();
using (var repo = RepositoryFactory.CreateEntityContainerRepository(uow, Constants.ObjectTypes.DocumentTypeContainerGuid))
{
var container = repo.Get(containerId);
if (container == null) return;
if (container == null) return OperationStatus.NoOperation(evtMsgs);
if (DeletingContentTypeContainer.IsRaisedEventCancelled(
new DeleteEventArgs<EntityContainer>(container, evtMsgs),
this))
{
return Attempt.Fail(new OperationStatus(OperationStatusType.FailedCancelledByEvent, evtMsgs));
}
repo.Delete(container);
uow.Commit();
DeletedContentTypeContainer.RaiseEvent(new DeleteEventArgs<EntityContainer>(container, evtMsgs), this);
return OperationStatus.Success(evtMsgs);
//TODO: Audit trail ?
}
}
public void DeleteMediaTypeContainer(int containerId, int userId = 0)
public Attempt<OperationStatus> DeleteMediaTypeContainer(int containerId, int userId = 0)
{
var evtMsgs = EventMessagesFactory.Get();
var uow = UowProvider.GetUnitOfWork();
using (var repo = RepositoryFactory.CreateEntityContainerRepository(uow, Constants.ObjectTypes.MediaTypeContainerGuid))
{
var container = repo.Get(containerId);
if (container == null) return;
if (container == null) return OperationStatus.NoOperation(evtMsgs);
if (DeletingMediaTypeContainer.IsRaisedEventCancelled(
new DeleteEventArgs<EntityContainer>(container, evtMsgs),
this))
{
return Attempt.Fail(new OperationStatus(OperationStatusType.FailedCancelledByEvent, evtMsgs));
}
repo.Delete(container);
uow.Commit();
DeletedMediaTypeContainer.RaiseEvent(new DeleteEventArgs<EntityContainer>(container, evtMsgs), this);
return OperationStatus.Success(evtMsgs);
//TODO: Audit trail ?
}
}
@@ -1172,13 +1252,23 @@ namespace Umbraco.Core.Services
uow.Commit();
}
}
#region Event Handlers
/// <summary>
/// Occurs before Delete
/// </summary>
public static event TypedEventHandler<IContentTypeService, DeleteEventArgs<IContentType>> DeletingContentType;
public static event TypedEventHandler<IContentTypeService, SaveEventArgs<EntityContainer>> SavingContentTypeContainer;
public static event TypedEventHandler<IContentTypeService, SaveEventArgs<EntityContainer>> SavedContentTypeContainer;
public static event TypedEventHandler<IContentTypeService, DeleteEventArgs<EntityContainer>> DeletingContentTypeContainer;
public static event TypedEventHandler<IContentTypeService, DeleteEventArgs<EntityContainer>> DeletedContentTypeContainer;
public static event TypedEventHandler<IContentTypeService, SaveEventArgs<EntityContainer>> SavingMediaTypeContainer;
public static event TypedEventHandler<IContentTypeService, SaveEventArgs<EntityContainer>> SavedMediaTypeContainer;
public static event TypedEventHandler<IContentTypeService, DeleteEventArgs<EntityContainer>> DeletingMediaTypeContainer;
public static event TypedEventHandler<IContentTypeService, DeleteEventArgs<EntityContainer>> DeletedMediaTypeContainer;
/// <summary>
/// Occurs before Delete
/// </summary>
public static event TypedEventHandler<IContentTypeService, DeleteEventArgs<IContentType>> DeletingContentType;
/// <summary>
/// Occurs after Delete

View File

@@ -29,8 +29,9 @@ namespace Umbraco.Core.Services
#region Containers
public Attempt<int> CreateContainer(int parentId, string name, int userId = 0)
public Attempt<OperationStatus<EntityContainer, OperationStatusType>> CreateContainer(int parentId, string name, int userId = 0)
{
var evtMsgs = EventMessagesFactory.Get();
var uow = UowProvider.GetUnitOfWork();
using (var repo = RepositoryFactory.CreateEntityContainerRepository(uow, Constants.ObjectTypes.DataTypeContainerGuid))
{
@@ -42,15 +43,26 @@ namespace Umbraco.Core.Services
ParentId = parentId,
CreatorId = userId
};
if (SavingContainer.IsRaisedEventCancelled(
new SaveEventArgs<EntityContainer>(container, evtMsgs),
this))
{
return Attempt.Fail(new OperationStatus<EntityContainer, OperationStatusType>(container, OperationStatusType.FailedCancelledByEvent, evtMsgs));
}
repo.AddOrUpdate(container);
uow.Commit();
return Attempt.Succeed(container.Id);
SavedContainer.RaiseEvent(new SaveEventArgs<EntityContainer>(container, evtMsgs), this);
//TODO: Audit trail ?
return Attempt.Succeed(new OperationStatus<EntityContainer, OperationStatusType>(container, OperationStatusType.Success, evtMsgs));
}
catch (Exception ex)
{
return Attempt<int>.Fail(ex);
return Attempt.Fail(new OperationStatus<EntityContainer, OperationStatusType>(null, OperationStatusType.FailedExceptionThrown, evtMsgs), ex);
}
//TODO: Audit trail ?
}
}
@@ -107,31 +119,65 @@ namespace Umbraco.Core.Services
}
}
public void SaveContainer(EntityContainer container, int userId = 0)
public Attempt<OperationStatus> SaveContainer(EntityContainer container, int userId = 0)
{
if (container.ContainedObjectType != Constants.ObjectTypes.DataTypeGuid)
throw new InvalidOperationException("Not a data type container.");
var evtMsgs = EventMessagesFactory.Get();
if (container.ContainedObjectType != Constants.ObjectTypes.DataTypeGuid)
{
var ex = new InvalidOperationException("Not a " + Constants.ObjectTypes.DataTypeGuid + " container.");
return OperationStatus.Exception(evtMsgs, ex);
}
if (container.HasIdentity && container.IsPropertyDirty("ParentId"))
throw new InvalidOperationException("Cannot save a container with a modified parent, move the container instead.");
{
var ex = new InvalidOperationException("Cannot save a container with a modified parent, move the container instead.");
return OperationStatus.Exception(evtMsgs, ex);
}
if (SavingContainer.IsRaisedEventCancelled(
new SaveEventArgs<EntityContainer>(container, evtMsgs),
this))
{
return OperationStatus.Cancelled(evtMsgs);
}
var uow = UowProvider.GetUnitOfWork();
using (var repo = RepositoryFactory.CreateEntityContainerRepository(uow, Constants.ObjectTypes.DataTypeContainerGuid))
{
repo.AddOrUpdate(container);
uow.Commit();
//TODO: Audit trail ?
}
SavedContainer.RaiseEvent(new SaveEventArgs<EntityContainer>(container, evtMsgs), this);
//TODO: Audit trail ?
return OperationStatus.Success(evtMsgs);
}
public void DeleteContainer(int containerId, int userId = 0)
public Attempt<OperationStatus> DeleteContainer(int containerId, int userId = 0)
{
var evtMsgs = EventMessagesFactory.Get();
var uow = UowProvider.GetUnitOfWork();
using (var repo = RepositoryFactory.CreateEntityContainerRepository(uow, Constants.ObjectTypes.DataTypeContainerGuid))
{
var container = repo.Get(containerId);
if (container == null) return;
if (container == null) return OperationStatus.NoOperation(evtMsgs);
if (DeletingContainer.IsRaisedEventCancelled(
new DeleteEventArgs<EntityContainer>(container, evtMsgs),
this))
{
return Attempt.Fail(new OperationStatus(OperationStatusType.FailedCancelledByEvent, evtMsgs));
}
repo.Delete(container);
uow.Commit();
DeletedContainer.RaiseEvent(new DeleteEventArgs<EntityContainer>(container, evtMsgs), this);
return OperationStatus.Success(evtMsgs);
//TODO: Audit trail ?
}
}
@@ -537,6 +583,12 @@ namespace Umbraco.Core.Services
}
#region Event Handlers
public static event TypedEventHandler<IDataTypeService, SaveEventArgs<EntityContainer>> SavingContainer;
public static event TypedEventHandler<IDataTypeService, SaveEventArgs<EntityContainer>> SavedContainer;
public static event TypedEventHandler<IDataTypeService, DeleteEventArgs<EntityContainer>> DeletingContainer;
public static event TypedEventHandler<IDataTypeService, DeleteEventArgs<EntityContainer>> DeletedContainer;
/// <summary>
/// Occurs before Delete
/// </summary>

View File

@@ -33,7 +33,7 @@ namespace Umbraco.Core.Services
new DeleteEventArgs<IDomain>(domain, evtMsgs),
this))
{
return Attempt.Fail(OperationStatus.Cancelled(evtMsgs));
return OperationStatus.Cancelled(evtMsgs);
}
var uow = UowProvider.GetUnitOfWork();
@@ -45,7 +45,7 @@ namespace Umbraco.Core.Services
var args = new DeleteEventArgs<IDomain>(domain, false, evtMsgs);
Deleted.RaiseEvent(args, this);
return Attempt.Succeed(OperationStatus.Success(evtMsgs));
return OperationStatus.Success(evtMsgs);
}
public IDomain GetByName(string name)
@@ -91,7 +91,7 @@ namespace Umbraco.Core.Services
new SaveEventArgs<IDomain>(domainEntity, evtMsgs),
this))
{
return Attempt.Fail(OperationStatus.Cancelled(evtMsgs));
return OperationStatus.Cancelled(evtMsgs);
}
var uow = UowProvider.GetUnitOfWork();
@@ -102,7 +102,7 @@ namespace Umbraco.Core.Services
}
Saved.RaiseEvent(new SaveEventArgs<IDomain>(domainEntity, false, evtMsgs), this);
return Attempt.Succeed(OperationStatus.Success(evtMsgs));
return OperationStatus.Success(evtMsgs);
}
#region Event Handlers

View File

@@ -21,10 +21,11 @@ namespace Umbraco.Core.Services
/// <returns></returns>
Attempt<string[]> ValidateComposition(IContentTypeComposition compo);
Attempt<int> CreateContentTypeContainer(int parentId, string name, int userId = 0);
Attempt<int> CreateMediaTypeContainer(int parentId, string name, int userId = 0);
void SaveContentTypeContainer(EntityContainer container, int userId = 0);
void SaveMediaTypeContainer(EntityContainer container, int userId = 0);
Attempt<OperationStatus<EntityContainer, OperationStatusType>> CreateContentTypeContainer(int parentId, string name, int userId = 0);
Attempt<OperationStatus<EntityContainer, OperationStatusType>> CreateMediaTypeContainer(int parentId, string name, int userId = 0);
Attempt<OperationStatus> SaveContentTypeContainer(EntityContainer container, int userId = 0);
Attempt<OperationStatus> SaveMediaTypeContainer(EntityContainer container, int userId = 0);
EntityContainer GetContentTypeContainer(int containerId);
EntityContainer GetContentTypeContainer(Guid containerId);
IEnumerable<EntityContainer> GetContentTypeContainers(int[] containerIds);
@@ -35,8 +36,8 @@ namespace Umbraco.Core.Services
IEnumerable<EntityContainer> GetMediaTypeContainers(int[] containerIds);
IEnumerable<EntityContainer> GetMediaTypeContainers(string folderName, int level);
IEnumerable<EntityContainer> GetMediaTypeContainers(IMediaType mediaType);
void DeleteMediaTypeContainer(int folderId, int userId = 0);
void DeleteContentTypeContainer(int containerId, int userId = 0);
Attempt<OperationStatus> DeleteMediaTypeContainer(int folderId, int userId = 0);
Attempt<OperationStatus> DeleteContentTypeContainer(int containerId, int userId = 0);
/// <summary>
/// Gets all property type aliases.

View File

@@ -10,14 +10,14 @@ namespace Umbraco.Core.Services
/// </summary>
public interface IDataTypeService : IService
{
Attempt<int> CreateContainer(int parentId, string name, int userId = 0);
void SaveContainer(EntityContainer container, int userId = 0);
Attempt<OperationStatus<EntityContainer, OperationStatusType>> CreateContainer(int parentId, string name, int userId = 0);
Attempt<OperationStatus> SaveContainer(EntityContainer container, int userId = 0);
EntityContainer GetContainer(int containerId);
EntityContainer GetContainer(Guid containerId);
IEnumerable<EntityContainer> GetContainers(string folderName, int level);
IEnumerable<EntityContainer> GetContainers(IDataTypeDefinition dataTypeDefinition);
IEnumerable<EntityContainer> GetContainers(int[] containerIds);
void DeleteContainer(int containerId, int userId = 0);
Attempt<OperationStatus> DeleteContainer(int containerId, int userId = 0);
/// <summary>
/// Gets a <see cref="IDataTypeDefinition"/> by its Name

View File

@@ -747,7 +747,7 @@ namespace Umbraco.Core.Services
if (Deleting.IsRaisedEventCancelled(
new DeleteEventArgs<IMedia>(media, evtMsgs), this))
{
return Attempt.Fail(OperationStatus.Cancelled(evtMsgs));
return OperationStatus.Cancelled(evtMsgs);
}
//Delete children before deleting the 'possible parent'
@@ -772,7 +772,7 @@ namespace Umbraco.Core.Services
Audit(AuditType.Delete, "Delete Media performed by user", userId, media.Id);
return Attempt.Succeed(OperationStatus.Success(evtMsgs));
return OperationStatus.Success(evtMsgs);
}
/// <summary>
@@ -791,7 +791,7 @@ namespace Umbraco.Core.Services
new SaveEventArgs<IMedia>(media, evtMsgs),
this))
{
return Attempt.Fail(OperationStatus.Cancelled(evtMsgs));
return OperationStatus.Cancelled(evtMsgs);
}
}
@@ -816,7 +816,7 @@ namespace Umbraco.Core.Services
Audit(AuditType.Save, "Save Media performed by user", userId, media.Id);
return Attempt.Succeed(OperationStatus.Success(evtMsgs));
return OperationStatus.Success(evtMsgs);
}
/// <summary>
@@ -836,7 +836,7 @@ namespace Umbraco.Core.Services
new SaveEventArgs<IMedia>(asArray, evtMsgs),
this))
{
return Attempt.Fail(OperationStatus.Cancelled(evtMsgs));
return OperationStatus.Cancelled(evtMsgs);
}
}
@@ -864,7 +864,7 @@ namespace Umbraco.Core.Services
Audit(AuditType.Save, "Save Media items performed by user", userId, -1);
return Attempt.Succeed(OperationStatus.Success(evtMsgs));
return OperationStatus.Success(evtMsgs);
}
/// <summary>
@@ -966,7 +966,7 @@ namespace Umbraco.Core.Services
if (Trashing.IsRaisedEventCancelled(
new MoveEventArgs<IMedia>(new MoveEventInfo<IMedia>(media, originalPath, Constants.System.RecycleBinMedia)), this))
{
return Attempt.Fail(OperationStatus.Cancelled(evtMsgs));
return OperationStatus.Cancelled(evtMsgs);
}
var moveInfo = new List<MoveEventInfo<IMedia>>
@@ -1008,7 +1008,7 @@ namespace Umbraco.Core.Services
Audit(AuditType.Move, "Move Media to Recycle Bin performed by user", userId, media.Id);
return Attempt.Succeed(OperationStatus.Success(evtMsgs));
return OperationStatus.Success(evtMsgs);
}
/// <summary>

View File

@@ -45,14 +45,25 @@ namespace Umbraco.Core.Services
#region Static Helper methods
internal static OperationStatus Cancelled(EventMessages eventMessages)
internal static Attempt<OperationStatus> Exception(EventMessages eventMessages, Exception ex)
{
return new OperationStatus(OperationStatusType.FailedCancelledByEvent, eventMessages);
eventMessages.Add(new EventMessage("", ex.Message, EventMessageType.Error));
return Attempt.Fail(new OperationStatus(OperationStatusType.FailedExceptionThrown, eventMessages), ex);
}
internal static OperationStatus Success(EventMessages eventMessages)
internal static Attempt<OperationStatus> Cancelled(EventMessages eventMessages)
{
return new OperationStatus(OperationStatusType.Success, eventMessages);
return Attempt.Fail(new OperationStatus(OperationStatusType.FailedCancelledByEvent, eventMessages));
}
internal static Attempt<OperationStatus> Success(EventMessages eventMessages)
{
return Attempt.Succeed(new OperationStatus(OperationStatusType.Success, eventMessages));
}
internal static Attempt<OperationStatus> NoOperation(EventMessages eventMessages)
{
return Attempt.Succeed(new OperationStatus(OperationStatusType.NoOperation, eventMessages));
}
#endregion

View File

@@ -12,11 +12,21 @@ namespace Umbraco.Core.Services
/// The saving was successful.
/// </summary>
Success = 0,
/// <summary>
/// The saving has been cancelled by a 3rd party add-in
/// </summary>
FailedCancelledByEvent = 14
FailedCancelledByEvent = 14,
/// <summary>
/// Failed, an exception was thrown/handled
/// </summary>
FailedExceptionThrown = 15,
/// <summary>
/// When no operation is executed because it was not needed (i.e. deleting an item that doesn't exist)
/// </summary>
NoOperation = 100,
//TODO: In the future, we might need to add more operations statuses, potentially like 'FailedByPermissions', etc...
}

View File

@@ -480,7 +480,7 @@ namespace Umbraco.Core.Services
_logger.Error<PackagingService>("Could not create folder: " + rootFolder, tryCreateFolder.Exception);
throw tryCreateFolder.Exception;
}
var rootFolderId = tryCreateFolder.Result;
var rootFolderId = tryCreateFolder.Result.Entity.Id;
current = _contentTypeService.GetContentTypeContainer(rootFolderId);
}
@@ -514,7 +514,7 @@ namespace Umbraco.Core.Services
_logger.Error<PackagingService>("Could not create folder: " + folderName, tryCreateFolder.Exception);
throw tryCreateFolder.Exception;
}
return _contentTypeService.GetContentTypeContainer(tryCreateFolder.Result);
return _contentTypeService.GetContentTypeContainer(tryCreateFolder.Result.Entity.Id);
}
private IContentType CreateContentTypeFromXml(XElement documentType)
@@ -982,7 +982,7 @@ namespace Umbraco.Core.Services
_logger.Error<PackagingService>("Could not create folder: " + rootFolder, tryCreateFolder.Exception);
throw tryCreateFolder.Exception;
}
current = _dataTypeService.GetContainer(tryCreateFolder.Result);
current = _dataTypeService.GetContainer(tryCreateFolder.Result.Entity.Id);
}
importedFolders.Add(name, current.Id);
@@ -1015,7 +1015,7 @@ namespace Umbraco.Core.Services
_logger.Error<PackagingService>("Could not create folder: " + folderName, tryCreateFolder.Exception);
throw tryCreateFolder.Exception;
}
return _dataTypeService.GetContainer(tryCreateFolder.Result);
return _dataTypeService.GetContainer(tryCreateFolder.Result.Entity.Id);
}
private void SavePrevaluesFromXml(List<IDataTypeDefinition> dataTypes, IEnumerable<XElement> dataTypeElements)

View File

@@ -183,7 +183,7 @@ namespace Umbraco.Core.Services
new SaveEventArgs<PublicAccessEntry>(entry, evtMsgs),
this))
{
return Attempt.Fail(OperationStatus.Cancelled(evtMsgs));
return OperationStatus.Cancelled(evtMsgs);
}
repo.AddOrUpdate(entry);
@@ -191,7 +191,7 @@ namespace Umbraco.Core.Services
uow.Commit();
Saved.RaiseEvent(new SaveEventArgs<PublicAccessEntry>(entry, false, evtMsgs), this);
return Attempt.Succeed(OperationStatus.Success(evtMsgs));
return OperationStatus.Success(evtMsgs);
}
}
@@ -207,7 +207,7 @@ namespace Umbraco.Core.Services
new SaveEventArgs<PublicAccessEntry>(entry, evtMsgs),
this))
{
return Attempt.Fail(OperationStatus.Cancelled(evtMsgs));
return OperationStatus.Cancelled(evtMsgs);
}
var uow = UowProvider.GetUnitOfWork();
@@ -218,7 +218,7 @@ namespace Umbraco.Core.Services
}
Saved.RaiseEvent(new SaveEventArgs<PublicAccessEntry>(entry, false, evtMsgs), this);
return Attempt.Succeed(OperationStatus.Success(evtMsgs));
return OperationStatus.Success(evtMsgs);
}
/// <summary>
@@ -232,7 +232,7 @@ namespace Umbraco.Core.Services
new DeleteEventArgs<PublicAccessEntry>(entry, evtMsgs),
this))
{
return Attempt.Fail(OperationStatus.Cancelled(evtMsgs));
return OperationStatus.Cancelled(evtMsgs);
}
var uow = UowProvider.GetUnitOfWork();
@@ -243,7 +243,7 @@ namespace Umbraco.Core.Services
}
Deleted.RaiseEvent(new DeleteEventArgs<PublicAccessEntry>(entry, false, evtMsgs), this);
return Attempt.Succeed(OperationStatus.Success(evtMsgs));
return OperationStatus.Success(evtMsgs);
}
/// <summary>

View File

@@ -179,7 +179,7 @@ namespace Umbraco.Tests.Persistence.Repositories
Assert.AreEqual(@"@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@{
" + "\t" + @"Layout = null;
}", template.Content);
}".Lf(), template.Content.Lf());
}
}
@@ -209,7 +209,7 @@ namespace Umbraco.Tests.Persistence.Repositories
Assert.AreEqual(@"@inherits Umbraco.Web.Mvc.UmbracoTemplatePage
@{
" + "\t" + @"Layout = ""test.cshtml"";
}", template2.Content);
}".Lf(), template2.Content.Lf());
}
}

View File

@@ -0,0 +1,28 @@
(function() {
'use strict';
function SelectWhen($timeout) {
function link(scope, el, attr, ctrl) {
attr.$observe("umbSelectWhen", function(newValue) {
if (newValue === "true") {
$timeout(function() {
el.select();
});
}
});
}
var directive = {
restrict: 'A',
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('umbSelectWhen', SelectWhen);
})();

View File

@@ -24,12 +24,10 @@
// scope object, but that would mean we'd have to watch that value too in order to set the outer
// ngModelCtrl.$modelValue. It's seems like less overhead to just do this and not have 2x watches.
scope.lockedFieldForm.lockedField.$modelValue = undefined;
scope.lockedFieldForm.lockedField.$setViewValue(newValue);
scope.lockedFieldForm.lockedField.$render();
scope.lockedFieldForm.lockedField.$render();
}
scope.lockedFieldForm.lockedField.$setViewValue(scope.lockedFieldForm.lockedField.$modelValue);
});
var input = el.find('.umb-locked-field__input');
function activate() {
@@ -57,36 +55,14 @@
scope.lock = function() {
scope.locked = true;
input.unbind("blur");
};
scope.unlock = function() {
scope.locked = false;
autoFocusField();
};
function autoFocusField() {
var onBlurHandler = function() {
scope.$apply(function(){
scope.lock();
});
};
$timeout(function() {
input.focus();
input.select();
input.on("blur", onBlurHandler);
});
}
activate();
scope.$on('$destroy', function() {
input.unbind('blur');
});
}
var directive = {

View File

@@ -1,167 +1,194 @@
(function() {
'use strict';
'use strict';
function MediaGridDirective($filter, mediaHelper) {
function MediaGridDirective($filter, mediaHelper) {
function link(scope, el, attr, ctrl) {
function link(scope, el, attr, ctrl) {
var itemDefaultHeight = 200;
var itemDefaultWidth = 200;
var itemMaxWidth = 200;
var itemMaxHeight = 200;
var itemDefaultHeight = 200;
var itemDefaultWidth = 200;
var itemMaxWidth = 200;
var itemMaxHeight = 200;
var itemMinWidth = 125;
var itemMinHeight = 125;
function activate() {
function activate() {
for (var i = 0; scope.items.length > i; i++) {
var item = scope.items[i];
setItemData(item);
setOriginalSize(item, itemMaxHeight);
}
if (scope.itemMaxWidth) {
itemMaxWidth = scope.itemMaxWidth;
}
if(scope.items.length > 0) {
setFlexValues(scope.items);
}
if (scope.itemMaxHeight) {
itemMaxHeight = scope.itemMaxHeight;
}
}
if (scope.itemMinWidth) {
itemMinWidth = scope.itemMinWidth;
}
function setItemData(item) {
item.isFolder = !mediaHelper.hasFilePropertyType(item);
if(!item.isFolder){
item.thumbnail = mediaHelper.resolveFile(item, true);
item.image = mediaHelper.resolveFile(item, false);
}
}
if (scope.itemMinWidth) {
itemMinHeight = scope.itemMinHeight;
}
function setOriginalSize(item, maxHeight) {
for (var i = 0; scope.items.length > i; i++) {
var item = scope.items[i];
setItemData(item);
setOriginalSize(item, itemMaxHeight);
}
//set to a square by default
item.width = itemDefaultWidth;
item.height = itemDefaultHeight;
item.aspectRatio = 1;
var widthProp = _.find(item.properties, function(v) { return (v.alias === "umbracoWidth"); });
if (widthProp && widthProp.value) {
item.width = parseInt(widthProp.value, 10);
if (isNaN(item.width)) {
item.width = itemDefaultWidth;
}
}
var heightProp = _.find(item.properties, function(v) { return (v.alias === "umbracoHeight"); });
if (heightProp && heightProp.value) {
item.height = parseInt(heightProp.value, 10);
if (isNaN(item.height)) {
item.height = itemDefaultWidth;
}
}
item.aspectRatio = item.width / item.height;
// set max width and height
// landscape
if(item.aspectRatio >= 1) {
if(item.width > itemMaxWidth) {
item.width = itemMaxWidth;
item.height = itemMaxWidth / item.aspectRatio;
}
// portrait
} else {
if(item.height > itemMaxHeight) {
item.height = itemMaxHeight;
item.width = itemMaxHeight * item.aspectRatio;
}
}
}
function setFlexValues(mediaItems) {
var flexSortArray = mediaItems;
var smallestImageWidth = null;
var widestImageAspectRatio = null;
// sort array after image width with the widest image first
flexSortArray = $filter('orderBy')(flexSortArray, 'width', true);
// find widest image aspect ratio
widestImageAspectRatio = flexSortArray[0].aspectRatio;
// find smallest image width
smallestImageWidth = flexSortArray[flexSortArray.length - 1].width;
for (var i = 0; flexSortArray.length > i; i++) {
var mediaItem = flexSortArray[i];
var flex = 1 / (widestImageAspectRatio / mediaItem.aspectRatio);
if (flex === 0) {
flex = 1;
}
var imageMinWidth = smallestImageWidth * flex;
var flexStyle = {
"flex": flex + " 1 " + imageMinWidth + "px",
"max-width": mediaItem.width + "px",
"min-width": "125px"
};
mediaItem.flexStyle = flexStyle;
if (scope.items.length > 0) {
setFlexValues(scope.items);
}
}
}
scope.clickItem = function(item, $event, $index) {
if(scope.onClick) {
scope.onClick(item, $event, $index);
function setItemData(item) {
item.isFolder = !mediaHelper.hasFilePropertyType(item);
if (!item.isFolder) {
item.thumbnail = mediaHelper.resolveFile(item, true);
item.image = mediaHelper.resolveFile(item, false);
}
}
};
scope.clickItemName = function(item, $event, $index) {
if(scope.onClickName) {
scope.onClickName(item, $event, $index);
$event.stopPropagation();
function setOriginalSize(item, maxHeight) {
//set to a square by default
item.width = itemDefaultWidth;
item.height = itemDefaultHeight;
item.aspectRatio = 1;
var widthProp = _.find(item.properties, function(v) {
return (v.alias === "umbracoWidth");
});
if (widthProp && widthProp.value) {
item.width = parseInt(widthProp.value, 10);
if (isNaN(item.width)) {
item.width = itemDefaultWidth;
}
}
var heightProp = _.find(item.properties, function(v) {
return (v.alias === "umbracoHeight");
});
if (heightProp && heightProp.value) {
item.height = parseInt(heightProp.value, 10);
if (isNaN(item.height)) {
item.height = itemDefaultWidth;
}
}
item.aspectRatio = item.width / item.height;
// set max width and height
// landscape
if (item.aspectRatio >= 1) {
if (item.width > itemMaxWidth) {
item.width = itemMaxWidth;
item.height = itemMaxWidth / item.aspectRatio;
}
// portrait
} else {
if (item.height > itemMaxHeight) {
item.height = itemMaxHeight;
item.width = itemMaxHeight * item.aspectRatio;
}
}
}
};
scope.hoverItemDetails = function(item, $event, hover) {
if(scope.onDetailsHover) {
scope.onDetailsHover(item, $event, hover);
function setFlexValues(mediaItems) {
var flexSortArray = mediaItems;
var smallestImageWidth = null;
var widestImageAspectRatio = null;
// sort array after image width with the widest image first
flexSortArray = $filter('orderBy')(flexSortArray, 'width', true);
// find widest image aspect ratio
widestImageAspectRatio = flexSortArray[0].aspectRatio;
// find smallest image width
smallestImageWidth = flexSortArray[flexSortArray.length - 1].width;
for (var i = 0; flexSortArray.length > i; i++) {
var mediaItem = flexSortArray[i];
var flex = 1 / (widestImageAspectRatio / mediaItem.aspectRatio);
if (flex === 0) {
flex = 1;
}
var imageMinFlexWidth = smallestImageWidth * flex;
var flexStyle = {
"flex": flex + " 1 " + imageMinFlexWidth + "px",
"max-width": mediaItem.width + "px",
"min-width": itemMinWidth + "px",
"min-height": itemMinHeight + "px"
};
mediaItem.flexStyle = flexStyle;
}
}
};
var unbindItemsWatcher = scope.$watch('items', function(newValue, oldValue){
if(angular.isArray(newValue)) {
activate();
}
});
scope.clickItem = function(item, $event, $index) {
if (scope.onClick) {
scope.onClick(item, $event, $index);
}
};
scope.$on('$destroy', function(){
unbindItemsWatcher();
});
scope.clickItemName = function(item, $event, $index) {
if (scope.onClickName) {
scope.onClickName(item, $event, $index);
$event.stopPropagation();
}
};
}
scope.hoverItemDetails = function(item, $event, hover) {
if (scope.onDetailsHover) {
scope.onDetailsHover(item, $event, hover);
}
};
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/umb-media-grid.html',
scope: {
items: '=',
onDetailsHover: "=",
onClick: '=',
onClickName: "=",
filterBy: "="
},
link: link
};
var unbindItemsWatcher = scope.$watch('items', function(newValue, oldValue) {
if (angular.isArray(newValue)) {
activate();
}
});
return directive;
}
scope.$on('$destroy', function() {
unbindItemsWatcher();
});
angular.module('umbraco.directives').directive('umbMediaGrid', MediaGridDirective);
}
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/umb-media-grid.html',
scope: {
items: '=',
onDetailsHover: "=",
onClick: '=',
onClickName: "=",
filterBy: "=",
itemMaxWidth: "@",
itemMaxHeight: "@",
itemMinWidth: "@",
itemMinHeight: "@"
},
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('umbMediaGrid', MediaGridDirective);
})();

View File

@@ -37,7 +37,7 @@
.umb-card-icons{
text-align: center;
vertical-align: center;
vertical-align: middle;
display: block;
list-style: none;
margin: 0;

View File

@@ -27,7 +27,7 @@ label.control-label {
}
.controls-row label{padding: 0 10px 0 10px; vertical-align: center}
.controls-row label{padding: 0 10px 0 10px; vertical-align: middle;}

View File

@@ -38,7 +38,7 @@ body {
line-height: @baseLineHeight;
color: @textColor;
vertical-align: center;
vertical-align: middle;
text-align: center;
}

View File

@@ -140,7 +140,10 @@ h5.-black {
/* LABELS*/
.umb-control-group label.control-label {
text-align: left
text-align: left;
}
.umb-control-group label.control-label > div > label {
padding-left: 0;
}
.umb-control-group label .help-block,
.umb-control-group label small {
@@ -150,7 +153,7 @@ h5.-black {
padding-top: 5px;
}
.umb-nolabel .controls {
margin-left: 0px;
margin-left: 0;
}
.controls-row {
@@ -159,11 +162,15 @@ h5.-black {
}
.umb-user-panel .controls-row {
margin-left: 0px;
margin-left: 0;
}
.controls-row label {
display: inline-block
display: inline-block;
}
.controls-row > div > label {
padding-left: 0;
}
.block-form .controls-row {
@@ -171,10 +178,24 @@ h5.-black {
padding-top: 0;
}
.hidelabel > div > .controls-row, .hidelabel > .controls-row, .hidelabel .controls {
padding: 0px;
.hidelabel > div > .controls-row, .hidelabel > .controls-row, .hidelabel > div > .controls {
padding: 0;
border: none;
margin: 0px !important;
margin: 0 !important;
}
.controls-row > .vertical-align-items {
display: flex;
align-items: center;
}
.controls-row > .vertical-align-items > input.umb-editor-tiny {
margin-left: 5px;
margin-right: 5px;
}
.controls-row > .vertical-align-items > input.umb-editor-tiny:first-child {
margin-left: 0;
}
.thumbnails .selected {

View File

@@ -76,7 +76,11 @@
items="images"
filter-by="searchTerm"
on-click="clickHandler"
on-click-name="clickItemName">
on-click-name="clickItemName"
item-max-width="150"
item-max-height="150"
item-min-width="100"
item-min-height="100">
</umb-media-grid>
</div>

View File

@@ -21,7 +21,10 @@
umb-auto-resize
required
val-server-field="{{serverValidationField}}"
title="{{ngModel}}" />
title="{{ngModel}}"
focus-when="{{!locked}}"
umb-select-when="{{!locked}}"
on-blur="lock()" />
</div>

View File

@@ -1,20 +1,20 @@
<div class="umb-media-grid">
<div class="umb-media-grid__item" ng-click="clickItem(item, $event, $index)" ng-repeat="item in items | filter:filterBy" ng-style="item.flexStyle" ng-class="{'-selected': item.selected, '-file': !item.thumbnail}">
<div>
<div class="umb-media-grid__item" ng-click="clickItem(item, $event, $index)" ng-repeat="item in items | filter:filterBy" ng-style="item.flexStyle" ng-class="{'-selected': item.selected, '-file': !item.thumbnail}">
<i ng-show="item.selected" class="icon-check umb-media-grid__checkmark"></i>
<i ng-show="item.selected" class="icon-check umb-media-grid__checkmark"></i>
<div class="umb-media-grid__item-overlay" ng-class="{'-locked': item.selected}" ng-click="clickItemName(item, $event, $index)">
<i ng-if="onDetailsHover" class="icon-info umb-media-grid__info" ng-mouseover="hoverItemDetails(item, $event, true)" ng-mouseleave="hoverItemDetails(item, $event, false)"></i>
<div class="umb-media-grid__item-name">{{item.name}}</div>
</div>
<div class="umb-media-grid__item-overlay" ng-class="{'-locked': item.selected}" ng-click="clickItemName(item, $event, $index)">
<i ng-if="onDetailsHover" class="icon-info umb-media-grid__info" ng-mouseover="hoverItemDetails(item, $event, true)" ng-mouseleave="hoverItemDetails(item, $event, false)"></i>
<div class="umb-media-grid__item-name">{{item.name}}</div>
</div>
<div class="umb-media-grid__image-background" ng-if="item.thumbnail"></div>
<img class="umb-media-grid__item-image" width="{{item.width}}" height="{{item.height}}" ng-if="item.thumbnail" ng-src="{{item.thumbnail}}" alt="{{item.name}}" draggable="false"/>
<img class="umb-media-grid__item-image-placeholder" ng-if="!item.thumbnail" src="assets/img/transparent.png" alt="{{item.name}}" draggable="false"/>
<div class="umb-media-grid__image-background" ng-if="item.thumbnail"></div>
<img class="umb-media-grid__item-image" width="{{item.width}}" height="{{item.height}}" ng-if="item.thumbnail" ng-src="{{item.thumbnail}}" alt="{{item.name}}" draggable="false" />
<img class="umb-media-grid__item-image-placeholder" ng-if="!item.thumbnail" src="assets/img/transparent.png" alt="{{item.name}}" draggable="false" />
<i class="umb-media-grid__item-icon {{ item.icon }}" ng-if="!item.thumbnail"></i>
</div>
<i class="umb-media-grid__item-icon {{ item.icon }}" ng-if="!item.thumbnail"></i>
</div>
</div>
</div>

View File

@@ -4,7 +4,7 @@
ng-if="entityType !== 'media'">
<umb-content-grid
ng-if="items || !items && !options.filter"
ng-if="items"
content="items"
content-properties="options.includeProperties"
on-click="vm.selectContentItem"
@@ -12,9 +12,15 @@
</umb-content-grid>
<umb-empty-state
ng-if="!items && options.filter"
ng-if="!items && options.filter.length > 0"
position="center">
Sorry, we can not find what you are looking for
<localize key="general_searchNoResult"></localize>
</umb-empty-state>
<umb-empty-state
ng-if="!items && vm.isRecycleBin"
position="center">
<localize key="general_recycleBinEmpty"></localize>
</umb-empty-state>
</div>
@@ -31,7 +37,7 @@
files-uploaded="vm.onUploadComplete"
accept="{{vm.acceptedFileTypes}}"
max-file-size="{{vm.maxFileSize}}"
hide-dropzone="{{!vm.activeDrag && items.length > 0 || !items && options.filter }}"
hide-dropzone="{{ options.filter.length > 0 }}"
compact="{{ items.length > 0 }}"
files-queued="vm.onFilesQueue">
</umb-file-dropzone>
@@ -68,9 +74,9 @@
</umb-empty-state>
<umb-empty-state
ng-if="vm.itemsWithoutFolders.length === 0 && options.filter"
ng-if="vm.itemsWithoutFolders.length === 0 && options.filter.length > 0"
position="center">
Sorry, we can not find what you are looking for
<localize key="general_searchNoResult"></localize>
</umb-empty-state>
</div>

View File

@@ -19,7 +19,7 @@
vm.activeDrag = false;
vm.mediaDetailsTooltip = {};
vm.itemsWithoutFolders = [];
vm.isRecycleBin = $scope.contentId === '-21';
vm.isRecycleBin = $scope.contentId === '-21' || $scope.contentId === '-20';
vm.dragEnter = dragEnter;
vm.dragLeave = dragLeave;

View File

@@ -14,7 +14,7 @@
files-uploaded="vm.onUploadComplete"
accept="{{vm.acceptedFileTypes}}"
max-file-size="{{vm.maxFileSize}}"
hide-dropzone="{{!vm.activeDrag && items.length > 0 || !items && options.filter }}"
hide-dropzone="{{options.filter.length > 0}}"
compact="{{ items.length > 0 }}"
files-queued="vm.onFilesQueue">
</umb-file-dropzone>
@@ -31,18 +31,12 @@
on-sort="vm.sort">
</umb-table>
<umb-empty-state
ng-if="!items && vm.isRecycleBin"
position="center">
<localize key="general_recycleBinEmpty"></localize>
</umb-empty-state>
</div>
<div ng-if="entityType !== 'media'">
<umb-table
ng-if="items || !items && !options.filter"
ng-if="items"
items="items"
item-properties="options.includeProperties"
on-select="vm.selectItem"
@@ -56,9 +50,15 @@
</div>
<umb-empty-state
ng-if="!items && options.filter"
ng-if="!items && options.filter.length > 0"
position="center">
Sorry, we can not find what you are looking for
<localize key="general_searchNoResult"></localize>
</umb-empty-state>
<umb-empty-state
ng-if="!items && vm.isRecycleBin"
position="center">
<localize key="general_recycleBinEmpty"></localize>
</umb-empty-state>
</div>

View File

@@ -10,7 +10,7 @@
vm.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes);
vm.maxFileSize = Umbraco.Sys.ServerVariables.umbracoSettings.maxFileSize + "KB";
vm.activeDrag = false;
vm.isRecycleBin = $scope.contentId === '-21';
vm.isRecycleBin = $scope.contentId === '-21' || $scope.contentId === '-20';
vm.selectItem = selectItem;
vm.clickItem = clickItem;

View File

@@ -2,35 +2,41 @@
<umb-control-group label="Toolbar" hide-label="false">
<div ng-repeat="cmd in tinyMceConfig.commands">
<input type="checkbox"
ng-checked="selected(cmd, cmd.frontEndCommand, model.value.toolbar)"
ng-model="cmd.selected"
ng-change="selectCommand(cmd)"
/>
<label>
<input type="checkbox"
ng-checked="selected(cmd, cmd.frontEndCommand, model.value.toolbar)"
ng-model="cmd.selected"
ng-change="selectCommand(cmd)" />
<img ng-src="{{cmd.icon}}" />
{{cmd.frontEndCommand}}
</label>
</div>
</umb-control-group>
<umb-control-group label="Stylesheets" hide-label="0">
<div ng-repeat="css in stylesheets">
<input type="checkbox"
ng-checked="selected(css, css.name, model.value.stylesheets)"
ng-model="css.selected"
ng-change="selectStylesheet(css)"
/>
{{css.name}}
<label>
<input type="checkbox"
ng-checked="selected(css, css.name, model.value.stylesheets)"
ng-model="css.selected"
ng-change="selectStylesheet(css)" />
{{css.name}}
</label>
</div>
</umb-control-group>
<umb-control-group label="Dimensions" description="Width x Height">
<input type="number" ng-model="model.value.dimensions.width" class="umb-editor-tiny" placeholder="Width" /> &times;
<input type="number" ng-model="model.value.dimensions.height" class="umb-editor-tiny" placeholder="Height" /> Pixels
<div class="vertical-align-items">
<input type="number" min="0" ng-model="model.value.dimensions.width" class="umb-editor-tiny" placeholder="Width" /> &times;
<input type="number" min="0" ng-model="model.value.dimensions.height" class="umb-editor-tiny" placeholder="Height" /> Pixels
</div>
</umb-control-group>
<umb-control-group label="Maximum size for inserted images" description="0 to disable resizing">
<input type="number" ng-model="model.value.maxImageSize" class="umb-editor-tiny" placeholder="Width/Height" /> Pixels
<div class="vertical-align-items">
<input type="number" min="0" ng-model="model.value.maxImageSize" class="umb-editor-tiny" placeholder="Width/Height" /> Pixels
</div>
</umb-control-group>
</div>

View File

@@ -448,6 +448,7 @@
<key alias="retry">Retry</key>
<key alias="rights">Permissions</key>
<key alias="search">Search</key>
<key alias="searchNoResult">Sorry, we can not find what you are looking for</key>
<key alias="server">Server</key>
<key alias="show">Show</key>
<key alias="showPageOnSend">Show page on Send</key>

View File

@@ -141,9 +141,7 @@ namespace Umbraco.Web.Cache
ApplicationContext.Current.ApplicationCache.RuntimeCache.ClearCacheByKeySearch(CacheKeys.ContentTypeCacheKey);
//clear static object cache
global::umbraco.cms.businesslogic.ContentType.RemoveAllDataTypeCache();
PublishedContentType.ClearAll();
base.RefreshAll();
}
@@ -280,9 +278,7 @@ namespace Umbraco.Web.Cache
//clears the dictionary object cache of the legacy ContentType
global::umbraco.cms.businesslogic.ContentType.RemoveFromDataTypeCache(payload.Alias);
PublishedContentType.ClearContentType(payload.Id);
//need to recursively clear the cache for each child content type
foreach (var descendant in payload.DescendantPayloads)
{

View File

@@ -108,7 +108,6 @@ namespace Umbraco.Web.Cache
if (dataTypeCache)
dataTypeCache.Result.ClearCacheByKeySearch(string.Format("{0}{1}", CacheKeys.DataTypePreValuesCacheKey, payload.Id));
PublishedContentType.ClearDataType(payload.Id);
});
base.Refresh(jsonPayload);

View File

@@ -81,7 +81,8 @@ namespace Umbraco.Web.Editors
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[EnsureUserPermissionForContent("id")]
[OutgoingEditorModelEvent]
[EnsureUserPermissionForContent("id")]
public ContentItemDisplay GetById(int id)
{
var foundContent = GetObjectFromRequest(() => Services.ContentService.GetById(id));
@@ -116,6 +117,7 @@ namespace Umbraco.Web.Editors
/// If this is a container type, we'll remove the umbContainerView tab for a new item since
/// it cannot actually list children if it doesn't exist yet.
/// </returns>
[OutgoingEditorModelEvent]
public ContentItemDisplay GetEmpty(string contentTypeAlias, int parentId)
{
var contentType = Services.ContentTypeService.GetContentType(contentTypeAlias);

View File

@@ -0,0 +1,85 @@
using System;
using System.Web.Http.Filters;
using Umbraco.Core.Events;
using Umbraco.Web.Models.ContentEditing;
namespace Umbraco.Web.Editors
{
public abstract class EditorModelEventArgs : EventArgs
{
protected EditorModelEventArgs(object model, UmbracoContext umbracoContext)
{
Model = model;
UmbracoContext = umbracoContext;
}
public object Model { get; private set; }
public UmbracoContext UmbracoContext { get; private set; }
}
public sealed class EditorModelEventArgs<T> : EditorModelEventArgs
{
public EditorModelEventArgs(T model, UmbracoContext umbracoContext)
: base(model, umbracoContext)
{
Model = model;
}
public new T Model { get; private set; }
}
/// <summary>
/// Used to emit events for editor models in the back office
/// </summary>
public sealed class EditorModelEventManager
{
public static event TypedEventHandler<HttpActionExecutedContext, EditorModelEventArgs<ContentItemDisplay>> SendingContentModel;
public static event TypedEventHandler<HttpActionExecutedContext, EditorModelEventArgs<MediaItemDisplay>> SendingMediaModel;
public static event TypedEventHandler<HttpActionExecutedContext, EditorModelEventArgs<MemberDisplay>> SendingMemberModel;
private static void OnSendingContentModel(HttpActionExecutedContext sender, EditorModelEventArgs<ContentItemDisplay> e)
{
var handler = SendingContentModel;
if (handler != null) handler(sender, e);
}
private static void OnSendingMediaModel(HttpActionExecutedContext sender, EditorModelEventArgs<MediaItemDisplay> e)
{
var handler = SendingMediaModel;
if (handler != null) handler(sender, e);
}
private static void OnSendingMemberModel(HttpActionExecutedContext sender, EditorModelEventArgs<MemberDisplay> e)
{
var handler = SendingMemberModel;
if (handler != null) handler(sender, e);
}
/// <summary>
/// Based on the type, emit's a specific event
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
internal static void EmitEvent(HttpActionExecutedContext sender, EditorModelEventArgs e)
{
var contentItemDisplay = e.Model as ContentItemDisplay;
if (contentItemDisplay != null)
{
OnSendingContentModel(sender, (EditorModelEventArgs<ContentItemDisplay>) e);
}
var mediaItemDisplay = e.Model as MediaItemDisplay;
if (mediaItemDisplay != null)
{
OnSendingMediaModel(sender, (EditorModelEventArgs<MediaItemDisplay>)e);
}
var memberItemDisplay = e.Model as MemberDisplay;
if (memberItemDisplay != null)
{
OnSendingMemberModel(sender, (EditorModelEventArgs<MemberDisplay>)e);
}
}
}
}

View File

@@ -63,13 +63,14 @@ namespace Umbraco.Web.Editors
: base(umbracoContext)
{
}
/// <summary>
/// Gets an empty content item for the
/// </summary>
/// <param name="contentTypeAlias"></param>
/// <param name="parentId"></param>
/// <returns></returns>
[OutgoingEditorModelEvent]
public MediaItemDisplay GetEmpty(string contentTypeAlias, int parentId)
{
var contentType = Services.ContentTypeService.GetMediaType(contentTypeAlias);
@@ -92,6 +93,7 @@ namespace Umbraco.Web.Editors
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[OutgoingEditorModelEvent]
[EnsureUserPermissionForMedia("id")]
public MediaItemDisplay GetById(int id)
{

View File

@@ -145,6 +145,7 @@ namespace Umbraco.Web.Editors
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
[OutgoingEditorModelEvent]
public MemberDisplay GetByKey(Guid key)
{
MembershipUser foundMembershipMember;
@@ -196,6 +197,7 @@ namespace Umbraco.Web.Editors
/// </summary>
/// <param name="contentTypeAlias"></param>
/// <returns></returns>
[OutgoingEditorModelEvent]
public MemberDisplay GetEmpty(string contentTypeAlias = null)
{
IMember emptyContent;

View File

@@ -40,9 +40,14 @@ namespace Umbraco.Web.Install.InstallSteps
var clientDependencyConfig = new ClientDependencyConfiguration(_applicationContext.ProfilingLogger.Logger);
var clientDependencyUpdated = clientDependencyConfig.IncreaseVersionNumber();
var security = new WebSecurity(_httpContext, _applicationContext);
security.PerformLogin(0);
//During a new install we'll log the default user in (which is id = 0).
// During an upgrade, the user will already need to be logged in in order to run the installer.
if (InstallTypeTarget == InstallationType.NewInstall)
{
var security = new WebSecurity(_httpContext, _applicationContext);
security.PerformLogin(0);
}
//reports the ended install
var ih = new InstallHelper(UmbracoContext.Current);
ih.InstallStatus(true, "");

View File

@@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Umbraco.Web.PublishedContentModels
{
class Empty
{
}
}

View File

@@ -7,8 +7,8 @@ using Umbraco.Web.Models;
namespace Umbraco.Web.Mvc
{
public class RenderModelBinder : IModelBinder
{
public class RenderModelBinder : IModelBinder, IModelBinderProvider
{
/// <summary>
/// Binds the model to a value by using the specified controller context and binding context.
/// </summary>
@@ -18,14 +18,13 @@ namespace Umbraco.Web.Mvc
/// <param name="controllerContext">The controller context.</param><param name="bindingContext">The binding context.</param>
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (bindingContext.ModelType != typeof (RenderModel)) return null;
object model;
if (controllerContext.RouteData.DataTokens.TryGetValue("umbraco", out model) == false)
return null;
return model as RenderModel;
}
var culture = UmbracoContext.Current.PublishedContentRequest.Culture;
return BindModel(model, bindingContext.ModelType, culture);
}
// source is the model that we have
// modelType is the type of the model that we need to bind to
@@ -103,5 +102,18 @@ namespace Umbraco.Web.Mvc
throw new ModelBindingException(string.Format("Cannot bind source type {0} to model type {1}.",
sourceType, modelType));
}
public IModelBinder GetBinder(Type modelType)
{
// can bind to RenderModel
if (modelType == typeof(RenderModel)) return this;
// can bind to RenderModel<TContent>
if (modelType.IsGenericType && modelType.GetGenericTypeDefinition() == typeof(RenderModel<>)) return this;
// can bind to TContent where TContent : IPublishedContent
if (typeof(IPublishedContent).IsAssignableFrom(modelType)) return this;
return null;
}
}
}

View File

@@ -319,6 +319,7 @@
<Compile Include="Editors\EditorValidationResolver.cs" />
<Compile Include="Editors\EditorValidator.cs" />
<Compile Include="Editors\IEditorValidator.cs" />
<Compile Include="Editors\EditorModelEventManager.cs" />
<Compile Include="HtmlHelperBackOfficeExtensions.cs" />
<Compile Include="Install\InstallSteps\Version73FileCleanup.cs" />
<Compile Include="Models\ContentEditing\DocumentTypeDisplay.cs" />
@@ -686,6 +687,7 @@
<Compile Include="WebApi\Filters\EnableOverrideAuthorizationAttribute.cs" />
<Compile Include="WebApi\Filters\FilterGrouping.cs" />
<Compile Include="WebApi\Filters\LegacyTreeAuthorizeAttribute.cs" />
<Compile Include="WebApi\Filters\OutgoingEditorModelEventAttribute.cs" />
<Compile Include="WebApi\Filters\OutgoingNoHyphenGuidFormatAttribute.cs" />
<Compile Include="WebApi\Filters\OverridableAuthorizationAttribute.cs" />
<Compile Include="WebApi\Filters\SetAngularAntiForgeryTokensAttribute.cs" />

View File

@@ -125,6 +125,7 @@ namespace Umbraco.Web
if (umbracoSettings == null) throw new ArgumentNullException("umbracoSettings");
if (urlProviders == null) throw new ArgumentNullException("urlProviders");
//if there's already a singleton, and we're not replacing then there's no need to ensure anything
if (UmbracoContext.Current != null)
{
if (replaceContext == false)
@@ -132,6 +133,39 @@ namespace Umbraco.Web
UmbracoContext.Current._replacing = true;
}
var umbracoContext = CreateContext(httpContext, applicationContext, webSecurity, umbracoSettings, urlProviders, preview ?? false);
//assign the singleton
UmbracoContext.Current = umbracoContext;
return UmbracoContext.Current;
}
/// <summary>
/// Creates a standalone UmbracoContext instance
/// </summary>
/// <param name="httpContext"></param>
/// <param name="applicationContext"></param>
/// <param name="webSecurity"></param>
/// <param name="umbracoSettings"></param>
/// <param name="urlProviders"></param>
/// <param name="preview"></param>
/// <returns>
/// A new instance of UmbracoContext
/// </returns>
public static UmbracoContext CreateContext(
HttpContextBase httpContext,
ApplicationContext applicationContext,
WebSecurity webSecurity,
IUmbracoSettingsSection umbracoSettings,
IEnumerable<IUrlProvider> urlProviders,
bool preview)
{
if (httpContext == null) throw new ArgumentNullException("httpContext");
if (applicationContext == null) throw new ArgumentNullException("applicationContext");
if (webSecurity == null) throw new ArgumentNullException("webSecurity");
if (umbracoSettings == null) throw new ArgumentNullException("umbracoSettings");
if (urlProviders == null) throw new ArgumentNullException("urlProviders");
var umbracoContext = new UmbracoContext(
httpContext,
applicationContext,
@@ -142,15 +176,15 @@ namespace Umbraco.Web
// create the RoutingContext, and assign
var routingContext = new RoutingContext(
umbracoContext,
//TODO: Until the new cache is done we can't really expose these to override/mock
new Lazy<IEnumerable<IContentFinder>>(() => ContentFinderResolver.Current.Finders),
new Lazy<IContentFinder>(() => ContentLastChanceFinderResolver.Current.Finder),
// create the nice urls provider
// there's one per request because there are some behavior parameters that can be changed
new Lazy<UrlProvider>(
() => new UrlProvider(
() => new UrlProvider(
umbracoContext,
umbracoSettings.WebRouting,
urlProviders),
@@ -159,9 +193,7 @@ namespace Umbraco.Web
//assign the routing context back
umbracoContext.RoutingContext = routingContext;
//assign the singleton
UmbracoContext.Current = umbracoContext;
return UmbracoContext.Current;
return umbracoContext;
}
/// <summary>
@@ -460,17 +492,9 @@ namespace Umbraco.Web
protected override void DisposeResources()
{
Security.DisposeIfDisposable();
Security = null;
_umbracoContext = null;
//Before we set these to null but in fact these are application lifespan singletons so
//there's no reason we need to set them to null and this also caused a problem with packages
//trying to access the cache properties on RequestEnd.
//http://issues.umbraco.org/issue/U4-2734
//http://our.umbraco.org/projects/developer-tools/301-url-tracker/version-2/44327-Issues-with-URL-Tracker-in-614
//ContentCache = null;
//MediaCache = null;
//Application = null;
//If not running in a web ctx, ensure the thread based instance is nulled
_umbracoContext = null;
}
}
}

View File

@@ -0,0 +1,38 @@
using System;
using System.Net.Http;
using System.Web.Http.Filters;
using Umbraco.Core;
using Umbraco.Web.Editors;
using Umbraco.Web.Models.ContentEditing;
namespace Umbraco.Web.WebApi.Filters
{
/// <summary>
/// Used to emit outgoing editor model events
/// </summary>
internal sealed class OutgoingEditorModelEventAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
if (actionExecutedContext.Response == null) return;
var user = UmbracoContext.Current.Security.CurrentUser;
if (user == null) return;
var objectContent = actionExecutedContext.Response.Content as ObjectContent;
if (objectContent != null)
{
var model = objectContent.Value;
if (model != null)
{
EditorModelEventManager.EmitEvent(actionExecutedContext, new EditorModelEventArgs<ContentItemDisplay>(
(dynamic)model,
UmbracoContext.Current));
}
}
base.OnActionExecuted(actionExecutedContext);
}
}
}

View File

@@ -135,7 +135,7 @@ namespace Umbraco.Web
ViewEngines.Engines.Add(new PluginViewEngine());
//set model binder
ModelBinders.Binders.Add(new KeyValuePair<Type, IModelBinder>(typeof(RenderModel), new RenderModelBinder()));
ModelBinderProviders.BinderProviders.Add(new RenderModelBinder()); // is a provider
////add the profiling action filter
//GlobalFilters.Filters.Add(new ProfilingActionFilter());

View File

@@ -161,7 +161,9 @@ namespace umbraco.cms.businesslogic.media
protected override void setupNode()
{
var mediaType = ApplicationContext.Current.Services.ContentTypeService.GetMediaType(Id);
SetupNode(mediaType);
// If it's null, it's probably a folder
if (mediaType != null)
SetupNode(mediaType);
}
#endregion