Orchard - How to display custom Term fields in a taxonomy field? - orchardcms

I've got a contentType (product) that has a taxonomy field (features). The taxonomy term (product feature term) has been customized to include an image field and a description field.
I'd like for the product detail view to display the image from the term along with the name, but I can't find the property to access it.
I've created the following:
Taxonomy
ProductFeature Taxonomony
Vocabulary: Feat1, Feat2, Feat3
ContentTypes
Product
Fields: Features(Taxonomy)
Product Features Term
Fields: Description(Html), Image(Image)
Views
Fields.Contrib.TaxonomyField-Features.cshtml
<!-- Old Code -->
#if (Model.Terms.Count > 0) {
<p class="taxonomy-field">
<span class="name">#name.CamelFriendly():</span>
#(new HtmlString( string.Join(", ", terms.Select(t => Html.ItemDisplayLink(Html.Encode(t.Name), t.ContentItem ).ToString()).ToArray()) ))
</p>
}
<!-- New Code -->
#if (Model.Terms.Count > 0)
{
<div>
#foreach (var myTerm in Model.Terms)
{
#Display(???)
}
</div>
}
What do replace the question marks with? I'd thought it'd be myTerm.Image but that field doesn't exist on the dynamic object.
I've attached an image of the designer viewer.

If you wanted to use the current dev branch on the module, you could access the TermsPart of the content items, which leads you to all currently applied terms.
If you are using version 0.9 of the module, then you can dynamically have access to the fields by getting a reference to your Content Item, then do contentItem.PARTNAME.FIELDNAME. In the case of a type named Product, and a field name Feature it would be contentItem.Product.Feature. Then if this term has a property named Image, it will be termContentItem.ProductTerm.Image.
I would need more information to give you the exact syntax, like the type of field, exact name of content types. Or you can post the question on the module's codeplex project discussion forum.

As Sebastien helped me figure out over on http://orchardtaxonomies.codeplex.com/discussions/263844
Below is what ended up working.
(The key bit being: contentField = myTerm.ContentItem.Features.TermImage;)
#foreach (var myTerm in Model.Terms)
{
var contentField = myTerm.ContentItem.Features.TermImage;
if (!String.IsNullOrWhiteSpace(contentField.FileName)) {
<p class="image-field">
<img src="#Url.Content(contentField.FileName)" alt="#contentField.AlternateText" width="#contentField.Width" height="#contentField.Height"/>
</p>
}
}

Related

Image Metadata: altText , height in Orchard CMS

For many of Custom Types created, we have a Query for them. These queries are being used by Projection Widgets (Within Zones).
Few of the custom types have Media Picker field.The Layout type I used for my Queries is the Shape type as seen below:
=>>> Queries:
=>>> Layout:
. I have followed the steps from here.. I specified the name of the shape as : UpcomingHighlightsImages as seen below:
and then included the view: UpcomingHighlightsImages.cshtml in my Themes/MyFirstTheme/Views folder.
Everything works fine upto here.
In the View, problem is that there is NO way to read the Image Metadata such as altText, altHeight etc... Neither there seems to be a way to set these metadata firstly in Orchard itself.
#using Orchard.ContentManagement
#using Orchard.Core.Title.Models
#using Orchard.Fields.Fields
#using Orchard.Taxonomies.Fields
#using Orchard.Core.Common.Fields;
#using Orchard.MediaLibrary.Fields;
#{
var HighlightItems = ((IEnumerable<ContentItem>)Model.ContentItems).ToList();
}
#foreach (var item in HighlightItems)
{
String LinkUrl = ((TextField)item.Parts.SelectMany(x => x.Fields).Single(x => x.Name == "LinkURL")).Value;
String ImagePath = ((MediaLibraryPickerField)item.Parts.SelectMany(x => x.Fields).Single(x => x.Name == "MainImage")).MediaParts.First().MediaUrl;
<div>
<a target="_blank" href="#LinkUrl">
<img src="#ImagePath" />
</a>
</div>
}
So, as seen in above code and the tag, I need:
Set the altText, altHeight property of Image in ORchard CMS
Read these in my view , the way I read LinkUrl and ImagePath
Please guide me !
See how you got the first media part in that ImagePath expression? Well, then you can take that part and get Title, Caption, AlternateText, etc. from it. You can also As<ImagePart>() it and get its Width and Height.

