diff --git a/src/Umbraco.Core/Constants-ObjectTypes.cs b/src/Umbraco.Core/Constants-ObjectTypes.cs
index 4a79437c61..a1e6e55ee9 100644
--- a/src/Umbraco.Core/Constants-ObjectTypes.cs
+++ b/src/Umbraco.Core/Constants-ObjectTypes.cs
@@ -222,6 +222,16 @@ namespace Umbraco.Core
/// Guid for a Forms DataSource.
///
public static readonly Guid LanguageGuid = new Guid(Language);
+
+ ///
+ /// Guid for an Identifier Reservation.
+ ///
+ public const string IdReservation = "92849B1E-3904-4713-9356-F646F87C25F4";
+
+ ///
+ /// Guid for an Identifier Reservation.
+ ///
+ public static readonly Guid IdReservationGuid = new Guid(IdReservation);
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/UmbracoObjectTypes.cs b/src/Umbraco.Core/Models/UmbracoObjectTypes.cs
index d0136a10a4..d4caaef768 100644
--- a/src/Umbraco.Core/Models/UmbracoObjectTypes.cs
+++ b/src/Umbraco.Core/Models/UmbracoObjectTypes.cs
@@ -185,6 +185,13 @@ namespace Umbraco.Core.Models
///
[UmbracoObjectType(Constants.ObjectTypes.Language)]
[FriendlyName("Language")]
- Language
+ Language,
+
+ ///
+ /// Reserved Identifier
+ ///
+ [UmbracoObjectType(Constants.ObjectTypes.IdReservation)]
+ [FriendlyName("Identifier Reservation")]
+ IdReservation
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
index eac9d197be..a01cbaa2e8 100644
--- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
@@ -435,7 +435,24 @@ namespace Umbraco.Core.Persistence.Repositories
nodeDto.Path = parent.Path;
nodeDto.Level = short.Parse(level.ToString(CultureInfo.InvariantCulture));
nodeDto.SortOrder = sortOrder;
- var o = Database.IsNew(nodeDto) ? Convert.ToInt32(Database.Insert(nodeDto)) : Database.Update(nodeDto);
+
+ // note:
+ // there used to be a check on Database.IsNew(nodeDto) here to either Insert or Update,
+ // but I cannot figure out what was the point, as the node should obviously be new if
+ // we reach that point - removed.
+
+ // see if there's a reserved identifier for this unique id
+ var sql = new Sql("SELECT id FROM umbracoNode WHERE uniqueID=@0 AND nodeObjectType=@1", nodeDto.UniqueId, Constants.ObjectTypes.IdReservationGuid);
+ var id = Database.ExecuteScalar(sql);
+ if (id > 0)
+ {
+ nodeDto.NodeId = id;
+ Database.Update(nodeDto);
+ }
+ else
+ {
+ Database.Insert(nodeDto);
+ }
//Update with new correct path
nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId);
diff --git a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs
index 598b16d78d..b22e8981b9 100644
--- a/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/MediaRepository.cs
@@ -84,7 +84,7 @@ namespace Umbraco.Core.Persistence.Repositories
#endregion
#region Overrides of PetaPocoRepositoryBase
-
+
protected override Sql GetBaseQuery(BaseQueryType queryType)
{
var sql = new Sql();
@@ -153,7 +153,7 @@ namespace Umbraco.Core.Persistence.Repositories
/// This is the underlying method that processes most queries for this repository
///
///
- /// The full SQL to select all media data
+ /// The full SQL to select all media data
///
///
/// The Id SQL to just return all media ids - used to process the properties for the media item
@@ -164,7 +164,7 @@ namespace Umbraco.Core.Persistence.Repositories
{
// fetch returns a list so it's ok to iterate it in this method
var dtos = Database.Fetch(sqlFull);
-
+
//This is a tuple list identifying if the content item came from the cache or not
var content = new List>();
var defs = new DocumentDefinitionCollection();
@@ -180,7 +180,7 @@ namespace Umbraco.Core.Persistence.Repositories
if (withCache)
{
var cached = IsolatedCache.GetCacheItem(GetCacheIdKey(dto.NodeId));
- //only use this cached version if the dto returned is the same version - this is just a safety check, media doesn't
+ //only use this cached version if the dto returned is the same version - this is just a safety check, media doesn't
//store different versions, but just in case someone corrupts some data we'll double check to be sure.
if (cached != null && cached.Version == dto.VersionId)
{
@@ -303,7 +303,7 @@ namespace Umbraco.Core.Persistence.Repositories
.From(SqlSyntax)
.InnerJoin(SqlSyntax)
.On(SqlSyntax, left => left.NodeId, right => right.NodeId);
-
+
if (contentTypeIdsA.Length > 0)
{
xmlIdsQuery.InnerJoin(SqlSyntax)
@@ -314,7 +314,7 @@ namespace Umbraco.Core.Persistence.Repositories
}
xmlIdsQuery.Where(dto => dto.NodeObjectType == mediaObjectType, SqlSyntax);
-
+
var allXmlIds = Database.Fetch(xmlIdsQuery);
var toRemove = allXmlIds.Except(allMediaIds).ToArray();
@@ -380,7 +380,24 @@ namespace Umbraco.Core.Persistence.Repositories
nodeDto.Path = parent.Path;
nodeDto.Level = short.Parse(level.ToString(CultureInfo.InvariantCulture));
nodeDto.SortOrder = sortOrder;
- var o = Database.IsNew(nodeDto) ? Convert.ToInt32(Database.Insert(nodeDto)) : Database.Update(nodeDto);
+
+ // note:
+ // there used to be a check on Database.IsNew(nodeDto) here to either Insert or Update,
+ // but I cannot figure out what was the point, as the node should obviously be new if
+ // we reach that point - removed.
+
+ // see if there's a reserved identifier for this unique id
+ var sql = new Sql("SELECT id FROM umbracoNode WHERE uniqueID=@0 AND nodeObjectType=@1", nodeDto.UniqueId, Constants.ObjectTypes.IdReservationGuid);
+ var id = Database.ExecuteScalar(sql);
+ if (id > 0)
+ {
+ nodeDto.NodeId = id;
+ Database.Update(nodeDto);
+ }
+ else
+ {
+ Database.Insert(nodeDto);
+ }
//Update with new correct path
nodeDto.Path = string.Concat(parent.Path, ",", nodeDto.NodeId);
@@ -564,7 +581,7 @@ namespace Umbraco.Core.Persistence.Repositories
private IMedia CreateMediaFromDto(ContentVersionDto dto, Sql docSql)
{
var contentType = _mediaTypeRepository.Get(dto.ContentDto.ContentTypeId);
-
+
var media = MediaFactory.BuildEntity(dto, contentType);
var docDef = new DocumentDefinition(dto, contentType);
@@ -584,7 +601,7 @@ namespace Umbraco.Core.Persistence.Repositories
if (EnsureUniqueNaming == false)
return nodeName;
- var names = Database.Fetch("SELECT id, text AS name FROM umbracoNode WHERE nodeObjectType=@objectType AND parentId=@parentId",
+ var names = Database.Fetch("SELECT id, text AS name FROM umbracoNode WHERE nodeObjectType=@objectType AND parentId=@parentId",
new { objectType = NodeObjectTypeId, parentId });
return SimilarNodeName.GetUniqueName(names, id, nodeName);
diff --git a/src/Umbraco.Core/Services/EntityService.cs b/src/Umbraco.Core/Services/EntityService.cs
index 04d6c276b2..c10311dc01 100644
--- a/src/Umbraco.Core/Services/EntityService.cs
+++ b/src/Umbraco.Core/Services/EntityService.cs
@@ -646,5 +646,34 @@ namespace Umbraco.Core.Services
return exists;
}
}
+
+ ///
+ public int ReserveId(Guid key)
+ {
+ NodeDto node;
+ using (var scope = UowProvider.ScopeProvider.CreateScope())
+ {
+ var sql = new Sql("SELECT * FROM umbracoNode WHERE uniqueID=@0 AND nodeObjectType=@1", key, Constants.ObjectTypes.IdReservationGuid);
+ node = scope.Database.SingleOrDefault(sql);
+ if (node != null) throw new InvalidOperationException("An identifier has already been reserved for this Udi.");
+ node = new NodeDto
+ {
+ UniqueId = key,
+ Text = "RESERVED.ID",
+ NodeObjectType = Constants.ObjectTypes.IdReservationGuid,
+
+ CreateDate = DateTime.Now,
+ UserId = 0,
+ ParentId = -1,
+ Level = 1,
+ Path = "-1",
+ SortOrder = 0,
+ Trashed = false
+ };
+ scope.Database.Insert(node);
+ scope.Complete();
+ }
+ return node.NodeId;
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Services/IEntityService.cs b/src/Umbraco.Core/Services/IEntityService.cs
index 82e5227cf2..d1da84a1c8 100644
--- a/src/Umbraco.Core/Services/IEntityService.cs
+++ b/src/Umbraco.Core/Services/IEntityService.cs
@@ -170,7 +170,7 @@ namespace Umbraco.Core.Services
///
///
///
- ///
+ ///
///
IEnumerable GetPagedDescendants(int id, UmbracoObjectTypes umbracoObjectType, long pageIndex, int pageSize, out long totalRecords,
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "");
@@ -270,5 +270,13 @@ namespace Umbraco.Core.Services
///
/// Type of the entity
Type GetEntityType(UmbracoObjectTypes umbracoObjectType);
+
+ ///
+ /// Reserves an identifier for a key.
+ ///
+ /// They key.
+ /// The identifier.
+ /// When a new content or a media is saved with the key, it will have the reserved identifier.
+ int ReserveId(Guid key);
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Services/IdkMap.cs b/src/Umbraco.Core/Services/IdkMap.cs
index 015bb104dc..411316fca2 100644
--- a/src/Umbraco.Core/Services/IdkMap.cs
+++ b/src/Umbraco.Core/Services/IdkMap.cs
@@ -46,6 +46,10 @@ namespace Umbraco.Core.Services
if (val == null) return Attempt.Fail();
+ // cache reservations, when something is saved this cache is cleared anyways
+ //if (umbracoObjectType == UmbracoObjectTypes.IdReservation)
+ // Attempt.Succeed(val.Value);
+
try
{
_locker.EnterWriteLock();
@@ -95,6 +99,10 @@ namespace Umbraco.Core.Services
if (val == null) return Attempt.Fail();
+ // cache reservations, when something is saved this cache is cleared anyways
+ //if (umbracoObjectType == UmbracoObjectTypes.IdReservation)
+ // Attempt.Succeed(val.Value);
+
try
{
_locker.EnterWriteLock();