How to use Orchard CMS template module - orchardcms

There is a module in Orchard CMS that allows for editing templates within the dashboard, however it is unclear how to properly utilize the output. What is the correct way to access a template in an MVC module controller, pass the template a model, and then render it within a View cshtml file?

You can build any shape with the shapefactory:
public MyController(IShapeFactory shapeFactory) {
Shape = shapeFactory;
}
public dynamic Shape { get; set; }
public ActionResult Index() {
var someModel = new SomeModel();
// You can build your shape here:
var shape = Shape.TheTemplateShapeName(SomeData: someModel.SomeProperty);
// or: Shape.New("TheTemplateShapeName").SomeData(someData);
// you can just chain on the dynamic shape
var viewModel = Shape
.ViewModel() // simple orchard method to build a viewmodel with chaining
.MyShape(shape)
.SomethingElse("Some string");
return View(viewModel);
}
Then in your MyController/Index.cshtml:
<h1>#Model.SomethingElse</h1>
#Display(Model.MyShape)

Related

How to use ServiceStack Templates to support dynamic results based on request type?

With ServiceStack's Razor Story we have a variety of ways of selecting which Razor View we want to use to render a page. Even better, and critical in my case, is we can pass in a Content-Type header (or query string parameter, or even page "suffix") as well to return the raw model in a variety of formats.
Is there any way to use ServiceStack Templates (now known as SharpScript) to do the same thing? I follow the example here but I just get back the standard HTML format response. It doesn't use my template, no matter how named.
Following the example in the v5.5 Release Notes:
[Route("/hello/{Name}")]
public class Hello : IReturn<HelloResponse>
{
public string Name { get; set; }
}
public class HelloResponse
{
public string Result { get; set; }
}
public class HelloService : Service
{
public object Any(Hello request) => new HelloResponse { Result = $"Hello, {request.Name}!" };
}
Going to /hello/World?format=html provides me the standard HTML report, not my template. I followed another example to force it to use the template ....
public object Any(Hello request) =>
new PageResult(Request.GetPage("examples/hello")) {
Model = request.Name
};
... and it ALWAYS returns my template, even if I specify /hello/World?format=json.
Is there any way to have Razor-like view selection for ServiceStack + ScriptSharp pages, but also support different response formats?
It's hard to answer a vague question like this without details of a specific scenario you want to achieve that's not working.
You can return Sharp Pages in a number of ways:
When it's requested directly as a content page, e.g /dir/page -> /dir/page.html
Using Page Based Routing, e.g /dir/1 -> /dir/_id.html
As a View Page in response to a Service when it's named after the Request DTO or Response DTO, e.g /contacts/1 -> /Views/GetContact.html or /Views/GetContactResponse.html
Select which view to render inside your Service by returning your Response DTO inside a custom HttpResult:
public object Any(MyRequest request)
{
...
return new HttpResult(response)
{
View = "CustomPage", // -> /Views/CustomPage.html
//Template = "_custom-layout",
};
}
Add the [ClientCanSwapTemplates] Request Filter attribute to let the View and Template by modified on the QueryString, e.g: ?View=CustomPage&Template=_custom-layout
[ClientCanSwapTemplates]
public object Any(MyRequest request) => ...
Choosing which page you want to render inside your Model View Controller Service by returning a custom PageResult:
public class CustomerServices : Service
{
public object Any(ViewCustomer request) =>
new PageResult(Request.GetPage("examples/customer")) {
Model = TemplateQueryData.GetCustomer(request.Id)
};
}
Note: That the SharpPagesFeature resolves pages using your cascading AppHost.VirtualFileSources. In .NET Core it's configured to use its WebRoot, e.g /wwwroot.
For Sharp Pages to return its Response in Multiple Content Types:
as well to return the raw model in a variety of formats.
You need to use a Sharp APIs which return a value, e.g. /hello/_name/index.html:
{{ { result: `Hello, ${name}!` } | return }}
To succinctly answer my own question, the first option from #mythz is what I needed. After calling Plugins.Add(new SharpPagesFeature()) in my AppHost, I needed to return HttpResult from my service method:
public object Any(MyRequest request)
{
...
return new HttpResult(response)
{
View = "CustomPage", // -> /Views/CustomPage.html
//Template = "_custom-layout",
};
}

