如何跳过在Orchard CMS中显示内容项?

时间:2016-06-07 16:21:21

标签: datetime orchardcms

我有一个内容部分,提供开始时间戳和结束时间戳选项。这两个字段用于定义应显示内容项的时间段。

我现在很难实现跳过方法,而当时间段不跨越当前时间时,不应显示/跳过内容项。

挖掘源代码并尝试为我的方法找到一个入口点,产生了以下内容处理程序

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
    }
  }
}

这有效,但有几种副作用

  • 我必须更改BuildDisplayContext的源代码,因为Shape通常是只读的
  • 列表形状在包含内容部分的内容项时可能会显示错误的寻呼机,因为Count()中的ContainerPartDriver.Display()来电已在BuildDisplay()之前执行
  • 调用被跳过的内容项的网址会导致异常,因为View(null)很神奇

那么,这里的正确方法是什么,或者是否有任何模块可以完成这项工作?我找不到一个。

1 个答案:

答案 0 :(得分:1)

这是一项非常复杂的任务。实现正确跳过显示项目需要几个步骤:

  1. 正确创建零件

    这里有一些陷阱,因为在进行添加零件视图的任务时,可能会使用Orchards日期时间编辑器与DateTime属性相关联。但这会带来很多额外的问题,但这些问题并没有真正与这个问题有关。

    如果有人对如何使用Orchards日期时间编辑器感兴趣,那么我也可以发布此代码,但是现在它只会不必要地炸掉代码。

    所以我们去,部分类......

    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);
        }
    
      ...
    }
    

    ......和记录班......

    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; }
    }
    
  2. 创建自定义内容查询类

    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 (e.g. 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;
    }
    

    这实质上是将有效性部分字段的左连接添加到SQL查询(内容查询),并使用有效性条件扩展WHERE语句。

    请注意,此步骤仅适用于描述以下问题的解决方案:https://github.com/OrchardCMS/Orchard/issues/6978

  3. 注册内容查询类

    public class ContentModule : Autofac.Module
    {
      protected override void Load(Autofac.ContainerBuilder aBuilder)
      {
        aBuilder.RegisterType<MyContentQuery>().As<Orchard.ContentManagement.IContentQuery>().InstancePerDependency();
      }
    }
    
  4. 创建自定义内容管理器

    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;
    }
    
  5. 具有内容项时需要步骤2-4,而内容类型具有ContainerContainable部分或甚至是单独处理/显示的内容项。在这里,您通常无法自定义幕后执行的内容查询。

    如果使用Projection模块,则步骤2-4 。但同样,这会带来一些其他问题,如本期所述:https://github.com/OrchardCMS/Orchard/issues/6979