Noobie relating multiple content on a single page

Topics: Administration, Core, General, Writing modules
Dec 19, 2012 at 4:46 PM

I've spent some time now trying to familiarise myself with Orchard and frankly, I like what I see.  Apart from: I'm struggling with the whole Widget/Content/Layer thing, maybe someone can provide the insight I need??

My basic requirement is that any "page" can contain a number of sections, different pages might have different layouts (rather like the old masterpage idea).  On any one page I want section a to contain one bit of content, section b another and so on.  I can't seem to do this as just content - I need to involve widgets which are enabled per layer and I select a layer based on the url.  This seems awkward and confusing because the "content admin" ends up in two places - "widgets" and "content".  So I've convinced myself I'm missing something because it should be really easy.

I could write my own content part, record, driver, handler, views, etc. but as each of the sections is only a chunk of html, I feel like I'd be re-inventing rather than re-using.

Help?!?!

Thanks.

Dec 19, 2012 at 5:03 PM

Orchard uses "Shapes" to represent things that are going to be displayed. Shapes are sort of like ViewModels in Orchard. When you display content like a blog post, the Driver creates a shape, and that shape is arranged inside the layout defined by the Theme. Likewise, when you create a widget, the widget's driver creates a shape, and that shape is displayed in a certain part of the page. 

Widgets let you target content into a specific Zone, but if you don't want to use Widgets to map shapes to particular zones, you can still use normal content parts, and use the placement.info file to push specific shapes to specific Zones. You can also do it programmatically (look up some of my recent replies on this message board, I gave a code example). 

How do you get multiple content items displaying on a page? How do you get related content items displaying? You can use Content Picker field to define relations between two content items. I haven't used that, but I believe when you do, the related item is available as a field in the Content Part, but I'm not sure what the standard way of displaying is, or if you need to explicitly display it, or if it will automatically display with the parent content item.

Then you also have other options: Drivers can return multiple shapes, so you can query the items you need from the Driver and return multiple shapes, and use placements.info to map those shapes to specific Zones. You can also create a custom route and controller and gather the shapes you need in the controller action, and use a custom view, in combo with placements.info & programmatic pushing of shapes to Zones. 

Hope this helps. 

Dec 20, 2012 at 11:00 AM

Hi TheMonarch, thanks for your help.  Still a little unsure though - the content picker sounds like a good place to start, but of course the devil is in the detail - I've created a content picker field but how do I use it in the display?!?!  Or if I'm using a driver to return multiple shapes ... how?  The examples I come across all seem to return a single shape so can you provide an example???

 

Thanks again.

Dec 20, 2012 at 2:42 PM

Hi again.

Would it please be possible to see a quick working example of how to get multiple sections of content onto a single page?  I am still going round and round in circles of creating parts, types, etc, trying to assign part ids, looking at whether this is a view/placement.info issue, deleting & trying another tack etc. and I just can't see the wood for the trees.

Thanks again.

Dec 20, 2012 at 3:14 PM

Can you give more details on what you are trying to do? It would help if you pasted the code for your Content Type and/or Content Parts, and describe what you want to display on the page (which pieces to display and where on the page to display them). 

Dec 20, 2012 at 3:22 PM

For the driver, you can either define two drivers for the same content part, and have each one return a different shape, or you can return two shapes from the Display() method. To do the latter, use the "Combined" method of ContentPartDriver. Look around in the code for this and you'll find lots of examples where this is used. Here's where it's being used in BodyPartDriver.cs: 

 

        protected override DriverResult Display(BodyPart part, string displayType, dynamic shapeHelper) {
            return Combined(
                ContentShape("Parts_Common_Body",
                             () => {
                                 var bodyText = _htmlFilters.Aggregate(part.Text, (text, filter) => filter.ProcessContent(text, GetFlavor(part)));
                                 return shapeHelper.Parts_Common_Body(Html: new HtmlString(bodyText));
                             }),
                ContentShape("Parts_Common_Body_Summary",
                             () => {
                                 var bodyText = _htmlFilters.Aggregate(part.Text, (text, filter) => filter.ProcessContent(text, GetFlavor(part)));
                                 return shapeHelper.Parts_Common_Body_Summary(Html: new HtmlString(bodyText));
                             })
                );
        }

