November Happy Hour will be moved to Thursday December 5th.

Detect when StopPublish is set via "Manage Expiration and Archiving"?

Vote:
 

Hello!

We have a little difficult situation regarding expiration dates and I hope I can get some assistance!

We have an Event page type. This has am EventData property containing a JSON array of start/end dates (one Event can have several pairs of start/end dates).

We have an InitializationModule that adds a PageEventHandler for the DataFactory.Instance.SavingPage method. In this method, we check if the page is of the Event type and if it contains any start/end dates. If so, we set the StopPublish to the page to the last end date in the JSON array.

This makes our Event pages automatically archived when their last end date has passed, and if the Event is updated with new start/end dates, the expiry date is updated accordingly. All good.

The problem arises when we want to manually expire an Event using the "Manage Expiration and Archiving" menu (this is in Episerver 7.5 by the way). When we set it to expire "now" and hit Save, of course our SavingPage method will kick in, and sets the expiry date to the Event's last end date, instead of "now". Which pretty much makes it impossible to manually expire an Event page.

So, in this SavingPage event handler, is there any way to detect whether the new StopPublish value was updated from the UI (when the "Save" button was clicked in "Manage Expiration and Archiving"), or if its EventData property was updated ?

Things we need to support:

  • We have to allow a previously expired Event to be reactivated by updating its EventData with a future start/end date
  • Unpublished Event pages must also be allowed to get a StopPublish from the last end date in its EventData property
    (this means that we cannot exclude unpublished pages in our SavingPage method)

Any help is greatly appreciated.

John

#120418
Apr 16, 2015 14:28
Vote:
 

Hi!

One idea is to replace IContentChangeManager service with your own implementation.

[ServiceConfiguration(typeof(IContentChangeManager))]
public class MyContentChangeManager : ContentChangeManager
{
    public MyContentChangeManager(IContentRepository contentRepository, IContentTypeRepository contentTypeRepository, IValidationService validationService, LanguageSelectorFactory languageSelectorFactory, IEnumerable<ICommitter> committers, PropertyResolver propertyResolver, IObjectSerializerFactory serializerFactory, IMetaDataResolver metaDataResolver, IPropertyDefinitionRepository propertyDefinitionRepository, LocalizationService localizationService, ContentAssetHelper contentAssetHelper, Settings settings, SiteDefinitionRepository siteDefinitionRepository, EPiServer.Security.PermissionService permissionService)
        : base(contentRepository, contentTypeRepository, validationService, languageSelectorFactory, committers, propertyResolver, serializerFactory, metaDataResolver, propertyDefinitionRepository, localizationService, contentAssetHelper, settings, siteDefinitionRepository, permissionService)
    {
    }

    public override PropertiesUpdateResult UpdateContentProperties(ContentReference contentReference, IDictionary<string, string> properties, SaveAction saveAction)
    {
        if (IsContentExpirationChanged(properties) && !HttpContext.Current.Items.Contains("ContentExpirationChanged"))
        {
            HttpContext.Current.Items.Add("ContentExpirationChanged", "true");
        }

        return base.UpdateContentProperties(contentReference, properties, saveAction);
    }

    private bool IsContentExpirationChanged(IDictionary<string, string> properties)
    {
        return properties != null && properties.Count == 1 && properties.ContainsKey("iversionable_expire");
    }
}

If you're using StructureMap you can just replace it with the following code:

For<IContentChangeManager>().Use<MyContentChangeManager>();

Then in your PageEventHandler you can check if HttpContext.Current.Items contains the item that gets added in MyContentChangeManager:

var isContentExpirationChanged = HttpContext.Current.Items.Contains("ContentExpirationChanged");

if (isContentExpirationChanged)
{
    bool dontTouchStopPublish = true;
}
#120640
Edited, Apr 21, 2015 18:35
Vote:
 

Fantastic set of dependencies ;)

#120644
Apr 21, 2015 19:34
Vote:
 

Thanks for the suggestions, but I opted for the scheduled jobs path instead. Here is my solution:

	[ScheduledPlugIn(DisplayName = "Sätt avpubliceringsdatum på evenemang", SortIndex = 999)]
	public class SetExpirationOnEventsJob : JobBase
	{
		public override string Execute()
		{
			var contentRepository = ServiceLocator.Current.GetInstance<IContentRepository>();
			var startPage = contentRepository.Get<StartPage>(ContentReference.StartPage);
			var eventPages = contentRepository.GetChildren<EventPage>(startPage.EventsRoot, LanguageSelector.AutoDetect(true));

			var returnString = "";
			var updatedEvents = new Dictionary<string, int>();

			foreach (var eventPage in eventPages)
			{
				var languageVersions = contentRepository.GetLanguageBranches<EventPage>(eventPage.PageLink);
				foreach (var languageVersion in languageVersions)
				{
					var content = languageVersion.CreateWritableClone() as EventPage;
					var updated = EventExpireHelper.SetExpiryDate(content);
					if (updated)
					{
						if (updatedEvents.ContainsKey(languageVersion.Language.EnglishName))
						{
							updatedEvents[languageVersion.Language.EnglishName]++;
						}
						else
						{
							updatedEvents.Add(languageVersion.Language.EnglishName, 1);
						}
						contentRepository.Save(content, SaveAction.ForceCurrentVersion);
						if (content != null) DataFactoryCache.RemovePage(content.ContentLink);
					}
				}
			}

			if (updatedEvents.Any())
			{
				foreach (var keyValuePair in updatedEvents)
				{
					returnString += string.Format("{0} event(s) updated in {1}, ", keyValuePair.Value, keyValuePair.Key);
				}
				returnString = returnString.TrimEnd(new[] { ' ', ',' });
			}
			else
			{
				returnString = "No events updated";
			}

			return returnString;
		}
	}
#120722
Apr 23, 2015 11:13
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.