Umbraco Razor Orderby custom property - asp.net-mvc

I'm using a partial view in Umbraco v6.2.4 that's pulling a list of related articles. The script below works perfectly and uses the "Refactored.Umbraco.MVCExtensions" to pull out a view count of the articles. So far so good but I would like to order/sort the list of articles by the most popular article i.e. based on the view count. However, the view count is not part of the Umbraco Model so I don't know how to order/sort the list based on this or if there's another way of doing this?
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage
#using Refactored.UmbracoViewCounter
#{
var articles = Model.Content.AncestorOrSelf(4).Children.Where(x => x.IsVisible()).OrderBy("articleCategory, articleDate desc");
int pagerank = 1;
string elementId;
<div>
<p class="h3">Most Popular</p>
#foreach (var item in articles.Take(6))
{
elementId = "content_" + pagerank.ToString();
<div class="items #elementId">
<span class="pagerank">#pagerank</span>
<a href="#item.Url">
<p>#(item.GetPropertyValue<string>("title"))</p>
</a>
<span class="views-counter"> #ViewCount.GetViewCount(item.Id, category: "articles", increment: false)</span>
</div>
pagerank = pagerank + 1;
}
</div>
}

You can use Linq to combine both lists and then sort, something like below, I assume you are using petapoco for view count table, I can't test code since I don't have your custom data table but I think you can move to right direction for here:
var db = ApplicationContext.DatabaseContext.Database;
var sql = new Sql()
.Select("*")
.From<RefViewCount>(new SqlServerSyntaxProvider());
var refViewCountList = db.Query<RefViewCount>(sql);
var articles = Model.Content.AncestorOrSelf(4).Children.Where(x => x.IsVisible()).Select(r => new {Item = r, ViewCount = refViewCountList.Where(v => v.nodeId == r.Id).Select(v => v.count).SingleOrDefault()}).OrderBy(r => r.ViewCount).ToList();

Related

Can't get Properties of a Specific Child Page

I just started using umbraco 4 days ago and I'm stuck with this problem.
I have a page named "About Us" that is a child under "Home" and I need to get some of its properties in the "Home" Page.
I created a partial view macro to do this but I get the error loading partial view script instead.
Below is my code:
#{ var selection = CurrentPage.Children.Where(x => x.Name == "aboutus"); }
#if (selection.Any())
{
<ul>
#foreach (var item in selection)
{
<div class="sc-inner">
<h4>
#item.PageTitle</h4>
<p>
#Umbraco.Truncate(#item.mainAboutDescription, 100)</p>
<a class="btn btn-danger" href="#item.Url">Read More...</a>
</div>
break;
}
</ul>
}
Can someone please tell me what I'm doing wrong?
Thanks in advance.
Edits:
The error on the website I got is below;
Error loading Partial View script (file: ~/Views/MacroPartials/homePage_aboutUsSection.cshtml)
Assuming the partial is inheriting UmbracoTemplatePage the CurrentPage property is a dynamic, you cannot use lambda expressions as an argument to a dynamically dispatched operation.
If you wish to use Linq to query the content use Model.Content rather than CurrentPage which is an IPublishedContent e.g.
#{ var selection = Model.Content.Children.Where(x => x.Name == "aboutus"); }
Note: the Name property will return the documents Name as entered in the CMS most likely 'About Us' instead of 'aboutus'
Using the about will return an IEnumerable<IPublishedContent> so you will need to use the GetPropertyValue method rather than accessing the content dynamically:
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage
#{ var selection = Model.Content.Children.Where(x => x.Name == "aboutus"); }
#if (selection.Any())
{
<ul>
#foreach (var item in selection)
{
<li class="sc-inner">
<h4>
#item.GetPropertyValue("pageTitle")
</h4>
<p>
#Umbraco.Truncate(#item.GetPropertyValue<string>("mainAboutDescription"), 100)
</p>
<a class="btn btn-danger" href="#item.Url">Read More...</a>
</li>
break;
}
</ul>
}
Very bad practice to use the Name property for querying in your code - what if somebody changes the Name to About for example. Your code would break. If there is something else that is unique on the node then it would be better.
For example if you have an aboutUs document type for your about us page you could use:
Model.Content.Children.Where(x => x.DocumentTypeAlias == "exactNameOfYourDocTypeAliasHere")
or, although not so robust, the Id of the page could be used.
Model.Content.Children.Where(x => x.Id == 1234)
The Id is far less likely to change than the Name of a key page like this.
I would generally not use the Id in code either but it is far better than Name

