Got database and persistence models in place for scheduled publishing, now need to start writing tests, fixing tests and cleaning up fixme statements
This commit is contained in:
@@ -142,6 +142,7 @@ namespace Umbraco.Core.Migrations.Upgrade
|
||||
Chain<DropPreValueTable>("{23275462-446E-44C7-8C2C-3B8C1127B07D}");
|
||||
Chain<DropDownPropertyEditorsMigration>("{6B251841-3069-4AD5-8AE9-861F9523E8DA}");
|
||||
Chain<DropTemplateDesignColumn>("{08919C4B-B431-449C-90EC-2B8445B5C6B1}");
|
||||
Chain<TablesForScheduledPublishing>("{7EB0254C-CB8B-4C75-B15B-D48C55B449EB}");
|
||||
|
||||
//FINAL
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
using NPoco;
|
||||
using System;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
|
||||
namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
|
||||
{
|
||||
public class TablesForScheduledPublishing : MigrationBase
|
||||
{
|
||||
public TablesForScheduledPublishing(IMigrationContext context)
|
||||
: base(context)
|
||||
{ }
|
||||
|
||||
public override void Migrate()
|
||||
{
|
||||
//Get anything currently scheduled
|
||||
var scheduleSql = new Sql()
|
||||
.Select("nodeId", "releaseDate", "expireDate")
|
||||
.From("umbracoDocument")
|
||||
.Where("releaseDate IS NOT NULL OR expireDate IS NOT NULL");
|
||||
var schedules = Database.Dictionary<int, (DateTime? releaseDate, DateTime? expireDate)> (scheduleSql);
|
||||
|
||||
//drop old cols
|
||||
Delete.Column("cmsDocument").FromTable("releaseDate").Do();
|
||||
Delete.Column("cmsDocument").FromTable("expireDate").Do();
|
||||
//add new table
|
||||
Create.Table<ContentScheduleDto>().Do();
|
||||
|
||||
//migrate the schedule
|
||||
foreach(var s in schedules)
|
||||
{
|
||||
var date = s.Value.releaseDate;
|
||||
var action = ContentScheduleChange.Start.ToString();
|
||||
if (!date.HasValue)
|
||||
{
|
||||
date = s.Value.expireDate;
|
||||
action = ContentScheduleChange.End.ToString();
|
||||
}
|
||||
|
||||
Insert.IntoTable(ContentScheduleDto.TableName)
|
||||
.Row(new { nodeId = s.Key, date = date.Value, action = action })
|
||||
.Do();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
private IContentType _contentType;
|
||||
private ITemplate _template;
|
||||
private ContentScheduleCollection _schedule;
|
||||
private bool _published;
|
||||
private PublishedState _publishedState;
|
||||
private DateTime? _releaseDate;
|
||||
@@ -86,11 +87,17 @@ namespace Umbraco.Core.Models
|
||||
{
|
||||
public readonly PropertyInfo TemplateSelector = ExpressionHelper.GetPropertyInfo<Content, ITemplate>(x => x.Template);
|
||||
public readonly PropertyInfo PublishedSelector = ExpressionHelper.GetPropertyInfo<Content, bool>(x => x.Published);
|
||||
public readonly PropertyInfo ReleaseDateSelector = ExpressionHelper.GetPropertyInfo<Content, DateTime?>(x => x.ReleaseDate);
|
||||
public readonly PropertyInfo ExpireDateSelector = ExpressionHelper.GetPropertyInfo<Content, DateTime?>(x => x.ExpireDate);
|
||||
public readonly PropertyInfo ContentScheduleSelector = ExpressionHelper.GetPropertyInfo<Content, ContentScheduleCollection>(x => x.ContentSchedule);
|
||||
public readonly PropertyInfo PublishCultureInfosSelector = ExpressionHelper.GetPropertyInfo<Content, IReadOnlyDictionary<string, ContentCultureInfos>>(x => x.PublishCultureInfos);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ContentScheduleCollection ContentSchedule
|
||||
{
|
||||
get => _schedule ?? (_schedule = new ContentScheduleCollection());
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _schedule, Ps.Value.ContentScheduleSelector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the template used by the Content.
|
||||
/// This is used to override the default one from the ContentType.
|
||||
@@ -100,7 +107,7 @@ namespace Umbraco.Core.Models
|
||||
/// the Default template from the ContentType will be returned.
|
||||
/// </remarks>
|
||||
[DataMember]
|
||||
public virtual ITemplate Template
|
||||
public ITemplate Template
|
||||
{
|
||||
get => _template ?? _contentType.DefaultTemplate;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _template, Ps.Value.TemplateSelector);
|
||||
@@ -117,10 +124,14 @@ namespace Umbraco.Core.Models
|
||||
if(Trashed)
|
||||
return ContentStatus.Trashed;
|
||||
|
||||
if(ExpireDate.HasValue && ExpireDate.Value > DateTime.MinValue && DateTime.Now > ExpireDate.Value)
|
||||
//fixme - deal with variants
|
||||
var expires = ContentSchedule.GetSchedule(ContentScheduleChange.End).FirstOrDefault();
|
||||
if (expires != null && expires.Date > DateTime.MinValue && DateTime.Now > expires.Date)
|
||||
return ContentStatus.Expired;
|
||||
|
||||
if(ReleaseDate.HasValue && ReleaseDate.Value > DateTime.MinValue && ReleaseDate.Value > DateTime.Now)
|
||||
//fixme - deal with variants
|
||||
var release = ContentSchedule.GetSchedule(ContentScheduleChange.Start).FirstOrDefault();
|
||||
if (release != null && release.Date > DateTime.MinValue && release.Date > DateTime.Now)
|
||||
return ContentStatus.AwaitingRelease;
|
||||
|
||||
if(Published)
|
||||
@@ -169,26 +180,6 @@ namespace Umbraco.Core.Models
|
||||
[IgnoreDataMember]
|
||||
public bool Edited { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// The date this Content should be released and thus be published
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public DateTime? ReleaseDate
|
||||
{
|
||||
get => _releaseDate;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _releaseDate, Ps.Value.ReleaseDateSelector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The date this Content should expire and thus be unpublished
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public DateTime? ExpireDate
|
||||
{
|
||||
get => _expireDate;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _expireDate, Ps.Value.ExpireDateSelector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ContentType used by this content object
|
||||
/// </summary>
|
||||
|
||||
49
src/Umbraco.Core/Models/ContentSchedule.cs
Normal file
49
src/Umbraco.Core/Models/ContentSchedule.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Model for scheduled content
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataContract(IsReference = true)]
|
||||
public class ContentSchedule
|
||||
{
|
||||
public ContentSchedule(int id, string culture, DateTime date, ContentScheduleChange change)
|
||||
{
|
||||
Id = id;
|
||||
Culture = culture;
|
||||
Date = date;
|
||||
Change = change;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The unique Id of the schedule item
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public int Id { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The culture for the schedule item
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// string.Empty represents invariant culture
|
||||
/// </remarks>
|
||||
[DataMember]
|
||||
public string Culture { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The date for the schedule
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public DateTime Date { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The action to take for the schedule
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public ContentScheduleChange Change { get; }
|
||||
}
|
||||
}
|
||||
11
src/Umbraco.Core/Models/ContentScheduleChange.cs
Normal file
11
src/Umbraco.Core/Models/ContentScheduleChange.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// The scheduled change types of scheduled content
|
||||
/// </summary>
|
||||
public enum ContentScheduleChange
|
||||
{
|
||||
Start,
|
||||
End
|
||||
}
|
||||
}
|
||||
136
src/Umbraco.Core/Models/ContentScheduleCollection.cs
Normal file
136
src/Umbraco.Core/Models/ContentScheduleCollection.cs
Normal file
@@ -0,0 +1,136 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public class ContentScheduleCollection
|
||||
{
|
||||
//underlying storage for the collection backed by a sorted list so that the schedule is always in order of date
|
||||
private readonly Dictionary<string, SortedList<DateTime, ContentSchedule>> _schedule = new Dictionary<string, SortedList<DateTime, ContentSchedule>>();
|
||||
|
||||
/// <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);
|
||||
}
|
||||
|
||||
/// <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)
|
||||
changes.Add(releaseDate.Value, new ContentSchedule(0, culture, releaseDate.Value, ContentScheduleChange.Start));
|
||||
if (expireDate.HasValue)
|
||||
changes.Add(expireDate.Value, new ContentSchedule(0, culture, expireDate.Value, ContentScheduleChange.End));
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a scheduled change
|
||||
/// </summary>
|
||||
/// <param name="change"></param>
|
||||
public void Remove(ContentSchedule change)
|
||||
{
|
||||
if (_schedule.TryGetValue(change.Culture, out var s))
|
||||
{
|
||||
s.Remove(change.Date);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all of the scheduled change type for invariant content
|
||||
/// </summary>
|
||||
/// <param name="changeType"></param>
|
||||
public void Clear(ContentScheduleChange changeType)
|
||||
{
|
||||
Clear(string.Empty, changeType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all of the scheduled change type for the culture
|
||||
/// </summary>
|
||||
/// <param name="culture"></param>
|
||||
/// <param name="changeType"></param>
|
||||
public void Clear(string culture, ContentScheduleChange changeType)
|
||||
{
|
||||
if (_schedule.TryGetValue(culture, out var s))
|
||||
{
|
||||
foreach (var ofChange in s.Where(x => x.Value.Change == changeType).ToList())
|
||||
s.Remove(ofChange.Value.Date);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all schedules for both invariant and variant cultures
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IReadOnlyDictionary<string, IEnumerable<ContentSchedule>> GetFullSchedule()
|
||||
{
|
||||
return _schedule.ToDictionary(x => x.Key, x => (IEnumerable<ContentSchedule>)x.Value.Values);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Represents a document.
|
||||
/// </summary>
|
||||
@@ -11,6 +12,11 @@ namespace Umbraco.Core.Models
|
||||
/// </remarks>
|
||||
public interface IContent : IContentBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the content schedule
|
||||
/// </summary>
|
||||
ContentScheduleCollection ContentSchedule { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the template used to render the content.
|
||||
/// </summary>
|
||||
@@ -60,16 +66,6 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
DateTime? PublishDate { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time the content item should be published.
|
||||
/// </summary>
|
||||
DateTime? ReleaseDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the date and time the content should be unpublished.
|
||||
/// </summary>
|
||||
DateTime? ExpireDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content type of this content.
|
||||
/// </summary>
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace Umbraco.Core
|
||||
public const string DocumentCultureVariation = TableNamePrefix + "DocumentCultureVariation";
|
||||
public const string DocumentVersion = TableNamePrefix + "DocumentVersion";
|
||||
public const string MediaVersion = TableNamePrefix + "MediaVersion";
|
||||
public const string ContentSchedule = TableNamePrefix + "ContentSchedule";
|
||||
|
||||
public const string PropertyType = /*TableNamePrefix*/ "cms" + "PropertyType";
|
||||
public const string PropertyTypeGroup = /*TableNamePrefix*/ "cms" + "PropertyTypeGroup";
|
||||
|
||||
33
src/Umbraco.Core/Persistence/Dtos/ContentScheduleDto.cs
Normal file
33
src/Umbraco.Core/Persistence/Dtos/ContentScheduleDto.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using NPoco;
|
||||
using Umbraco.Core.Persistence.DatabaseAnnotations;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Dtos
|
||||
{
|
||||
[TableName(TableName)]
|
||||
[PrimaryKey("id", AutoIncrement = true)]
|
||||
[ExplicitColumns]
|
||||
internal class ContentScheduleDto
|
||||
{
|
||||
public const string TableName = Constants.DatabaseSchema.Tables.ContentSchedule;
|
||||
|
||||
[Column("id")]
|
||||
[PrimaryKeyColumn(AutoIncrement = true)]
|
||||
public int Id { get; set; }
|
||||
|
||||
[Column("nodeId")]
|
||||
[ForeignKey(typeof(ContentDto))]
|
||||
public int NodeId { get; set; }
|
||||
|
||||
[Column("languageId")]
|
||||
[ForeignKey(typeof(LanguageDto))]
|
||||
[NullSetting(NullSetting = NullSettings.Null)] // can be invariant
|
||||
public int? LanguageId { get; set; }
|
||||
|
||||
[Column("date")]
|
||||
public DateTime Date { get; set; }
|
||||
|
||||
[Column("action")]
|
||||
public string Action { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using NPoco;
|
||||
using NPoco;
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Persistence.DatabaseAnnotations;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Dtos
|
||||
{
|
||||
|
||||
[TableName(TableName)]
|
||||
[PrimaryKey("nodeId", AutoIncrement = false)]
|
||||
[ExplicitColumns]
|
||||
@@ -23,14 +24,6 @@ namespace Umbraco.Core.Persistence.Dtos
|
||||
[Column("edited")]
|
||||
public bool Edited { get; set; }
|
||||
|
||||
[Column("releaseDate")]
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
public DateTime? ReleaseDate { get; set; }
|
||||
|
||||
[Column("expireDate")]
|
||||
[NullSetting(NullSetting = NullSettings.Null)]
|
||||
public DateTime? ExpiresDate { get; set; }
|
||||
|
||||
//[Column("publishDate")]
|
||||
//[NullSetting(NullSetting = NullSettings.Null)] // is contentVersionDto.VersionDate for the published version
|
||||
//public DateTime? PublishDate { get; set; }
|
||||
@@ -62,5 +55,9 @@ namespace Umbraco.Core.Persistence.Dtos
|
||||
[ResultColumn]
|
||||
[Reference(ReferenceType.OneToOne)]
|
||||
public DocumentVersionDto PublishedVersionDto { get; set; }
|
||||
|
||||
[ResultColumn]
|
||||
[Reference(ReferenceType.Many, ReferenceMemberName = "NodeId")]
|
||||
public IEnumerable<ContentScheduleDto> ContentSchedule { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
@@ -12,7 +14,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
/// <summary>
|
||||
/// Builds an IContent item from a dto and content type.
|
||||
/// </summary>
|
||||
public static Content BuildEntity(DocumentDto dto, IContentType contentType)
|
||||
public static Content BuildEntity(DocumentDto dto, IContentType contentType, ILanguageRepository languageRepository )
|
||||
{
|
||||
var contentDto = dto.ContentDto;
|
||||
var nodeDto = contentDto.NodeDto;
|
||||
@@ -45,8 +47,18 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
|
||||
content.Published = dto.Published;
|
||||
content.Edited = dto.Edited;
|
||||
content.ExpireDate = dto.ExpiresDate;
|
||||
content.ReleaseDate = dto.ReleaseDate;
|
||||
|
||||
var schedule = new ContentScheduleCollection();
|
||||
foreach(var entry in dto.ContentSchedule)
|
||||
{
|
||||
schedule.Add(new ContentSchedule(entry.Id,
|
||||
languageRepository.GetIsoCodeById(entry.LanguageId),
|
||||
entry.Date,
|
||||
entry.Action == ContentScheduleChange.Start.ToString()
|
||||
? ContentScheduleChange.Start
|
||||
: ContentScheduleChange.End));
|
||||
}
|
||||
content.ContentSchedule = schedule;
|
||||
|
||||
// fixme - shall we get published infos or not?
|
||||
//if (dto.Published)
|
||||
@@ -142,7 +154,7 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
content.CreateDate = nodeDto.CreateDate;
|
||||
content.UpdateDate = contentVersionDto.VersionDate;
|
||||
|
||||
content.ProviderUserKey = content.Key; // fixme explain
|
||||
content.ProviderUserKey = content.Key; // The `ProviderUserKey` is a membership provider thing
|
||||
|
||||
// reset dirty initial properties (U4-1946)
|
||||
content.ResetDirtyProperties(false);
|
||||
@@ -155,9 +167,9 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Buils a dto from an IContent item.
|
||||
/// Builds a dto from an IContent item.
|
||||
/// </summary>
|
||||
public static DocumentDto BuildDto(IContent entity, Guid objectType)
|
||||
public static DocumentDto BuildDto(IContent entity, Guid objectType, ILanguageRepository languageRepository)
|
||||
{
|
||||
var contentDto = BuildContentDto(entity, objectType);
|
||||
|
||||
@@ -165,13 +177,27 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
{
|
||||
NodeId = entity.Id,
|
||||
Published = entity.Published,
|
||||
ReleaseDate = entity.ReleaseDate,
|
||||
ExpiresDate = entity.ExpireDate,
|
||||
|
||||
ContentDto = contentDto,
|
||||
DocumentVersionDto = BuildDocumentVersionDto(entity, contentDto)
|
||||
};
|
||||
|
||||
var schedule = new List<ContentScheduleDto>();
|
||||
foreach(var schedByCulture in entity.ContentSchedule.GetFullSchedule())
|
||||
{
|
||||
foreach(var cultureSched in schedByCulture.Value)
|
||||
{
|
||||
schedule.Add(new ContentScheduleDto
|
||||
{
|
||||
Action = cultureSched.Change.ToString(),
|
||||
Date = cultureSched.Date,
|
||||
NodeId = entity.Id,
|
||||
LanguageId = languageRepository.GetIdByIsoCode(schedByCulture.Key),
|
||||
Id = cultureSched.Id
|
||||
});
|
||||
}
|
||||
}
|
||||
dto.ContentSchedule = schedule;
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,8 +37,6 @@ namespace Umbraco.Core.Persistence.Mappers
|
||||
CacheMap<Content, ContentDto>(src => src.ContentTypeId, dto => dto.ContentTypeId);
|
||||
|
||||
CacheMap<Content, ContentVersionDto>(src => src.UpdateDate, dto => dto.VersionDate);
|
||||
CacheMap<Content, DocumentDto>(src => src.ExpireDate, dto => dto.ExpiresDate);
|
||||
CacheMap<Content, DocumentDto>(src => src.ReleaseDate, dto => dto.ReleaseDate);
|
||||
CacheMap<Content, DocumentDto>(src => src.Published, dto => dto.Published);
|
||||
|
||||
//CacheMap<Content, DocumentDto>(src => src.Name, dto => dto.Alias);
|
||||
|
||||
@@ -277,7 +277,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
entity.SanitizeEntityPropertiesForXmlStorage();
|
||||
|
||||
// create the dto
|
||||
var dto = ContentBaseFactory.BuildDto(entity, NodeObjectTypeId);
|
||||
var dto = ContentBaseFactory.BuildDto(entity, NodeObjectTypeId, LanguageRepository);
|
||||
|
||||
// derive path and level from parent
|
||||
var parent = GetParentNodeDto(entity.ParentId);
|
||||
@@ -364,6 +364,12 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
content.Edited = dto.Edited = !dto.Published || edited; // if not published, always edited
|
||||
Database.Insert(dto);
|
||||
|
||||
//insert the schedule
|
||||
foreach(var c in dto.ContentSchedule)
|
||||
{
|
||||
Database.Insert(c);
|
||||
}
|
||||
|
||||
// persist the variations
|
||||
if (content.ContentType.VariesByCulture())
|
||||
{
|
||||
@@ -474,7 +480,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
}
|
||||
|
||||
// create the dto
|
||||
var dto = ContentBaseFactory.BuildDto(entity, NodeObjectTypeId);
|
||||
var dto = ContentBaseFactory.BuildDto(entity, NodeObjectTypeId, LanguageRepository);
|
||||
|
||||
// update the node dto
|
||||
var nodeDto = dto.ContentDto.NodeDto;
|
||||
@@ -582,6 +588,23 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
content.Edited = dto.Edited = !dto.Published || edited; // if not published, always edited
|
||||
Database.Update(dto);
|
||||
|
||||
//update the schedule, get the existing one so we know what to update
|
||||
var existingSched = Database.Fetch<int>(Sql()
|
||||
.Select<ContentScheduleDto>(x => x.Id)
|
||||
.Where<ContentScheduleDto>(x => x.NodeId == content.Id));
|
||||
//remove any that no longer exist
|
||||
var schedToRemove = existingSched.Except(dto.ContentSchedule.Select(x => x.Id));
|
||||
foreach(var c in schedToRemove)
|
||||
Database.DeleteWhere<ContentScheduleDto>("id = @id", new { id = c });
|
||||
//add/update the rest
|
||||
foreach (var c in dto.ContentSchedule)
|
||||
{
|
||||
if (c.Id == 0)
|
||||
Database.Insert(c);
|
||||
else
|
||||
Database.Update(c);
|
||||
}
|
||||
|
||||
// if entity is publishing, update tags, else leave tags there
|
||||
// means that implicitely unpublished, or trashed, entities *still* have tags in db
|
||||
if (content.PublishedState == PublishedState.Publishing)
|
||||
@@ -952,7 +975,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
if (contentTypes.TryGetValue(contentTypeId, out var contentType) == false)
|
||||
contentTypes[contentTypeId] = contentType = _contentTypeRepository.Get(contentTypeId);
|
||||
|
||||
var c = content[i] = ContentBaseFactory.BuildEntity(dto, contentType);
|
||||
var c = content[i] = ContentBaseFactory.BuildEntity(dto, contentType, LanguageRepository);
|
||||
|
||||
if (!slim)
|
||||
{
|
||||
@@ -1024,7 +1047,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
private IContent MapDtoToContent(DocumentDto dto)
|
||||
{
|
||||
var contentType = _contentTypeRepository.Get(dto.ContentDto.ContentTypeId);
|
||||
var content = ContentBaseFactory.BuildEntity(dto, contentType);
|
||||
var content = ContentBaseFactory.BuildEntity(dto, contentType, LanguageRepository);
|
||||
|
||||
// get template
|
||||
if (dto.DocumentVersionDto.TemplateId.HasValue && dto.DocumentVersionDto.TemplateId.Value > 0)
|
||||
|
||||
@@ -40,7 +40,10 @@ namespace Umbraco.Core.Publishing
|
||||
{
|
||||
try
|
||||
{
|
||||
d.ReleaseDate = null;
|
||||
throw new NotImplementedException("implement scheduled publishing");
|
||||
//d.ReleaseDate = null;
|
||||
//fixme - need to clear this particular schedule for this item
|
||||
|
||||
d.PublishCulture(); // fixme variants?
|
||||
var result = _contentService.SaveAndPublish(d, userId: _userService.GetProfileById(d.WriterId).Id);
|
||||
_logger.Debug<ContentService>("Result of publish attempt: {PublishResult}", result.Result);
|
||||
@@ -67,7 +70,10 @@ namespace Umbraco.Core.Publishing
|
||||
{
|
||||
try
|
||||
{
|
||||
d.ExpireDate = null;
|
||||
throw new NotImplementedException("implement scheduled publishing");
|
||||
//d.ExpireDate = null;
|
||||
//fixme - need to clear this particular schedule for this item
|
||||
|
||||
var result = _contentService.Unpublish(d, userId: _userService.GetProfileById(d.WriterId).Id);
|
||||
if (result.Success)
|
||||
{
|
||||
|
||||
@@ -749,8 +749,11 @@ namespace Umbraco.Core.Services.Implement
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
scope.ReadLock(Constants.Locks.ContentTree);
|
||||
var query = Query<IContent>().Where(x => x.Published && x.ExpireDate <= DateTime.Now);
|
||||
return _documentRepository.Get(query);
|
||||
|
||||
//fixme - need to get the DB updated to query for this
|
||||
//var query = Query<IContent>().Where(x => x.Published && x.ExpireDate <= DateTime.Now);
|
||||
//return _documentRepository.Get(query);
|
||||
throw new NotImplementedException("Implement GetContentForExpiration");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -763,8 +766,11 @@ namespace Umbraco.Core.Services.Implement
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
scope.ReadLock(Constants.Locks.ContentTree);
|
||||
var query = Query<IContent>().Where(x => x.Published == false && x.ReleaseDate <= DateTime.Now);
|
||||
return _documentRepository.Get(query);
|
||||
|
||||
//fixme - need to get the DB updated to query for this
|
||||
//var query = Query<IContent>().Where(x => x.Published == false && x.ReleaseDate <= DateTime.Now);
|
||||
//return _documentRepository.Get(query);
|
||||
throw new NotImplementedException("Implement GetContentForRelease");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1241,7 +1247,10 @@ namespace Umbraco.Core.Services.Implement
|
||||
PublishResult result;
|
||||
try
|
||||
{
|
||||
d.ReleaseDate = null;
|
||||
//d.ReleaseDate = null;
|
||||
//fixme - need to clear this particular schedule for this item
|
||||
throw new NotImplementedException("Implement PerformScheduledPublish");
|
||||
|
||||
d.PublishCulture(); // fixme variants?
|
||||
result = SaveAndPublish(d, userId: d.WriterId);
|
||||
if (result.Success == false)
|
||||
@@ -1258,7 +1267,10 @@ namespace Umbraco.Core.Services.Implement
|
||||
{
|
||||
try
|
||||
{
|
||||
d.ExpireDate = null;
|
||||
//d.ExpireDate = null;
|
||||
//fixme - need to clear this particular schedule for this item
|
||||
throw new NotImplementedException("Implement PerformScheduledPublish");
|
||||
|
||||
var result = Unpublish(d, userId: d.WriterId);
|
||||
if (result.Success == false)
|
||||
Logger.Error<ContentService>(null, "Failed to unpublish document id={DocumentId}, reason={Reason}.", d.Id, result.Result);
|
||||
@@ -2328,14 +2340,17 @@ namespace Umbraco.Core.Services.Implement
|
||||
if (attempt.Success == false)
|
||||
return attempt;
|
||||
|
||||
// if the document has a release date set to before now,
|
||||
// it should be removed so it doesn't interrupt an unpublish
|
||||
// if the document has any release dates set to before now,
|
||||
// they should be removed so they don't interrupt an unpublish
|
||||
// otherwise it would remain released == published
|
||||
if (content.ReleaseDate.HasValue && content.ReleaseDate.Value <= DateTime.Now)
|
||||
{
|
||||
content.ReleaseDate = null;
|
||||
|
||||
var pastReleases = content.ContentSchedule.GetFullSchedule().SelectMany(x => x.Value)
|
||||
.Where(x => x.Change == ContentScheduleChange.End && x.Date <= DateTime.Now)
|
||||
.ToList();
|
||||
foreach (var p in pastReleases)
|
||||
content.ContentSchedule.Remove(p);
|
||||
if(pastReleases.Count > 0)
|
||||
Logger.Info<ContentService>("Document {ContentName} (id={ContentId}) had its release date removed, because it was unpublished.", content.Name, content.Id);
|
||||
}
|
||||
|
||||
// change state to unpublishing
|
||||
((Content) content).PublishedState = PublishedState.Unpublishing;
|
||||
|
||||
@@ -366,6 +366,7 @@
|
||||
<Compile Include="Migrations\Upgrade\V_8_0_0\RefactorMacroColumns.cs" />
|
||||
<Compile Include="Migrations\Upgrade\V_8_0_0\RefactorVariantsModel.cs" />
|
||||
<Compile Include="Migrations\Upgrade\V_8_0_0\SuperZero.cs" />
|
||||
<Compile Include="Migrations\Upgrade\V_8_0_0\TablesForScheduledPublishing.cs" />
|
||||
<Compile Include="Migrations\Upgrade\V_8_0_0\TagsMigration.cs" />
|
||||
<Compile Include="Migrations\Upgrade\V_8_0_0\FallbackLanguage.cs" />
|
||||
<Compile Include="Migrations\Upgrade\V_8_0_0\UpdateDefaultMandatoryLanguage.cs" />
|
||||
@@ -376,6 +377,9 @@
|
||||
<Compile Include="Models\ConsentState.cs" />
|
||||
<Compile Include="Models\ContentEditing\ContentApp.cs" />
|
||||
<Compile Include="Models\ContentEditing\IContentAppDefinition.cs" />
|
||||
<Compile Include="Models\ContentSchedule.cs" />
|
||||
<Compile Include="Models\ContentScheduleChange.cs" />
|
||||
<Compile Include="Models\ContentScheduleCollection.cs" />
|
||||
<Compile Include="Models\ContentTagsExtensions.cs" />
|
||||
<Compile Include="Models\ContentTypeBaseExtensions.cs" />
|
||||
<Compile Include="Models\ContentCultureInfos.cs" />
|
||||
@@ -409,6 +413,7 @@
|
||||
<Compile Include="Models\PublishedContent\VariationContextAccessorExtensions.cs" />
|
||||
<Compile Include="Persistence\Dtos\AuditEntryDto.cs" />
|
||||
<Compile Include="Persistence\Dtos\ConsentDto.cs" />
|
||||
<Compile Include="Persistence\Dtos\ContentScheduleDto.cs" />
|
||||
<Compile Include="Persistence\Dtos\ContentVersionCultureVariationDto.cs" />
|
||||
<Compile Include="Persistence\Dtos\DocumentCultureVariationDto.cs" />
|
||||
<Compile Include="Persistence\Dtos\MediaDto.cs" />
|
||||
|
||||
@@ -228,11 +228,10 @@ namespace Umbraco.Tests.Models
|
||||
content.Id = 10;
|
||||
content.CreateDate = DateTime.Now;
|
||||
content.CreatorId = 22;
|
||||
content.ExpireDate = DateTime.Now;
|
||||
content.Key = Guid.NewGuid();
|
||||
content.Level = 3;
|
||||
content.Path = "-1,4,10";
|
||||
content.ReleaseDate = DateTime.Now;
|
||||
content.ContentSchedule.Add(DateTime.Now, DateTime.Now.AddDays(1));
|
||||
//content.ChangePublishedState(PublishedState.Published);
|
||||
content.SortOrder = 5;
|
||||
content.Template = new Template((string) "Test Template", (string) "testTemplate")
|
||||
@@ -287,11 +286,10 @@ namespace Umbraco.Tests.Models
|
||||
content.Id = 10;
|
||||
content.CreateDate = DateTime.Now;
|
||||
content.CreatorId = 22;
|
||||
content.ExpireDate = DateTime.Now;
|
||||
content.Key = Guid.NewGuid();
|
||||
content.Level = 3;
|
||||
content.Path = "-1,4,10";
|
||||
content.ReleaseDate = DateTime.Now;
|
||||
content.ContentSchedule.Add(DateTime.Now, DateTime.Now.AddDays(1));
|
||||
content.SortOrder = 5;
|
||||
content.Template = new Template((string) "Test Template", (string) "testTemplate")
|
||||
{
|
||||
@@ -326,11 +324,11 @@ namespace Umbraco.Tests.Models
|
||||
Assert.AreEqual(clone.ContentTypeId, content.ContentTypeId);
|
||||
Assert.AreEqual(clone.CreateDate, content.CreateDate);
|
||||
Assert.AreEqual(clone.CreatorId, content.CreatorId);
|
||||
Assert.AreEqual(clone.ExpireDate, content.ExpireDate);
|
||||
Assert.AreEqual(clone.Key, content.Key);
|
||||
Assert.AreEqual(clone.Level, content.Level);
|
||||
Assert.AreEqual(clone.Path, content.Path);
|
||||
Assert.AreEqual(clone.ReleaseDate, content.ReleaseDate);
|
||||
//fixme: Need to maybe override equals or determine another way of comparing these
|
||||
Assert.AreEqual(clone.ContentSchedule, content.ContentSchedule);
|
||||
Assert.AreEqual(clone.Published, content.Published);
|
||||
Assert.AreEqual(clone.PublishedState, content.PublishedState);
|
||||
Assert.AreEqual(clone.SortOrder, content.SortOrder);
|
||||
@@ -386,11 +384,10 @@ namespace Umbraco.Tests.Models
|
||||
content.Id = 10;
|
||||
content.CreateDate = DateTime.Now;
|
||||
content.CreatorId = 22;
|
||||
content.ExpireDate = DateTime.Now;
|
||||
content.Key = Guid.NewGuid();
|
||||
content.Level = 3;
|
||||
content.Path = "-1,4,10";
|
||||
content.ReleaseDate = DateTime.Now;
|
||||
content.ContentSchedule.Add(DateTime.Now, DateTime.Now.AddDays(1));
|
||||
//content.ChangePublishedState(PublishedState.Publishing);
|
||||
content.SortOrder = 5;
|
||||
content.Template = new Template((string) "Test Template", (string) "testTemplate")
|
||||
|
||||
@@ -260,11 +260,11 @@ namespace Umbraco.Tests.Services
|
||||
// Act
|
||||
var content = contentService.CreateAndSave("Test", -1, "umbTextpage", 0);
|
||||
|
||||
content.ReleaseDate = DateTime.Now.AddHours(2);
|
||||
content.ContentSchedule.Add(null, DateTime.Now.AddHours(2));
|
||||
contentService.Save(content, 0);
|
||||
|
||||
content = contentService.GetById(content.Id);
|
||||
content.ReleaseDate = null;
|
||||
content.ContentSchedule.Clear(ContentScheduleChange.End);
|
||||
contentService.Save(content, 0);
|
||||
|
||||
|
||||
@@ -1164,7 +1164,7 @@ namespace Umbraco.Tests.Services
|
||||
var root = contentService.GetById(NodeDto.NodeIdSeed + 2);
|
||||
contentService.SaveAndPublish(root);
|
||||
var content = contentService.GetById(NodeDto.NodeIdSeed + 4);
|
||||
content.ExpireDate = DateTime.Now.AddSeconds(1);
|
||||
content.ContentSchedule.Add(null, DateTime.Now.AddSeconds(1));
|
||||
contentService.SaveAndPublish(content);
|
||||
|
||||
// Act
|
||||
@@ -1504,7 +1504,7 @@ namespace Umbraco.Tests.Services
|
||||
// Arrange
|
||||
var contentService = ServiceContext.ContentService;
|
||||
var content = contentService.GetById(NodeDto.NodeIdSeed + 4); //This Content expired 5min ago
|
||||
content.ExpireDate = DateTime.Now.AddMinutes(-5);
|
||||
content.ContentSchedule.Add(null, DateTime.Now.AddMinutes(-5));
|
||||
contentService.Save(content);
|
||||
|
||||
var parent = contentService.GetById(NodeDto.NodeIdSeed + 2);
|
||||
@@ -1527,7 +1527,7 @@ namespace Umbraco.Tests.Services
|
||||
// Arrange
|
||||
var contentService = ServiceContext.ContentService;
|
||||
var content = contentService.GetById(NodeDto.NodeIdSeed + 3);
|
||||
content.ReleaseDate = DateTime.Now.AddHours(2);
|
||||
content.ContentSchedule.Add(null, DateTime.Now.AddHours(2));
|
||||
contentService.Save(content, 0);
|
||||
|
||||
var parent = contentService.GetById(NodeDto.NodeIdSeed + 2);
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Umbraco.Tests.Services
|
||||
|
||||
//Create and Save Content "Text Page 1" based on "umbTextpage" -> 1062
|
||||
Content subpage = MockedContent.CreateSimpleContent(contentType, "Text Page 1", textpage.Id);
|
||||
subpage.ReleaseDate = DateTime.Now.AddMinutes(-5);
|
||||
subpage.ContentSchedule.Add(null, DateTime.Now.AddMinutes(-5));
|
||||
ServiceContext.ContentService.Save(subpage, 0);
|
||||
|
||||
//Create and Save Content "Text Page 1" based on "umbTextpage" -> 1063
|
||||
|
||||
@@ -1425,8 +1425,8 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
//TODO: We need to support 'send to publish'
|
||||
|
||||
contentSave.PersistedContent.ExpireDate = contentSave.ExpireDate;
|
||||
contentSave.PersistedContent.ReleaseDate = contentSave.ReleaseDate;
|
||||
//fixme - deal with variants too
|
||||
contentSave.PersistedContent.ContentSchedule.Add(contentSave.ExpireDate, contentSave.ReleaseDate);
|
||||
|
||||
//only set the template if it didn't change
|
||||
var templateChanged = (contentSave.PersistedContent.Template == null && contentSave.TemplateAlias.IsNullOrWhiteSpace() == false)
|
||||
|
||||
Reference in New Issue
Block a user