How to go programmatically from content to template?

Topics: General
Jun 13, 2011 at 8:47 AM
Edited Jun 14, 2011 at 7:37 AM

Still a perpetual n00b here. I have a new content record of a custom content type. I’d like to display it in Razor directly in a blank-canvas theme, but I don’t know how to get to it in the model. Perhaps I have to add it to the shape first, in which case I am lost; the documentation I’ve seen on various loosely similar scenarios seems to be demonstrating larger requirements than mine.

For one extremely simple web site I’m trying to take a minimalist approach to Orchard so that I can know how I can reproduce what I previously accomplished in raw markup without a CMS but still use Orchard to store and define content. I have what I would like for now to be rendered as a bulleted list of list items (I’ll later be making this something else, using a custom template for this content type) that are stored as containable content items in a list. I’m using the Core recipe and I’ve enabled the Lists feature and I’d like to show my custom List by itself on this very simple web page. I’d like to see what I can accomplish without using the Widgets feature.

http://www.orchardproject.net/docs/Creating-lists.ashx

.. describes how to add a widget, add the list to your widget, add the widget to the zone, etc. I’m trying to approach this more minimalistically; I’d like to see if I can just slap the list up onto the view in Razor such as something like @Display(Model.MyCustomList). No widgets, not even any zones. While I know that there is documentation for templating specific content types, I don’t know how to get specific content instances to show up in the Model.

I am using the Layout.cshtml template in a copy of the SafeMode template using the Core recipe.

Jon

Jun 13, 2011 at 1:46 PM

I think you're missing something here. By your List having "RoutePart" there should already be a page that displays your list. Is that not the case?

Jun 13, 2011 at 3:54 PM
Edited Jun 14, 2011 at 7:37 AM

This is for the default view, not ~/list-name/, and my intention was to prove out the ability to add content into the Razor template programmatically as I might add something in addition to this list. I'd like to reference various named content items--a named list here, a custom individual content item there--all in one Razor template. If "that's not how it's done" then I intend to make it doable. I have the source code here. I'd appreciate any help.

Coordinator
Jun 13, 2011 at 7:37 PM

Just to be sure I understand what you're trying to do: are you saying you want to inject the rendering for a specific content item, from the template code?

Jun 13, 2011 at 8:43 PM
Edited Jun 14, 2011 at 7:38 AM

Pretty much. I realize that Orchard has its way of doing things, but for very custom site implementations there are times that I'd like to retain the ASP.NET development workflow as much as possible, and to let the CMS be a tool I can leverage (i.e. store and manage content) and not to let the CMS get in my way with respect to productivity and encapsulation of the site design. I generally plan on using Orchard as it is designed for standard situations with widgets/zones/etc but the ability to inject specific content items directly in the template easily using C#/Razor is kind of a must-have for me for the odd scenarios that come up in every project I have ever had. If it's an issue of properly identifying the content to be selected then some kind of parameterized filter on an exposed method would be expected.

Coordinator
Jun 13, 2011 at 8:55 PM

Ew. Not just because that sort of code doesn't belong in the view, but also because it makes the whole thing extraordinarily fragile. What if you change the theme? Or someone adds an alternate? Well, you'll have to duplicate code or lose features.

Orchard is designed so that you never have to do something like this, but still can retain complete control over all the markup.

It's almost trivial to build a little handler that injects new shapes into the view based on whatever condition you come up with, or from drivers, or controllers.

You can do what you are trying to do (by getting an instance of the content manager from the work context, querying it and then building a shape on the fly), but you shouldn't. Templates should render data that was prepared by other layers in the system. They should not fetch their own data, they should not do anything besides dumbly render data.

Jun 13, 2011 at 9:41 PM
Edited Jun 14, 2011 at 7:38 AM

The custom code to populate the model would be part of the template (i.e. the Templates directory), but not part of the .cshtml file which would indeed just dumbly render it. My ideal would be to prepare it in the model somehow as an exposed member--as I already said, something like @Display(Model.MyCustomList)--and a custom template would exist for the type of content that is being referenced as that member. I just don't know what the path would be for first of all populating that member.

>> It's almost trivial to build a little handler that injects new shapes into the view based on whatever condition you come up with, or from drivers, or controllers. <<

I'm at the edge of my seat. I was just hoping I wouldn't have to memorize all suitable paths in order to find the correct one. If no one can suggest such a path I'll resume my delving into documentation and follow up here with my findings.

Coordinator
Jun 13, 2011 at 9:45 PM

Ah, OK, got it. That sounds a lot more reasonable. It still is a little brittle and backwards as you are essentially opting out of placement by doing it this way (hard coding where your model gets rendered), but at least the data fetching is out of the view. Sorry I suspected you of that :)

I would give you more specific explanations, but I still don't know where you want to write that code. Essentially, wherever you are, you will want to get a reference to the content manager, query that, and then build new shapes from that data. Then, inject those shapes into the zone of your choice and voilà. But again I'm not sure that is exactly what you want to do.

