如何在Orchard CMS中跳过显示内容? [英] How to skip displaying a content item in Orchard CMS?

查看:277
本文介绍了如何在Orchard CMS中跳过显示内容?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



我现在无法实现跳过方式,而内容项不应该当时间段不超过当前时间时显示/跳过。



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

  public class SkipContentHandler:Orchard.ContentManagement.Handlers.ContentHandler 
{
protected override void BuildDisplayShape Orchard.ContentManagement.Handlers.BuildDisplayContext aContext)
{
if(...)//我的条件只处理需要跳过的内容形状
{
aContext.Shape = null; // return null shape to skip it
}
}
}

这个工作,但有几个副作用




  • 我不得不更改源代码 BuildDisplayContext as Shape 通常只读

  • 当内容包含内容的内容时,列表形状可能会显示错误的寻呼机部分是因为 Count()调用 ContainerPartDriver.Display() BuildDisplay( )

  • 调用被跳过的内容项的URL会导致异常,因为 View(null)



那么这里有什么正确的方法,还有就是有这样的工作吗?我找不到一个。

解决方案

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


  1. 创建零件正确



    在这里有一些缺陷,当进入添加零件视图的任务时,可能会使用Orchards日期时间编辑器与 DateTime 属性。但是这带来了许多额外的问题,但这些问题并没有真正涉及到这个问题。



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



    所以这里我们去零件类...

      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 
    {
    //以UTC值的形式使用Orchard惯例(请参阅CommonPart表)并与投影兼容
    //(日期/时间令牌使用UTC值,请参见https://github.com) / OrchardCMS / Orchard / issues / 6963相关问题)
    public virtual System.DateTime? ValidFromUtc {get;组; }

    //以UTC值的形式使用Orchard约定(见CommonPart表),并与投影兼容
    //(日期/时间令牌使用UTC值,请参见https: //github.com/OrchardCMS/Orchard/issues/6963针对相关问题)
    public virtual System.DateTime? ValidTillUtc {get;组;
    }


  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
    :base(aContentManager,aTransactionManager,aCacheManager,aSignals,aContentTypeRepository)
    {
    mWorkContextAccessor == = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = aWorkContextAccessor;
    }

    protected override void BeforeExecuteQuery(NHibernate.ICriteria aContentItemVersionCriteria)
    {
    base.BeforeExecuteQuery(aContentItemVersionCriteria);

    //注意:
    //每次将要执行多个项目的查询(例如容器,图层,菜单的内容项)时,将调用此方法,
    //这给了我们添加有效性条件的机会

    var lWorkContext = mWorkContextAccessor.GetContext();

    //排除admin作为内容项目仍然可以显示/无效,因为有效性需要被编辑
    if(lWorkContext == null ||!Orchard.UI.Admin.AdminFilter。 IsApplied(lWorkContext.HttpContext.Request.RequestContext))
    {
    var lUtcNow = System.DateTime.UtcNow;

    // ValidityPartRecord表的外部连接作为部分可选(不存在于所有内容类型)
    var ValidityPartRecordCriteria = aContentItemVersionCriteria.CreateCriteria(
    ContentItemRecord.ValidityPartRecord,/ / string从foreach循环中采用Orchard.ContentManagement.DefaultContentQuery.WithQueryHints()
    NHibernate.SqlCommand.JoinType.LeftOuterJoin
    );

    //添加有效性标准
    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>() 。< 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.ICont entDisplay> 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:
    //此处完成有效性检查(查询执行完成后)将base.GetManyImplementation()更改为
    //将有效性critera直接应用于查询(如ContentQuery中)将无法工作,因为第二次尝试从IRepository<>中检索
    //内容项(参见base.GetManyImplementation(),注释检入内存)当查询
    //返回没有数据(当有效性critera为false时查询不应返回数据)
    //
    // http://stackoverflow.com/q/37841249/3936440

    var lWorkContext = mWorkContextAccessor.GetContext();

    //排除admin作为内容项目仍然可以显示/无效,因为有效性需要被编辑
    if(lWorkContext == null ||!Orchard.UI.Admin.AdminFilter。 IsApplied(lWorkContext.HttpContext.Request.RequestContext))
    {
    var lValidityPart = lResult.As< ValidityPart>();
    如果(lValidityPart!= null)
    {
    if(lValidityPart.IsContentItemValid())
    {
    //内容项有效
    }
    else
    {
    //内容项无效,返回null(从base.Get()中获取)

    lResult = null;
    }
    }
    }
    }

    return lResult;
    }

    // private
    Orchard.IWorkContextAccessor mWorkContextAccessor;
    }


步骤2-4当内容项目是必需的,而内容类型具有容器可容纳部分或甚至内容项目被处理/分别显示。您通常无法自定义幕后执行的内容查询。



如果使用投影模块,步骤2-4不需要 。但是,再次,这个问题还带来了一些其他问题: https:// github .com / OrchardCMS / Orchard / issues / 6979


I have a content part that provides a begin timestamp and end timestamp option. These 2 fields are used to define a period of time in which the content item should be displayed.

I now have difficulties to implement a skip approach whereas content items should not be displayed / skipped when the period of time does not span the current time.

Digging in the source code and trying to find an entry point for my approach resulted in the following 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
    }
  }
}

This works but there are several side effects

  • I had to alter the source code of BuildDisplayContext as the Shape is normally read only
  • List shape may displayed a wrong pager when it contains content items with my content part because the Count() call in ContainerPartDriver.Display() is executed before BuildDisplay()
  • calling the URL of a content item that is skipped results in an exception because View(null) is abigious

So, what would be the correct approach here or is there any module in existence that does the job? I couldn't find one.

解决方案

This is a quite complex task. There are several steps needed to achieve a proper skipping of display items:

  1. Create the part correctly

    There are a few pitfalls here as when coming to the task of adding a part view one might utilize Orchards date time editor in connection with the DateTime properties. But this brings a heck of a lot of additional issues to the table but these don't really relate to the question.

    If someone is interested in how to use Orchards date time editor then i can post this code too, but for now it would only blow up the code unnecessarly.

    So here we go, the part class...

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

  2. Create a customized content query 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 (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;
    }
    

    This essentially adds a left join of the validity part fields to the SQL query (content query) and extends the WHERE statement with the validity condition.

    Please note that this step is only possible with the solution described the following issue: https://github.com/OrchardCMS/Orchard/issues/6978

  3. Register the content query class

    public class ContentModule : Autofac.Module
    {
      protected override void Load(Autofac.ContainerBuilder aBuilder)
      {
        aBuilder.RegisterType<MyContentQuery>().As<Orchard.ContentManagement.IContentQuery>().InstancePerDependency();
      }
    }
    

  4. Create a customized 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 needed when having content items whereas the content type has a Container and Containable part or even content items which are processed / displayed separately. Here you normally cannot customize the content query that is executed behind the scenes.

Steps 2-4 are not needed if you use the Projection module. But again, this brings a few other issues to the table as reported in this issue: https://github.com/OrchardCMS/Orchard/issues/6979

这篇关于如何在Orchard CMS中跳过显示内容?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