示例#1
0
        protected override Item LoadItem(Item item, LoadOptions options)
        {
            Assert.ArgumentNotNull(item, "item");

            var itemData = new ItemData(item);

            var configuration = _helper.GetConfigurationForItem(itemData);

            if (configuration == null) return base.LoadItem(item, options);

            var sourceStore = configuration.Resolve<ISourceDataStore>();
            var targetStore = configuration.Resolve<ITargetDataStore>();

            var targetItem = targetStore.GetByPathAndId(itemData.Path, itemData.Id, itemData.DatabaseName);

            if (targetItem == null)
            {
                Log.Warn("Unicorn: Unable to load item because it was not serialized.", this);
                return base.LoadItem(item, options);
            }

            sourceStore.Save(targetItem);

            return Database.GetItem(item.Uri);
        }
示例#2
0
        protected override Item LoadItem(Item item, LoadOptions options)
        {
            Assert.ArgumentNotNull(item, "item");

            var itemData = new ItemData(item);

            var configuration = _helper.GetConfigurationForItem(itemData);

            if (configuration == null)
            {
                return(base.LoadItem(item, options));
            }

            var sourceStore = configuration.Resolve <ISourceDataStore>();
            var targetStore = configuration.Resolve <ITargetDataStore>();

            var targetItem = targetStore.GetByPathAndId(itemData.Path, itemData.Id, itemData.DatabaseName);

            if (targetItem == null)
            {
                Log.Warn("Unicorn: Unable to load item because it was not serialized.", this);
                return(base.LoadItem(item, options));
            }

            sourceStore.Save(targetItem);

            return(Database.GetItem(item.Uri));
        }
示例#3
0
        protected override Item LoadItem(Item item, LoadOptions options)
        {
            Assert.ArgumentNotNull(item, "item");

            var itemData = new ItemData(item);

            var configuration = _helper.GetConfigurationsForItem(itemData).FirstOrDefault();             // if multiple configs contain item, load from first one

            if (configuration == null)
            {
                return(base.LoadItem(item, options));
            }

            var sourceStore = configuration.Resolve <ISourceDataStore>();
            var targetStore = configuration.Resolve <ITargetDataStore>();

            var targetItem = targetStore.GetByPathAndId(itemData.Path, itemData.Id, itemData.DatabaseName);

            if (targetItem == null)
            {
                Log.Warn("Unicorn: Unable to load item because it was not serialized.", this);
                return(base.LoadItem(item, options));
            }

            var result = configuration.Resolve <IPredicate>().Includes(targetItem);

            sourceStore.Save(targetItem, result.FieldValueManipulator);

            return(Database.GetItem(item.Uri));
        }
示例#4
0
        public virtual void SaveItem(ItemDefinition itemDefinition, ItemChanges changes, CallContext context)
        {
            if (DisableSerialization)
            {
                return;
            }

            Assert.ArgumentNotNull(itemDefinition, "itemDefinition");
            Assert.ArgumentNotNull(changes, "changes");

            // get the item we're saving to evaluate with the predicate
            // NOTE: the item in this state may be incomplete as Sitecore can sometimes send partial item data and rely on changes to do the save
            // e.g. during package installations. So we have to merge the changes with any existing item data if we save it later, to keep it consistent.
            IItemData sourceItem = new ItemData(changes.Item);

            if (!_predicate.Includes(sourceItem).IsIncluded)
            {
                return;
            }

            string oldName = changes.Renamed ? changes.Properties["name"].OriginalValue.ToString() : string.Empty;

            if (changes.Renamed && !oldName.Equals(sourceItem.Name, StringComparison.Ordinal))
            // it's a rename, in which the name actually changed (template builder will cause 'renames' for the same name!!!)
            {
                using (new DatabaseCacheDisabler())
                {
                    // disabling the DB caches while running this ensures that any children of the renamed item are retrieved with their proper post-rename paths and thus are not saved at their old location

                    // this allows us to filter out any excluded children by predicate when the data store moves children
                    var predicatedItem = new PredicateFilteredItemData(sourceItem, _predicate);

                    _targetDataStore.MoveOrRenameItem(predicatedItem, changes.Item.Paths.ParentPath + "/" + oldName);
                }

                _logger.RenamedItem(_targetDataStore.FriendlyName, sourceItem, oldName);
            }
            else if (HasConsequentialChanges(changes))
            // it's a simple update - but we reject it if only inconsequential fields (last updated, revision) were changed - again, template builder FTW
            {
                var existingSerializedItem = _targetDataStore.GetByPathAndId(sourceItem.Path, sourceItem.Id, sourceItem.DatabaseName);

                // generated an IItemData from the item changes we received, and apply those changes to the existing serialized item if any
                if (existingSerializedItem != null)
                {
                    sourceItem = new ItemChangeApplyingItemData(existingSerializedItem, changes);
                }
                else
                {
                    sourceItem = new ItemChangeApplyingItemData(changes);
                }

                _targetDataStore.Save(sourceItem);

                AddBlobsToCache(sourceItem);

                _logger.SavedItem(_targetDataStore.FriendlyName, sourceItem, "Saved");
            }
        }
