Umbraco - Get Property by type - umbraco

I'm new to Umbraco, so this may not even be feasible. I've created my own Datatype using Archetype and want to be able to get an instance of that type on the page by type, not alias.
I know that I can do the following:
model.Content.GetPropertyValue("myAlias")
But I want to know if it's feasible to get the property by the type. Something along the lines of:
model.Content.GetPropertiesByType("TypeName")
which would return a list of controls on the page of that type?
Is this feasible?

It's possible, but not exactly straight forward.
Take a look at the available Umbraco Data Services - you'll need to retrieve the DataTypeDefinitions from the DataTypeService and retrieve the ContentType for the Model's IPublishedContent using the ContentTypeService.
Once you have these, you can match up the PropertyTypes on the ContentType with the retrieved DataTypeDefionitions based on the PropertyType's DataTypeDefinitionId.
The PropertyTypes have an Alias property which will match up with the Property Aliases on the Content itself.

You can use the content service if you get the id of the datatype you trying to find the multiples of from the url when you edit/create the datatype.
#foreach (var p in ApplicationContext.Current.Services.ContentService.GetById(Model.Content.Id).PropertyTypes.Where(p => p.DataTypeDefinitionId == -89))
{
<p>#p.DataTypeDefinitionId</p>
}

Related

MvcSiteMap: Dynamic node provider not reached

