public virtual bool Save(IItemData item, IFieldValueManipulator fieldValueManipulator) { Assert.ArgumentNotNull(item, "item"); var result = _deserializer.Deserialize(item, fieldValueManipulator); return(result != null); }
public override void Save(IItemData item, IFieldValueManipulator fieldValueManipulator) { if (ReactorContext.SqlPrecache != null) { ReactorContext.SqlPrecache.Update(item); } base.Save(item, fieldValueManipulator); }
public virtual bool Save(IItemData item, IFieldValueManipulator fieldValueManipulator) { // fieldValueManipulator deliberately ignored. We never transform field values going to the file system var tree = GetTreeForPath(item.Path, item.DatabaseName); if (tree == null) { throw new InvalidOperationException("No trees contained the global path " + item.Path); } tree.Save(item); return(true); }
protected virtual bool ShouldUpdateExisting(IItemData sourceItem, IItemData targetItem, DeferredLogWriter <ISerializedAsMasterEvaluatorLogger> deferredUpdateLog, IFieldValueManipulator fieldValueManipulator) { Assert.ArgumentNotNull(targetItem, "targetItem"); Assert.ArgumentNotNull(sourceItem, "sourceItem"); if (sourceItem.Id == RootId) { return(false); // we never want to update the Sitecore root item } // Taking a shortcut for now. If there is a dynamic field value manipiulator, it's true result can only really be obtained when doing the _actual_ write, not when trying to second guess if a write is needed if (fieldValueManipulator != null) { return(true); } // filter out ignored fields before we do the comparison var filteredTargetItem = new FilteredItem(targetItem, _fieldFilter); var filteredSourceItem = new FilteredItem(sourceItem, _fieldFilter); var comparison = _itemComparer.FastCompare(filteredSourceItem, filteredTargetItem); if (comparison.IsRenamed || comparison.IsMoved) { deferredUpdateLog.AddEntry(log => log.Renamed(sourceItem, targetItem)); } if (comparison.IsTemplateChanged) { deferredUpdateLog.AddEntry(log => log.TemplateChanged(sourceItem, targetItem)); } foreach (var sharedChange in comparison.ChangedSharedFields) { deferredUpdateLog.AddEntry(log => log.SharedFieldIsChanged( targetItem, (sharedChange.TargetField ?? sharedChange.SourceField).FieldId, sharedChange.TargetField?.Value, sharedChange.SourceField?.Value)); } foreach (var unversionedChange in comparison.ChangedUnversionedFields) { foreach (var uvField in unversionedChange.ChangedFields) { deferredUpdateLog.AddEntry(log => log.UnversionedFieldIsChanged( targetItem, unversionedChange.Language.Language, (uvField.TargetField ?? uvField.SourceField).FieldId, uvField.TargetField?.Value, uvField.SourceField?.Value)); } } foreach (var versionChange in comparison.ChangedVersions) { if (versionChange.SourceVersion == null) { deferredUpdateLog.AddEntry(log => log.NewTargetVersion(versionChange.TargetVersion, targetItem, sourceItem)); } else if (versionChange.TargetVersion == null) { deferredUpdateLog.AddEntry(log => log.OrphanSourceVersion(sourceItem, targetItem, new[] { versionChange.SourceVersion })); } else { foreach (var field in versionChange.ChangedFields) { var sourceFieldValue = field.SourceField?.Value; var targetFieldValue = field.TargetField?.Value; var fieldId = (field.SourceField ?? field.TargetField).FieldId; deferredUpdateLog.AddEntry(log => log.VersionedFieldIsChanged(targetItem, versionChange.SourceVersion ?? versionChange.TargetVersion, fieldId, targetFieldValue, sourceFieldValue)); } } } return(!comparison.AreEqual); }
public bool Save(IItemData item, IFieldValueManipulator fieldValueManipulator) { return(_innerDataStore.Value.Save(item, fieldValueManipulator)); }
protected virtual bool PasteField(Item item, IItemFieldValue field, bool creatingNewItem, IFieldValueManipulator fieldValueManipulator) { if (!_fieldFilter.Includes(field.FieldId)) { _logger.SkippedPastingIgnoredField(item, field); return(false); } Template template = AssertTemplate(item.Database, item.TemplateID, item.Paths.Path); if (template.GetField(new ID(field.FieldId)) == null) { item.Database.Engines.TemplateEngine.Reset(); template = AssertTemplate(item.Database, item.TemplateID, item.Paths.Path); } if (template.GetField(new ID(field.FieldId)) == null) { throw new TemplateMissingFieldException(item.TemplateName, item.Database.Name + ":" + item.Paths.FullPath, field); } Field itemField = item.Fields[new ID(field.FieldId)]; if (itemField.IsBlobField) { bool hasExistingId = Guid.TryParse(itemField.Value, out var existingBlobId); // serialized blob has no value (media item with detached media) if (!field.BlobId.HasValue) { if (!hasExistingId || existingBlobId == Guid.Empty) { return(false); // no blob ID and none in DB either, so we're cool } // existing blob in DB but none in serialized // so clear the blob and field value ItemManager.RemoveBlobStream(existingBlobId, item.Database); itemField.SetValue(string.Empty, true); _logger.WroteBlobStream(item, field); return(true); } // check if existing blob is here with the same ID; abort if so if (hasExistingId && existingBlobId == field.BlobId.Value) { return(false); } byte[] buffer = Convert.FromBase64String(field.Value); // if an existing blob of a different ID exists, drop it from the database if (existingBlobId != default(Guid)) { ItemManager.RemoveBlobStream(existingBlobId, item.Database); } // write the new blob to the database ItemManager.SetBlobStream(new MemoryStream(buffer, false), field.BlobId.Value, item.Database); // set the value of the blob field to the correct blob ID itemField.SetValue(MainUtil.GuidToString(field.BlobId.Value), true); if (!creatingNewItem) { _logger.WroteBlobStream(item, field); } return(true); } var proposedValue = field.Value; var destinationValue = itemField.GetValue(false, false); var fieldTransformer = fieldValueManipulator?.GetFieldValueTransformer(itemField.Name); if (fieldTransformer != null) { if (fieldTransformer.ShouldDeployFieldValue(destinationValue, proposedValue)) { var oldValue = destinationValue; itemField.SetValue(fieldTransformer.GetFieldValue(oldValue, proposedValue), true); if (!creatingNewItem) { _logger.UpdatedChangedFieldValue(item, field, oldValue); } return(true); } return(false); } if (field.Value != null && !field.Value.Equals(itemField.GetValue(false, false))) { var oldValue = itemField.Value; itemField.SetValue(field.Value, true); if (!creatingNewItem) { _logger.UpdatedChangedFieldValue(item, field, oldValue); } return(true); } return(false); }
protected virtual bool PasteUnversionedLanguage(Item item, IItemLanguage serializedLanguage, bool newItemWasCreated, List <TemplateMissingFieldException> softErrors, IFieldValueManipulator fieldValueManipulator) { Language language = Language.Parse(serializedLanguage.Language.Name); Item targetItem = item.Database.GetItem(item.ID, language); if (targetItem == null) { // this can occasionally occur when syncing a tpSync config CacheManager.ClearAllCaches(); targetItem = item.Database.GetItem(item.ID, language); } Assert.IsNotNull(targetItem, "Target item language to paste unversioned fields into was null."); bool commitEdit = false; try { targetItem.Editing.BeginEdit(); targetItem.RuntimeSettings.ReadOnlyStatistics = true; targetItem.RuntimeSettings.SaveAll = true; var allTargetUnversionedFields = serializedLanguage.Fields.ToLookup(field => field.FieldId); foreach (Field field in targetItem.Fields) { // field was not serialized. Which means the field is either blank or has its standard value, so let's reset it if (field.Unversioned && fieldValueManipulator?.GetFieldValueTransformer(field.Name) == null && !field.Shared && !allTargetUnversionedFields.Contains(field.ID.Guid) && _fieldFilter.Includes(field.ID.Guid)) { _logger.ResetFieldThatDidNotExistInSerialized(field); field.Reset(); commitEdit = true; } } var transformersArray = fieldValueManipulator?.GetFieldValueTransformers(); List <IFieldValueTransformer> transformersList = new List <IFieldValueTransformer>(); if (transformersArray != null) { transformersList = new List <IFieldValueTransformer>(transformersArray); } foreach (var field in serializedLanguage.Fields) { try { if (PasteField(targetItem, field, newItemWasCreated, fieldValueManipulator)) { commitEdit = true; } var t = transformersList.FirstOrDefault(x => x.FieldName.Equals(field.NameHint)); if (t != null) { transformersList.Remove(t); } } catch (TemplateMissingFieldException tex) { softErrors.Add(tex); } } // Whatever remains here are field transformers that are NOT represented in the serialized data foreach (var t in transformersList) { var fieldMeta = targetItem.Template.GetField(t.FieldName); // If the field doesn't exist on the target template, it's time to just skip if (fieldMeta != null) { if (!fieldMeta.IsUnversioned) { continue; } var existingField = targetItem.Fields[fieldMeta.ID]; if (t.ShouldDeployFieldValue(existingField.Value, null)) { var fakeField = new ItemFieldValue(existingField, null); if (PasteField(targetItem, fakeField, newItemWasCreated, fieldValueManipulator)) { commitEdit = true; } } } } // we commit the edit context - and write to the DB - only if we changed something if (commitEdit) { targetItem.Editing.EndEdit(); return(true); } } finally { if (targetItem.Editing.IsEditing) { targetItem.Editing.CancelEdit(); } } return(false); }
protected virtual bool PasteUnversionedFields(IItemData serializedItemData, Item targetItem, bool newItemWasCreated, List <TemplateMissingFieldException> softErrors, IFieldValueManipulator fieldValueManipulator) { bool changeHappened = false; foreach (var language in serializedItemData.UnversionedFields) { bool result = PasteUnversionedLanguage(targetItem, language, newItemWasCreated, softErrors, fieldValueManipulator); if (result) { changeHappened = true; } } return(changeHappened); }
public IItemData Deserialize(IItemData serializedItemData, IFieldValueManipulator fieldValueManipulator) { Assert.ArgumentNotNull(serializedItemData, "serializedItem"); // In regards to https://github.com/SitecoreUnicorn/Unicorn/issues/280 // At no point in these processes, do we expect anything but raw API results - not filtered by version disablers or anything similar // Encapsulating the entire Deserialize call to ensure this // Updated to try and bring back Sitecore 7 compatibility for Rainbow moving forward. https://github.com/SitecoreUnicorn/Rainbow/issues/25 using (new VersionSafeEnforceVersionPresenceDisabler()) { var targetItem = GetOrCreateTargetItem(serializedItemData, out var newItemWasCreated); var softErrors = new List <TemplateMissingFieldException>(); try { bool changeHappened = false; var templateChangeHappened = ChangeTemplateIfNeeded(serializedItemData, targetItem); bool brancheChangeHappened = false; if (!IgnoreBranchId) { brancheChangeHappened = ChangeBranchIfNeeded(serializedItemData, targetItem, newItemWasCreated); } var renameHappened = RenameIfNeeded(serializedItemData, targetItem); ResetTemplateEngineIfItemIsTemplate(targetItem); var sharedFieldsWereChanged = PasteSharedFields(serializedItemData, targetItem, newItemWasCreated, softErrors, fieldValueManipulator); var unversionedFieldsWereChanged = PasteUnversionedFields(serializedItemData, targetItem, newItemWasCreated, softErrors, fieldValueManipulator); var fieldSharingWasUpdated = UpdateFieldSharingIfNeeded(serializedItemData, targetItem); var versionChangeHappened = PasteVersions(serializedItemData, targetItem, newItemWasCreated, softErrors, fieldValueManipulator); if (softErrors.Count > 0) { throw TemplateMissingFieldException.Merge(softErrors); } changeHappened = templateChangeHappened | brancheChangeHappened | renameHappened | sharedFieldsWereChanged | unversionedFieldsWereChanged | fieldSharingWasUpdated | versionChangeHappened; if (!changeHappened) { return(null); } return(new ItemData(targetItem, ParentDataStore)); } catch (ParentForMovedItemNotFoundException) { throw; } catch (ParentItemNotFoundException) { throw; } catch (TemplateMissingFieldException) { throw; } catch (Exception ex) { if (newItemWasCreated) { targetItem.Delete(); ClearCaches(targetItem.Database, new ID(serializedItemData.Id)); } throw new DeserializationException("Failed to paste item: " + serializedItemData.Path, ex); } } }
protected virtual Tuple <bool, Item> PasteVersion(Item item, IItemVersion serializedVersion, bool creatingNewItem, List <TemplateMissingFieldException> softErrors, IFieldValueManipulator fieldValueManipulator) { Language language = Language.Parse(serializedVersion.Language.Name); var targetVersion = Version.Parse(serializedVersion.VersionNumber); Item languageItem = item.Database.GetItem(item.ID, language); if (languageItem == null) { throw new InvalidOperationException("Item retrieved from the database was null. This may indicate issues with the Sitecore cache. Recycle your app pool and try again and it should work."); } Item languageVersionItem = languageItem.Versions[targetVersion]; IDictionary <Guid, IItemFieldValue> serializedVersionFieldsLookup = serializedVersion.Fields.ToDictionary(field => field.FieldId); // Add a new version if we need to for the serialized version we're pasting // NOTE: normally Sitecore will return an empty version when requested (e.g. you can ask for 'en#25' and get a blank version back) // so this null check is only here for legacy support if (languageVersionItem == null) { languageVersionItem = languageItem.Versions.AddVersion(); if (languageVersionItem == null) { throw new InvalidOperationException("Failed to add a new version: AddVersion() returned null."); } if (!creatingNewItem) { _logger.AddedNewVersion(languageVersionItem); } } // Based on the note above, in order to detect the modern form where item.Versions[] returns a blank version no matter what, // we need to check again to see if the version we requested is actually currently present on the item or not, so we can correctly log version adds // ReSharper disable once SimplifyLinqExpression if (!languageVersionItem.Versions.GetVersionNumbers().Any(x => x.Number == languageVersionItem.Version.Number)) { if (!creatingNewItem) { _logger.AddedNewVersion(languageVersionItem); } } // begin writing the version data bool commitEdit = false; try { languageVersionItem.Editing.BeginEdit(); languageVersionItem.RuntimeSettings.ReadOnlyStatistics = true; if (languageVersionItem.Versions.Count == 0) { languageVersionItem.Fields.ReadAll(); } // find versioned fields that need to be reset to standard value, because they are not in serialized // (we do all these checks so we can back out of the edit context and avoid a DB write if we don't need one) foreach (Field field in languageVersionItem.Fields) { // if the field is excluded by the fieldFilter if (!_fieldFilter.Includes(field.ID.Guid)) { continue; } // shared/unversioned fields = ignore, those are handled in their own paste methods if (field.Shared || field.Unversioned) { continue; } // if we have a value in the serialized item, we don't need to reset the field if (serializedVersionFieldsLookup.ContainsKey(field.ID.Guid)) { continue; } // If the field manipulator has something to say about the field, we won't reset it (it might not be present in the serialization data, but needs to be forced a value) if (fieldValueManipulator?.GetFieldValueTransformer(field.Name) != null) { continue; } // if the field is one of revision, updated, or updated by we can specially ignore it, because these will get set below if actual field changes occur // so there's no need to reset them as well if (field.ID == FieldIDs.Revision || field.ID == FieldIDs.UpdatedBy || field.ID == FieldIDs.Updated) { continue; } _logger.ResetFieldThatDidNotExistInSerialized(field); field.Reset(); commitEdit = true; } var transformersArray = fieldValueManipulator?.GetFieldValueTransformers(); List <IFieldValueTransformer> transformersList = new List <IFieldValueTransformer>(); if (transformersArray != null) { transformersList = new List <IFieldValueTransformer>(transformersArray); } bool wasOwnerFieldParsed = false; foreach (IItemFieldValue field in serializedVersion.Fields) { if (field.FieldId == FieldIDs.Owner.Guid) { wasOwnerFieldParsed = true; } try { if (PasteField(languageVersionItem, field, creatingNewItem, fieldValueManipulator)) { commitEdit = true; } var t = transformersList.FirstOrDefault(x => x.FieldName.Equals(field.NameHint)); if (t != null) { transformersList.Remove(t); } } catch (TemplateMissingFieldException tex) { softErrors.Add(tex); } } // Whatever remains here are field transformers that are NOT represented in the serialized data for Versioned fields foreach (var t in transformersList) { var fieldMeta = languageVersionItem.Template.GetField(t.FieldName); // If the field doesn't exist on the target template, it's time to just skip if (fieldMeta != null) { // We're only dealing with Versioned fields here if (fieldMeta.IsUnversioned || fieldMeta.IsShared) { continue; } var existingField = languageVersionItem.Fields[fieldMeta.ID]; if (t.ShouldDeployFieldValue(existingField.Value, null)) { var fakeField = new ItemFieldValue(existingField, null); if (PasteField(languageVersionItem, fakeField, creatingNewItem, fieldValueManipulator)) { commitEdit = true; } } } } if (!wasOwnerFieldParsed) { languageVersionItem.Fields[FieldIDs.Owner].Reset(); commitEdit = true; } // if the item came with blank statistics, and we're creating the item or have version updates already, let's set some sane defaults - update revision, set last updated, etc if (creatingNewItem || commitEdit) { if (!serializedVersionFieldsLookup.ContainsKey(FieldIDs.Revision.Guid)) { languageVersionItem.Fields[FieldIDs.Revision].SetValue(Guid.NewGuid().ToString(), true); } if (!serializedVersionFieldsLookup.ContainsKey(FieldIDs.Updated.Guid)) { languageVersionItem[FieldIDs.Updated] = DateUtil.ToIsoDate(DateTime.UtcNow); } if (!serializedVersionFieldsLookup.ContainsKey(FieldIDs.UpdatedBy.Guid)) { languageVersionItem[FieldIDs.UpdatedBy] = @"sitecore\unicorn"; } } // we commit the edit context - and write to the DB - only if we changed something if (commitEdit) { languageVersionItem.Editing.EndEdit(); } } finally { if (languageVersionItem.Editing.IsEditing) { languageVersionItem.Editing.CancelEdit(); } } if (commitEdit) { ClearCaches(languageVersionItem.Database, languageVersionItem.ID); ResetTemplateEngineIfItemIsTemplate(languageVersionItem); } return(new Tuple <bool, Item>(commitEdit, languageVersionItem)); }
protected virtual bool PasteVersions(IItemData serializedItemData, Item targetItem, bool newItemWasCreated, List <TemplateMissingFieldException> softErrors, IFieldValueManipulator fieldValueManipulator) { Hashtable versionTable = CommonUtils.CreateCIHashtable(); // this version table allows us to detect and remove orphaned versions that are not in the // serialized version, but are in the database version foreach (Item version in targetItem.Versions.GetVersions(true)) { versionTable[version.Uri] = null; } bool anythingChanged = false; foreach (var syncVersion in serializedItemData.Versions) { var res = PasteVersion(targetItem, syncVersion, newItemWasCreated, softErrors, fieldValueManipulator); var committed = res.Item1; if (committed) { anythingChanged = true; } var version = res.Item2; if (versionTable.ContainsKey(version.Uri)) { versionTable.Remove(version.Uri); } } foreach (ItemUri uri in versionTable.Keys) { anythingChanged = true; var versionToRemove = Database.GetItem(uri); _logger.RemovingOrphanedVersion(versionToRemove); versionToRemove.Versions.RemoveVersion(); } return(anythingChanged); }
protected virtual bool PasteSharedFields(IItemData serializedItemData, Item targetItem, bool newItemWasCreated, List <TemplateMissingFieldException> softErrors, IFieldValueManipulator fieldValueManipulator) { bool commitEdit = false; try { targetItem.Editing.BeginEdit(); targetItem.RuntimeSettings.ReadOnlyStatistics = true; targetItem.RuntimeSettings.SaveAll = true; var allTargetSharedFields = serializedItemData.SharedFields.ToLookup(field => field.FieldId); foreach (Field field in targetItem.Fields) { if (field.Shared && fieldValueManipulator?.GetFieldValueTransformer(field.Name) == null && !allTargetSharedFields.Contains(field.ID.Guid) && _fieldFilter.Includes(field.ID.Guid)) { _logger.ResetFieldThatDidNotExistInSerialized(field); field.Reset(); commitEdit = true; } } var transformersArray = fieldValueManipulator?.GetFieldValueTransformers(); List <IFieldValueTransformer> transformersList = new List <IFieldValueTransformer>(); if (transformersArray != null) { transformersList = new List <IFieldValueTransformer>(transformersArray); } foreach (var field in serializedItemData.SharedFields) { try { if (PasteField(targetItem, field, newItemWasCreated, fieldValueManipulator)) { commitEdit = true; } var t = transformersList.FirstOrDefault(x => x.FieldName.Equals(field.NameHint)); if (t != null) { transformersList.Remove(t); } } catch (TemplateMissingFieldException tex) { softErrors.Add(tex); } } // Whatever remains here are field transformers that are NOT represented in the serialized data shared fields foreach (var t in transformersList) { var fieldMeta = targetItem.Template.GetField(t.FieldName); // If the field doesn't exist on the target template, it's time to just skip if (fieldMeta != null) { // We're only dealing with Shared Fields in this method if (!fieldMeta.IsShared) { continue; } var existingField = targetItem.Fields[fieldMeta.ID]; if (t.ShouldDeployFieldValue(existingField.Value, null)) { var fakeField = new ItemFieldValue(existingField, null); if (PasteField(targetItem, fakeField, newItemWasCreated, fieldValueManipulator)) { commitEdit = true; } } } } // we commit the edit context - and write to the DB - only if we changed something if (commitEdit) { targetItem.Editing.EndEdit(); ClearCaches(targetItem.Database, new ID(serializedItemData.Id)); targetItem.Reload(); ResetTemplateEngineIfItemIsTemplate(targetItem); return(true); } } finally { if (targetItem.Editing.IsEditing) { targetItem.Editing.CancelEdit(); } } return(false); }
public void Save(IItemData item, IFieldValueManipulator fieldValueManipulator) { _innerDataStore.Value.Save(item, fieldValueManipulator); }