Files
Umbraco-CMS/src/Umbraco.Core/Models/ContentScheduleCollection.cs

220 lines
8.8 KiB
C#
Raw Normal View History

using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
namespace Umbraco.Core.Models
{
public class ContentScheduleCollection : INotifyCollectionChanged, IDeepCloneable, IEquatable<ContentScheduleCollection>
{
2018-11-08 13:15:56 +11:00
//underlying storage for the collection backed by a sorted list so that the schedule is always in order of date and that duplicate dates per culture are not allowed
private readonly Dictionary<string, SortedList<DateTime, ContentSchedule>> _schedule
= new Dictionary<string, SortedList<DateTime, ContentSchedule>>(StringComparer.InvariantCultureIgnoreCase);
public event NotifyCollectionChangedEventHandler CollectionChanged;
private void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
{
CollectionChanged?.Invoke(this, args);
}
/// <summary>
/// Add an existing schedule
/// </summary>
/// <param name="schedule"></param>
public void Add(ContentSchedule schedule)
{
if (!_schedule.TryGetValue(schedule.Culture, out var changes))
{
changes = new SortedList<DateTime, ContentSchedule>();
_schedule[schedule.Culture] = changes;
}
//TODO: Below will throw if there are duplicate dates added, validate/return bool?
changes.Add(schedule.Date, schedule);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, schedule));
}
/// <summary>
/// Adds a new schedule for invariant content
/// </summary>
/// <param name="releaseDate"></param>
/// <param name="expireDate"></param>
public void Add(DateTime? releaseDate, DateTime? expireDate)
{
Add(string.Empty, releaseDate, expireDate);
}
/// <summary>
/// Adds a new schedule for a culture
/// </summary>
/// <param name="culture"></param>
/// <param name="releaseDate"></param>
/// <param name="expireDate"></param>
public void Add(string culture, DateTime? releaseDate, DateTime? expireDate)
{
if (culture == null) throw new ArgumentNullException(nameof(culture));
if (releaseDate.HasValue && expireDate.HasValue && releaseDate >= expireDate)
throw new InvalidOperationException($"The {nameof(releaseDate)} must be less than {nameof(expireDate)}");
if (!releaseDate.HasValue && !expireDate.HasValue) return;
//TODO: Do we allow passing in a release or expiry date that is before now?
if (!_schedule.TryGetValue(culture, out var changes))
{
changes = new SortedList<DateTime, ContentSchedule>();
_schedule[culture] = changes;
2018-11-08 16:33:19 +01:00
}
//TODO: Below will throw if there are duplicate dates added, should validate/return bool?
// but the bool won't indicate which date was in error, maybe have 2 diff methods to schedule start/end?
if (releaseDate.HasValue)
{
var entry = new ContentSchedule(0, culture, releaseDate.Value, ContentScheduleChange.Start);
changes.Add(releaseDate.Value, entry);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, entry));
}
2018-11-08 16:33:19 +01:00
if (expireDate.HasValue)
{
var entry = new ContentSchedule(0, culture, expireDate.Value, ContentScheduleChange.End);
changes.Add(expireDate.Value, entry);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, entry));
}
}
/// <summary>
/// Remove a scheduled change
/// </summary>
/// <param name="change"></param>
public void Remove(ContentSchedule change)
{
if (_schedule.TryGetValue(change.Culture, out var s))
{
var removed = s.Remove(change.Date);
if (removed)
{
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, change));
if (s.Count == 0)
_schedule.Remove(change.Culture);
}
2018-11-08 16:33:19 +01:00
}
}
/// <summary>
/// Clear all of the scheduled change type for invariant content
/// </summary>
/// <param name="changeType"></param>
/// <param name="changeDate">If specified, will clear all entries with dates less than or equal to the value</param>
public void Clear(ContentScheduleChange changeType, DateTime? changeDate = null)
{
Clear(string.Empty, changeType, changeDate);
}
/// <summary>
/// Clear all of the scheduled change type for the culture
/// </summary>
/// <param name="culture"></param>
/// <param name="changeType"></param>
/// <param name="changeDate">If specified, will clear all entries with dates less than or equal to the value</param>
public void Clear(string culture, ContentScheduleChange changeType, DateTime? changeDate = null)
{
if (_schedule.TryGetValue(culture, out var s))
{
foreach (var ofChange in s.Where(x => x.Value.Change == changeType
&& (changeDate.HasValue ? x.Value.Date <= changeDate.Value : true)).ToList())
{
var removed = s.Remove(ofChange.Value.Date);
if (removed)
{
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, ofChange.Value));
if (s.Count == 0)
_schedule.Remove(culture);
2018-11-08 16:33:19 +01:00
}
}
2018-11-08 16:33:19 +01:00
}
}
/// <summary>
/// Returns all pending schedules based on the date and type provided
/// </summary>
/// <param name="changeType"></param>
/// <param name="date"></param>
/// <returns></returns>
public IReadOnlyList<ContentSchedule> GetPending(ContentScheduleChange changeType, DateTime date)
{
return _schedule.Values.SelectMany(x => x.Values).Where(x => x.Date <= date).ToList();
}
/// <summary>
/// Gets the schedule for invariant content
/// </summary>
/// <returns></returns>
public IEnumerable<ContentSchedule> GetSchedule(ContentScheduleChange? changeType = null)
{
return GetSchedule(string.Empty, changeType);
}
/// <summary>
/// Gets the schedule for a culture
/// </summary>
/// <param name="culture"></param>
/// <returns></returns>
public IEnumerable<ContentSchedule> GetSchedule(string culture, ContentScheduleChange? changeType = null)
{
if (_schedule.TryGetValue(culture, out var changes))
return changeType == null ? changes.Values : changes.Values.Where(x => x.Change == changeType.Value);
return Enumerable.Empty<ContentSchedule>();
}
/// <summary>
2018-11-08 13:15:56 +11:00
/// Returns all schedules registered
/// </summary>
/// <returns></returns>
2018-11-08 13:15:56 +11:00
public IReadOnlyList<ContentSchedule> FullSchedule => _schedule.SelectMany(x => x.Value.Values).ToList();
public object DeepClone()
{
var clone = new ContentScheduleCollection();
foreach(var cultureSched in _schedule)
{
var list = new SortedList<DateTime, ContentSchedule>();
foreach (var schedEntry in cultureSched.Value)
list.Add(schedEntry.Key, (ContentSchedule)schedEntry.Value.DeepClone());
clone._schedule[cultureSched.Key] = list;
}
return clone;
}
public override bool Equals(object obj)
2018-11-08 16:33:19 +01:00
=> obj is ContentScheduleCollection other && Equals(other);
public bool Equals(ContentScheduleCollection other)
{
2018-11-08 16:33:19 +01:00
if (other == null) return false;
var thisSched = _schedule;
2018-11-08 13:15:56 +11:00
var thatSched = other._schedule;
2018-11-08 16:33:19 +01:00
if (thisSched.Count != thatSched.Count)
return false;
// fixme/review - code was returning false *if* thatList.SequenceEqual(thisList) and not the opposite?
foreach (var (culture, thisList) in thisSched)
{
2018-11-08 16:33:19 +01:00
// if culture is missing, or actions differ, false
if (!thatSched.TryGetValue(culture, out var thatList) || !thatList.SequenceEqual(thisList))
return false;
}
2018-11-08 16:33:19 +01:00
return true;
}
}
}