Orchard CMS - Display a list of items in view from model

I am an Orchard CMS beginner and I do not understand how to get and display a list of items in a view.
I've read the article Writing a ContentPart but the examples show how to use 2 properties instead of a list:
Driver
return ContentShape("Parts_Map", () => shapeHelper.Parts_Map(
Longitude: part.Longitude,
Latitude: part.Latitude));
View
<img alt="Location" border="1" src="http://maps.google.com/maps/api/staticmap?
&zoom=14
&size=256x256
&maptype=roadmap
&markers=color:blue|#Model.Latitude,#Model.Longitude
&sensor=false" />
I want to use a list of items in a view.
I've also read the article Orchard CMS Custom Widget View but i do not understand how it works, especially the following line:
var files = (IContentQuery<FilePart>)Model.Files;
Where can i find additional examples?
Is it below correct? I Use IContentManager contentManager for transfer data to view.
Driver:
public class MyModuleWidgetPartDriver : ContentPartDriver
{
private readonly IContentManager contentManager;
public MyModuleWidgetPartDriver(IContentManager contentManager)
{
this.contentManager = contentManager;
}
protected override DriverResult Display(MyModuleWidgetPart part, string displayType, dynamic shapeHelper)
{
var MyModuleItems = this.contentManager.List<MyModulePart>(MyModulePart.ContentTypeName).ToArray();
// List of items
var MyModuleItemsViewModel = MyModuleItems.Select(MyModule => new MyModuleItemsViewModel
{
Title = MyModule.Title,
Html = MyModule.Html
});
return ContentShape("Parts_MyModules", () => shapeHelper.Parts_MyModules(
promo: Json.Encode(MyModuleItemsViewModel)));
}
MyModulePart:
public class MyModulePart : ContentPart
{
public const string ContentTypeName = "MyModule";
public string Title
{
get { return this.As<ITitleAspect>().Title; }
}
public string Html
{
get { return this.As<BodyPart>().Text; }
}
}
View\Parts\MyModule.cshtml:
#{
var MyModuleItems = Model.promo;
}
You can set anything you need in the view onto the shape you're creating from your driver. Do make sure that whenever you perform a "heavy" operation such as querying a database, you do so from within the shape factory lambda (the lambda that actually creates the shape). If you do this outside of the lambda, your query will execute even if your shape won't be displayed, which would obviously be inefficient. For example:
Driver
return ContentShape("Parts_Map", () => {
return shapeHelper.Parts_Map(
MyList: _someService.GetMyListOfData(); // The MyList property will be added on the fly to the Parts_Map shape you're creating, and will be available in the view, whose model is in fact this Parts_Map shape.
Longitude: part.Longitude,
Latitude: part.Latitude);
});
View
<ul>
#foreach(var item in Model.MyList){
<li>#item.SomeProperty</li>
}
</ul>
Remember: Shapes are dynamic objects to which you can add properties on the fly. When "rendering a shape", what really happens is that Orchard locates the appropriate Razor view based on metadata stored with the shape (each shape has a Metadata property), and sets the Model of that view to the shape object.

Can I register Orchard resources in an HtmlHelper?