Orchard CMS - Extending Users with Fields - exposing values in Blog Post

I'd like to extend the users content definition to include a short bio and picture that can be viewed on every blog post of an existing blog. I'm unsure of what the best method to do this is.
I have tried extending the User content type with those fields, but I can't seem to see them in the Model using the shape tracing tool on the front end.
Is there a way to pass through fields on the User shape in a blog post? If so, what is the best way to do it?
I also have done this a lot, and always include some custom functionality to achieve this.
There is a way to do this OOTB, but it's not the best IMO. You always have the 'Owner' property on the CommonPart of any content item, so in your blogpost view you can do this:
#{
var owner = Model.ContentItem.CommonPart.Owner;
}
<!-- This automatically builds anything that is attached to the user, except for what's in the UserPart (email, username, ..) -->
<h4>#owner.UserName</h4>
#Display(BuildDisplay((IUser) owner))
<!-- Or, with specific properties: -->
<h1>#T("Author:")</h1>
<h4>#owner.UserName</h4>
<label>#T("Biography")</label>
<p>
#Html.Raw(owner.BodyPart.Text)
</p>
<!-- <owner content item>.<Part with the image field>.<Name of the image field>.FirstMediaUrl (assuming you use MediaLibraryPickerField) -->
<img src="#owner.User.Image.FirstMediaUrl" />
What I often do though is creating a custom driver for this, so you can make use of placement.info and follow the orchard's best practices:
CommonPartDriver:
public class CommonPartDriver : ContentPartDriver<CommonPart> {
protected override DriverResult Display(CommonPart part, string displayType, dynamic shapeHelper) {
return ContentShape("Parts_Common_Owner", () => {
if (part.Owner == null)
return null;
var ownerShape = _contentManager.BuildDisplay(part.Owner);
return shapeHelper.Parts_Common_Owner(Owner: part.Owner, OwnerShape: ownerShape);
});
}
}
Views/Parts.Common.Owner.cshtml:
<h1>#T("Author")</h1>
<h3>#Model.Owner.UserName</h3>
#Display(Model.OwnerShape)
Placement.info:
<Placement>
<!-- Place in aside second zone -->
<Place Parts_Common_Owner="/AsideSecond:before" />
</Placement>
IMHO the best way to have a simple extension on an Orchard user, is to create a ContentPart, e.g. "UserExtensions", and attach it to the Orchard user.
This UserExtensions part can then hold your fields, etc.
This way, your extensions are clearly separated from the core user.
To access this part and its fields in the front-end, just add an alternate for the particular view you want to override.
Is there a way to pass through fields on the User shape in a blog post?
Do you want to display a nice picture / vita / whatever of the blog posts author? If so:
This could be your Content-BlogPost.Detail.cshtml - Alternate
#using Orchard.Blogs.Models
#using Orchard.MediaLibrary.Fields
#using Orchard.Users.Models
#using Orchard.Utility.Extensions
#{
// Standard Orchard stuff here...
if ( Model.Title != null )
{
Layout.Title = Model.Title;
}
Model.Classes.Add("content-item");
var contentTypeClassName = ( (string)Model.ContentItem.ContentType ).HtmlClassify();
Model.Classes.Add(contentTypeClassName);
var tag = Tag(Model, "article");
// And here we go:
// Get the blogPost
var blogPostPart = (BlogPostPart)Model.ContentItem.BlogPostPart;
// Either access the creator directly
var blogPostAuthor = blogPostPart.Creator;
// Or go this way
var blogPostAuthorAsUserPart = ( (dynamic)blogPostPart.ContentItem ).UserPart as UserPart;
// Access your UserExtensions part
var userExtensions = ( (dynamic)blogPostAuthor.ContentItem ).UserExtensions;
// profit
var profilePicture = (MediaLibraryPickerField)userExtensions.ProfilePicture;
}
#tag.StartElement
<header>
#Display(Model.Header)
#if ( Model.Meta != null )
{
<div class="metadata">
#Display(Model.Meta)
</div>
}
<div class="author">
<img src="#profilePicture.FirstMediaUrl"/>
</div>
</header>
#Display(Model.Content)
#if ( Model.Footer != null )
{
<footer>
#Display(Model.Footer)
</footer>
}
#tag.EndElement
Hope this helps, here's the proof:

How to get values returned by child action method in mvc 5 partial view

I am trying not very successfully to get my head around MVC. My home controller contains an Index method that runs OK, so far so good, but I don't know how to call the ChildAction method Home/TopArticle
Action Method
[ChildActionOnly]
public ActionResult TopArticle()
{
return PartialView(_service.GetTopArticle());
}
In my Index view I have the mark up:
#section featured {
#Html.Partial("_TopItem")
}
_TopItem View
#model IEnumerable<MySite.Models.NewPage>
<section class="featured">
<div id="TopItem">
<div id="TopItemImg">
<a href="http://www.mysite.co.uk/">
<img style="border: 1px solid lightgray" width="320" height="233" style="border:1px solid lightgray;" alt="Model.Title" src="/Img/Model.TopItemImage">
</a>
</div>
<div id="TopContent">
<h2></h2>
<div class="dt">
<div class="dl">
#Html.Label(Model.DatePublished.ToString())
#Html.Label(#Html.Action("TopArticle", "Home", new { // am lost at this point}))
</div>
<div class="tl">
#Html.Label(Model.InfoTags ?? "")
</div>
</div>
</div>
</div>
</section>
The Index view is also using #model IEnumerable and I don't actually know whether that's OK or not. The model itself contains everything needed for both the Index and the _TopItem views, it's just that there will be one record returned for the _TopItem view and many for the Index view. Plus the code that runs in _service.GetTopArticle does some non-query stuff that is relevant only for the top article record.
I need a lie down ... and time to learn this stuff properly.
Firstly, regarding your question about calling the child action from your Index view:
Your featured section is currently calling #Html.Partial which means that it will find the "_TopItem" partial view and render it as an html encoded string in the current view (i.e. your Index view).
You specified that you are trying to call the child action TopArticle() and render the partial view returned as a html string in the view. To do this you would need to use:
#section featured {
#Html.Action("TopArticle", "Home")
}
However, I don't believe this is what you do need as you said that your Index view model contains all of the information for both Index and for the _TopItem partial view (see later).
For more information you should do a google search about the differences of views, partial views and child actions.
To correct the code I would start off by ensuring that the _TopItem partial view is correct. I have identified the following issues with the _TopItem partial view, some of which are beyond the scope of the original question:
The model passed in as an IEnumerable of NewPage but your code does not enumerate over several new page objects, it looks like it should just create the html for a single NewPage model. Therefore, I believe the model declaration should be:
#model MySite.Models.NewPage
The tag contains 2 references to the style attribute rather than 1.
The tag contains the alt attribute of alt="Model.Title" which means that alt="Model.Title" will be written directly as html where I expect you would like alt="#Model.Title" to render the contents of the model in the alt attribute.
Similarily, the tag contains src="/Img/Model.TopItemImage" where I expect this should be src="/Img/#Model.TopItemImage"
All of the label tags appear to be incorrect. For example, #Html.Label(Model.DatePublished.ToString()) - Model.DatePublished.ToString() will return a string and this string will then be attempted to be found on the model and will error as that field name does not exist. Therefore, you probably want to write: #Html.Label("DatePublished") or #Html.Label(m => m.DatePublished). With the second label i'm not sure what your trying to achieve but you may want to look up the appropriate articles.
Once, you have the corrected _TopActicle partial view, you can then return to your Index view to render the partial directly:
#section featured {
#Html.Partial("_TopItem", Model.TopArticle)
}
Note, as you have said that your Index model contains the information to pass to the _TopItem partial view, I have assumed that the Index model contains a property called TopArticle of type NewPage. Regardless, you can pass the model into the partial however you find appropriate through the call to #Html.Partial. If you pass the model through the call to #Html.Partial then you may not need the ChildOnlyAction.