I created a project that the nodes are defined using attributes, and I set it in the web.config to scan for attributes, and it works fine.
I don't use an XML file at all.
Now I want to add a dynamic node provider, how do I do it?
Is there a way to do it without the XML (.sitemap) file?
I need to make sure it's under the root, which has been set in code using MvcSiteMapNodeAttribute attribute.
I've read the documentation and I can't really figure out where to place this line:
<mvcSiteMapNode
title="Details" action="Details"
dynamicNodeProvider="Project.StoreDetailsDynamicNodeProvider, Prject" />
What action is it supposed to point to? Additionally as said above, the root element is defined using attributes, so my question is if there is a way to avoid XML, or alternatively what's the efficient way to declare the XML (the less the better) to include my dynamic provider.
Update
I've tried the following and the node provider still isn't reached (From HomeController.cs).
[MvcSiteMapNode(Title = "Home", Key = HomeMenuKey,
DynamicNodeProvider = "Project.Namespace.NodeProvider, Assembly")]
public ActionResult Index()
{
return View();
}
Can you define it in the controller method attributes (and not use XML at all)?
For example:
[MvcSiteMapNode(Title="Details",
DynamicNodeProvider = "Project.StoreDetailsDynamicNodeProvider, Project")]
public ActionResult Index()
{
return View();
}
Seems that the dynamicNodeProvider attribute is ignored in the root node, also when it's defined in attributes.
So the only way to add a dynamic node provider under the root, is either by specifying it on a dummy action etc. or using XML.
An interesting note: the actual difference between defining in XML and attributes is that if it's defined in attributes, it (i.e. the gen. menu items) will be last in the menu, whereas when defined in XML it will be right after the root item (I guess that would be Home), Note that this is still controllable via the Order property in the attributes.
In my Web.Config, I left the siteMapFile empty, relying in what it said in the wiki page, that the default value is ~/Web.sitemap, in fact this is false (I've already corrected that in the updated wiki).
I don't think this behavior should be like this, I do think the MvcSiteMap engine should scan for dynamic node providers just as it scans for dynamic node attributes (here is the issue I posted on site).

How to get HTML generated at runtime and control imagery based on host header and lookup table in .ASP.Net

There are 3 concepts each type of user loggin in. The imagery is controlled by a lookup table based on the host header. By this, I mean that there are several domain names that point to the same IP/web instance, and that instance serves up the content based on reading the host header.
What we are doing now by having all the HTML (not the code) stored in a table that is referenced by the host header lookup.
CAn somebody guide me on this requirement please? Thanks
There are probably better ways to deal with multi-tenancy but let's assume you can't change any of that. What you want is probably this
string domain = HttpContext.Current.Request.ServerVariables["HTTP_HOST"];
You could map this domain onto a customer Id and store this in the user's cookie and look up based on this, mapping as necessary. Or simply use domain directly for your table lookup.
Request.Url.Host
This will give you the domain name such as: "www.example.com". I would recommend to store records including "view name" and "domain". Then, on your action, I would imagine something like this:
var record = db.HostRecords.Where(r => r.Domain == Request.Url.Host).FirstOrDefault();
var view = "Page.cshtml";
if (record != null){
view = record.ViewName;
}
return View(view);

Get Umbraco node by path in c# (Language Switching)

I have a multi language site with the following structure:
siteroot
- en
-- home
-- login
-- etc.
- de
-- home
-- login
-- etc.
The content beneath the language nodes is not necessarily the same.
When switching languages I want to test if a parallel language node exists.
Currently I'm doing the following:
get current path
replace the language part of path
e.g. replace /en/login to /de/login
the closest I've found to test the existence of a page is:
XPathNodeIterator i = umbraco.library.GetXmlDocumentByUrl("http://localhost/de/login");
Debugging this shows, that umbraco actually hits the database. This can't be the best way to test the existence of a page.
Anybody have a better method at hand?
By the sounds your using the document class in cms.businesslogic.web namespace. This class is used for modifying/publishing nodes inside of umbraco.
Try using the node class that resides in umbraco.presentation.nodeFactory. This will interact with the in-memory XML cache only.
Node.GetCurrent() //static method - will give you the current loaded page.
Node.Parent //class property - will give parent method
The problem with the node class, that it can't take XPath queries (and will not give performance)
I've written a dynamic Linq provider that can be used to query the Umbraco XML structure using compiled xslt expressions. I going to be publishing in the next week or so. Let me know if your interested...
We have a multilingual site where we needed to do the same thing to set up our hreflang tags. There might be a better way, but I decided on building some Xpath to find out if a matching node exists in the other languages. We are using umbraco 7, and I would shy away from using NodeFactory if at all possible. It is depreciated. Using the umbraco helper won't hit the database, and is one of the best ways to query published content or media from umbraco for umbraco 7.
public static IPublishedContent GetLocalizedVersionOfPage(this IPublishedContent node, string regionName)
{
var umbracoHelper = new UmbracoHelper(UmbracoContext.Current);
var ancestorNames = node.AncestorsOrSelf()
.Where(n => n.Level > 1)
.OrderBy(n => n.Level)
.Select(n => n.Name).ToList();
var xpath = new StringBuilder();
xpath.AppendFormat("/root/HomePage[#nodeName='{0}']", regionName);
foreach (var ancestorName in ancestorNames)
{
xpath.AppendFormat("/*[#nodeName='{0}']", ancestorName);
}
var matchingNode = umbracoHelper.TypedContentAtXPath(xpath.ToString()).FirstOrDefault();
return matchingNode;
}
The above method is an extension method on the IPublishedContent. It allows you to pass in the region and it checks to see if a node with the same path determined by node name exists in the specified region. I thought about using the urlname instead of the node name. You could do that as well and maybe even make this faster by skipping the piece of code that does the .AncestorsOrSelf(). It just depends on how you want it to work. In my case, I wanted it to find a match based on the node name even if the url path was different, so I had to do the .AncestorsOrSelf(). Hope this helps.
Another thing to consider is how you call this method. if you use a loop like this:
#foreach (var region in Umbraco.TypedContentAtRoot().Where(n => n.IsDocumentType("HomePage")))
{
var localizedVersion = currentPage.GetLocalizedVersionOfPage(region.Name);
if (localizedVersion != null)
{
<link rel="alternate" href="#localizedVersion.UrlAbsolute()" hreflang="#LocalizeUtils.GetCulture(region.Name)" />
}
}
You will end up getting the ancestors of the current node over and over once for each region because it calls .AncestorsOrSelf() every time you call GetLocalizedVersionOfPage(). It probably makes sense to refactor the GetLocalizedVersionOfPage method so you only have to call .AncestorsOrSelf once. If you do this sort of thing too many times, it starts to affect performance (especially if your site is very nested).

AutoCompleteExtender ajax in asp.net ,

Hi I am using the AutoCompleteExtender ajax control. I am getting the list of strings in LIST collection. I want to populate only those strings, which user typing as prefix text. how to do this. I am following the example given in ajax toolkit.let say user typing "ca" then if list contain the list like,
'cat', 'dog', donkey', 'mouse','cart'....etc.
Then it should populate only 'cat' and cart'.
How to achieve this?
In the example there's a description of the properties. Quote:
ServiceMethod - The web service method to be called. The signature of
this method must match the following:
[System.Web.Services.WebMethod]
[System.Web.Script.Services.ScriptMethod]
public string[] GetCompletionList(string prefixText, int count) { ... }
Note that you can replace
"GetCompletionList" with a name of
your choice, but the return type and
parameter name and type must exactly
match, including case.
ServicePath - The path to the web service that the extender will
pull the word\sentence completions
from. If this is not provided, the
service method should be a page
method.
So you need write a web service which will contain a method returning the list of suggestions based on the user input.

How to pass an unpersisted modified object from view back to controller without a form?

Short: how does modelbinding pass objects from view to controller?
Long:
First, based on the parameters given by the user through a search form, some objects are retrieved from the database.
These objects are given meta data that are visible(but not defining) to the customer (e.g: naming and pricing of the objects differ from region to region).
Later on in the site, the user can click links that should show details of these objects.
Because these meta data are important for displaying, but not defining, I need to get the previously altered object back in the controller.
When I use the default asp.net mvc modelbinding, the .ToString() method is used. This off course doesn't return a relevant string for recreating the complete object.
I would have figured the ISerializable interface would be involved, but this is not so.
How should I go about to get the desired effect? I can't imagine I'm the first one to be faced with this question, so I guess I'm missing something somewhere...
The default model binding takes form parameters by name and matches them up with the properties of the type specified in the argument list. For example, your model has properties "Price" and "Name", then the form would need to contain inputs with ids/names "Price" and "Name" (I suspect it does a case insensitive match). The binder uses reflection to convert the form values associated with these keys into the appropriate type and assigns it to the properties of a newly created object of the type specified by the parameter (again derived by reflection).
You can actually look at (and download) the source for this at http://www.codeplex.com/aspnet, although you'll have to drill down into the MVC source from there. I'd give a link to the DefaultModelBinder source, but the way they are constructed, I believe the link changes as revisions are introduced.
So, to answer your question, you need to have parameters (could be hidden) on your form that correspond to the properties of the object that you want to recreate. When you POST the form (in the view) to the controller, the binder should reconstitute an object of the specified type using the form parameters. If you need to do translation from the values in the form parameter to the object properties, you'll probably need to implement your own custom model binder.
[EDIT] In response to your second post:
Let's say that we want to have a link back to an action that uses a customized object. We can store the customized object in TempData (or the Session if we need it to last more through more than one postback) with a particular key. We can then construct the action link and provide the key of the object as value to the ActionLink in an anonymous class. This will pass back the key as a Request parameter. In our action we can use the key from this parameter to retrieve the object from TempData.
<%= Html.ActionLink( ViewData["CustomObject1",
"Select",
new { TempDataKey = ViewData["CustomObject1_Key"] }
) %>
public ActionResult Select()
{
Entity custObj = null;
string objKey = Request.Params["TempDataKey"];
if (!string.IsNullOrEmpty(objKey))
{
custObj = (Entity)TempData[objKey];
}
... continue processing
}
#tvanfosson
Thanks for your explanation, but what about links? (no forms involved)
Currently the Html.ActionLink(c=>c.Action(parameter), "label") takes objects as parameter. These have to be translated into URL parts. For this, MVC ALWAYS goes to the .ToString() method. I don't want to serialize my object in the ToString method.
Shouldn't I be able to somehow help the framework serialize my object? Say through the ISerialize interface or something?

Resources