How to skip content display in Orchard CMS?

I have a piece of content that provides a timestamp and timestamp parameter. These 2 fields are used to determine the time period in which the content item should be displayed.

Now I am having difficulty implementing the skip method, whereas content elements should not be displayed / skipped if the time period does not extend to the current time.

Digging the source code and trying to find an entry point for my approach led to the next content handler

public class SkipContentHandler : Orchard.ContentManagement.Handlers.ContentHandler { protected override void BuildDisplayShape(Orchard.ContentManagement.Handlers.BuildDisplayContext aContext) { if (...) // my condition to process only content shapes which need to be skipped { aContext.Shape = null; // return null shape to skip it } } } 

It works, but there are a few side effects.

  • I had to change the source code of the BuildDisplayContext , since Shape usually only reads
  • The list form may display the wrong pager when it contains content elements with my piece of content, because the call to Count() in ContainerPartDriver.Display() is done before BuildDisplay()
  • calling the URL of a skipped content item throws an exception because View(null) is absurd

So, what would be the right approach here, or is there any module that does this work? I could not find him.

+1
datetime orchardcms
source share
1 answer

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

+1
source share

All Articles