0
votes

In Orchard CMS I have a service that pulls data from an external data source, and loads the data into an Orchard Content Part. The Part has a migration that welds it with a title part, and I have a route so that my controller is being hit via a URL:

I am using a controller to access the item via a URL, much like the Blog Part controller. However I can't render my part... The Blog Controller does similar to the following:

    var asset = _assetService.Get(1234);
    if (asset == null) return HttpNotFound();

    var model = _services.ContentManager.BuildDisplay(asset); 

    return new ShapeResult(this, model);

But if I do this, the 'BuildDisplay' method looks for asset.ContentItem but this is null, despite deriving my part from 'ContentPart'.

What do I need to do to get my data to display?

2
What does your _assetService.Get() do? Is your asset a content type? - devqon
The Asset is Derived from ContentPart. (In theory I can then create a ContentItem that uses this part and others welded together). The first stage is getting a ContentPart - populated from an external source - to show on a page....then add other parts (in code) to it so that it is an Orchard Content Item, with all teh bells and whistles that go with it. - GadgetGeekUK
I'm not sure if you can use BuildDisplay on a single part - devqon
That's the question - what is the correct way to get from a Content Part - to ??? - to a Shape? - to be displayed? - GadgetGeekUK
Why do you insist on it being a part? A part should never be used outside of a content item. You don't need a part to use shapes. It looks like you;'d make your life a lot less difficult by just building a shape from your controller action. - Bertrand Le Roy

2 Answers

0
votes

If I understand correctly, you are trying to display only one part, and not a whole content item.

To display a single shape, you can do the following:

private readonly IAssetService _assetService;

public MyController(IShapeFactory shapeFactory, IAssetService assetService) {
    _assetService = assetService;
    Shape = shapeFactory;
}

public dynamic Shape { get; set; }

public ActionResult MyAction(int assetId) {
    var asset = _assetService.Get(1234);
    if (asset == null) return HttpNotFound();

    // the shape factory can call any shape (.cshtml) that is defined
    // this method assumes you have a view called SomeShapeName.cshtml
    var model = Shape.SomeShapeName(asset);

    return new ShapeResult(this, model);
}

!!Note: This does not kick of the (display)driver of the part, it only returns the .cshtml with the given model

0
votes

By having my part deriving from ContentPart, I can use the following Controller method:

private readonly IAssetService _assetService;
private readonly IOrchardServices _services;

public MyController(IShapeFactory shapeFactory, IAssetService assetService, IOrchardServices services) {
    _assetService = assetService;
    _services = services;
    Shape = shapeFactory;
}

public dynamic Shape { get; set; }

public ActionResult MyAction(int assetId) {
    var asset = _assetService.Get(1234);
    if (asset == null) return HttpNotFound();

    // this method assumes you have a view called Parts.Asset.cshtml (see the AssetPartDriver)
    var model = _services.ContentManager.New("Asset");
    var item = contentItem.As<AssetPart>();
    item.Populate(asset) // Method that just populates the service loaded object into the ContentPart

    return new ShapeResult(this, _services.ContentManager.BuildDisplay(item));
}

This will use the 'AssetPartDriver':

public class AssetPartDriver : ContentPartDriver<AssetPart>
    {
        protected override DriverResult Display(AssetPart part, string displayType, dynamic shapeHelper)
        {
            return ContentShape("Parts_Asset", () => shapeHelper.Parts_Asset()); // Uses Parts.Asset.cshtml
        }
    }

And in conjunction with the 'Placement.info' file renders on the screen:

<Placement>
  <Match ContentType="Asset">
    <Match DisplayType="Detail">
      <Place Parts_Asset="Content"/>
    </Match>
  </Match>
</Placement>

The migration file combines my web service part with other Orchard parts:

public class Migrations : DataMigrationImpl
    {
        public int Create()
        {
            ContentDefinitionManager.AlterTypeDefinition("Asset", cfg => cfg
                .WithPart("AssetPart")
                .WithPart("AutoroutePart", builder => builder
                    .WithSetting("AutorouteSettings.AllowCustomPattern", "True"))
                .Listable()
                .Securable()
                .Creatable(false));

            ContentDefinitionManager.AlterPartDefinition("AssetPart", part => part
                .WithDescription("A part that contains details of an individual Web Service loaded asset."));
            return 1;
        }
    }

These additional parts are not yet used, but can be populated during creation and displayed individually using the placement file. This is the first step of what I was trying to achieve!!