示例#5
0
        public void SaveItem(ItemDefinition itemDefinition, ItemChanges changes, CallContext context)
        {
            if (DisableSerialization)
            {
                return;
            }

            Assert.ArgumentNotNull(itemDefinition, "itemDefinition");
            Assert.ArgumentNotNull(changes, "changes");

            // get the item from the database (note: we don't allow TpSync to be a database here, because we handle that below)
            var sourceItem = GetSourceFromId(changes.Item.ID, allowTpSyncFallback: false);

            if (sourceItem == null)
            {
                if (DisableTransparentSync)
                {
                    return;
                }

                // if TpSync is enabled, we wrap the item changes item directly; the TpSync item will NOT have the new changes as we need to write those here
                sourceItem = new ItemData(changes.Item);
            }

            if (!_predicate.Includes(sourceItem).IsIncluded)
            {
                return;
            }

            string oldName = changes.Renamed ? changes.Properties["name"].OriginalValue.ToString() : string.Empty;

            if (changes.Renamed && !oldName.Equals(sourceItem.Name, StringComparison.Ordinal))
            // it's a rename, in which the name actually changed (template builder will cause 'renames' for the same name!!!)
            {
                using (new DatabaseCacheDisabler())
                {
                    // disabling the DB caches while running this ensures that any children of the renamed item are retrieved with their proper post-rename paths and thus are not saved at their old location

                    // this allows us to filter out any excluded children by predicate when the data store moves children
                    var predicatedItem = new PredicateFilteredItemData(sourceItem, _predicate);

                    _targetDataStore.MoveOrRenameItem(predicatedItem, changes.Item.Paths.ParentPath + "/" + oldName);
                }

                _logger.RenamedItem(_targetDataStore.FriendlyName, sourceItem, oldName);
            }
            else if (HasConsequentialChanges(changes))
            // it's a simple update - but we reject it if only inconsequential fields (last updated, revision) were changed - again, template builder FTW
            {
                _targetDataStore.Save(sourceItem);

                AddBlobsToCache(sourceItem);

                _logger.SavedItem(_targetDataStore.FriendlyName, sourceItem, "Saved");
            }
        }
示例#6
0
        protected override void ProcessRecord()
        {
            var touchedConfigs = new List <IConfiguration>();

            IItemData itemData      = new ItemData(Item);
            var       configuration = _helper.GetConfigurationsForItem(itemData).FirstOrDefault();               // if multiple configs contain item, load from first one

            if (configuration == null)
            {
                throw new InvalidOperationException($"{itemData.GetDisplayIdentifier()} was not part of any Unicorn configurations.");
            }

            touchedConfigs.Add(configuration);

            var logger = new WebConsoleLogger(new PowershellProgressStatus(Host, "Partial Sync Unicorn"), LogLevel);

            var helper          = configuration.Resolve <SerializationHelper>();
            var targetDataStore = configuration.Resolve <ITargetDataStore>();

            itemData = targetDataStore.GetByPathAndId(itemData.Path, itemData.Id, itemData.DatabaseName);

            if (itemData == null)
            {
                throw new InvalidOperationException($"Could not do partial sync of {Item.Database.Name}:{Item.Paths.FullPath} because it was not serialized. You may need to perform initial serialization.");
            }

            try
            {
                logger.Info(
                    $"Processing partial Unicorn configuration {itemData.GetDisplayIdentifier()} (Config: {configuration.Name})");


                using (new LoggingContext(logger, configuration))
                {
                    if (Recurse.IsPresent)
                    {
                        helper.SyncTree(configuration, partialSyncRoot: itemData);
                    }
                    else
                    {
                        var sourceStore = configuration.Resolve <ISourceDataStore>();
                        var result      = configuration.Resolve <IPredicate>().Includes(itemData);
                        sourceStore.Save(itemData, result.FieldValueManipulator);
                    }
                }
            }
            catch (Exception ex)
            {
                logger.Error(ex);
                throw;
            }

            CorePipeline.Run("unicornSyncEnd", new UnicornSyncEndPipelineArgs(new SitecoreLogger(), true, touchedConfigs.ToArray()));
        }
