I've created ContentType Product with ProductPart, but since html of the product is rather complex I decided to use one view Content-Product.cshtml and render all the part information manually instead of using shapes. But I still need to render one shape for price, because I need to format it through service and I'd like to do that in a ProductPart driver.
I tried to define new zone in this view and use placement.info to place the shape. All it does is that it renders shape's type name instead of html.
Orchard.DisplayManagement.Shapes.Shape
Can I even use this approach to insert shape? Or should I choose different approach?
Driver:
protected override DriverResult Display(ProductPart part, string displayType, dynamic shapeHelper)
{
return ContentShape("Parts_Product_Price",
() => shapeHelper.Parts_Product_Price(
ContentPart: part,
ContentItem: part.ContentItem));
}
Content-Product.cshtml:
#Model.PriceZone()
Placement.info:
<Place Parts_Product_Price="PriceZone:1"/>
In your Content-Product.cshtml view you want this instead:
#Display(Model.PriceZone)
Related
We are trying to implement a generic reusable paywall as a ContentPart in Orchard 1.10. The goal is to obscure certain content from non-paying users while keeping its detail page accessible as a teaser.
The original idea was to use a ShapeTableProvider to inspect every "Content" shape and change its display type according to the permissions of the user. Then it would be possible to customize what we want to show in placement on a per-content basis. e.g. only display the Layout_Summary instead of the full Layout if users don't have access to a news article.
Something along these lines:
builder.Describe("Content").OnDisplaying(displaying => {
if(displaying.Shape.Metadata.DisplayType == "Detail" && !_authorizer.Authorize(ViewPaidContent) {
displaying.Shape.Metadata.DisplayType == "DetailPaywall";
}
});
--
<Placement>
<Match DisplayType="DetailPaywall">
<Place Parts_Layout="-"/>
...
</Match>
</Placement>
However, it turns out that OnDisplaying happens too late, placement has already been decided and ContentDrivers get invoked. OnCreated happens too early and would be overridden again. I believe the relevant code that triggers these is the BuildDisplay method in Orchard.ContentManagement.DefaultContentDisplay. Whichever displayType parameter is passed here is the one that ends up getting used.
Is there any way to influence display type in code based on some condition, or a different approach we can use to achieve similar functionality?
The display type is usually determined by the driver for the part. What you should probably do instead is introduce an alternate:
builder.Describe("Content").OnDisplaying(displaying => {
if(displaying.Shape.Metadata.DisplayType == "Detail" && !_authorizer.Authorize(ViewPaidContent) {
displaying.Shape.Metadata.Alternates.Add("Content__Paywall");
}
});
You can then add a Content-Paywall.cshtml template override to your theme and you should be done.
I need an alternate for the EditorTemplate of an Enumerator Field that's used when the Field has a particular name (PublishingMethod).
Based on the docs, I created a view with the pattern [ShapeType__FieldName] in the same folder as the original shape:
This is not working and still uses the original. I've thought of changing the Editor method in the Driver, but I think that defeats the purpose of alternates, which is that Orchard automatically detects the correct shape as I understand from the docs:
The Orchard framework automatically creates many alternates that you can use in your application. However, you can create templates for these alternate shapes.
Note: I can't use the Shape Tracing module, it never worked even with a clean Orchard install.
The editors in Orchard work different to how Display works. I guess it is so you get a MVC-style experience. Basically, the actual shape returned is of type EditorTemplate, which then binds your model and prefix then renders a partial view with the template name you gave it. What this means is alternates wont work as expected, or as the docs state. The alternates for your field name are actually added to the EditorTemplate shape. So what you can do is add a view called EditorTemplate-PublishingMethod.cshtml with contents like:
#{
var m = (Orchard.Fields.Fields.EnumerationField)Model.Model;
}
#Html.Partial("PublishingMethodEditor", m, new ViewDataDictionary {
TemplateInfo = new TemplateInfo { HtmlFieldPrefix = Model.Prefix }
})
Then add another view called PublishingMethodEditor.cshtml with the overrides you want for your editor. All these views should go in the root of your Views folder.
Another approach would be to implement the IShapeTableProvider interface and adjust the TemplateName property on a certain condition but, meh, that requires code...
Edit 1
If you have that field name on other content types that you don't want to override you can use the override EditorTemplate-ContentTypeName-PublishingMethod.cshtml
I'd like to have the layout reflect some data from a model. However, the render method from the CController class passes the structured data only to the view file, while the layout file only gets the rendered view passed.
So, how to best have the layout display data from a model?
Two possibilities come to mind:
Make Yii's layout file a no-op, mimicking layout logic manually from the view.
Override CController's render method in its subclass.
I'm not so happy with either variant, so maybe someone has a cleaner idea on how to do it?
Another way is to define a public variable in your controller class, something like:
class MyController extends Controller {
public $test = 'foo';
....
That value can then be accessed within a layout:
echo $this->test;
And manipulated in an action:
public function actionMyaction(){
$this->test = "bar";
...
Obviously it's not ideal if you have many variables that you need to use in a layout. One solution is to use an array of parameters. Alternatively you could look at making your layout more minimal and using CWidget to create reusable components for use inside your views.
For example, you obviously wouldn't want to have the code for your main navigation duplicated inside every view, so the obvious solution is to have in the layout, but if it becomes inconvenient to handle the data you could have an instance of a widget that renders out the navigation inside each view (and you can pass data to the CWidget class) something like:
$this->widget("MainNavigation",array("params"=>$params));
I created a form using the CustomForms module and need to control the markup of the input fields I've included on the form (to add bootstrap specific classes). I added a view to my theme at the location /Views/EditorTemplates/Fields/Input.Edit.cshtml and that allowed me to update the markup for the input fields.
My problem is that the view in my theme is also being picked up in the admin views. I didn't expect this behavior but it's happening. I tried scoping the view override to the url (Input.Edit-url-contact.cshtml) and content type (Input.Edit-ContactRequest.cshtml) using the alternate naming conventions but they do not appear to work in this case.
Is there a way to scope the Input.Edit.cshtml view in my theme so it only applies to the front-end of the site? Or is there a better way to achieve what I'm trying to do?
I ended up working around this issue by implementing a shape table provider (based on Bertrand's suggestion) to specify different template names in my theme so they weren't picked up in the admin. Here's what it looks like:
public class EditorFieldShapeProvider : IShapeTableProvider
{
public void Discover(ShapeTableBuilder builder)
{
builder.Describe("EditorTemplate")
.OnDisplaying(displaying =>
{
var shape = displaying.Shape;
if (shape.ContentField is InputField) {
shape.TemplateName = "CustomInputField";
}
});
}
}
Just drop that class somewhere in your theme and create your view at /ThemeName/Views/EditorTemplates/CustomInputField.cshtml
I'm trying to modify the EditorPart controller for my web part. Basically what I want is to have my custom controls inside a box like the standard properties that can toggle between visible and hidden.
I've been googling for a while, but I cannot seem to find an answer.
Just to clarify: I know I can use the Category property to accomplish this when adding web part properties directly to the web part, but I've extended the EditorPart controller and so I don't think I can simply add [Category("Feed settings")] to the TextBox and LiteralControls I'm creating (correct?).
What you'd need for a standard property is to mark it with the Category attribute:
[Category("My Category")]
public string FeedQuery { get; set; }
(You'll need to add the System.ComponentModel namespace to your class file).
For editor parts it is not so simple. It appears that you can't add them to the standard categories. It is possible to style the editor part to resemble the OOB panels as shown here