Sitefinity Custom Fields for list widgets and how to use them in MVC view templates

I've added a Custom Field to the List Widget in Sitefinity 8.1, it's Type is Related data and it's Data type is Pages. The field name is LinkedPageUrl.
Works perfectly in the back end allowing me to select a page from system and store it against that particular List Item.
I can't find any place in Sitefinity's documentation that explains how I would programmatically use this field in a MVC based List.SimpleList.cshtml view template that I'm customizing.
I've seen this being used in the News widget where there is an associated Image for each News Article:
<img src="#Html.Raw(item.Fields.RelatedImg.Fields.MediaUrl)" class="img-responsive" />
But I don't get close to this because I don't have the slightest idea where to begin... What's the model structure, syntax, etc.
My objective is to change each list item rendered out into an anchor and the anchor should make use of this Related Data field's URL for it's Href property.
EDIT:
You can do something like this in the widget template:
#foreach (var item in Model.Items)
{
<h3 #Html.InlineEditingAttributes(Model.ProviderName, Model.ContentType.FullName, (Guid)item.Fields.Id)
#Html.InlineEditingFieldAttributes("Title", "ShortText")>
#item.Fields.Title
</h3>
<ul>
#foreach (var listItem in ((ListViewModel)item).ListItemViewModel.Items)
{
<li #Html.InlineEditingAttributes(Model.ProviderName, ((ListViewModel)item).ListItemViewModel.ContentType.FullName, (Guid)listItem.Fields.Id)>
<div #Html.InlineEditingFieldAttributes("Title", "ShortText")>
#listItem.Fields.Title
</div>
<div class="sfMultiRelatedItmsWrp">
<h2 class="sfrelatedItmTitle">Related pages</h2>
#foreach (var link in listItem.Fields.LinkedPageUrl)
{
var node = PageManager.GetManager().GetPageNode(link.Fields.Id);
var url = PageExtesnsions.GetFullUrl(node);
<div>#link.Fields.Title - #url</div>
}
</div>
</li>
}
</ul>
}
This is given that you selected that multiple pages can be selected per list item.
EDIT: Make sure to include the following namespaces
#model Telerik.Sitefinity.Frontend.Mvc.Models.ContentListViewModel
#using Telerik.Sitefinity.Frontend.Lists.Mvc.Models;
#using Telerik.Sitefinity.Frontend.Mvc.Helpers;
#using Telerik.Sitefinity.Modules.Pages;
EDIT2: If the custom field allows for only 1 page to be selected then it should look like this:
<div class="#Model.CssClass">
#foreach (var item in Model.Items)
{
<h3 #Html.InlineEditingAttributes(Model.ProviderName, Model.ContentType.FullName, (Guid)item.Fields.Id)
#Html.InlineEditingFieldAttributes("Title", "ShortText")>
#item.Fields.Title
</h3>
<ul>
#foreach (var listItem in ((ListViewModel)item).ListItemViewModel.Items)
{
<li #Html.InlineEditingAttributes(Model.ProviderName, ((ListViewModel)item).ListItemViewModel.ContentType.FullName, (Guid)listItem.Fields.Id)>
<div #Html.InlineEditingFieldAttributes("Title", "ShortText")>
#listItem.Fields.Title
</div>
<div class="sfMultiRelatedItmsWrp">
<h2 class="sfrelatedItmTitle">Related pages</h2>
#{
var node = PageManager.GetManager().GetPageNode(listItem.Fields.LinkedPageUrl.Fields.Id);
var url = PageExtesnsions.GetFullUrl(node);
}
<div>#listItem.Fields.Title - #url</div>
</div>
</li>
}
</ul>
}
A good starting point is this article

