Как пропустить отображение элемента контента в Orchard CMS?

У меня есть часть контента, которая предоставляет метки начала и конца. Эти 2 поля используются для определения периода времени, в который должен отображаться элемент контента.

Теперь у меня возникают трудности с реализацией подхода с пропуском, в то время как элементы контента не должны отображаться / пропускаться, когда период времени не охватывает текущее время.

Копание исходного кода и попытка найти точку входа для моего подхода привело к следующему обработчику контента

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()вызов URL пропущенного элемента контента приводит к исключению, потому чтоView(null) это отвратительно

Итак, что будет правильным подходом или существует какой-либо модуль, который выполняет эту работу? Я не мог найти один.

 devqon08 июн. 2016 г., 12:58
Возможно, вы захотите просто зарегистрировать событие «отменить публикацию» по истечении времени
 ViRuSTriNiTy09 июн. 2016 г., 11:27
@Xceno Это интересный подход. В настоящее время я получил список (), работающий с использованием проекций вместо контейнеров / вмещаемых. Поэтому я могу настроить запросы по мере необходимости из области администратора. Но я также хотел бы узнать больше о подходе DefaultContentQuery. Кстати: у вас есть идея, как отменить рендеринг одной фигуры (так как это единственная проблема, оставленная сейчас)?
 devqon08 июн. 2016 г., 13:47
Возможно, вам удастся остановить его рендеринг, но тогда у вас все еще будут проблемы сCount как вы упомянули. Так что пейджинг, возможно, не удается, и, вероятно, больше с этим
 ViRuSTriNiTy08 июн. 2016 г., 13:43
@devqon Да, я тоже об этом думал. Но для этого потребуется некоторая фоновая задача, которая запускается периодически, и эта задача должна запрашивать все элементы содержимого, чтобы найти нужные для отмены публикации. Кроме того, я не фанат отмены публикации, так как это может запутать пользователя, который нажимал кнопку публикации ранее. Я все еще надеюсь, что есть способ как-нибудь безопасно прервать рендеринг фигуры ...
 Xceno14 июн. 2016 г., 14:55
@ViRuSTriNiTy Я только что застрял с той же проблемой несколько минут назад. Я буду держать вас в курсе, когда найду окончательное решение. Но, черт возьми, вы правы в DefaultContentQuery; Похоже, вам придется реализовать интерфейс самостоятельно (и просто c / p из стандартной реализации ... Мех)
 ViRuSTriNiTy14 июн. 2016 г., 18:31
@Xceno Да, я сейчас работаю над решением для реализации IContentQuery.ForPart <TPart> (), так как это кажется точкой входа для всех запросов. Я все еще борюсь за левое соединение моей части контента здесь, чтобы проверить мои поля.
 ViRuSTriNiTy14 июн. 2016 г., 13:38
@Xceno Хм, кажется, унаследовал отDefaultContentQuery не будет работать почти все полезные методы являются частными. Я мог бы создать копию всего файла и использовать его вместо этого.
 Xceno09 июн. 2016 г., 10:56
Если вы не можете или не хотите изменять запросы, которые возвращают ваш контент, вы можете создать модуль, который предоставляет поля для отметок времени, которые вы упомянули в качестве присоединяемой части, например,DisplayDurationPart, Тогда я бы создал кастомContentQuery класс, который наследуется отDefaultContentQuery и, например, изменить.List()-Метод для фильтрации этих полей перед вызовомSlice(), После этого вам нужно будет зарегистрировать кастомContentQuery класс с автофаком; проверятьOrchard.ContentManagement.ContentModule, Если вы заинтересованы в таком подходе, я мог бы создать небольшую демонстрацию.

Ответы на вопрос(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; }
}

Создать настроенный класс запроса контента

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

Зарегистрировать класс запроса контента

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

Создать персонализированный менеджер контента

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

Шаги 2-4 необходимы при наличии элементов контента, тогда как тип контента имеетContainer а такжеContainable части или даже элементы контента, которые обрабатываются / отображаются отдельно. Здесь вы обычно не можете настроить запрос контента, который выполняется за кулисами.

Шаги 2-4 являютсяне необходимо, если вы используете модуль проекции. Но опять же, это приводит к рассмотрению нескольких других проблем, о которых сообщается в этом выпуске:https://github.com/OrchardCMS/Orchard/issues/6979

Ваш ответ на вопрос