Use Sitecore / GlassView RenderImage with an item (MVC) - sitecore

I have a bunch of items in View that aren't fields on the model and just items. I really want to user RenderImage so I don't have to re-invent all the html code, but it really wants an item or and item of the GlassView type.
Is there a simple way to just force feed an item into GlassView.RenderImage?
How about sitecore's Render Image? It just wants a field value, but I want to give it an item?

You should add a new model which extends the existing (Sitecore template based) one.
For example you have the IArticle model, which has every field of the item, but not much else, as usual. You should create a new model, which inherits from the original, and you can add new fields, which will be mapped by Glass, if set properly. You can use the following attributes for example:
[SitecoreNode] (define an item by id or path)
[SitecoreParent] (by hierarchy)
[SitecoreQuery] (sitecore query)
[SitecoreChildren] (hierarchy)
Models
/// This model is based on the Sitecore template
[SitecoreType(TemplateId = "something")]
public interface IArticle : IBaseItem {
[SitecoreField]
string Title { get; set; }
[SitecoreField]
string Content { get; set; }
}
/// This model defines additional items.
public interface IArticleDetail : IArticle {
[SitecoreNode(Id = Constants.MainBannerId)]
IBanner PromoBanner { get; set; }
[SitecoreQuery("somequery")]
IEnumerable<ITag> Tags { get; set; }
}
In this case you GlassView inherits from the IArticleDetail, and the model binder propagates the additional fields as well.
If you want to render (editable images), you can just use the following syntax:
#RenderImage(Model.PromoBanner, m => BannerImage, isEditable: true)
or
#Html.Glass().RenderImage(Model.PromoBanner, m => BannerImage, isEditable: true)

#RenderImage will only accept a type of Glass.Mapper.Sc.Fields.Image but if you have a property on your view of Item, I would suggest just using the standard HTML helper field render:
#Html.Sitecore().Field("Field Name", Model.SomeSubItem)

Related

Glass Mapper Droplink Show Name instead of Guid

I'm using glass mapper and my template has a droplink list in it. When i attempt to retreive the item, it's bringing back the selected guid in the droplink instead of the name. How do I go about showing the name selected in the droplink versus the guid?
If you'd like the name of the item instead of the GUID, why not use a Droplist? Or do you need the GUID for other purposes? Glass is simply returning what Sitecore is actually storing in this case (which for a Droplink would be the item GUID).
Otherwise, you should create a new class for the linked item which includes the item name, and then change your mapped property to return that class instead of a string.
FYI, though a Droplist might be the easier fix, use of that field type is not a good practice. Since the item name is stored instead of the GUID, changes to that item name do not cascade to linking items.
I am giving a working example for techphoria414's answer for future use.
Imagine your droplink field contains the items of KeyValuePair type:
[SitecoreType(TemplateId = "Id")]
public partial interface IKeyValuePair
{
[SitecoreField(FieldId = "Id")]
string Key { get; set; }
[SitecoreField(FieldId = "Id")]
string Value { get; set; }
}
If your droplink field is SelectColourBar which is from template ColourBar
[SitecoreType(TemplateId = "Id")]
public partial interface IColourBar
{
[SitecoreField(FieldId = "Id")]
IKeyValuePair SelectColourBar { get; set; }
}
This will map SelectColourBar to KeyValuePair model, then one can access key or value.

Sitecore Glass Mapper Get All Siblings

I am trying to get all items at the current item level. I am using Glass Mapper SitecoreQuery for the same. I am able to get the current item but not able to map all siblings
public class TestModel:BaseModel
{
[SitecoreQuery("../*")]
public virtual IEnumerable<Model1> Siblings { get; set; }
}
[SitecoreType(AutoMap = true)]
public class Model1 : BaseModel
{
}
Base Model has all the required fields and correctly mapped. I am actually trying to display all items at the level of current item.
Add second parameter to SitecoreQuery : IsRelative = true like that:
[SitecoreQuery("../*", IsRelative = true)]
public virtual IEnumerable<Model1> Siblings { get; set; }
It tells Sitecore to start query at your item level instead of starting at the tree root.
You can find more information in the Official Sitecore Glass Mapper Tutorial

Map two templates for different sites

We are developing a multisite sitecore solution where each sites can have have their own News as well as able to display the combined news from other Sites.
Problem:
Each site have their unique News requirements where 90% of the template fields matches but rest 10% are different.
For example, Site-A has news template with Authors drop down list where Author List are authored on Configuration Node. Where as Site-B has news template where Author is a FREE TEXT Field.
Therefore, when Glass Mapper automatically tries to Map Authors field it fails for Free Text one.
Solution:
This can be resolved either by creating a Author as drop down on all Sites but Product owners don't want this.
The other solution is manual mapping of news fields from both sources or use AUTOMAP etc.
Desired Solution:
Glassmapper automatically resolves and populates the Author Text Field or Drop Down Field on the fly.
Is above possible?
Thank you.
I would solve this by "fluent configuration", http://glass.lu/Mapper/Sc/Tutorials/Tutorial8.aspx.
Combined with the new Delegate feature added to the Glass Mapper recently.
The Delegate feature was originally introduced and described here: http://cardinalcore.co.uk/2014/07/02/controlling-glass-fields-from-your-own-code/
Nuget package for the Delegate feature: https://www.nuget.org/packages/Cardinal.Glass.Extensions.Mapping/
You can use Infer types as follows:
public interface IBaseNews
{
string Author {get; set;}
//List all other shared fields below
}
[SitecoreType(TemplateId="....", AutoMap = true)]
public class NewsSiteA : IBaseNews
{
[SitecoreField]
public string Author {get; set;}
//List all fields which are unique for SiteA
}
[SitecoreType(TemplateId="....", AutoMap = true)]
public class NewsSiteB : IBaseNews
{
[SitecoreField]
public string Author {get; set;}
//List all fields which are unique for SiteB
}
Now, Your code should be:
IBaseNews newsClass = NewsItem.GlassCast<IBaseNews>(true,true);
//You can use Author property now
Firstly, I would recommend updating to the latest version of Glass for many other reasons including the delegate feature.
From the infer type example in the comment - you shouldn't use GlassCast, use CreateType(Item item) from the sitecore service / context. If you adopt the version with Delegate in, there is now an official Cast(Item item) on the sitecore service instead.
Also the example there uses a would not solve the difference in type. Delegate would make this very easy. Remember with delegate that there is no lazy loading, this shouldn't matter in this case.
public interface INews
{
// All my other fields
string Author { get; set; }
}
The fluent configuration would be something like (to be done in GlassScCustom)
SitecoreType<INews> = new SitecoreType<INews>();
sitecoreType.Delegate(y => y.Author).GetValue(GetAuthor);
fluentConfig.Add(sitecoreType);
private string GetAuthor(SitecoreDataMappingContext arg)
{
Item item = arg.Item;
if(item.TemplateID == <templateid>)
{
// return the value from the drop link
}
return item["Authors"];
}

