2018-11-02 14:55:34 +11:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Collections.ObjectModel;
|
2018-11-05 17:20:26 +11:00
|
|
|
|
using System.Collections.Specialized;
|
2018-11-02 14:55:34 +11:00
|
|
|
|
|
|
|
|
|
|
namespace Umbraco.Core.Models
|
|
|
|
|
|
{
|
2018-11-07 19:42:49 +11:00
|
|
|
|
public class ContentScheduleCollection : INotifyCollectionChanged, IDeepCloneable, IEquatable<ContentScheduleCollection>
|
2018-11-02 14:55:34 +11:00
|
|
|
|
{
|
|
|
|
|
|
//underlying storage for the collection backed by a sorted list so that the schedule is always in order of date
|
2018-11-06 21:33:24 +11:00
|
|
|
|
private readonly Dictionary<string, SortedList<DateTime, ContentSchedule>> _schedule
|
|
|
|
|
|
= new Dictionary<string, SortedList<DateTime, ContentSchedule>>(StringComparer.InvariantCultureIgnoreCase);
|
2018-11-02 14:55:34 +11:00
|
|
|
|
|
2018-11-05 17:20:26 +11:00
|
|
|
|
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
|
|
|
|
|
|
|
|
|
|
|
private void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
|
|
|
|
|
|
{
|
|
|
|
|
|
CollectionChanged?.Invoke(this, args);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-11-02 14:55:34 +11:00
|
|
|
|
/// <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);
|
2018-11-05 17:20:26 +11:00
|
|
|
|
|
|
|
|
|
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, schedule));
|
2018-11-02 14:55:34 +11:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//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)
|
2018-11-05 17:20:26 +11:00
|
|
|
|
{
|
|
|
|
|
|
var entry = new ContentSchedule(0, culture, releaseDate.Value, ContentScheduleChange.Start);
|
|
|
|
|
|
changes.Add(releaseDate.Value, entry);
|
|
|
|
|
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, entry));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-11-02 14:55:34 +11:00
|
|
|
|
if (expireDate.HasValue)
|
2018-11-05 17:20:26 +11:00
|
|
|
|
{
|
|
|
|
|
|
var entry = new ContentSchedule(0, culture, expireDate.Value, ContentScheduleChange.End);
|
|
|
|
|
|
changes.Add(expireDate.Value, entry);
|
|
|
|
|
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, entry));
|
|
|
|
|
|
}
|
2018-11-02 14:55:34 +11:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Remove a scheduled change
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="change"></param>
|
|
|
|
|
|
public void Remove(ContentSchedule change)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_schedule.TryGetValue(change.Culture, out var s))
|
|
|
|
|
|
{
|
2018-11-05 17:20:26 +11:00
|
|
|
|
var removed = s.Remove(change.Date);
|
|
|
|
|
|
if (removed)
|
|
|
|
|
|
{
|
|
|
|
|
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, change));
|
|
|
|
|
|
if (s.Count == 0)
|
|
|
|
|
|
_schedule.Remove(change.Culture);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-11-02 14:55:34 +11:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Clear all of the scheduled change type for invariant content
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="changeType"></param>
|
2018-11-07 19:42:49 +11:00
|
|
|
|
/// <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)
|
2018-11-02 14:55:34 +11:00
|
|
|
|
{
|
2018-11-07 19:42:49 +11:00
|
|
|
|
Clear(string.Empty, changeType, changeDate);
|
2018-11-02 14:55:34 +11:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Clear all of the scheduled change type for the culture
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="culture"></param>
|
|
|
|
|
|
/// <param name="changeType"></param>
|
2018-11-07 19:42:49 +11:00
|
|
|
|
/// <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)
|
2018-11-02 14:55:34 +11:00
|
|
|
|
{
|
|
|
|
|
|
if (_schedule.TryGetValue(culture, out var s))
|
|
|
|
|
|
{
|
2018-11-07 19:42:49 +11:00
|
|
|
|
foreach (var ofChange in s.Where(x => x.Value.Change == changeType
|
|
|
|
|
|
&& (changeDate.HasValue ? x.Value.Date <= changeDate.Value : true)).ToList())
|
2018-11-05 17:20:26 +11:00
|
|
|
|
{
|
|
|
|
|
|
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-02 14:55:34 +11:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-11-07 19:42:49 +11: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 IEnumerable<ContentSchedule> GetPending(ContentScheduleChange changeType, DateTime date)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_schedule.TryGetValue(string.Empty, out var changes))
|
|
|
|
|
|
return changes.Values.Where(x => x.Date <= date);
|
|
|
|
|
|
return Enumerable.Empty<ContentSchedule>();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-11-02 14:55:34 +11:00
|
|
|
|
/// <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);
|
2018-11-05 13:59:55 +11:00
|
|
|
|
return Enumerable.Empty<ContentSchedule>();
|
2018-11-02 14:55:34 +11:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-11-06 21:33:24 +11:00
|
|
|
|
//fixme - should this just return IEnumerable<ContentSchedule> since the culture is part of the ContentSchedule object already?
|
2018-11-02 14:55:34 +11:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Returns all schedules for both invariant and variant cultures
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns></returns>
|
2018-11-06 21:33:24 +11:00
|
|
|
|
public IReadOnlyDictionary<string, IEnumerable<ContentSchedule>> FullSchedule => _schedule.ToDictionary(x => x.Key, x => (IEnumerable<ContentSchedule>)x.Value.Values);
|
2018-11-07 19:42:49 +11:00
|
|
|
|
//public IEnumerable<ContentSchedule> FullSchedule => _schedule.SelectMany(x => x.Value.Values);
|
2018-11-05 17:20:26 +11:00
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
2018-11-07 19:42:49 +11:00
|
|
|
|
|
|
|
|
|
|
public override bool Equals(object obj)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!(obj is ContentScheduleCollection c))
|
|
|
|
|
|
return false;
|
|
|
|
|
|
return Equals(c);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public bool Equals(ContentScheduleCollection other)
|
|
|
|
|
|
{
|
|
|
|
|
|
var thisSched = this.FullSchedule;
|
|
|
|
|
|
var thatSched = other.FullSchedule;
|
|
|
|
|
|
|
|
|
|
|
|
var equal = false;
|
|
|
|
|
|
if (thisSched.Count == thatSched.Count)
|
|
|
|
|
|
{
|
|
|
|
|
|
equal = true;
|
|
|
|
|
|
foreach (var pair in thisSched)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (thatSched.TryGetValue(pair.Key, out var val))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (val.SequenceEqual(pair.Value))
|
|
|
|
|
|
{
|
|
|
|
|
|
equal = false;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// Require key be present.
|
|
|
|
|
|
equal = false;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return equal;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-11-02 14:55:34 +11:00
|
|
|
|
}
|
|
|
|
|
|
}
|