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>
2018-11-13 14:31:37 +11:00
public bool Add ( DateTime ? releaseDate , DateTime ? expireDate )
2018-11-02 14:55:34 +11:00
{
2018-11-13 14:31:37 +11:00
return Add ( string . Empty , releaseDate , expireDate ) ;
2018-11-02 14:55:34 +11:00
}
/// <summary>
/// Adds a new schedule for a culture
/// </summary>
/// <param name="culture"></param>
/// <param name="releaseDate"></param>
/// <param name="expireDate"></param>
2018-11-13 14:31:37 +11:00
/// <returns>true if successfully added, false if validation fails</returns>
public bool Add ( string culture , DateTime ? releaseDate , DateTime ? expireDate )
2018-11-02 14:55:34 +11:00
{
if ( culture = = null ) throw new ArgumentNullException ( nameof ( culture ) ) ;
if ( releaseDate . HasValue & & expireDate . HasValue & & releaseDate > = expireDate )
2018-11-13 14:31:37 +11:00
return false ;
2018-11-02 14:55:34 +11:00
2018-11-13 14:31:37 +11:00
if ( ! releaseDate . HasValue & & ! expireDate . HasValue ) return false ;
2018-11-02 14:55:34 +11:00
//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
{
2018-11-14 13:59:53 +01:00
var entry = new ContentSchedule ( culture , releaseDate . Value , ContentScheduleAction . Release ) ;
2018-11-05 17:20:26 +11:00
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
{
2018-11-14 13:59:53 +01:00
var entry = new ContentSchedule ( culture , expireDate . Value , ContentScheduleAction . Expire ) ;
2018-11-05 17:20:26 +11:00
changes . Add ( expireDate . Value , entry ) ;
OnCollectionChanged ( new NotifyCollectionChangedEventArgs ( NotifyCollectionChangedAction . Add , entry ) ) ;
}
2018-11-13 14:31:37 +11:00
return true ;
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>
2018-11-14 09:16:22 +01:00
/// <param name="action"></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>
2018-11-14 09:16:22 +01:00
public void Clear ( ContentScheduleAction action , DateTime ? changeDate = null )
2018-11-02 14:55:34 +11:00
{
2018-11-14 09:16:22 +01:00
Clear ( string . Empty , action , 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>
2018-11-14 09:16:22 +01:00
/// <param name="action"></param>
/// <param name="date">If specified, will clear all entries with dates less than or equal to the value</param>
public void Clear ( string culture , ContentScheduleAction action , DateTime ? date = null )
2018-11-02 14:55:34 +11:00
{
2018-11-14 09:16:22 +01:00
if ( ! _schedule . TryGetValue ( culture , out var schedules ) )
return ;
var removes = schedules . Where ( x = > x . Value . Action = = action & & ( ! date . HasValue | | x . Value . Date < = date . Value ) ) . ToList ( ) ;
foreach ( var remove in removes )
2018-11-02 14:55:34 +11:00
{
2018-11-14 09:16:22 +01:00
var removed = schedules . Remove ( remove . Value . Date ) ;
if ( ! removed )
continue ;
2018-11-08 16:33:19 +01:00
2018-11-14 09:16:22 +01:00
OnCollectionChanged ( new NotifyCollectionChangedEventArgs ( NotifyCollectionChangedAction . Remove , remove . Value ) ) ;
2018-11-02 14:55:34 +11:00
}
2018-11-14 09:16:22 +01:00
if ( schedules . 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>
2018-11-14 09:16:22 +01:00
/// <param name="action"></param>
2018-11-07 19:42:49 +11:00
/// <param name="date"></param>
/// <returns></returns>
2018-11-14 09:16:22 +01:00
public IReadOnlyList < ContentSchedule > GetPending ( ContentScheduleAction action , 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>
2018-11-14 09:16:22 +01:00
public IEnumerable < ContentSchedule > GetSchedule ( ContentScheduleAction ? action = null )
2018-11-02 14:55:34 +11:00
{
2018-11-14 09:16:22 +01:00
return GetSchedule ( string . Empty , action ) ;
2018-11-02 14:55:34 +11:00
}
/// <summary>
/// Gets the schedule for a culture
/// </summary>
/// <param name="culture"></param>
/// <returns></returns>
2018-11-14 09:16:22 +01:00
public IEnumerable < ContentSchedule > GetSchedule ( string culture , ContentScheduleAction ? action = null )
2018-11-02 14:55:34 +11:00
{
if ( _schedule . TryGetValue ( culture , out var changes ) )
2018-11-14 09:16:22 +01:00
return action = = null ? changes . Values : changes . Values . Where ( x = > x . Action = = action . 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 ;
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
}
}