Jun 13, 2011 at 10:19 PM
Edited Jun 14, 2011 at 7:38 AM

bertrandleroy wrote:

It still is a little brittle and backwards as you are essentially opting out of placement by doing it this way (hard coding where your model gets rendered)

Understood, but sometimes for some people in some scenarios an implementation is much more maintainable via its template rather than the dashboard, particularly while transitioning from an existing ASP.NET site.

I would give you more specific explanations, but I still don't know where you want to write that code. Essentially, wherever you are, you will want to get a reference to the content manager, query that, and then build new shapes from that data. Then, inject those shapes into the zone of your choice and voilà. But again I'm not sure that is exactly what you want to do.

Wherever/however, ideally within the template directory (a filter maybe?) Goal is to have a minimum amount of code to add a particular piece of content to the model and get it up in the template without relying on placement information in the database from the dashboard.

I'll keep reading up on docs and tutorials in order to figure out how to "get a reference to the content manager, query that, and then build new shapes from that data" in a convenient manner. That sounds very familiar and I was hoping you would suggest that, I just need to figure out exactly what you're asking me to identify. You're asking me "where you want to write that code" and I'm asking exactly the same thing. Where do I want to write that code? :)

Jun 13, 2011 at 10:23 PM
Edited Jun 13, 2011 at 10:27 PM

The thing with Orchard, is if you try to do things your own way too much, you'll create a whole load of extra workload for yourself while not realising there are already core features or existing modules that will do it for you already :)

So you want to generate markup for arbitrary content items and place it into the template - you can already do that by calling IContentManager.BuildDisplay(...) which will build a dynamic shape that you can attach to the model, and then call @Display(Model.MyCustomShape) or whatever. Edit: You want WorkContext.Resolve<IContentManager>().BuildDisplay(...)

The thing is, as Bertrand points out, there are a lot of different places where you can do that from. You can do it from the view, but you'll need to implement some code somewhere to handle how you're going to access the item you want to render. Exactly which extensibility points to use depends largely on the scenario you're trying to achieve, so the best thing is you can do is ask about a specific requirement rather than generalising things too much. For a lot of tasks there will already be modules that save you having to do anything :)

 

Jun 13, 2011 at 10:25 PM

Oh yeah - I think you're a bit confused about Placement - it's edited from a file called Placement.info; not from the database - and in general it's actually easier to do things there than in your templates...

Coordinator
Jun 13, 2011 at 10:28 PM

It depends on what you are trying to do.

For example, once I needed to add a "buy from Amazon" badge. The main content item on the page was what provided the Amazon SKU. In that case, the course was clear: create a part and have the driver inject the shape into a zone (tha you could configure from settings). You can see the code for that here: http://orchardproject.net/gallery/List/Modules/Orchard.Module.Vandelay.BuyFromAmazon and explained here: http://weblogs.asp.net/bleroy/archive/2011/03/26/dispatching-orchard-shapes-to-arbitrary-zones.aspx.

But that's just one example. Part of the problem is that you are not asking how to do that in a specific situation but rather in a class of situations. So obviously the answer is going to be more vague. I'd be happy to provide specific advice on specific scenarios :)

Jun 13, 2011 at 10:32 PM
Edited Jun 14, 2011 at 7:39 AM