示例#7
0
        public void CopyItem(ItemDefinition source, ItemDefinition destination, string copyName, ID copyId, CallContext context)
        {
            if (DisableSerialization)
            {
                return;
            }

            // copying is easy - all we have to do is serialize the copyID. Copied children will all result in multiple calls to CopyItem so we don't even need to worry about them.
            var copiedItem = new ItemData(Database.GetItem(copyId), _sourceDataStore);

            if (!_predicate.Includes(copiedItem).IsIncluded)
            {
                return;                                                          // destination parent is not in a path that we are serializing, so skip out
            }
            _targetDataStore.Save(copiedItem);
            _logger.CopiedItem(_targetDataStore.FriendlyName, () => GetSourceFromId(source.ID), copiedItem);
        }
示例#8
0
        protected override Item LoadItem(Item item, LoadOptions options)
        {
            Assert.ArgumentNotNull(item, "item");

            IItemData itemData = new ItemData(item);

            var configuration = _helper.GetConfigurationForItem(itemData);

            if (configuration == null)
            {
                return(base.LoadItem(item, options));
            }


            var logger          = configuration.Resolve <ILogger>();
            var helper          = configuration.Resolve <SerializationHelper>();
            var targetDataStore = configuration.Resolve <ITargetDataStore>();

            itemData = targetDataStore.GetByPathAndId(itemData.Path, itemData.Id, itemData.DatabaseName);

            if (itemData == null)
            {
                logger.Warn("Command sync: Could not do partial sync of " + item.Paths.FullPath + " because the root was not serialized.");
                return(item);
            }

            try
            {
                logger.Info("Command Sync: Processing partial Unicorn configuration " + configuration.Name + " under " + itemData.Path);

                helper.SyncTree(configuration, roots: new[] { itemData });

                logger.Info("Command Sync: Completed syncing partial Unicorn configuration " + configuration.Name + " under " + itemData.Path);
            }
            catch (Exception ex)
            {
                logger.Error(ex);
                throw;
            }

            CorePipeline.Run("unicornSyncEnd", new UnicornSyncEndPipelineArgs(configuration));

            return(Database.GetItem(item.Uri));
        }
        protected override void ProcessRecord()
        {
            IItemData itemData = new ItemData(Item);

            if (Recurse.IsPresent)
            {
                if (!_helper.ReserializeTree(itemData))
                {
                    throw new InvalidOperationException($"{itemData.GetDisplayIdentifier()} was not part of any Unicorn configuration.");
                }
            }
            else
            {
                if (!_helper.ReserializeItem(itemData))
                {
                    throw new InvalidOperationException($"{itemData.GetDisplayIdentifier()} was not part of any Unicorn configuration.");
                }
            }
        }
示例#10
0
		protected override Item LoadItem(Item item, LoadOptions options)
		{
			Assert.ArgumentNotNull(item, "item");

			IItemData itemData = new ItemData(item);

			var configuration = _helper.GetConfigurationsForItem(itemData).FirstOrDefault(); // if multiple configs contain item, load from first one

			if (configuration == null) return base.LoadItem(item, options);


			var logger = configuration.Resolve<ILogger>();
			var helper = configuration.Resolve<SerializationHelper>();
			var targetDataStore = configuration.Resolve<ITargetDataStore>();

			itemData = targetDataStore.GetByPathAndId(itemData.Path, itemData.Id, itemData.DatabaseName);

			if (itemData == null)
			{
				logger.Warn("Command sync: Could not do partial sync of " + item.Paths.FullPath + " because the root was not serialized.");
				return item;
			}

			try
			{
				logger.Info("Command Sync: Processing partial Unicorn configuration " + configuration.Name + " under " + itemData.Path);

				helper.SyncTree(configuration, roots: new[] { itemData });

				logger.Info("Command Sync: Completed syncing partial Unicorn configuration " + configuration.Name + " under " + itemData.Path);
			}
			catch (Exception ex)
			{
				logger.Error(ex);
				throw;
			}

			CorePipeline.Run("unicornSyncEnd", new UnicornSyncEndPipelineArgs(new StringProgressStatus(), configuration));

			return Database.GetItem(item.Uri);
		}
