This is a rather difficult task. To achieve the correct skipping of display elements, you need to perform several steps:
Create part correctly
There are several pitfalls, since when moving on to the task of adding a part view, you can use the Sadchany time editor in connection with the DateTime properties. But this leads to a lot of additional problems for the table, but in fact this does not apply to the question.
If someone is interested in how to use the date time editor in the garden, I can post this code too, but for now, it would just blow up the code unnecessary.
So, let's go, class details ...
public class ValidityPart : Orchard.ContentManagement.ContentPart<ValidityPartRecord> { // public public System.DateTime? ValidFromUtc { get { return Retrieve(r => r.ValidFromUtc); } set { Store(r => r.ValidFromUtc, value); } } ... public System.DateTime? ValidTillUtc { get { return Retrieve(r => r.ValidTillUtc); } set { Store(r => r.ValidTillUtc, value); } } ... public bool IsContentItemValid() { var lUtcNow = System.DateTime.UtcNow; return (ValidFromUtc == null || ValidFromUtc.Value <= lUtcNow) && (ValidTillUtc == null || ValidTillUtc.Value >= lUtcNow); } ... }
... and the recording class ...
public class ValidityPartRecord : Orchard.ContentManagement.Records.ContentPartRecord { // valid from value as UTC to use Orchard convention (see CommonPart table) and to be compatible with projections // (date/time tokens work with UTC values, see https://github.com/OrchardCMS/Orchard/issues/6963 for a related issue) public virtual System.DateTime? ValidFromUtc { get; set; } // valid from value as UTC to use Orchard convention (see CommonPart table) and to be compatible with projections // (date/time tokens work with UTC values, see https://github.com/OrchardCMS/Orchard/issues/6963 for a related issue) public virtual System.DateTime? ValidTillUtc { get; set; } }
Create a custom content request class
public class MyContentQuery : Orchard.ContentManagement.DefaultContentQuery { // public public ContentQuery(Orchard.ContentManagement.IContentManager aContentManager, Orchard.Data.ITransactionManager aTransactionManager, Orchard.Caching.ICacheManager aCacheManager, Orchard.Caching.ISignals aSignals, Orchard.Data.IRepository<Orchard.ContentManagement.Records.ContentTypeRecord> aContentTypeRepository, Orchard.IWorkContextAccessor aWorkContextAccessor) : base(aContentManager, aTransactionManager, aCacheManager, aSignals, aContentTypeRepository) { mWorkContextAccessor = aWorkContextAccessor; } protected override void BeforeExecuteQuery(NHibernate.ICriteria aContentItemVersionCriteria) { base.BeforeExecuteQuery(aContentItemVersionCriteria); // note: // this method will be called each time a query for multiple items is going to be executed (eg content items of a container, layers, menus), // this gives us the chance to add a validity criteria var lWorkContext = mWorkContextAccessor.GetContext(); // exclude admin as content items should still be displayed / accessible when invalid as validity needs to be editable if (lWorkContext == null || !Orchard.UI.Admin.AdminFilter.IsApplied(lWorkContext.HttpContext.Request.RequestContext)) { var lUtcNow = System.DateTime.UtcNow; // left outer join of ValidityPartRecord table as part is optional (not present on all content types) var ValidityPartRecordCriteria = aContentItemVersionCriteria.CreateCriteria( "ContentItemRecord.ValidityPartRecord", // string adopted from foreach loops in Orchard.ContentManagement.DefaultContentQuery.WithQueryHints() NHibernate.SqlCommand.JoinType.LeftOuterJoin ); // add validity criterion ValidityPartRecordCriteria.Add( NHibernate.Criterion.Restrictions.And( NHibernate.Criterion.Restrictions.Or( NHibernate.Criterion.Restrictions.IsNull("ValidFromUtc"), NHibernate.Criterion.Restrictions.Le("ValidFromUtc", lUtcNow) ), NHibernate.Criterion.Restrictions.Or( NHibernate.Criterion.Restrictions.IsNull("ValidTillUtc"), NHibernate.Criterion.Restrictions.Ge("ValidTillUtc", lUtcNow) ) ) ); } } // private Orchard.IWorkContextAccessor mWorkContextAccessor; }
This significantly adds the left join of the validity part fields to the SQL query (content request) and extends the WHERE with a valid condition.
Please note that this step is only possible if the solution describes the following problem: https://github.com/OrchardCMS/Orchard/issues/6978
Register content request class
public class ContentModule : Autofac.Module { protected override void Load(Autofac.ContainerBuilder aBuilder) { aBuilder.RegisterType<MyContentQuery>().As<Orchard.ContentManagement.IContentQuery>().InstancePerDependency(); } }
Create a personalized content manager
public class ContentManager : Orchard.ContentManagement.DefaultContentManager { // public public ContentManager( Autofac.IComponentContext aContext, Orchard.Data.IRepository<Orchard.ContentManagement.Records.ContentTypeRecord> aContentTypeRepository, Orchard.Data.IRepository<Orchard.ContentManagement.Records.ContentItemRecord> aContentItemRepository, Orchard.Data.IRepository<Orchard.ContentManagement.Records.ContentItemVersionRecord> aContentItemVersionRepository, Orchard.ContentManagement.MetaData.IContentDefinitionManager aContentDefinitionManager, Orchard.Caching.ICacheManager aCacheManager, System.Func<Orchard.ContentManagement.IContentManagerSession> aContentManagerSession, System.Lazy<Orchard.ContentManagement.IContentDisplay> aContentDisplay, System.Lazy<Orchard.Data.ITransactionManager> aTransactionManager, System.Lazy<System.Collections.Generic.IEnumerable<Orchard.ContentManagement.Handlers.IContentHandler>> aHandlers, System.Lazy<System.Collections.Generic.IEnumerable<Orchard.ContentManagement.IIdentityResolverSelector>> aIdentityResolverSelectors, System.Lazy<System.Collections.Generic.IEnumerable<Orchard.Data.Providers.ISqlStatementProvider>> aSqlStatementProviders, Orchard.Environment.Configuration.ShellSettings aShellSettings, Orchard.Caching.ISignals aSignals, Orchard.IWorkContextAccessor aWorkContextAccessor) : base(aContext, aContentTypeRepository, aContentItemRepository, aContentItemVersionRepository, aContentDefinitionManager, aCacheManager, aContentManagerSession, aContentDisplay, aTransactionManager, aHandlers, aIdentityResolverSelectors, aSqlStatementProviders, aShellSettings, aSignals) { mWorkContextAccessor = aWorkContextAccessor; } public override ContentItem Get(int aId, Orchard.ContentManagement.VersionOptions aOptions, Orchard.ContentManagement.QueryHints aHints) { var lResult = base.Get(aId, aOptions, aHints); if (lResult != null) { // note: // the validity check is done here (after the query has been executed!) as changing base.GetManyImplementation() to // apply the validity critera directly to the query (like in ContentQuery) will not work due to a second attempt to retrieve the // content item from IRepository<> (see base.GetManyImplementation(), comment "check in memory") when the query // returns no data (and the query should not return data when the validity critera is false) // // http://stackoverflow.com/q/37841249/3936440 var lWorkContext = mWorkContextAccessor.GetContext(); // exclude admin as content items should still be displayed / accessible when invalid as validity needs to be editable if (lWorkContext == null || !Orchard.UI.Admin.AdminFilter.IsApplied(lWorkContext.HttpContext.Request.RequestContext)) { var lValidityPart = lResult.As<ValidityPart>(); if (lValidityPart != null) { if (lValidityPart.IsContentItemValid()) { // content item is valid } else { // content item is not valid, return null (adopted from base.Get()) lResult = null; } } } } return lResult; } // private Orchard.IWorkContextAccessor mWorkContextAccessor; }
Steps 2-4 are necessary if there are content elements, whereas the content type has Container and Containable elements or even content elements that are processed / displayed separately. Here you usually cannot customize the request for content that runs behind the scenes.
Steps 2-4 are not needed if you are using the Projection module. But again, this leads to several other problems in the table, as reported in this problem: https://github.com/OrchardCMS/Orchard/issues/6979