I am aware that this violates the MVC principle and best practices.
I have a whole bunch of custom Angular components that each take a whole bunch of optional parameters and each require a different remote stylesheet and javascript file. I would like to render these with an HtmlHelper without having to manually include the right resources everywhere I use them.
I was hoping that this would do the trick but it doesn't
public static class HtmlExtensions
{
private static IResourceManager _resourceManager;
// Executed in the Activated method of an OrchardShellEvents implementation
public static void SetResourceManager(IResourceManager resourceManager)
{
_resourceManager = resourceManager;
}
public static MvcHtmlString Angular(this HtmlHelper helper, CustomAngularComponent component)
{
// Require the resources
var _styleRegister = new ResourceRegister(helper.ViewDataContainer, _resourceManager, "Style");
var _scriptRegister = new ResourceRegister(helper.ViewDataContainer, _resourceManager, "Script");
_styleRegister.Require(component.StyleSheet).AtHead();
if (!string.IsNullOrEmpty(component.Script))
{
_scriptRegister.Require(component.Script).AtFoot();
}
// Create tag
var tag = new TagBuilder(component.Tag);
tag.MergeAttributes(component.Parameters);
return new MvcHtmlString(tag.ToString(TagRenderMode.SelfClosing));
}
}
I could use an arbitrary Shape as a helper, something like:
#Display(New.Angular(Model: new CustomAngularComponent(...)))
but a helper with strongly typed parameters feels a lot better.
This comment on another Orchard question tipped me off. As it turns out, simply injecting Work<IResourceManager> and using it directly was enough to make my setup work. Please note that this does not conform to best MVC practices, by doing this I sacrifice maintainability in favor of readability.
public static class HtmlExtensions
{
private static Work<IResourceManager> _resourceManager;
// Executed in the Activated method of an OrchardShellEvents implementation
public static void SetResourceManager(Work<IResourceManager> resourceManager)
{
_resourceManager = resourceManager;
}
public static MvcHtmlString Angular(this HtmlHelper helper, CustomAngularComponent component)
{
// Require the resources
_resourceManager.Value.Require("stylesheet", component.StyleSheet).AtHead();
if (!string.IsNullOrEmpty(component.Script))
{
_resourceManager.Value.Require("script", component.Script).AtFoot();
}
// Create tag
var tag = new TagBuilder(component.Tag);
tag.MergeAttributes(component.Parameters);
return new MvcHtmlString(tag.ToString(TagRenderMode.SelfClosing));
}
}

How to send Model to _Layout.cshtml (I need to send another model to Index view too)

I need to send 2 different Models, one to Index view and another one to _Layout.cshtml, how I can do it?
My HomeController:
[Route("")]
public ActionResult Index()
{
HomeViewModel model = new HomeViewModel();
model.A = _repoA.GetLatest(4);
model.B = _repoB.GetLatest(4);
model.C = _repoC.GetLatest(4);
return View(model);
}
I don't like using ViewBag, ViewData & ..., I'm looking for passing the model in same way as we passing model to Views.
You can place this in your Layout to load a partial each time... Pretty useful for loading in a piece of a dynamic menu or a widget on each page.
Along with this line in your layout you can just do your Index page as you normally would.
#{ Html.RenderAction("_widget", "Home"); }
You'll need to send it along in the ViewBag. I found the best bet was to make an abstract controller:
public abstract class ApplicationController : Controller
{
protected ApplicationController()
{
UserStateViewModel = new UserStateViewModel();
//Modify the UserStateViewModel here.
ViewBag["UserStateViewModel"] = UserStateViewModel;
}
public UserStateViewModel UserStateViewModel { get; set; }
}
Then, have all of your controllers inherit from this abstract controller.
In your _Layout.cshtml (or whatever you called it), you'll need to include the following at the top:
#{
var userState = (UserStateViewModel)ViewBag.UserStateViewModel;
}
Duplicate but refined from the 2nd answer to ASP.NET MVC Razor pass model to layout.

How do you display shape result from module to page on orchard

I am new to module creation and I created an mvc app that is getting data from another source other than the orchard database. I turned it into a module to where I just have a controller and view what I am trying to do is display that view into different zones. Is there a way to achieve this.
When you display a view from a controller it is chucked into the content zone. If you want to display data in different zones you will most likely want to use widgets. However, you can dispatch an arbitrary shape to a zone from, well anywhere in Orchard really. This would add a shape called MyShape to the AsideFirst zone.
public class MyController : Controller {
private readonly IWorkContextAccessor _workContextAccessor;
private readonly IOrchardServices _orchardServices;
public MyController(IOrchardServices orchardServices, IWorkContextAccessor workContextAccessor) {
_workContextAccessor = workContextAccessor;
_orchardServices = orchardServices;
}
public ActionResult Random() {
var shape = _orchardServices.New.MyShape();
var zone = "AsideFirst";
var position = "2";
_workContextAccessor.GetContext().Layout.Zones[zone].Add(shape, position);
return View("MainView");
}
}
I don't really know what your scenario is but I would say your best bet is probably to look at Orchard Widgets.

Resources