示例#11
0
        /// <summary>
        /// Restoring items from the recycle bin does not invoke the data provider at all, so we have to attach to its event
        /// to cause restored items to be rewritten to disk if they are included.
        /// </summary>
        protected virtual void HandleItemRestored(RestoreItemCompletedEvent restoreItemCompletedEvent)
        {
            if (!restoreItemCompletedEvent.DatabaseName.Equals(Database.Name, StringComparison.Ordinal))
            {
                return;
            }

            // we use a timer to delay the execution of our handler for a couple seconds.
            // at the time the handler is called, calling Database.GetItem(id) returns NULL,
            // or without cache an item with an orphan path. The delay allows Sitecore to catch up
            // with it.
            new Timer(state =>
            {
                var item = GetItemFromId(new ID(restoreItemCompletedEvent.ItemId), true);

                Assert.IsNotNull(item, "Item that was restored was null.");

                var iitem = new ItemData(item);

                SerializeItemIfIncluded(iitem, "Restored");
            }, null, 2000, Timeout.Infinite);
        }
		protected override string GetDialogText(SaveArgs args)
		{
			var results = new Dictionary<Item, IList<string>>();

			foreach (var item in args.Items)
			{
				// we grab the existing item from the database. This will NOT include the changed values we're saving.
				// this is because we want to verify that the base state of the item matches serialized, NOT the state we're saving.
				// if the base state and the serialized state match we can be pretty sure that the changes we are writing won't clobber anything serialized but not synced
				Item existingItem = Client.ContentDatabase.GetItem(item.ID, item.Language, item.Version);

				Assert.IsNotNull(existingItem, "Existing item {0} did not exist! This should never occur.", item.ID);

				var existingSitecoreItem = new ItemData(existingItem);

				foreach (var configuration in _configurations)
				{
					// ignore conflict checks if Transparent Sync is turned on (in which case this is a tautology - 'get from database' would be 'get from disk' so it always matches
					if (configuration.Resolve<IUnicornDataProviderConfiguration>().EnableTransparentSync) continue;

					// ignore conflicts on items that Unicorn is not managing
					if (!configuration.Resolve<IPredicate>().Includes(existingSitecoreItem).IsIncluded) continue;

					// evaluator signals that it does not care about conflicts (e.g. NIO)
					if (!configuration.Resolve<IEvaluator>().ShouldPerformConflictCheck(existingItem)) continue;

					IItemData serializedItemData = configuration.Resolve<ITargetDataStore>().GetByPathAndId(existingSitecoreItem.Path, existingSitecoreItem.Id, existingSitecoreItem.DatabaseName);

					// not having an existing serialized version means no possibility of conflict here
					if (serializedItemData == null) continue;

					var fieldFilter = configuration.Resolve<IFieldFilter>();
					var itemComparer = configuration.Resolve<IItemComparer>();

					var fieldIssues = GetFieldSyncStatus(existingSitecoreItem, serializedItemData, fieldFilter, itemComparer);

					if (fieldIssues.Count == 0) continue;

					results[existingItem] = fieldIssues;
				}
			}

			// no problems
			if (results.Count == 0) return null;

			var sb = new StringBuilder();

			if (About.Version.StartsWith("7") || About.Version.StartsWith("6"))
			{
				// older Sitecores used \n to format dialog text

				sb.Append("CRITICAL MESSAGE FROM UNICORN:\n");
				sb.Append("You need to run a Unicorn sync. The following fields did not match the serialized version:\n");

				foreach (var item in results)
				{
					if (results.Count > 1)
						sb.AppendFormat("\n{0}: {1}", item.Key.DisplayName, string.Join(", ", item.Value));
					else
						sb.AppendFormat("\n{0}", string.Join(", ", item.Value));
				}

				sb.Append("\n\nDo you want to overwrite anyway?\nTHIS MAY CAUSE LOST WORK.");
			}
			else
			{
				// Sitecore 8.x+ uses HTML to format dialog text
				sb.Append("<p style=\"font-weight: bold; margin-bottom: 1em;\">CRITICAL MESSAGE FROM UNICORN:</p>");
				sb.Append("<p>You need to run a Unicorn sync. The following fields did not match the serialized version:</p><ul style=\"margin: 1em 0\">");

				foreach (var item in results)
				{
					if (results.Count > 1)
						sb.AppendFormat("<li>{0}: {1}</li>", item.Key.DisplayName, string.Join(", ", item.Value));
					else
						sb.AppendFormat("<li>{0}</li>", string.Join(", ", item.Value));
				}

				sb.Append("</ul><p>Do you want to overwrite anyway?<br>THIS MAY CAUSE LOST WORK.</p>");
			}

			return sb.ToString();
		}
