protected virtual void ExtractLinks(BulkLoadContext context, BulkLoadItem item, BulkField field, LinkedList <BulkItemLink> links) { if (string.IsNullOrWhiteSpace(field.Value)) { return; } if (IgnoredFields.Value.Contains(field.Name)) { return; } var ids = IdRegex.Value.Matches(field.Value).Cast <Match>() .Select(x => { ID id; return(ID.TryParse(x.Value, out id) ? id : (ID)null); }) .Concat(LinkRegex.Value.Matches(field.Value).Cast <Match>().Select(x => { Guid guid; return(Guid.TryParse(x.Value, out guid) ? new ID(guid) : (ID)null); })) .Where(x => x != (ID)null); foreach (var link in ids .Select(x => new BulkItemLink( context.Database, new ID(item.Id), new ID(field.Id), context.Database, x, x.ToString()))) { link.ItemAction = item.LoadAction; links.AddLast(link); } }
protected virtual void AddStatisticsFieldsWhenMissing(BulkLoadItem bulkItem, string language, int versionNumber = 1) { var user = global::Sitecore.Context.User.Name; if (bulkItem.GetField(FieldIDs.Created.Guid, language, versionNumber) == null) { bulkItem.AddVersionedField(FieldIDs.Created.Guid, language, versionNumber, DateUtil.IsoNow, name: "__Created"); } if (bulkItem.GetField(FieldIDs.CreatedBy.Guid, language, versionNumber) == null) { bulkItem.AddVersionedField(FieldIDs.CreatedBy.Guid, language, versionNumber, user, name: "__Created by"); } if (bulkItem.LoadAction == BulkLoadAction.Update || bulkItem.LoadAction == BulkLoadAction.UpdateExistingItem) { if (bulkItem.GetField(FieldIDs.UpdatedBy.Guid, language, versionNumber) == null) { bulkItem.AddVersionedField(FieldIDs.UpdatedBy.Guid, language, versionNumber, user, name: "__Updated"); } if (bulkItem.GetField(FieldIDs.Updated.Guid, language, versionNumber) == null) { bulkItem.AddVersionedField(FieldIDs.Updated.Guid, language, versionNumber, DateUtil.IsoNowWithTicks, name: "__Updated by"); } } }
protected virtual void ExtractLinks(BulkLoadContext context, BulkLoadItem item, LinkedList <BulkItemLink> links) { foreach (var field in item.Fields) { ExtractLinks(context, item, field, links); } }
public virtual BulkLoadItem ToBulkLoadItem(IItemData itemData, BulkLoadContext context, BulkLoadAction loadAction) { if (itemData == null) { throw new ArgumentNullException(nameof(itemData)); } if (context == null) { throw new ArgumentNullException(nameof(context)); } var bulkItem = new BulkLoadItem( loadAction, itemData.Id, itemData.TemplateId, itemData.BranchId, itemData.ParentId, itemData.Path, sourceInfo: itemData.SerializedItemId); foreach (var sharedField in itemData.SharedFields) { AddSyncField(context, bulkItem, sharedField); } foreach (var languagedFields in itemData.UnversionedFields) { foreach (var field in languagedFields.Fields) { AddSyncField(context, bulkItem, field, languagedFields.Language.Name); } } foreach (var versionFields in itemData.Versions) { foreach (var field in versionFields.Fields) { AddSyncField(context, bulkItem, field, versionFields.Language.Name, versionFields.VersionNumber); } AddStatisticsFieldsWhenMissing(bulkItem, versionFields.Language.Name, versionFields.VersionNumber); } // Serialized items don't contain the original blob id. context.LookupBlobIds = true; return(bulkItem); }
protected virtual void AddSyncField(BulkLoadContext context, BulkLoadItem bulkItem, IItemFieldValue itemField, string language = null, int versionNumber = 1) { var fieldId = itemField.FieldId; var fieldValue = itemField.Value; var fieldName = itemField.NameHint; var isBlob = itemField.BlobId.HasValue; Func <Stream> blob = null; if (isBlob) { byte[] blobBytes; try { blobBytes = Convert.FromBase64String(fieldValue); } catch (Exception ex) { blobBytes = new byte[] { }; context.Log.Error( $"Unable to read blob from field '{fieldId}' in item with id '{bulkItem.Id}', " + $"item path '{bulkItem.ParentId}' and source info '{bulkItem.SourceInfo}', defaulting to empty value.", ex); } blob = () => new MemoryStream(blobBytes); // Field value needs to be set to the blob id. fieldValue = itemField.BlobId.Value.ToString("B").ToUpper(); } if (language == null) { bulkItem.AddSharedField(fieldId, fieldValue, blob, isBlob, fieldName); } else { bulkItem.AddVersionedField(fieldId, language, versionNumber, fieldValue, blob, isBlob, fieldName); } }
protected virtual void AddStatisticsFieldsWhenMissing(BulkLoadItem bulkItem, string language, int versionNumber = 1) { var user = Sitecore.Context.User.Name; // Make sure revision is updated when item is created or updated so that smart publish works. // Unicorn doesn't track revisions, so we need to generate one ourselves. if (bulkItem.GetField(FieldIDs.Revision.Guid, language, versionNumber) == null) { bulkItem.AddVersionedField(FieldIDs.Revision.Guid, language, versionNumber, Guid.NewGuid().ToString("D"), name: "__Revision", postProcessor: x => x.DependsOnCreate = x.DependsOnUpdate = true); } if (bulkItem.GetField(FieldIDs.Created.Guid, language, versionNumber) == null) { bulkItem.AddVersionedField(FieldIDs.Created.Guid, language, versionNumber, DateUtil.IsoNow, name: "__Created", postProcessor: x => x.DependsOnCreate = true); } if (bulkItem.GetField(FieldIDs.CreatedBy.Guid, language, versionNumber) == null) { bulkItem.AddVersionedField(FieldIDs.CreatedBy.Guid, language, versionNumber, user, name: "__Created by", postProcessor: x => x.DependsOnCreate = true); } if (bulkItem.GetField(FieldIDs.Updated.Guid, language, versionNumber) == null) { bulkItem.AddVersionedField(FieldIDs.Updated.Guid, language, versionNumber, DateUtil.IsoNow, name: "__Updated", postProcessor: x => x.DependsOnCreate = x.DependsOnUpdate = true); } if (bulkItem.GetField(FieldIDs.UpdatedBy.Guid, language, versionNumber) == null) { bulkItem.AddVersionedField(FieldIDs.UpdatedBy.Guid, language, versionNumber, user, name: "__Updated by", postProcessor: x => x.DependsOnCreate = x.DependsOnUpdate = true); } }
protected virtual IEnumerable <BulkLoadItem> EnsureAncestorBulkItems(BulkLoadItem item, ItemReference root, Template ancestorTemplate, Guid dependsOnItemCreation, BulkLoadContext context) { if (item == null) { throw new ArgumentNullException(nameof(item)); } if (root == null) { throw new ArgumentNullException(nameof(root)); } if (ancestorTemplate == null) { throw new ArgumentNullException(nameof(ancestorTemplate)); } if (context == null) { throw new ArgumentNullException(nameof(context)); } if (!item.ItemPath.StartsWith(root.ItemPath)) { throw new ArgumentException("Bulk item should be a descendant of the root."); } // Detect all the ancestors to generate. var ancestorNames = item.ItemPath.Substring(root.ItemPath.Length) .Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); ancestorNames = ancestorNames.Take(ancestorNames.Length - 1).ToArray(); // Don't include item. // Generate all ancestors. var parent = root; foreach (var name in ancestorNames) { var itemPath = $"{parent.ItemPath}/{name}"; // Maybe we have already generated this path in a previous call to EnsureAncestors within the same context. var itemId = context.GetProcessedPath(itemPath); if (itemId.HasValue) { // Continue with next. parent = new ItemReference(itemId.Value, itemPath); continue; } // Generate stable guid for this ancestor item within its parent. itemId = _guidUtility.Create(parent.ItemId, name); // In case of forced updates, also update the child item. This will result in an ItemChange which makes sure the item gets re-published. var childLoadAction = context.ForceUpdates ? BulkLoadAction.Update : BulkLoadAction.AddOnly; // Create new bulk item. var child = new BulkLoadItem(childLoadAction, itemId.Value, ancestorTemplate.ID.Guid, Guid.Empty, parent.ItemId, itemPath, templateName: ancestorTemplate.Name, sourceInfo: item.SourceInfo) { // Only create ancestor when child is created, skip ancestor creation when child is updated. DependsOnItemCreation = dependsOnItemCreation }; // Attach asap to context, because import profiles might eagerly bucket. context.TrackPathAndTemplateInfo(child); yield return(child); parent = new ItemReference(child.Id, child.ItemPath); } // Reset parent reference for initial item. item.ParentId = parent.ItemId; }
/// <summary> /// Returns ancestor items for every needed level between the item and the root. /// </summary> /// <param name="item">Item to generate ancestors for.</param> /// <param name="root">Root that marks the ancestor that should already exist or be present in the stream.</param> /// <param name="ancestorTemplate">Template for the ancestors to generate.</param> /// <param name="context">Context to generate the ancestors in.</param> /// <returns>Stream of generated ancestors, migh be empty if ancestors were already created, /// or it might be partial if shared ancestors were already created for another item in the same context.</returns> public virtual IEnumerable <BulkLoadItem> EnsureAncestorBulkItems(BulkLoadItem item, ItemReference root, Template ancestorTemplate, BulkLoadContext context) { return(EnsureAncestorBulkItems(item, root, ancestorTemplate, item.Id, context)); }
protected virtual IEnumerable <BulkLoadItem> Bucket(BulkLoadItem item, BulkLoadContext context, bool skipIfNotBucket) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (item == null) { throw new ArgumentNullException(nameof(item)); } if (item.Bucketed) { yield return(item); yield break; } var db = Factory.GetDatabase(context.Database); var bucketItem = db.GetItem(new ID(item.ParentId)); if (bucketItem == null && !skipIfNotBucket) { throw new ArgumentException( $"Unable to bucket item because parent with id '{item.ParentId}' doesn't exist."); } if (bucketItem == null) { yield return(item); yield break; } if (!bucketItem.IsABucket()) { if (skipIfNotBucket) { yield return(item); yield break; } throw new InvalidOperationException( $"Item with path '{bucketItem.Paths.Path}' is not bucket."); } // Get template for ancestors. var bucketFolderTemplate = TemplateManager.GetTemplate(new ID(BucketFolderTemplate), db); // Default to configured bucket folder generation. if (context.BucketFolderPath == null) { context.BucketFolderPath = new BucketFolderPathResolver(); } // Try to find out when item was created. var createdField = item.Fields.FirstOrDefault( x => x.Id == FieldIDs.Created.Guid && !string.IsNullOrWhiteSpace(x.Value)); var created = createdField == null ? DateTime.UtcNow : DateUtil.IsoDateToDateTime(createdField.Value); var bucketFolderPath = context.BucketFolderPath.GetFolderPath(db, item.Name.Replace(' ', '0'), // Sitecore's name based bucket folder generation doesn't handle spaces. new ID(item.TemplateId), new ID(item.Id), bucketItem.ID, created); item.ItemPath = bucketItem.Paths.Path + "/" + bucketFolderPath + "/" + item.Name; item.Bucketed = true; // Check if bucket folder path depends on creation date. if (context.GetState <bool?>("Load.BucketByDate") == null) { created = new DateTime(2000, 01, 01, 01, 01, 01); var testBucketFolderPath = context.BucketFolderPath.GetFolderPath(db, item.Name, new ID(item.TemplateId), new ID(item.Id), bucketItem.ID, created); if (!bucketFolderPath.Equals(testBucketFolderPath, StringComparison.OrdinalIgnoreCase)) { context.Log.Warn( "Bucket strategy is based on creation date, this will affect import performance, " + "but might also not be repeatable."); context.GetOrAddState <bool?>("Load.BucketByDate", () => true); } } // If bucketing depends on date, lookup item by name with wildcard. var dependsOnDate = context.GetState <bool?>("Load.BucketsByDate", false); if (dependsOnDate.GetValueOrDefault(false)) { context.LookupItemIds = true; item.ItemLookupPath = bucketItem.Paths.Path + "/**/" + item.Name; } foreach (var ancestor in _ancestorGenerator.EnsureAncestorBulkItems(item, new ItemReference(bucketItem.ID.Guid, bucketItem.Paths.Path), bucketFolderTemplate, context)) { // Make sure ancestor doesn't get re-bucketed. ancestor.Bucketed = true; yield return(ancestor); } yield return(item); }