How to implement Sitecore Template inheritance with Glass Mapper

I've been trying to achieve the following with glass mapper but can't get it to work.
I have a Home Page template which doesn't have any fields itself but inherits the following two templates:
Navigation Template
Fields: Navigation Title
Meta Information Template
Fields: Page Title, Meta Description
I've created the corresponding interfaces / classes as follows:
[SitecoreType(TemplateId = "{5BAB563C-12AD-4398-8C4A-BF623F7DBCDC}", AutoMap = true)]
public interface INavigation
{
[SitecoreField(FieldName = "Navigation Title")]
string NavigationTitle { get; set; }
}
[SitecoreType(TemplateId = "{95539498-31A5-4CB5-8DD6-C422D505C482}", AutoMap = true)]
public interface IMetaInformation
{
[SitecoreField]
string PageTitle { get; set; }
[SitecoreField]
string MetaDescription { get; set; }
}
[SitecoreType(TemplateId = "{F08693E5-8660-4B13-BBD6-7B9DC6091750}", AutoMap = true)]
public class HomePage : INavigation, IMetaInformation
{
public virtual string NavigationTitle { get; set; }
public virtual string PageTitle { get; set; }
public virtual string MetaDescription { get; set; }
}
When I then try accessing my page all attributes are always null:
var context = new SitecoreContext();
var page = context.GetCurrentItem<HomePage>();
I've tried several different approaches to this but nothing works. Also what was described in different tutorials didn't work. The only thing that works is when I add the fields directly on the Home Page template, but I don't want that since I have more than one page type and I therefore want to inherit the fields.
Does anyone have any idea what I'm missing here?! I'm using Sitecore 7 with .NET 4.5 by the way if that makes a difference.
Your fields are not mapped because you use a space in the Fieldname in the Sitecore Template.
Either remove the space or add the attribute [SitecoreField(FieldName ="Page Title")] to the Model.
I think that the Homepage class is trying to map the NavigationTitle on the Homepage template with the fieldName NavigationTitle and ignores the FieldName attribute on the base model.
By the way: I am using only interfaces for the current project I'm working on and it works as expected with inheritance. No need to add a property more then once ;)
Try to set infer type to true. I cannot get it to work at all without having that set.
ex.
item.GlassCast<HomePage>(false, true);
or
context.GetCurrentItem<HomePage>(false, true);
I find it does not work without this set.
You should render the common fields in a separate sublayout as a GlassUserControl.
public partial class NavigationTemplate : GlassUserControl<NavigationTemplate>
{
protected void Page_Load(object sender, EventArgs e)
Here you will have direct access to the NavigationTemplate fields no matter what item you are loading, it will always be cast to NavigationTemplate and will read the field values of the item you are loading.
It seems that you expect to get the properties on the HomePage instance, but you need to ask for the exact interface that contains the property as seen here
I.e.
Instead of doing:
var page = context.GetCurrentItem<HomePage>();
You should explicitly get current item as INavigation and get the field from the interface:
var navigationTitle = context.GetCurrentItem<INavigation>().NavigationTitle;

MinLength constraint on ICollection fails, Entity Framework

This is the datamodel I have:
public class Team
{
[Key]
public int Id { get; set;}
[Required]
public string Name { get; set; }
[MinLength(1)]
public virtual ICollection<User> Users { get; set; }
}
My issue is that when I later try to create a new Team (that has one user) I get the following issue when the context is saving.
An unexpected exception was thrown during validation of 'Users' when invoking System.ComponentModel.DataAnnotations.MinLengthAttribute.IsValid. See the inner exception for details.
The inner exception is the following:
{"Unable to cast object of type 'System.Collections.Generic.List`1[MyNameSpace.Model.User]' to type 'System.Array'."}
Here is the code for the actual saving (which for now is in the controller):
if (ModelState.IsValid)
{
team.Users = new List<User>();
team.Users.Add(CurrentUser);//CurrentUser is a property that gives me the currently active User (MyNamespace.Model.User).
DB.Teams.Add(team);//DB is a DbContext object that holds DbSets of all my models
DB.SaveChanges();
return RedirectToAction("Index");
}
So, what's going on here? Am I doing something wrong, or is there something else happening?
I do not believe that you will be able to use the MinLength Attribute for what you are trying to achieve. Here is the msdn page for the MinLength Attribute. Based on the description: "Specifies the minimum length of array of string data allowed in a property." So as you can see it can only be used against arrays of string data. You may need to create your own custom ValidationAttribute to handle your scenario.