示例#13
0
        public void CopyItem(ItemDefinition source, ItemDefinition destination, string copyName, ID copyId, CallContext context)
        {
            if (DisableSerialization) return;

            // copying is easy - all we have to do is serialize the copyID. Copied children will all result in multiple calls to CopyItem so we don't even need to worry about them.
            var copiedItem = new ItemData(Database.GetItem(copyId), _sourceDataStore);

            if (!_predicate.Includes(copiedItem).IsIncluded) return; // destination parent is not in a path that we are serializing, so skip out

            _targetDataStore.Save(copiedItem);
            _logger.CopiedItem(_targetDataStore.FriendlyName, () => GetSourceFromId(source.ID), copiedItem);
        }
示例#14
0
        public void SaveItem(ItemDefinition itemDefinition, ItemChanges changes, CallContext context)
        {
            if (DisableSerialization) return;

            Assert.ArgumentNotNull(itemDefinition, "itemDefinition");
            Assert.ArgumentNotNull(changes, "changes");

            // get the item from the database (note: we don't allow TpSync to be a database here, because we handle that below)
            var sourceItem = GetSourceFromId(changes.Item.ID, allowTpSyncFallback: false);

            if (sourceItem == null)
            {
                if (DisableTransparentSync) return;

                // if TpSync is enabled, we wrap the item changes item directly; the TpSync item will NOT have the new changes as we need to write those here
                sourceItem = new ItemData(changes.Item);
            }

            if (!_predicate.Includes(sourceItem).IsIncluded) return;

            string oldName = changes.Renamed ? changes.Properties["name"].OriginalValue.ToString() : string.Empty;
            if (changes.Renamed && !oldName.Equals(sourceItem.Name, StringComparison.Ordinal))
            // it's a rename, in which the name actually changed (template builder will cause 'renames' for the same name!!!)
            {
                using (new DatabaseCacheDisabler())
                {
                    // disabling the DB caches while running this ensures that any children of the renamed item are retrieved with their proper post-rename paths and thus are not saved at their old location

                    // this allows us to filter out any excluded children by predicate when the data store moves children
                    var predicatedItem = new PredicateFilteredItemData(sourceItem, _predicate);

                    _targetDataStore.MoveOrRenameItem(predicatedItem, changes.Item.Paths.ParentPath + "/" + oldName);
                }

                _logger.RenamedItem(_targetDataStore.FriendlyName, sourceItem, oldName);
            }
            else if (HasConsequentialChanges(changes))
            // it's a simple update - but we reject it if only inconsequential fields (last updated, revision) were changed - again, template builder FTW
            {
                _targetDataStore.Save(sourceItem);

                AddBlobsToCache(sourceItem);

                _logger.SavedItem(_targetDataStore.FriendlyName, sourceItem, "Saved");
            }
        }
示例#15
0
        /// <summary>
        /// Restoring items from the recycle bin does not invoke the data provider at all, so we have to attach to its event
        /// to cause restored items to be rewritten to disk if they are included.
        /// </summary>
        protected virtual void HandleItemRestored(RestoreItemCompletedEvent restoreItemCompletedEvent)
        {
            if (!restoreItemCompletedEvent.DatabaseName.Equals(Database.Name, StringComparison.Ordinal)) return;

            // we use a timer to delay the execution of our handler for a couple seconds.
            // at the time the handler is called, calling Database.GetItem(id) returns NULL,
            // or without cache an item with an orphan path. The delay allows Sitecore to catch up
            // with it.
            new Timer(state =>
            {
                var item = GetItemFromId(new ID(restoreItemCompletedEvent.ItemId), true);

                Assert.IsNotNull(item, "Item that was restored was null.");

                var iitem = new ItemData(item);

                SerializeItemIfIncluded(iitem, "Restored");
            }, null, 2000, Timeout.Infinite);
        }
        protected override string GetDialogText(SaveArgs args)
        {
            var results = new Dictionary<Item, IList<string>>();

            try
            {
                foreach (var item in args.Items)
                {
                    Item existingItem = Client.ContentDatabase.GetItem(item.ID, item.Language, item.Version);

                    Assert.IsNotNull(existingItem, "Existing item {0} did not exist! This should never occur.", item.ID);

                    var existingSitecoreItem = new ItemData(existingItem);

                    foreach (var configuration in _configurations)
                    {
                        // ignore conflicts on items that Unicorn is not managing
                        if (!configuration.Resolve<IPredicate>().Includes(existingSitecoreItem).IsIncluded) continue;

                        IItemData serializedItemData = configuration.Resolve<ITargetDataStore>().GetByPathAndId(existingSitecoreItem.Path, existingSitecoreItem.Id, existingSitecoreItem.DatabaseName);

                        // not having an existing serialized version means no possibility of conflict here
                        if (serializedItemData == null) continue;

                        var fieldFilter = configuration.Resolve<IFieldFilter>();
                        var itemComparer = configuration.Resolve<IItemComparer>();

                        var fieldIssues = GetFieldSyncStatus(existingSitecoreItem, serializedItemData, fieldFilter, itemComparer);

                        if (fieldIssues.Count == 0) continue;

                        results.Add(existingItem, fieldIssues);
                    }

                }

                // no problems
                if (results.Count == 0) return null;

                var sb = new StringBuilder();
                sb.Append("CRITICAL MESSAGE FROM UNICORN:\n");
                sb.Append("You need to run a Unicorn sync. The following fields did not match the serialized version:\n");

                foreach (var item in results)
                {
                    if(results.Count > 1)
                        sb.AppendFormat("\n{0}: {1}", item.Key.DisplayName, string.Join(", ", item.Value));
                    else
                        sb.AppendFormat("\n{0}", string.Join(", ", item.Value));
                }

                sb.Append("\n\nDo you want to overwrite anyway?\nTHIS MAY CAUSE LOST WORK.");

                return sb.ToString();
            }
            catch (Exception ex)
            {
                Log.Error("Exception occurred while performing serialization conflict check!", ex, this);
                return "Exception occurred: " + ex.Message; // this will cause a few retries
            }
        }