(This is in reply to randompete's first reply above.)

K so let's just get back to scenario #1 (see the original post above). I have a custom content type and a list for that type with some content instances. I want this list to be outputted directly on a completely blank slate. I have a copy of SafeMode and I want my Layout.cshtml to look like this:

@Display(Model.MyCustomList)

That's it. No other content. No <title> tag, no dashboard link, nothing, just what I have above. I might have a separate .cshtml file that describes how to render the outer portion of the list and/or .cshtml file that describes how to render each content item for my custom content type.

The whole point of this is in fact to prove out the exercise, guys. Orchard itself should be my clay, I am still running a standalone ASP.NET site in this scenario and I want to prove out its pliability as an ASP.NET developer and to learn what I must learn in order to leverage only what I want out of it.

Jun 13, 2011 at 10:37 PM
Edited Jun 14, 2011 at 7:39 AM

Thanks Bertrand, I'll take a look at the Amazon sample.

Coordinator
Jun 13, 2011 at 10:42 PM

One thing that will be useful: if you create a new shape called List and give it a property called ContentItems, you'll get list rendering for free. Example from a view: @Display(New.List(ContentItems: someEnumerableOfShapes))

Jun 13, 2011 at 10:51 PM
stimpy77 wrote:K so let's just get back to scenario #1 (see the original post above). I have a custom content type and a list for that type with some content instances. I want this list to be outputted directly on a completely blank slate.  I have a copy of SafeMode and I want my Layout.cshtml to look like this:

@Display(Model.MyCustomList)

That's it. No other content. No <title> tag, no dashboard link, nothing, just what I have above. I might have a separate .cshtml file that describes how to render the outer portion of the list and/or .cshtml file that describes how to render each content item for my custom content type. 

The whole point of this is in fact to prove out the exercise, guys. Orchard itself should be my clay, I am still running a standalone ASP.NET site in this scenario and I want to prove out its pliability as an ASP.NET developer and to learn what I must learn in order to leverage only what I want out of it.

If you changed your Layout.cshtml to just that, you'd have a lot of problems. You'd have no login page for starters. In fact you'd need to suppress all of Orchard's built-in controllers, because they'd all be passing in models that didn't have a MyCustomList property.

Actually, you could implement a request filter to pass MyCustomList into the layout model on every single page. But by deleting the rest of the template you ensure that absolutely no other features will work. Ever.

Have you looked at Layout.cshtml and tried to customise it by just deleting the bits you don't need? The only bit you really need is the Content zone - and that is populated from controllers, it's nothing to do with placement. So you probably want to build your own controller and return a ShapeResult with your custom list shape - that's how Orchard.Core.Containers produces its list view.

Anyway I'm thinking that's not actually a realistic requirement you've given us. Why would you want to use a CMS to make a website with nothing except a list? Why not try some simpler customisations first to get a feel for how the whole framework fits together...

Jun 13, 2011 at 11:19 PM
Edited Jun 13, 2011 at 11:21 PM
randompete wrote:

by deleting the rest of the template you ensure that absolutely no other features will work.

Exactly. Now we're getting somewhere. :)

I am defining my site's content, not Orchard. I'm defining my site's featureset, not Orchard. Now obviously I would restore some of this by the time I go into production. My goal right now is to focus on one task, that is the shortest path from getting the content item into my model and the model onto the template and having it render without an administrator defining widget or zone settings. Can we please stop arguing about whether I'm asking the right question or painting the right scenario or not? I definitely don't want dashboard/login links on my view, this is a proprietary site that has no use for that (I'll still use the login actions but will retain the link-ins separately). In this scenario I have absolutely no intention of making the final product be a standard Orchard site, so much as a proprietary ASP.NET site having an Orchard back-end.

Anyway, I'll continue from here based on what's been shared so far and post my findings.

Coordinator
Jun 13, 2011 at 11:24 PM

But the shortest path is to use what exists rather than reinvent it. It seemslike you'd like to start with the core recipe, but that won't be the shortest path as you'll have to restore lots of things that you'll need later. Still, that may be what will click best with you.

Coordinator
Jun 13, 2011 at 11:25 PM

or... maybe just maybe you don't need Orchard at all.

(just as an aside, everybody here is trying to help :) )

Jun 13, 2011 at 11:38 PM
Edited Jun 14, 2011 at 12:43 AM
bertrandleroy wrote:

But the shortest path is to use what exists rather than reinvent it. It seemslike you'd like to start with the core recipe, but that won't be the shortest path as you'll have to restore lots of things that you'll need later. Still, that may be what will click best with you.

I expect that what I envisioned is a FAR shorter path than the path that Orchard asks me to use with widgets/zones etc, with the exception of the difficulty of finding it. If I'm digging for a nonexistent feature then I intend to implement it and reuse it.

Jun 14, 2011 at 12:49 AM

You haven't answered my question; have you tried just looking at Layout.cshtml and deleting what you don't need?

Jun 14, 2011 at 12:52 AM
stimpy77 wrote:

I am defining my site's content, not Orchard. I'm defining my site's featureset, not Orchard.


Actually - it sounds like you should just build your project in pure MVC, since you don't want a CMS. By definition, a CMS defines your site's content and featureset - although Orchard does a very good job of making that extremely configurable and customisable, if you just take some time to understand what's going on, and then figure out the best ways of accomplishing specific tasks.

Jun 14, 2011 at 1:28 AM

We require certain features of a CMS, whether you like us picking and choosing what we want out of it or not. I will not keep debating this.

Jun 14, 2011 at 1:50 AM

I didn't say you can't pick and choose what you want; that's the point of Orchard, you can. But the available featureset and how everything fits together is still defined by the CMS. So instead of working against it and trying to do your own thing - I mean, it kind of sounds like you want to write everything yourself from scratch - you could try and explore the boundaries of the existing system and then see how what you want to do fits in. There's a reason why everything has been set up the way it has.

Now, if someone asks for advice, all I can do is recommend what I think is the best way to approach the problem. Since you don't seem to like my recommendations, I guess I'll just leave you to it :)

Jun 14, 2011 at 2:45 AM
Edited Jun 14, 2011 at 2:50 AM

I have every intention of using what Orchard already offers to get exactly what I want, acknowledging all caveats. As I said, my issue is identifying which path and subsequently how to implement it.

I'll follow up with documenting my findings later. Thanks to Bertrand I have a bit of a clue.