Dec 20, 2012 at 4:08 PM
Edited Dec 20, 2012 at 4:21 PM

Simple approach 1 = no code (yet).  I've created a new content type called "Sectioned Page" (within admin) consisting of a content picker field (multiples allowed) and title, common, navigation etc parts.  I've also created another content type (within admin) called "Page Chunk" containing a SectionId text field and common, body and title parts.

I can now create page chunks content and add them into sectioned page content.

When I look at the published page, I see :

Page chunks: Chunk title1, Chunk title 2

Both are links to other pages.

That makes me think I need to write a new template/view for this part, but I need to consider how to use the SectionId with Placement to get it into the right place when rendered.  Perhaps I need to write the whole SectionedPage ContentPart in code so I can customise every aspect of it?  

Thanks again, I appreciate your time explaining the simples.

Developer
Dec 20, 2012 at 4:21 PM

Perhaps what would be cool (and useful) is a ContentWidget that allows you to select a content item to render, optionally specifying a DisplayType as well. To implement that, all you would need is a custom part (ContentWidgetPart) with a ContentPickerField attached. From the driver, access this content picker field and the selected content item Id(s), and render the content items using the ContentManager.BuildDisplay method into a list of shapes.

Dec 20, 2012 at 5:01 PM

I agree that sounds useful.  But there are quite a few Orchard-specifics in there in need of some explaining - how do I attach a ContentPickerField?  How do I access the selected content item ids?  How do I use the ContentManager.BuildDisplay method (maybe what I'm asking here is: where is the API documentation)???

Thank you again!

Developer
Dec 20, 2012 at 11:20 PM
Edited Dec 20, 2012 at 11:21 PM

I don't think there's API documentation on all aspects involved, but the feature has been implemented in this fork: http://orchard.codeplex.com/SourceControl/network/forks/sfmskywalker/ContentPickerWidget?branch=1.x

Download or clone the fork, enable the ContentPicker Widget feature, create a sample page, add the ContentPickerWidget to the homepage for example, and select the sample page. The sample page should be rendered inline on the homepage.

May 16, 2013 at 11:52 PM
Edited May 16, 2013 at 11:57 PM
@TheMonarch: Thanks for your code for Combined method to return 2 shapes. I have tried this and come across problems in my template. I have two shapes, and thereby two models, but a cshtml file only accepts one @model. When I get to formatting my second shape, the Model is still the one it was for the first shape. Do you know of a way I can get around this problem?
May 17, 2013 at 5:05 PM
skitterm wrote:
@TheMonarch: Thanks for your code for Combined method to return 2 shapes. I have tried this and come across problems in my template. I have two shapes, and thereby two models, but a cshtml file only accepts one @model. When I get to formatting my second shape, the Model is still the one it was for the first shape. Do you know of a way I can get around this problem?
Each shape can be provided with different variables for the view's dynamic Model. eg:
protected override DriverResult Display(BodyPart part, string displayType, dynamic shapeHelper) {
            return Combined(
                ContentShape("Parts_Common_Body",
                             () => {
                                 var bodyText = _htmlFilters.Aggregate(part.Text, (text, filter) => filter.ProcessContent(text, GetFlavor(part)));
                                 return shapeHelper.Parts_Common_Body(
                                           Html: new HtmlString(bodyText), 
                                           MainTemplate: true //new property for the view
                                 );
                             }),
                ContentShape("Parts_Common_Body_Summary",
                             () => {
                                 var bodyText = _htmlFilters.Aggregate(part.Text, (text, filter) => filter.ProcessContent(text, GetFlavor(part)));
                                 return shapeHelper.Parts_Common_Body_Summary(Html: new HtmlString(bodyText), MainTemplate: fasle, SomeValue: 2);
                             })
                );
        }
Parts_Common_Body accessible properties:
  • Model.Html (string)
  • Model.MainTemplate (bool).
Parts_Common_Body_Summary accessible properties:
  • Model.Html (string)
  • Model.MainTemplate (bool)
  • Model.SomeValue (int).
Just feed the constructor of the shape the values you need.