diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs index 70cb877183..d7db11dc3b 100644 --- a/src/Umbraco.Core/Models/Content.cs +++ b/src/Umbraco.Core/Models/Content.cs @@ -267,6 +267,9 @@ namespace Umbraco.Core.Models [IgnoreDataMember] internal DateTime PublishedDate { get; set; } + [DataMember] + public bool IsBlueprint { get; internal set; } + /// /// Changes the Trashed state of the content object /// @@ -330,7 +333,7 @@ namespace Umbraco.Core.Models //turn off change tracking clone.DisableChangeTracking(); //need to manually clone this since it's not settable - clone._contentType = (IContentType)ContentType.DeepClone(); + clone._contentType = (IContentType)ContentType.DeepClone(); //this shouldn't really be needed since we're not tracking clone.ResetDirtyProperties(false); //re-enable tracking diff --git a/src/Umbraco.Core/Models/IContent.cs b/src/Umbraco.Core/Models/IContent.cs index 7caefc1121..3c92b17ae6 100644 --- a/src/Umbraco.Core/Models/IContent.cs +++ b/src/Umbraco.Core/Models/IContent.cs @@ -84,6 +84,11 @@ namespace Umbraco.Core.Models /// /// Gets the unique identifier of the published version, if any. /// - Guid PublishedVersionGuid { get; } + Guid PublishedVersionGuid { get; } + + /// + /// Gets a value indicating whether the content item is a blueprint. + /// + bool IsBlueprint { get; } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs index c44c8ac1dc..c019b694d2 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentRepository.cs @@ -95,8 +95,5 @@ namespace Umbraco.Core.Persistence.Repositories /// /// void AddOrUpdatePreviewXml(IContent content, Func xml); - - - } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/ContentService.cs b/src/Umbraco.Core/Services/ContentService.cs index 491e03a065..8cfe67b99b 100644 --- a/src/Umbraco.Core/Services/ContentService.cs +++ b/src/Umbraco.Core/Services/ContentService.cs @@ -177,7 +177,7 @@ namespace Umbraco.Core.Services content.WriterId = userId; uow.Events.Dispatch(Created, this, new NewEventArgs(content, false, contentTypeAlias, parentId)); - + uow.Commit(); } @@ -219,7 +219,7 @@ namespace Umbraco.Core.Services content.WriterId = userId; uow.Events.Dispatch(Created, this, new NewEventArgs(content, false, contentTypeAlias, parent)); - + uow.Commit(); } @@ -269,7 +269,7 @@ namespace Umbraco.Core.Services repository.AddOrUpdate(content); repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, _userService, c)); - uow.Events.Dispatch(Saved, this, new SaveEventArgs(content, false)); + uow.Events.Dispatch(Saved, this, new SaveEventArgs(content, false), "Saved"); uow.Events.Dispatch(Created, this, new NewEventArgs(content, false, contentTypeAlias, parentId)); Audit(uow, AuditType.New, string.Format("Content '{0}' was created with Id {1}", name, content.Id), content.CreatorId, content.Id); @@ -324,7 +324,7 @@ namespace Umbraco.Core.Services repository.AddOrUpdate(content); repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, _userService, c)); - uow.Events.Dispatch(Saved, this, new SaveEventArgs(content, false)); + uow.Events.Dispatch(Saved, this, new SaveEventArgs(content, false), "Saved"); uow.Events.Dispatch(Created, this, new NewEventArgs(content, false, contentTypeAlias, parent)); Audit(uow, AuditType.New, string.Format("Content '{0}' was created with Id {1}", name, content.Id), content.CreatorId, content.Id); @@ -1161,14 +1161,28 @@ namespace Umbraco.Core.Services using (var uow = UowProvider.GetUnitOfWork(readOnly: true)) { var repository = RepositoryFactory.CreateContentBlueprintRepository(uow); - return repository.Get(id); + var blueprint = repository.Get(id); + ((Content) blueprint).IsBlueprint = true; + return blueprint; } } + public IContent GetBlueprintById(Guid id) + { + // the repository implements a cache policy on int identifiers, not guids, + // and we are not changing it now, but we still would like to rely on caching + // instead of running a full query against the database, so relying on the + // id-key map, which is fast. + + var a = _idkMap.GetIdForKey(id, UmbracoObjectTypes.DocumentBlueprint); + return a.Success ? GetBlueprintById(a.Result) : null; + } + public void SaveBlueprint(IContent content, int userId = 0) { //always ensure the blueprint is at the root - content.ParentId = -1; + content.ParentId = -1; + ((Content) content).IsBlueprint = true; using (new WriteLock(Locker)) { @@ -1186,8 +1200,10 @@ namespace Umbraco.Core.Services content.CreatorId = userId; } content.WriterId = userId; - - repository.AddOrUpdate(content); + + repository.AddOrUpdate(content); + + uow.Events.Dispatch(SavedBlueprint, this, new SaveEventArgs(content), "SavedBlueprint"); uow.Commit(); } @@ -1199,9 +1215,10 @@ namespace Umbraco.Core.Services using (new WriteLock(Locker)) { using (var uow = UowProvider.GetUnitOfWork()) - { + { var repository = RepositoryFactory.CreateContentBlueprintRepository(uow); - repository.Delete(content); + repository.Delete(content); + uow.Events.Dispatch(DeletedBlueprint, this, new DeleteEventArgs(content), "DeletedBlueprint"); uow.Commit(); } } @@ -1215,19 +1232,11 @@ namespace Umbraco.Core.Services var content = new Content(name, -1, contentType); content.Path = string.Concat(content.ParentId.ToString(), ",", content.Id); - using (var uow = UowProvider.GetUnitOfWork()) - { - content.CreatorId = userId; - content.WriterId = userId; + content.CreatorId = userId; + content.WriterId = userId; - //Now we need to map all of the properties over! - foreach (var property in blueprint.Properties) - { - content.SetValue(property.Alias, property.Value); - } - - uow.Commit(); - } + foreach (var property in blueprint.Properties) + content.SetValue(property.Alias, property.Value); return content; } @@ -1298,7 +1307,7 @@ namespace Umbraco.Core.Services } if (raiseEvents) - uow.Events.Dispatch(Saved, this, new SaveEventArgs(asArray, false, evtMsgs)); + uow.Events.Dispatch(Saved, this, new SaveEventArgs(asArray, false, evtMsgs), "Saved"); Audit(uow, AuditType.Save, "Bulk Save content performed by user", userId == -1 ? 0 : userId, Constants.System.Root); uow.Commit(); @@ -1678,7 +1687,7 @@ namespace Umbraco.Core.Services repository.AddOrUpdate(copy); repository.AddOrUpdatePreviewXml(copy, c => _entitySerializer.Serialize(this, _dataTypeService, _userService, c)); - + //add permissions if (currentPermissions.Count > 0) { @@ -1860,7 +1869,7 @@ namespace Umbraco.Core.Services } if (raiseEvents) - uow.Events.Dispatch(Saved, this, new SaveEventArgs(asArray, false)); + uow.Events.Dispatch(Saved, this, new SaveEventArgs(asArray, false), "Saved"); if (shouldBePublished.Any()) { @@ -1887,7 +1896,11 @@ namespace Umbraco.Core.Services { query.Where(x => documentTypeIds.Contains(x.ContentTypeId)); } - return repository.GetByQuery(query); + return repository.GetByQuery(query).Select(x => + { + ((Content) x).IsBlueprint = true; + return x; + }); } } @@ -2294,7 +2307,7 @@ namespace Umbraco.Core.Services } if (raiseEvents) - uow.Events.Dispatch(Saved, this, new SaveEventArgs(content, false, evtMsgs)); + uow.Events.Dispatch(Saved, this, new SaveEventArgs(content, false, evtMsgs), "Saved"); //Save xml to db and call following method to fire event through PublishingStrategy to update cache if (published) @@ -2362,7 +2375,7 @@ namespace Umbraco.Core.Services repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, _userService, c)); if (raiseEvents) - uow.Events.Dispatch(Saved, this, new SaveEventArgs(content, false, evtMsgs)); + uow.Events.Dispatch(Saved, this, new SaveEventArgs(content, false, evtMsgs), "Saved"); Audit(uow, AuditType.Save, "Save Content performed by user", userId, content.Id); uow.Commit(); @@ -2625,6 +2638,17 @@ namespace Umbraco.Core.Services /// Occurs after the Recycle Bin has been Emptied /// public static event TypedEventHandler EmptiedRecycleBin; + + /// + /// Occurs after a blueprint has been saved. + /// + public static event TypedEventHandler> SavedBlueprint; + + /// + /// Occurs after a blueprint has been deleted. + /// + public static event TypedEventHandler> DeletedBlueprint; + #endregion } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index b048046ac0..e33f46e3f4 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -98,6 +98,7 @@ namespace Umbraco.Core.Services { IEnumerable GetBlueprintsForContentTypes(params int[] documentTypeIds); IContent GetBlueprintById(int id); + IContent GetBlueprintById(Guid id); void SaveBlueprint(IContent content, int userId = 0); void DeleteBlueprint(IContent content, int userId = 0); IContent CreateContentFromBlueprint(IContent blueprint, string name, int userId = 0); diff --git a/src/Umbraco.Core/UdiGetterExtensions.cs b/src/Umbraco.Core/UdiGetterExtensions.cs index 4ae6b05dca..9f824f6a26 100644 --- a/src/Umbraco.Core/UdiGetterExtensions.cs +++ b/src/Umbraco.Core/UdiGetterExtensions.cs @@ -132,7 +132,7 @@ namespace Umbraco.Core public static GuidUdi GetUdi(this IContent entity) { if (entity == null) throw new ArgumentNullException("entity"); - return new GuidUdi(Constants.UdiEntityType.Document, entity.Key).EnsureClosed(); + return new GuidUdi(entity.IsBlueprint ? Constants.UdiEntityType.DocumentBluePrint : Constants.UdiEntityType.Document, entity.Key).EnsureClosed(); } /// @@ -211,8 +211,8 @@ namespace Umbraco.Core if (entity == null) throw new ArgumentNullException("entity"); // we should throw on Unknown but for the time being, assume it means PartialView - var entityType = entity.ViewType == PartialViewType.PartialViewMacro - ? Constants.UdiEntityType.PartialViewMacro + var entityType = entity.ViewType == PartialViewType.PartialViewMacro + ? Constants.UdiEntityType.PartialViewMacro : Constants.UdiEntityType.PartialView; return new StringUdi(entityType, entity.Path.TrimStart('/')).EnsureClosed();