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
Related
I would like to display certain first level menuitem in bold.
This setting should be done by a checkbox when the user create / edit a menu item. (I have a workaround using the Model.Href, but it is not nice).
So I created a boolean field in Content definition / Menuitem URL (I don't know the name of the corresponding content definition in English Orchard).
How to access a custom field (Content Field) from a view?
(There already is a view which is used to customize the menu)
The examples I found use custom shapes, where the fields are accessed as built in fields (e.g. Model.ContentItem.FieldName ). But this is a different case.
With the help of "Piedone", the solution:
Model.Content.ContentItem.MenuItem.FieldTechnicalName.Value
Explanation
Examining the Model object in Visual Studio, the Model is a dynamic shape that have eg. Href property and a Content.
Content is a MenuPart, that is a content part that have a ContentItem property with the content item itself. Technically only content parts have Fields. When you (seemingly) add a field to a type it will be a part corresponding the type's name, that is MenuItem in this case (It's confusing that the display name of 'MenuItem' content type is Custom Link...)
The field's technical name is as you name it. When you add to a type, the Value will be a property of the BooleanField class. (By the way, it is nullable, so if you dont't save after adding the field, it will be null else the value you set).
I have created a custom module (actually I have created a handful in recent years, and this same obstacle frustrates me every time) following the Kentico documentation:
https://docs.kentico.com/display/K9/Creating+custom+modules
The problem I end up with every time, is in developing the User Interface for Parent/Child classes. I create a Vertical Tab node, and beneath it I add an edit tab and a Binding tab for the child class. This all works, and I can add and remove bindings at will, but what I can't do is ADD a new child class and bind it.
Using the Standard Edit Binding template, I am able to bind EXISTING Job Titles to the selected Category, but I cannot CREATE a new one from that page:
To solve this, I created a custom Edit Binding template, and added a New Child Class Header Action that points to a New / Edit Object child:
Which gives me a button that I can use to add a new child class (Job Title):
This approach works per se, in that I can click the New Job Title button and create a new item on the subsequent page:
But no binding is created to link the child object (Job Title) to the selected parent object (Category), An even bigger problem is that once I click Save, I am presented with the following:
The new object DOES SAVE, but the post-save navigation is somehow failing. The event log offers little in the way of diagnostics:
So I thought to create a completely custom interface to accomplish my needs here, according to the Kentico documentation:
https://docs.kentico.com/display/K9/Manually+creating+the+interface+for+custom+modules
So I change the Element Content of the New Job Title page to a custom page that I created to post a DataForm for the new object:
Taking care to assign the proper Object Types on the Properties Tab:
The intent was to programmatically create the binding upon save and also handle the correct navigation to avoid the ambiguous parameter error above, but when this page loads, the UIContext.ObjectID and UIContext.ParentObjectID are both 0:
So I cannot create the binding programmatically. I was able however to solve the error that I received by manually assigning the redirect. The experience is still lacking even with this hack, since it returns to the listing page, but the user still has to click "Add Items" to assign the binding after successfully creating it with the custom page I built.
This cannot be the proper way to do this, so any help with getting me on the right track would be greatly appreciated.
In order for the EditedObject to have a value you have to either decorate the page with the EditedObjectAtribute e.g. like this:
[EditedObject("<custom.objecttype>", "<objectid>", ...)]
or set the object yourself:
int objectId = QueryHelper.GetInteger("objectid", 0);
EditedObject = SomeInfoProvider.GetSomeInfo(objectId);
In your case, I'd recommend exploring what query parameters are available on the page and using them to fetch appropriate object(s). Also, make sure "JobCategoryId" is passed to the "New Job Title" dialog so that you can create the binding.
Btw - kudos for well asked question!
In orchard, I've added a boolean field called "IsDone" to the built in Content Menu Item content part via that Admin interface. I've then picked an item in Navigation and set the option to "yes" for the corresponding field i added.
In my custom theme, I've copied over MenuItem.cshtml.
How would I get the value of my custom "IsDone" field here?
I've tried something like
dynamic item = Model.ContentItem;
var myValue = item.MenuItem.IsDone.Value;
but I'm pretty sure my syntax is incorrect (because i get null binding errors at runtime).
thanks in advance!
First i suggest you use the shape alternate MenuItemLink-ContentMenuItem.cshtml instead of MenuItem.cshtml to target the content menu item directly.
Secondly, the field is attached to the ContentPart of the menu item. The following code retrieves the boolean field from this content part:
#using Orchard.ContentManagement;
#using System.Linq;
#{
Orchard.ContentManagement.ContentItem lContentItem = Model.Content.ContentItem;
var lBooleanField = lContentItem
.Parts
.Where(p => p.PartDefinition.Name == "ContentMenuItem") // *1
.SelectMany(p => p.Fields.Where(f => f.Name == "IsDone"))
.FirstOrDefault() as Orchard.Fields.Fields.BooleanField;
if (lBooleanField != null)
{
bool? v = lBooleanField.Value;
if (v.HasValue)
{
if (v.Value)
{
#("done")
}
else
{
#("not done")
}
}
else
{
#("not done")
}
}
}
*1
Sadly you cannot simply write lContentItem.As<Orchard.ContentManagement.ContentPart>() here as the first part in the part list is derived from this type, thus you would receive the wrong part.
While #ViRuSTriNiTy's answer is probably correct, it doesn't take advantage of the power of the dynamic objects that Orchard provides.
This is working for me but is a much shorter version:
#Model.Text
#{
bool? IsDone = Model.Content.ContentMenuItem.IsDone.Value;
var IsItDoneThough = (IsDone.HasValue ? IsDone.Value : false);
}
<p>Is it done? #IsItDoneThough</p>
You can see that in the first line I pull in the IsDone field using the dynamic nature of the Model.
For some reason (I'm sure there is a good one somewhere) the BooleanField uses a bool? as its backing value. This means that if you create the new menu item and just leave the checkbox blank it will be null when you query it. After you have saved it as checked it will be true and then if you go back and uncheck it then it will have the value false.
The second line that I've provided IsItDoneThough checks if it has a value yet. If it does then it uses that, otherwise it assumes it to be false.
Shape Alternate
#ViRuSTriNiTy's other advice, to change it to use the MenuItemLink-ContentMenuItem.cshtml instead of MenuItem.cshtml is also important.
The field doesn't exist on other menu items so it will crash if you try to access it. Just rename the .cshtml file to fix this.
Dynamic Model
Just to wrap this up with a little bit of insight as to how I got there (I'm still learning this as well) the way I figured it out is as follows:
.Content is a way of casting the current content item to dynamic, so you can use the dynamic advantages with the rest of line;
When you add the field in the admin panel it looks like it should be right there on the ContentItem, however it actually creates an invisible ContentPart to contain them and calls it whatever the ContentItem's type is.
So if you had added this field to a Page content type you would have used Model.Content.Page.IsDone.Value. If you had made a new content type called banana it would be Model.Content.Banana.IsDone.Value, etc.
Once you are inside the "invisible" part which holds the fields you can finally get at IsDone. This won't give you the actual value yet though. Each Field has its own properties which you can look up in the source code. the IsDone is actually a BooleanField and it exposes its data via the Value property.
Try doing a solution-wide search for : ContentField to see the classes for each of the fields you have available.
Hopefully this will have explained things clearly but I have actually written about using fields in a blog post and as part of my getting started with modules course over on the official docs (its way down in part 3 if you're curious).
Using built-in features instead of IsDone
This seems like a strange approach to do it this way. If you have a Content Item like a Page then you can just use the "Show on a menu" setting on the page.
Go to admin > content > open the page > down near the bottom you will find "Show on a menu":
This will automatically put it into your navigation and then you can move it around to where you want:
After it "IsDone" you can just go back and untick the "Show on a menu" option.
Setting up the alternative .cshtml
To clarify your comments about how to use the alternative, you need to
Copy the file you have at Orchard.Core/Shapes/Views/MenuItem.cshtml over to your theme's view folder so its /Views/MenuItem.cshtml
Rename the copy in your theme to MenuItem-ContentMenuItem.cshtml
Delete probably everything in it and paste in my sample at the start of this post. You don't want most of the original MenuItem.cshtml code in there as it is doing some special tricks to change itself into a different shape which isn't what you want.
Reset your original Orchard.Core/Shapes/Views/MenuItem.cshtml back to the factory default, grab it from the official Orchard repository
Understanding the view names
From your comments you asked about creating more specific views (known as alternates). You can use something call the Shape Tracer to view these. The name of them follows a certain pattern which makes them more and more specific.
You can learn about the alternates on the official docs site:
Accessing and Rendering Shapes
Alternates
To figure out what shape is being used and what alternates are available you can use the shape tracing module which is documented here:
Getting Started with Shape Tracing
We are looking for information on how to add content to an Editable Image programatically (with the Kentico C# API). Essentially, the equivalent of this Editable Region article for an Editable Image.
Any suggestions?
Thanks,
Victor
References:
CMSEditableImage Docs
Devnet Update EditableRegion Programatically
CMSEditableImage Class
You Sure Can
Each individual editable cms page control is stored in the document's DocumentContent field and can be accessed using an indexer field. For example:
TreeNode document = DocumentContext.CurrentDocument;
string editableImageControlId = "EditableImage1";
// get the field value
string editableImageContent = document.DocumentContent.EditableRegions[editableImageControlId];
// set it to something new
document.DocumentContent.EditableRegions[editableImageControlId] = newValue;
HOWEVER
If you look at the DocumentContent field in CMS_Document in the database you'll notice that all of the content is XML. That's because each control is serialized into XML and then nested inside this field. Thus, in this case, the value of the editableImageContent variable is an XML string:
<image>
<property name="imagepath">
~/Folder/ImageName.png
</property>
</image>
I wouldn't recommend trying to modify this directly since there's no telling if Kentico would ever change this code, or the individual control would ever change its serialization output.
But if you really must
You've got a couple of options:
1. Per #josh, you could create a new control that wraps the existing one and do some method override magic so that the control continues to do the serialization on your behalf and you just modify it after the fact. However this requires that the control is currently loading.
2. You could just hard code the beast and deal with it if it ever changes (which it likely will). Try:
// get the node from wherever you need to get the node
TreeNode document = DocumentHelper.GetDocuments().TopN(1).FirstObject;
var relativeMediaFilePath = "~/NewImage.png";
var xmlImage = string.Format("<image><property name=\"imagepath\">{0}</property></image>", relativeMediaFilePath);
var cmsControlId = "editableImage1";
if (document.DocumentContent.EditableRegions.ContainsKey(cmsControlId)) {
document.DocumentContent.EditableRegions[cmsControlId] = xmlImage;
}
else {
document.DocumentContent.EditableRegions.Add(cmsControlId, xmlImage);
}
// a little hack to get this field to be indicated as updated
document.SetValue("DocumentContent", document.DocumentContent.GetContentXml());
document.Update(true);
You could clone the editableimage webpart and then work in the prerender or change the override for the GetContent() method and add your own part of the string or do a string replace and add your code.
What is that you want to add to an Editable Image? - image path?! Not sure why you'd do that, but I'd take another direction: I'd add a field to a page type, which makes it much easier to work with through API. Having this field set up with API is should be quite easy to get it on the page... e.g. place editable image and use a macro to get field value.
Use
node.DocumentContent.EditableWebParts
and
node.DocumentContent.EditableRegions
collections to programmatically update editable content.
The best code example can be spotted at \CMS\CMSModules\Content\CMSDesk\Properties\Advanced\EditableContent\Main.aspx.cs
It's the dialog under Pages->General->Advanced->Edit regions & web parts.
I have a MainView which has an associated MainViewModel in my WPF application, assigned via its contructor.
App > start up uri > MainWindow.xaml
public MainWindow()
{
InitializeComponent();
var viewModel = new MainViewModel();
DataContext = viewModel;
}
My MainView holds as many as four nested views or child views who are hidden and displayed based upon the button that has been clicked on the MainView. So we toggling the visibility property vi a binding which gets updated via command bindings assigned to each button on the MainView.
Each nested View does not have an associated ViewModel, all bindings found on child views find their information in the MainViewModel. So binding system ends waling up the UI tree of the app to find out that the parent 'MainView' has an associated ViewModel.
So overall there is 'ONE' -> ViewModel. This seems to work fine and but as expected this VM has gotten to big and needs re-factoring. It holds information that contextually it should not. But this is a proof concept application. So i decided to keep it simple and make sure it was do-able.
PROBLEM:
When i tried assigning a empty view with an empty view model I noticed binding errors in the output window and as expected weird and broken behaviour. Which makes no sense ... Is there a more clear and concise way of letting WPF know how to handle any bindings it finds in a nested view control? I thought if each view's constructor assigned itself a corresponding VM as shown above, then it should work as this logically makes sense. Unfortunately all buttons on the MainView stop working when the corresponding view it is designated to switch on and hide the others has an associated ViewModel. On some buttons it works and the others it does not? This is really weird?
As mentioned in my answer above, the problem was that WPF binding system was struggling to to resolve bindings at run time. The main view has its associated view model instantiated and assigned via the Main View contructor and this pattern is repeated for all nested views that the MainView also houses.
By default, I tend to use the implied binding syntax which means that without explicitly specifiying a source the binding system will try to resolve the name you supply in the binding. So it's all implied and nothing is explicitly set!
Upgrading each nested view to have its own view model makes this auto discovery/resolution of bindings go a little crazy and I have not explicitly told the binding system where to find the property I am looking for hence the output window binding errors.
This leads to unexpected behaviour as the output window was telling that it was trying to resolve binding expressions in nested views --> viewmodels. When in actual fact that VM it is looking in, IS EMPTY!
So clearly the binding system is good when you do not explicitly set a source property inside the binding syntax. It is clever enough to find things on its own. In my case it needed some extra help, as it was unsure as to where to find things.
SOLUTION:
Remove the constructor declaration for the MainViewModel in the MainView constructor.
Scope in an xmlns for ViewModels namesapce into your MainView.xaml
Create a window resource inside the MainView .xaml
Give the resource a key.
Upgrade all your bindings in the MainView xaml file to include source property.
Give the source property a static resource binding that points to your ViewModel key value set up in step 4.
Only do step 6 for bindings who refer to the ViewModel that is associate with the MainView.
All nested views are to be left alone, they should handle their own bindingds in their own xaml files. The MainView simply instantiates them and places them onto the UI. For me this was the case, I did not have any more bindings concerning my nested views. All Bindings that lived on the MainView.xaml file referred to data in the MainViewModel.cs. This makes alot easier when viewing your problem.
For some reason the editor was being awkward so I chose to omit any sample code but the steps above are descriptive enough to follow the steps that I took. Above is what worked for me.
ANOTHER WAY OF SUMMING UP THIS PROBLEM
Most books teach the shorter binding syntax
What happens when more than one data context is available?
How is the binding system supposed to know how to resolve your short hand binding expressions.