Orchard Alternate Shape Template Not Displaying Values

I'm new to Orchard and have watched both the Pluralsight "Orchard Fundamentals" and "Advanced Orchard" tutorials. Its a great platform, but I'm having a hard time wrapping my head around a couple of things.
I'd like to create a blog showcase banner on the home page only that rotates blog posts on the site. I have the HTML sliced up and functioning on an HTML template. The banner looks like this:
http://arerra.com/news-slideshow.jpg
So far I have done the following:
I've created a Blog called "Articles" and have placed a single post in there for testing.
Added a Layer called "ArticleList" where I have placed a Widget for "Recent Blog Posts"
I've created a custom layout for the home page called "Layout-Url-HomePage.cshtml" in my theme.
In my Theme "Views" folder, I have created a file called "Widget.Wrapper.cshtml" with only #Display(Model.Child) in it to remove the <article><header/><footer /><article> tags globally from the widgets.
Added a file in "Views > Parts > Blogs.RecentBlogPosts.cshtml" to control the layout of my shape. The code is the following:
#using Orchard.ContentManagement;
#{
IEnumerable<object> blogPosts = Model.ContentItems.ContentItems;
}
#if (blogPosts != null) {
<div class="container news-slider">
<ul class="slide-images">
#foreach (dynamic post in blogPosts) {
string title = post.Title;
ContentItem item = post.ContentItem;
<img src="/Themes/MountainWestHoops/Content/img/placeholder-700x380.jpg" alt="#title" class="active" />
}
</ul>
#foreach (dynamic post in blogPosts) {
string title = post.Title;
string body = post.Body;
ContentItem item = post.ContentItem;
<div class="featured-story threeD active">
<h1>#title</h1>
<p>#body #Html.ItemDisplayLink("READ MORE", item)</p>
</div>
}
<aside>
<ul class="tabs">
#foreach (dynamic post in blogPosts) {
string title = post.Title;
string url = post.Url;
ContentItem item = post.ContentItem;
<li><h3>#title</h3></li>
}
</ul>
<div class="ad-three-day-trial">
<img src="/Themes/Content/img/placeholder-260x190.gif" />
</div>
</aside>
</div>
}
My HTML is rendering properly, but none of the values that I have specified are showing up.
I am using the "Shape Tracer" module to see what template is being used. What is funny, is that the #Html.ItemDisplayLink("READ MORE", item) is rendering the article's URL, and if I replace the "READ MORE" with the string title, the title renders properly.
What am I doing wrong here that is causing strings to not display? Am I missing a larger point and misunderstanding the fundamentals? The tutorials seems to say that you can simply move around parts, but in this case, I need to have very specific markup for this slider to work.
Seems like your source was http://weblogs.asp.net/bleroy/archive/2011/03/27/taking-over-list-rendering-in-orchard.aspx
That is a rather old post, and the way the title is handled has changed since then.
The DisplayLink works because the only correct property here is post.ContentItem, which is what that API takes. post.Title and post.Body on the other hand are very likely null, which is why you see nothing. To access the title, you can use post.ContentItem.TitlePart.Title and to get the body, post.ContentItem.BodyPart.Text.

How to extend Orchard navigation module to add images to menu items