示例#17
0
        public virtual void SaveItem(ItemDefinition itemDefinition, ItemChanges changes, CallContext context)
        {
            if (DisableSerialization)
            {
                return;
            }

            Assert.ArgumentNotNull(itemDefinition, "itemDefinition");
            Assert.ArgumentNotNull(changes, "changes");

            // get the item we're saving to evaluate with the predicate
            // NOTE: the item in this state may be incomplete as Sitecore can sometimes send partial item data and rely on changes to do the save
            // e.g. during package installations. So we have to merge the changes with any existing item data if we save it later, to keep it consistent.
            IItemData sourceItem = new ItemData(changes.Item);

            if (!_predicate.Includes(sourceItem).IsIncluded)
            {
                return;
            }

            // reject if only inconsequential fields (e.g. last updated, revision) were changed - again, template builder FTW with the junk saves
            if (!HasConsequentialChanges(changes))
            {
                return;
            }

            string existingItemPath = sourceItem.Path;

            // check if the save includes a rename as part of the operation, in which case we have to get the existing item, if any, from the OLD path pre-rename
            // note that if an item is renamed to the same name this will simply fall through as not a rename
            if (changes.Renamed)
            {
                string oldName = changes.Properties["name"].OriginalValue.ToString();
                existingItemPath = changes.Item.Paths.ParentPath + "/" + oldName;
            }

            // we find the existing serialized item, with which we want to merge the item changes, if it exists. If not then the changes are the source of all truth.
            var existingSerializedItem = _targetDataStore.GetByPathAndId(existingItemPath, sourceItem.Id, sourceItem.DatabaseName);

            // generate an IItemData from the item changes we received, and apply those changes to the existing serialized item if any
            var changesAppliedItem = existingSerializedItem != null ? new ItemChangeApplyingItemData(existingSerializedItem, changes) : new ItemChangeApplyingItemData(changes);

            // put any media blob IDs on this item into the media blob cache (used for TpSync media - does not cache the blob just the filename it lives in)
            AddBlobsToCache(changesAppliedItem);

            // check for renamed item (existing path != source path -> rename)
            if (!existingItemPath.Equals(sourceItem.Path, StringComparison.Ordinal))
            {
                // this allows us to filter out any excluded children when the data store moves children to the renamed path
                var predicatedItem = new PredicateFilteredItemData(changesAppliedItem, _predicate);

                // change the item's name before sending it to the data store (note: the data store will normalize any child paths for us)
                var alteredPathItem = new RenamedItemData(predicatedItem, sourceItem.Name);

                _targetDataStore.MoveOrRenameItem(alteredPathItem, existingItemPath);

                _logger.RenamedItem(_targetDataStore.FriendlyName, alteredPathItem, existingItemPath.Substring(existingItemPath.LastIndexOf('/') + 1));

                return;
            }

            // if we get here, it's just a save, not a rename
            _targetDataStore.Save(changesAppliedItem);

            _logger.SavedItem(_targetDataStore.FriendlyName, changesAppliedItem, "Saved");
        }
        protected override string GetDialogText(SaveArgs args)
        {
            var results = new Dictionary <Item, IList <string> >();

            try
            {
                foreach (var item in args.Items)
                {
                    // we grab the existing item from the database. This will NOT include the changed values we're saving.
                    // this is because we want to verify that the base state of the item matches serialized, NOT the state we're saving.
                    // if the base state and the serialized state match we can be pretty sure that the changes we are writing won't clobber anything serialized but not synced
                    Item existingItem = Client.ContentDatabase.GetItem(item.ID, item.Language, item.Version);

                    Assert.IsNotNull(existingItem, "Existing item {0} did not exist! This should never occur.", item.ID);

                    var existingSitecoreItem = new ItemData(existingItem);

                    foreach (var configuration in _configurations)
                    {
                        // ignore conflicts on items that Unicorn is not managing
                        if (!configuration.Resolve <IPredicate>().Includes(existingSitecoreItem).IsIncluded)
                        {
                            continue;
                        }

                        IItemData serializedItemData = configuration.Resolve <ITargetDataStore>().GetByPathAndId(existingSitecoreItem.Path, existingSitecoreItem.Id, existingSitecoreItem.DatabaseName);

                        // not having an existing serialized version means no possibility of conflict here
                        if (serializedItemData == null)
                        {
                            continue;
                        }

                        var fieldFilter  = configuration.Resolve <IFieldFilter>();
                        var itemComparer = configuration.Resolve <IItemComparer>();

                        var fieldIssues = GetFieldSyncStatus(existingSitecoreItem, serializedItemData, fieldFilter, itemComparer);

                        if (fieldIssues.Count == 0)
                        {
                            continue;
                        }

                        results.Add(existingItem, fieldIssues);
                    }
                }

                // no problems
                if (results.Count == 0)
                {
                    return(null);
                }

                var sb = new StringBuilder();
                sb.Append("CRITICAL MESSAGE FROM UNICORN:\n");
                sb.Append("You need to run a Unicorn sync. The following fields did not match the serialized version:\n");

                foreach (var item in results)
                {
                    if (results.Count > 1)
                    {
                        sb.AppendFormat("\n{0}: {1}", item.Key.DisplayName, string.Join(", ", item.Value));
                    }
                    else
                    {
                        sb.AppendFormat("\n{0}", string.Join(", ", item.Value));
                    }
                }

                sb.Append("\n\nDo you want to overwrite anyway?\nTHIS MAY CAUSE LOST WORK.");

                return(sb.ToString());
            }
            catch (Exception ex)
            {
                Log.Error("Exception occurred while performing serialization conflict check!", ex, this);
                return("Exception occurred: " + ex.Message);                // this will cause a few retries
            }
        }