How can I save the order a list of hidden form inputs which represent an array in ASP.NET MVC?

I have a sortable list of items which are generated from an array property on a model. I want to use jquery UI sortable to reorder the items and save this order in my Action method.
Here is the code I am using to output the items:
#using(#Html.BeginForm("Save", "MyController", FormMethod.Post))
{
<input type="submit" value="Save"/>
#Html.HiddenFor(x=>x.Id)
<ul id="sortable2" class="connectedSortable">
#{
int count = 0;
}
#foreach(var item in Model.CollectionOfThings)
{
<li class="ui-state-default">
#Html.Hidden("Things[" + count + "].Id", item.Id)
#Html.Hidden("Things[" + count + "].Title", item.Title)
#item.Title
</li>
count++;
}
</ul>
}
When I receive the model in my action method the order is the same as when I started. I understand that this is because of the index but I can't figure out a way (without writing some custom javascript to update the name attribute of each hidden input) to have the items bound to the array in correct order.
Can anyone suggest a way this could be achieved? I can probably write a bit of Javascript to do it but I hoped I could do this purely with model binding.
I tried outputting the inputs like this:
#Html.Hidden("Things[].Id", item.Id)
However this just resulted in my collection being null.
I have discovered a blog post from Phil Haack which contained the answer.
By using arbitrary indexes in a seperate hidden input with the name of my collection + .Index, the model binder seems to handle it and maintain the order.
#using(#Html.BeginForm("Save", "MyController", FormMethod.Post))
{
<input type="submit" value="Save"/>
#Html.HiddenFor(x=>x.Id)
<ul id="sortable2" class="connectedSortable">
#{
int count = 0;
}
#foreach(var item in Model.CollectionOfThings)
{
<li class="ui-state-default">
#Html.Hidden("Things.Index", "item" + count)
#Html.Hidden("Things[item" + count + "].Id", item.Id)
#Html.Hidden("Things[item" + count + "].Title", item.Title)
#item.Title
</li>
count++;
}
</ul>
}
Easier solution for you can be to collect only Ids. I assume titles can be recreated on server side. In this case you can just define:
#Html.Hidden("ThingIds", item.Id)
and bind to
List<int> thingsIds

Is there an equivalent to HTML.DropDownList that creates an unordered list in MVC3?

In my controller I use the following:
ViewBag.ReferenceId = new SelectList(_reference.Get("00")
.AsEnumerable()
.OrderBy(o => o.Order), "RowKey", "Value", "00");
Then in my view I use the following extension to create a dropdown:
#Html.DropDownList("ReferenceID",
null,
new {
id = "ReferenceID",
#class = "combobox2",
style="width: 150px;"
}
)
Now I would like to change this to create an unordered list. Something like the following:
<ul id="categories" class="pane list">
<li>Websites</li>
<li>Web Design</li>
<li>Print</li>
</ul>
Is there any MVC extension that can do this for me or is there an easy way I can code something?
There is no equivalent, though you could write it on your own like this:
tagsList = "<ul>{0}</ul>";
tagsListItem = "<li>{0}</li>";
StringBuilder result = new StringBuilder();
list.ForEach(listItem => result.AppendFormat(tagsListItem, item.ToString()));
return string.Format(tagsList, result.ToString());
And you could fit the code for your needs (like adding additional tags, attributes etc.)
Write your HtmlHelper http://www.asp.net/mvc/tutorials/older-versions/views/creating-custom-html-helpers-cs but the best ways is to browse MVC source code and learn http://aspnet.codeplex.com/SourceControl/changeset/view/72551#288014 or use a foreach.
<ul id="categories" class="pane list">
#foreach(var category in categories)
{
<li>#category</li>
}
</ul>
Regards