UPDATE: I've changed the original question drastically based on Bertrand's suggestions and my own findings. Now it provides an incomplete solution in its text instead of my own blind meanderings and commentary on Orchard, which were completely WRONG!
I need to display a menu using images instead of text, one standard, and another for when hovered/selected. The requirements for the site states that the end-user should be able to manage the menu item images. The standard navigation module now provides an HTML menu item, which is not what the end user wants. The customer wants a very simple, intuitive interface for configuring the sites many menus, and all menus are image-based.
Based on Bertrand's advice, and after realizing that Content Menu Item IS A CONTENT TYPE, I've created a new Content Part in the Admin Interface (not by code, I only want to write code for parts and content types when ultimately needed... I really want to see how far I can go with Orchard just by using the admin interface and templating/CSSing).
So, I've created a Menu Image Part, with two Content Picker fields added to it: Image and Hover Image. Then I've added this part to the Content Menu Item in the Manage Content Items admin interface.
Since I didn't write a Driver for it, the Model passed to the menu item template does not have an easily accessible property like #Model.Href... I've overriden the MenuItemLink-ContentMenuItem.cshtml with the following code so far:
#using Orchard.Core.Common.Models
#using Orchard.ContentManagement
#{
var contentManager = WorkContext.Resolve<IContentManager>();
var itemId = Model.Content.ContentItem.ContentMenuItemPart.Id;
ContentItem contentItem = contentManager.Get(itemId);
ContentField temp = null;
var menuImagePart = contentItem.Parts.FirstOrDefault(p => p.PartDefinition.Name == "MenuImagePart");
if (menuImagePart != null)
{
temp = menuImagePart.Fields.First();
}
}
<span>#temp</span>
#Model.Text
This yields the expected title for the Menu in a link, with a span before it with the following text:
Orchard.Fields.Fields.MediaPickerField
So all the above code (get the current content manager and the id of the ContentItem representing the ContentMenuItemPart, then use the content manager to get ContentItem itself, then linqing over its Parts to find the MenuImagePart (I can't use Get to get it because it requires a type and the MenuImagePart is not a type, it was created in the admin interface), then finally getting the first field for debugging purposes (this should be the Image field of the MenuImagePart I've created...)... all the above code actually got me to the Media Picker Field on my Meny Image Part...
What I'm not being able to do, and what makes me certainly a lot obtuse and stupid, is to find a way to read the MediaPickerField URL property! I've tried casting it to MediaPickerField, but I can't access its namespace from inside my template code above. I don't even know which reference to add to my theme to be able to add the following directive to it:
#using Orchard.Fields.Fields
I've finally succeeded in this task (thanks to Bertrand's direction).
UPDATE: And thanks again to Bertrand I've polished the solution which was running in circles, querying content items from the content manager when they were already available on the Model... now I'm leveraging the dynamic nature of content item, etc. And I'm finally satisfied with this solution.
It was necessary to create a new Content Part called Menu Image, then add this to the Content Type named Content Item Menu, and finally overriding the Content Item Menu template. This last part was the really tricky one. If it was not for Bertrand's directions the code bellow would have been smelly and daunting. The template ended up as follow:
#using Orchard.Utility.Extensions;
#using System.Dynamic
#{
/* Getting the menu content item
***************************************************************/
var menu = Model.Content.ContentItem;
/* Creating a unique CSS class name based on the menu item
***************************************************************/
// !!! for some reason the following code throws: 'string' does not contain a definition for 'HtmlClassify'
//string test = menu.ContentType.HtmlClassify();
string cssPrefix = Orchard.Utility.Extensions.StringExtensions.HtmlClassify(menu.ContentType);
var uniqueCSSClassName = cssPrefix + '-' + Model.Menu.MenuName;
/* Adds the normal and hovered styles to the html if any
***************************************************************/
if (menu.MenuImagePart != null)
{
if (!string.IsNullOrWhiteSpace(menu.MenuImagePart.Image.Url))
{
using(Script.Head()){
<style>
.#uniqueCSSClassName {
background-image: url('#Href(menu.MenuImagePart.Image.Url)');
width: #{#menu.MenuImagePart.Image.Width}px;
height: #{#menu.MenuImagePart.Image.Height}px;
display: block;
}
</style>
}
}
if (!string.IsNullOrWhiteSpace(menu.MenuImagePart.HoverImage.Url))
{
using(Script.Head()){
<style>
.#uniqueCSSClassName:hover {
background-image: url('#Href(menu.MenuImagePart.HoverImage.Url)');
width: #{#menu.MenuImagePart.HoverImage.Width}px;
height: #{#menu.MenuImagePart.HoverImage.Height}px;
}
</style>
}
}
}
}
<a class="#uniqueCSSClassName" href="#Model.Href">#Model.Text</a>
The only thing that I didn't understand is why I can't use HtmlClassify as an extension method with menu.ContentItem.HtmlClassify() and have to resort to calling the method as a standard static method (see the line with the comment `// !!! for some reason the following code throws´...)
Thanks again Bertrand!

Resources