示例#19
0
        protected override string GetDialogText(SaveArgs args)
        {
            var results = new Dictionary <Item, IList <string> >();

            foreach (var item in args.Items)
            {
                // we grab the existing item from the database. This will NOT include the changed values we're saving.
                // this is because we want to verify that the base state of the item matches serialized, NOT the state we're saving.
                // if the base state and the serialized state match we can be pretty sure that the changes we are writing won't clobber anything serialized but not synced
                Item existingItem = Client.ContentDatabase.GetItem(item.ID, item.Language, item.Version);

                Assert.IsNotNull(existingItem, "Existing item {0} did not exist! This should never occur.", item.ID);

                var existingSitecoreItem = new ItemData(existingItem);

                foreach (var configuration in _configurations)
                {
                    // ignore conflict checks if Transparent Sync is turned on (in which case this is a tautology - 'get from database' would be 'get from disk' so it always matches
                    if (configuration.Resolve <IUnicornDataProviderConfiguration>().EnableTransparentSync)
                    {
                        continue;
                    }

                    var result = configuration.Resolve <IPredicate>().Includes(existingSitecoreItem);
                    // ignore conflicts on items that Unicorn is not managing
                    if (!result.IsIncluded)
                    {
                        continue;
                    }

                    // If a Field Value Manipulator is attached to the predicate, we _expect_ Serialized content to be different from what is in Sitecore
                    if (result.FieldValueManipulator != null)
                    {
                        continue;
                    }

                    // evaluator signals that it does not care about conflicts (e.g. NIO)
                    if (!configuration.Resolve <IEvaluator>().ShouldPerformConflictCheck(existingItem))
                    {
                        continue;
                    }

                    IItemData serializedItemData = configuration.Resolve <ITargetDataStore>().GetByPathAndId(existingSitecoreItem.Path, existingSitecoreItem.Id, existingSitecoreItem.DatabaseName);

                    // not having an existing serialized version means no possibility of conflict here
                    if (serializedItemData == null)
                    {
                        continue;
                    }

                    var fieldFilter  = configuration.Resolve <IFieldFilter>();
                    var itemComparer = configuration.Resolve <IItemComparer>();

                    var fieldIssues = GetFieldSyncStatus(existingSitecoreItem, serializedItemData, fieldFilter, itemComparer);

                    if (fieldIssues.Count == 0)
                    {
                        continue;
                    }

                    results[existingItem] = fieldIssues;
                }
            }

            // no problems
            if (results.Count == 0)
            {
                return(null);
            }

            var sb = new StringBuilder();

            if (About.Version.StartsWith("7") || About.Version.StartsWith("6"))
            {
                // older Sitecores used \n to format dialog text

                sb.Append("CRITICAL MESSAGE FROM UNICORN:\n");
                sb.Append("You need to run a Unicorn sync. The following fields did not match the serialized version:\n");

                foreach (var item in results)
                {
                    if (results.Count > 1)
                    {
                        sb.AppendFormat("\n{0}: {1}", item.Key.DisplayName, string.Join(", ", item.Value));
                    }
                    else
                    {
                        sb.AppendFormat("\n{0}", string.Join(", ", item.Value));
                    }
                }

                sb.Append("\n\nDo you want to overwrite anyway?\nTHIS MAY CAUSE LOST WORK.");
            }
            else
            {
                // Sitecore 8.x+ uses HTML to format dialog text
                sb.Append("<p style=\"font-weight: bold; margin-bottom: 1em;\">CRITICAL MESSAGE FROM UNICORN:</p>");
                sb.Append("<p>You need to run a Unicorn sync. The following fields did not match the serialized version:</p><ul style=\"margin: 1em 0\">");

                foreach (var item in results)
                {
                    if (results.Count > 1)
                    {
                        sb.AppendFormat("<li>{0}: {1}</li>", item.Key.DisplayName, string.Join(", ", item.Value));
                    }
                    else
                    {
                        sb.AppendFormat("<li>{0}</li>", string.Join(", ", item.Value));
                    }
                }

                sb.Append("</ul><p>Do you want to overwrite anyway?<br>THIS MAY CAUSE LOST WORK.</p>");
            }

            return(sb.ToString());
        }