Can we dynamically change the URL with Ajaxy call on ASP.NET MVC page?

I have an ASP.NET MVC application that has a view called Products.
This Products View has a Left Menu Navigation that is implemented using NavMenuProducts.ascx Partial View.
This Menu is implemented using JQuery Treeview so that it has the list of ProductNames as the Parent Node and it is expandable(For Example: 10 Products).
Each of these Products have a ChildNode as DocTypeName and it is a hyperlink(For Example: 3 DocTypeNames).
When the user clicks ChildNode Hyperlink all the matching Documents are displayed and is implemented using Ajaxy call. So that the user has better UI experience.
But the problem with this is the url always is static(Example: http://DocShare )
But based on the Node that is clicked, I want the url like http://DocShare/Products/Product1/Letter
I am wondering how to make this dynamic url using Ajaxy call.
NOTE: If I am using HTML.ActionLINK, then I am getting dynamic URL. But this ActionLink, while the Page is loading, we are getting random treeview screen. TO avoid that flickering tree effect, I am making Ajax call for better UI experience.
Any solution would be appreciated for getting dynamic url with Ajaxy call.
Here is the Code:
NavigationProducts.ascx Page:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MedInfoDS.Controllers.ProductViewModel>" %>
<script type="text/javascript">
$(document).ready(function () {
$(".docId").click(function () {
alert("DocTypeName: " + this.id);
$("#docDetails").load('<%= Url.Action("DocumentDetails") %>', { ProductName: "darbepoetin alfa", DocTypeName: this.id }, function (responseText, status) {
});
return false;
});
});
<div id="treecontrol">
<a title="Collapse the entire tree below" href="#">Collapse All</a> | <a title="Expand the entire tree below"
href="#">Expand All</a> | <a title="Toggle the tree below, opening closed branches, closing open branches"
href="#">Toggle All</a>
</div>
<div id="divByProduct">
<ul id="red" class="treeview-red">
<% foreach (var item in Model.Products)
{ %>
<li><span>
<%=item.Name%></span>
<ul>
<%foreach (var item1 in Model.DocTypes) { %>
<li><span>
<%= Html.ActionLink(item1.DocTypeName, "Products", new { ProductName = item.Name, DocTypeName = item1.DocTypeName })%>
<br />
<a class="docId" href="#" id="<%=item1.DocTypeName%>"><%= item1.DocTypeName%></a>
<%= Html.Hidden("ProductName", item.Name)%>
</span></li>
<% } %>
</ul>
</li>
<% } %>
</ul>
</div>
Controller Method:
// Response to AJAXy call to populate details for given ProductName and DocType
[HttpPost]
public virtual ActionResult DocumentDetails(string ProductName, string DocTypeName)
{
var entities = new MIDSContainer();
if (ProductName == null) return View();
int ProductId = (entities.Products.FirstOrDefault(p => p.Name == ProductName)).ProductId;
int DocTypeId = (entities.DocTypes.FirstOrDefault(d => d.DocTypeName == DocTypeName)).DocTypeId;
var documents = (from d in entities.Documents.Where(p => p.ProductId == ProductId && p.DocTypeId == DocTypeId && p.AvailableForUse == true && p.Status == "Approved") orderby (d.Description) select d).ToList();
return View(documents);
}
There's a pretty comprehensive solution here: http://ajaxpatterns.org/Unique_URLs
I assume you want to "change the url" so that the Back button works in the browser to navigate between the different products. You can't actually change the URL without a postback, but you can change the url's Hash value. By changing the Hash value you will be able to support the browsers Back button just like the URL itself was changed but without any postbacks.
In the following URL:
http://site/page#SomeValue
The Hash value is "#SomeValue".
You can set the Hash using "document.Hash" in JavaScript.
To make it much easier to work with the "document.Hash" value and setup a function to get notified when it changes, I create the jHash project.
You'll probably want to look at jHash to help you do what you're looking for.
http://jhash.codeplex.com/

Resources