How to dynamically add a content part in a Orchard content handler?

こ雲淡風輕ζ 提交于 2019-12-01 10:13:08

问题


I have an Orchard content handler that calls

Filters.Add(new Orchard.ContentManagement.Handlers.ActivatingFilter<MyPart>("User"));

in its constructor to weld MyPart to a user content item.

How can i weld MyPart based on the content item id?

The issue here is that the content item is not yet created when the constructor is called. I tried hooking into the life cycle with overriding Activating() but that doesn't work either as the content item is also not created yet.


回答1:


Okay, this task is really difficult. Here's my solution.

1) Create an extension method that welds a content part to a content item (sadly, we cannot use ContentItemBuild.Weld() as there's no chance to pass the content item)

// adopted from ContentItemBuilder.Weld<>()
public static TPart Weld<TPart>(this Orchard.ContentManagement.ContentItem aContentItem)
  where TPart: Orchard.ContentManagement.ContentPart, new()
{
  var partName = typeof(TPart).Name;

  // obtain the type definition for the part
  var typePartDefinition = aContentItem.TypeDefinition.Parts.FirstOrDefault(p => p.PartDefinition.Name == partName);
  if (typePartDefinition == null) {
      // If the content item's type definition does not define the part; use an empty type definition.
      typePartDefinition = new Orchard.ContentManagement.MetaData.Models.ContentTypePartDefinition(
          new Orchard.ContentManagement.MetaData.Models.ContentPartDefinition(partName),
          new Orchard.ContentManagement.MetaData.Models.SettingsDictionary());
  }

  // build and weld the part
  var part = new TPart { TypePartDefinition = typePartDefinition };
  aContentItem.Weld(part); 

  return part;
}

2) Define a StorageFilter for dynamically welding the content part to the content item

public class BaseWeldBeforeStorageFilter<TPart, TRecord> : Orchard.ContentManagement.Handlers.IContentStorageFilter 
  where TPart: Orchard.ContentManagement.ContentPart, new()
  where TRecord: Orchard.ContentManagement.Records.ContentPartRecord
{
  // public
    public BaseWeldBeforeStorageFilter(Orchard.Data.IRepository<TRecord> aPartRecords)
    {
      mPartRecords = aPartRecords;
    }

    ...

    public void Loading(Orchard.ContentManagement.Handlers.LoadContentContext aContext)
    {
      // dynamically weld TPart to content item when condition is met (is a user, does record exist)
      if (aContext.ContentItem.Is<Orchard.Users.Models.UserPart>())
      {
        if (!aContext.ContentItem.Is<TPart>())
        {
          if (mPartRecords.Count(r => r.Id == aContext.ContentItem.Id) > 0)
            aContext.ContentItem.Weld<TPart>();
        }
      }
    }

    ...

  // private
    Orchard.Data.IRepository<TRecord> mPartRecords;
}

3) Define the content handler for the dynamic content part

public abstract class BasePartHandler<TPart, TRecord> : Orchard.ContentManagement.Handlers.ContentHandler
  where TPart: Orchard.ContentManagement.ContentPart<TRecord>, new()
  where TRecord: Orchard.ContentManagement.Records.ContentPartRecord, new()
{
  // public
    // the constructor of a content handler is called when a content item (e.g. user) is created
    public BasePartHandler(Orchard.Data.IRepository<TRecord> aPartRecords)
    {
      ...

      // add storage filter for dynamically welding TPart to content item
      Filters.Add(new BaseWeldBeforeStorageFilter<TPart, TRecord>(aPartRecords)); 

      // enable storing TPart to associated table
      Filters.Add(Orchard.ContentManagement.Handlers.StorageFilter.For<TRecord>(aPartRecords));

      ...

      // listen to user creation, update, removal...
      OnCreated<Orchard.Users.Models.UserPart>(UserCreated);
      ...
    }

    ...

  // private
    private void UserCreated(Orchard.ContentManagement.Handlers.CreateContentContext aContext, Orchard.Users.Models.UserPart aUserPart)
    {
      if (...) // condition for checking whether user 
        CreatePartRecordWhenNeededAndWeldPart(aContext.ContentItem, ...);
    }

    private void CreatePartRecordWhenNeededAndWeldPart(Orchard.ContentManagement.ContentItem aContentItem)
    {
      TPart lPart = aContentItem.Weld<TPart>();

      // assign record, adopted from StorageFilter.cs
      // todo: find a way to do it the "Orchard way" as this feels like hack
      lPart._record.Loader(r => 
        new TRecord {
          Id = aContentItem.Id, 
          ContentItemRecord = new Orchard.ContentManagement.Records.ContentItemRecord {Id = aContentItem.Id}
        });

      // there are situations where part record already exists in DB but part is not welded at this point, thus check for existing record to avoid
      //  - creating record multiple times 
      //  - NHibernate exception
      if (!mPartRecords.Table.Contains(lPart.Record))
        mPartRecords.Create(lPart.Record);
    }

    private Orchard.Data.IRepository<TRecord> mPartRecords;
}

As for now, the dynamic content part handling is working but I'm still unsure how to create a content part record in Orchard properly (see todo hint in source code of step 3).



来源:https://stackoverflow.com/questions/30397197/how-to-dynamically-add-a-content-part-in-a-orchard-content-handler

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!