示例#20
0
        public virtual void SaveItem(ItemDefinition itemDefinition, ItemChanges changes, CallContext context)
        {
            if (DisableSerialization) return;

            Assert.ArgumentNotNull(itemDefinition, "itemDefinition");
            Assert.ArgumentNotNull(changes, "changes");

            // get the item we're saving to evaluate with the predicate
            // NOTE: the item in this state may be incomplete as Sitecore can sometimes send partial item data and rely on changes to do the save
            // e.g. during package installations. So we have to merge the changes with any existing item data if we save it later, to keep it consistent.
            IItemData sourceItem = new ItemData(changes.Item);

            if (!_predicate.Includes(sourceItem).IsIncluded) return;

            string oldName = changes.Renamed ? changes.Properties["name"].OriginalValue.ToString() : string.Empty;
            if (changes.Renamed && !oldName.Equals(sourceItem.Name, StringComparison.Ordinal))
            // it's a rename, in which the name actually changed (template builder will cause 'renames' for the same name!!!)
            {
                using (new DatabaseCacheDisabler())
                {
                    // disabling the DB caches while running this ensures that any children of the renamed item are retrieved with their proper post-rename paths and thus are not saved at their old location

                    // this allows us to filter out any excluded children by predicate when the data store moves children
                    var predicatedItem = new PredicateFilteredItemData(sourceItem, _predicate);

                    _targetDataStore.MoveOrRenameItem(predicatedItem, changes.Item.Paths.ParentPath + "/" + oldName);
                }

                _logger.RenamedItem(_targetDataStore.FriendlyName, sourceItem, oldName);
            }
            else if (HasConsequentialChanges(changes))
            // it's a simple update - but we reject it if only inconsequential fields (last updated, revision) were changed - again, template builder FTW
            {
                var existingSerializedItem = _targetDataStore.GetByPathAndId(sourceItem.Path, sourceItem.Id, sourceItem.DatabaseName);

                // generated an IItemData from the item changes we received, and apply those changes to the existing serialized item if any
                if (existingSerializedItem != null) sourceItem = new ItemChangeApplyingItemData(existingSerializedItem, changes);
                else sourceItem = new ItemChangeApplyingItemData(changes);

                _targetDataStore.Save(sourceItem);

                AddBlobsToCache(sourceItem);

                _logger.SavedItem(_targetDataStore.FriendlyName, sourceItem, "Saved");
            }
        }