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
{
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
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 ;
2018-11-08 16:33:19 +01:00
}
2018-11-02 14:55:34 +11: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 )
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-08 16:33:19 +01:00
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-08 16:33:19 +01:00
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-08 16:33:19 +01:00
}
2018-11-05 17:20:26 +11:00
}
2018-11-08 16:33:19 +01:00
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>
2018-11-07 21:32:12 +11:00
public IReadOnlyList < ContentSchedule > GetPending ( ContentScheduleChange changeType , DateTime date )
2018-11-07 19:42:49 +11:00
{
2018-11-07 21:32:12 +11:00
return _schedule . Values . SelectMany ( x = > x . Values ) . Where ( x = > x . Date < = date ) . ToList ( ) ;
2018-11-07 19:42:49 +11:00
}
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
}
/// <summary>
2018-11-08 13:15:56 +11:00
/// Returns all schedules registered
2018-11-02 14:55:34 +11:00
/// </summary>
/// <returns></returns>
2018-11-08 13:15:56 +11:00
public IReadOnlyList < ContentSchedule > FullSchedule = > _schedule . SelectMany ( x = > x . Value . Values ) . ToList ( ) ;
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 )
2018-11-08 16:33:19 +01:00
= > obj is ContentScheduleCollection other & & Equals ( other ) ;
2018-11-07 19:42:49 +11:00
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-07 19:42:49 +11:00
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-07 19:42:49 +11:00
{
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-07 19:42:49 +11:00
}
2018-11-08 16:33:19 +01:00
return true ;
2018-11-07 19:42:49 +11:00
}
2018-11-02 14:55:34 +11:00
}
}