Im working with Umbraco 5.1 beta. On the internet (this information is from previous versions, could not find recent documentation on it) I find that I could ask a node if the user has Access. In that way I want to build up my menu. The thing is, I cant get it to work, the HasAccess and IsProtected properties are not working. What am I doing wrong? Or does it work different in the newer versions of Umbraco? (I also tried it as method, still no result)
This is the code I'm now using:
#inherits RenderViewPage
#using Umbraco.Cms.Web;
#{
var Homepage = #DynamicModel;
while (Homepage.ContentType.Alias != "homePage")
{
Homepage = Homepage.Parent;
}
}
<ul>
<li>Home</li>
#foreach (var item in Homepage.Children) {
if(!item.IsProtected || (item.IsProtected && item.HasAccess)) {
if(#item.CurrentTemplate != null) {
var childName = item.Name ?? "(No name yet)";
<li>#childName </li>
}
}
}
</ul>
If you are just looking to suppress nodes that the user cannot access. Then you can use the WhereCanAccess() method.
Example: (This will hide all child nodes that the user doesn't have access to)
#inherits RenderViewPage
#using Umbraco.Cms.Web;
#{
var Homepage = #DynamicModel;
while (Homepage.ContentType.Alias != "homePage")
{
Homepage = Homepage.Parent;
}
}
<ul>
<li>Home</li>
#foreach (var item in Homepage.Children.WhereCanAccess())
{
if(#item.CurrentTemplate != null)
{
var childName = item.Name ?? "(No name yet)";
<li>#childName </li>
}
}
</ul>
Trying to find if a node IsProtected seems to be somewhat more complex (although only a couple of lines of code. Well the only way I found find to do it anyway!)
Example: (This just puts an * next to the name of protected menu items)
#inherits RenderViewPage
#using Umbraco.Cms.Web;
#{
var Homepage = #DynamicModel;
while (Homepage.ContentType.Alias != "homePage")
{
Homepage = Homepage.Parent;
}
var appContext = DependencyResolver.Current.GetService<IUmbracoApplicationContext>();
}
<ul>
<li>Home</li>
#foreach (var item in Homepage.Children)
{
var isProtected = appContext.Security.PublicAccess.IsProtected(item.Id);
if (#item.CurrentTemplate != null)
{
var childName = item.Name ?? "(No name yet)";
childName = (isProtected) ? "* " + childName : childName;
<li>#childName </li>
}
}
</ul>
Related
I'm passing a list of files along with their directories to my Razor view. I have successfully managed to list them all to facilitate downloading however I have now been asked to organise them in an accordion style.
I am trying to use JqueryUi's accordion however it is proving difficult to implement it whilst using a for each to populate the links.
this is what I have thus far:
#foreach (var fullPath in Model)
{
var fileName = Path.GetFileName(fullPath);
var parent = System.IO.Directory.GetParent(fullPath);
string parentString = parent.ToString();
var downloadPath = #Path.GetDirectoryName(fullPath) + "\\" + #fileName;
string yearOne = Server.MapPath("~/Pdfs/YearOne");
string yearTwo = Server.MapPath("~/Pdfs/YearTwo");
<div id="accordion">
#*<h3>Year One</h3>*#
#if (parentString == yearOne)
{
<div>
<p>
<ul>
<li>#Html.ActionLink(fileName, "Download", new { path = downloadPath }) </li>
</ul>
</p>
</div>
}
#*<h3>Year Two</h3>*#
#if (parentString == yearTwo)
{
<div>
<p>
<ul>
<li>#Html.ActionLink(fileName, "Download", new { path = downloadPath }) </li>
</ul>
</p>
</div>
}
</div>
}
What's happening is, its placing the first record in the appropriate accordion section, then the rest are just listed below. Can anyone suggest how I might achieve this requirement within a for each?
The logic of finding which path is for year one and which is for year two could be moved into the controller. The corresponding model could be:
public class AccordionModel
{
public List<string> YearOne { get; set; }
public List<string> YearTwo { get; set; }
}
The view could be changed to:
#model AccordionModel
#functions {
string GetFileName(string path)
{
var fileName = Path.GetFileName(path);
return fileName;
}
string GetDownloadPath(string path)
{
var fileName = Path.GetFileName(path);
var parent = System.IO.Directory.GetParent(path);
string parentString = parent.ToString();
var downloadPath = Path.GetDirectoryName(path) + "\\" + fileName;
return downloadPath;
}
}
<div id="accordion">
#*<h3>Year One</h3>*#
<div>
<p>
<ul>
#foreach(var path in Model.YearOne)
{
<li>#Html.ActionLink(GetFileName(path), "Download", new { path = GetDownloadPath(path) }) </li>
}
</ul>
</p>
</div>
#*<h3>Year Two</h3>*#
<div>
<p>
<ul>
#foreach(var path in Model.YearTwo)
{
<li>#Html.ActionLink(GetFileName(path), "Download", new { path = GetDownloadPath(path) }) </li>
}
</ul>
</p>
</div>
</div>
i am working on mvc project. In my controller i am calling my stored procedure from terms class and I am returning Index page if it returns true or return terms page if it returns false.
Calling stored procedure in terms page :
public class Accept
{
public void Check()
{
using (var ctx = new termsEntities())
{
ctx.usp_ChkTerms(8, new ObjectParameter("Accepted", typeof(bool)));
ctx.SaveChanges();
}
}
}
Now i am calling this in my controller :
public ActionResult App()
{
// calling Stored procedure from Model to class
var accept = new Accept();
accept.Check();
// checking if accepted is true then return view else return another view
AppEntities Accepted = new AppEntities();
AppTerm user = new AppTerm();
AppHist history = new AppHist();
user = (from AppTerm app in Accepted.AppTerms
where app.userID == 8
select app).ToList().FirstOrDefault();
if (user != null)
{
if (user.Accepted)
{
return View("Index");
}
else
{
return View("terms");
}
}
And this is the code i am using in my terms view :
#{
ViewBag.Title = "terms";
}
<html>
<body>
<ul>
#foreach ( var item in Model)
{
<div class="Page" onclick="location.href='#Url.Action("Info", new { id = item.ID })'">
span class="Col1">
<br />
#item.ID
</span>
<span class="Title">#item.Name</span>
}
</ul>
</body>
</html>
Here when condition is true it is displaying Index page but when condition falls and when it tries to display terms page i am getting Object reference not set to an instance of an object and error is pointing to foreach loop. so what mistake i am doing here? i need help..
It is ugly, but you may try
<div class="Page" onclick='location.href="#Url.Action("Info", new { id = item.ID })"'>
<div class="Page" onclick="location.href='#Url.Action("Info", new { id = item.ID })'">
Change this to:
<div class="Page" onclick="location.href='#Url.Action('LinkText','Info', new { id = item.ID })'">
Note the quote marks around Info
edit:
Added extra argument to link.
I'm trying to set a "current" class on both top and sub menu items in an Umbraco installation.
Topmenu is like :
Home Products About Contact
Now when I click let's say Products, then i set the class name to "current". At the same time it loads a submenu like this :
Jeans
Sweeters
T-shirts
- Red
- Green
- Blue
And when I click let's say Sweeters then I wan't the products link and the sweeters link to have the the current class. How can I do this?
Code for topmenu
#{
<ul class="topnavigation">
#foreach (var c in Model.AncestorOrSelf(2).Children.Where("umbracoNaviHide!=true"))
{
<li class="#(Model.Id == c.Id ? "current" : "")">#c.Name</li>
}
</ul>
}
Code for submenu
#{
<ul>
#foreach (var page in #Model.AncestorOrSelf(3).Children)
{
string style = string.Empty;
if (Model.Id == page.Id) { style = "class=current"; }
<li #style><a href="#page.Url" #Html.Raw(style)>#page.Name</a></li>
if (page.Childen != null && page.Children.Count() > 0 && Model.AncestorsOrSelf().Where("Id == #0", page.Id).Count() > 0)
{
<ul>
#foreach (dynamic secondPage in page.Children.Where("!umbracoNaviHide"))
{
string style1 = string.Empty;
if (Model.Id == secondPage.Id) { style1 = "class=current"; }
<li #style1>
- #secondPage.Name
</li>
}
</ul>
}
}
</ul>
}
A page has a Path property which contains a comma-delimited list of IDs representing the ancestors of the page.
You could check whether the Products page's ID exists within the current page's Path property using something like #Model.Path.Contains(c.Id.ToString()).
I'm having trouble sorting a list of user profiles which I am passing to the view. I want to display the list of all users in a certain role and I want to sort them by familyName attribute.
I tried using OrderBy but it has no effect.
Code in the controller
public ActionResult Index()
{
//get all patients
var patients = Roles.GetUsersInRole("user").ToList();
//set up list of patient profiles
List<UserProfile> pprofiles = new List<UserProfile>();
foreach (var i in patients) {
pprofiles.Add(ZodiacPRO.Models.UserProfile.GetUserProfile(i));
}
pprofiles.OrderBy(x => x.familyName); //<-this has no effect the list produced is
// exactly the same it was without this line
return View(pprofiles);
}
And the View
<ul id= "patientList">
#foreach (var m in Model)
{
<li>
<ul class="patient">
<li class="ptitle">#m.title</li>
<li class="pname"> #Html.ActionLink(#m.givenName + " " + #m.familyName, "View", "Account", new { #username = #m.UserName.ToString() }, new { id = "try" })</li>
<li class="pprofile">#Ajax.ActionLink("Profile", "PatientSummary", new { #username = #m.UserName }, new AjaxOptions { UpdateTargetId = "pContent"},new{ #class = "profpic" })</li>
</ul>
</li>
}
</ul>
I will need to reuse this in more than one place and there could be a large number of users so not ordering them in someway would be terrible. How should I go about this?
pprofiles.OrderBy(x => x.familyName); will return an IEnumerable<T>, not sorting the array where it was called on.
You can change your code like this :
public ActionResult Index()
{
//get all patients
var patients = Roles.GetUsersInRole("user").ToList();
//set up list of patient profiles
List<UserProfile> pprofiles = new List<UserProfile>();
foreach (var i in patients) {
pprofiles.Add(ZodiacPRO.Models.UserProfile.GetUserProfile(i));
}
var ordered = pprofiles .OrderBy(x => x.familyName);
return View(ordered );
}
Or in a more Linq-styled way :
var orderedPatients = Roles.GetUsersInRole("user")
.Select(u=>ZodiacPRO.Models.UserProfile.GetUserProfile(u))
.OrderBy(u=>u.FamilyName);
return View(orderedPatients);
Or :
var orderedPatients = from u in Roles.GetUsersInRole("user")
let userProfile = ZodiacPRO.Models.UserProfile.GetUserProfile(u)
order by userProfile.FamilyName
select userProfile;
return View(orderedPatients);
OrderBy does not modify the order of pprofiles elements, rather it returns a new collection with the elements ordered. You can try this:
pprofiles = pprofiles.OrderBy(x => x.familyName);
Or you can use List(T).Sort
You need to assign it back to your variable, OrderBy returns sorted collection:
pprofiles = pprofiles.OrderBy(x => x.familyName);
I have a Page.cshtml similar to the following (that does not work):
#{
Layout = "../Shared/Layouts/_Layout.cshtml";
var mycollection = (ViewBag.TheCollection as IQueryable<MyCollectionType>);
}
<h2>#ViewBag.Title</h2>
content here
#if (mycollection != null && mycollection.Count() > 0)
{
#section ContentRight
{
<h2>
Stuff
</h2>
<ul class="stuff">
#foreach (MyCollectionType item in mycollection )
{
<li class="stuff-item">#item.Name</li>
}
</ul>
}
}
As I said, this does not work. I want to not define the section if there's nothing in the collection. Is there any way to have something like this work? If not, what are my other options? I'm very new to this Razor ViewEngine.
Edit
In my layout i have:
#if(IsSectionDefined("ContentRight"))
{
<div class="right">
RenderSection("ContentRight")
</div>
}
what i don't want is the div to output when the section is empty.
I ended up doing something a little hacky to get it working how I needed it.
on my page i have:
#{
Layout = "../Shared/Layouts/_Layout.cshtml";
var mycollection = (ViewBag.TheCollection as IQueryable<MyCollectionType>);
ViewBag.ShowContentRight = mycollection != null && mycollection.Count() > 0;
}
then in my layout i have:
#if(IsSectionDefined("ContentRight") && (ViewBag.ShowContentRight == null ||ViewBag.ShowContentRight == true))
{
<div class="right">
RenderSection("ContentRight")
</div>
}
else if(IsSectionDefined("ContentRight"))
{
RenderSection("ContentRight")
}
If the section is defined it has to be rendered, but if there's no content i dont want the <div>s
If there's a better way i'd like to know.
The renderer is expecting the method to be called sometime in the layout file. You can spoof the renderer and use "global" conditionals (think login).
#{
ViewBag.content = RenderBody();
}
#if (Request.IsAuthenticated) {
#ViewBag.content;
}
else {
#Html.Partial("_LoginPartial")
}
Extension method with private static readonly field info for perf:
private static readonly FieldInfo RenderedSectionsFieldInfo = typeof(WebPageBase).GetField("_renderedSections", BindingFlags.Instance | BindingFlags.NonPublic);
public static void EnsureSectionsAreRegisteredAsRendered(this WebPageBase webPageBase, params string[] sectionNames)
{
var renderedSections = RenderedSectionsFieldInfo.GetValue(webPageBase) as HashSet<string>;
if (renderedSections == null)
{
throw new WebCoreException("Could not get hashset from private field _renderedSections from WebPageBase");
}
foreach (var sectionName in sectionNames)
{
if (!renderedSections.Contains(sectionName))
{
renderedSections.Add(sectionName);
}
}
}
In your cshtml:
#{ this.EnsureSectionsAreRegisteredAsRendered("SectionName1", " SectionName2", "…"); }
Yes, yes, yes.... I know.... bad reflection! Use at your own risk :)
I use the following method in my view base class (from this excellent blog post http://haacked.com/archive/2011/03/05/defining-default-content-for-a-razor-layout-section.aspx/):
public HelperResult RenderSection(string name, Func<dynamic, HelperResult> defaultContents)
{
if (IsSectionDefined(name))
{
return RenderSection(name);
}
return defaultContents(null);
}
If you don't have a view base class, I recommend one because it lets you add all sorts of little extra functionality to your views. Just create a class with the following signature: public abstract class MyViewPage<T> : WebViewPage<T> and then set it in your web.config:
<system.web.webPages.razor>
<pages pageBaseType="MyViewPage">
...
</pages>
</system.web.webPages.razor>
You can wrap your whole section in an if statement with IsSectionDefined
Layout.cshtml:
#if (IsSectionDefined("ContentRight"))
{
<div>
#RenderSection(name: "ContentRight", required: false)
</div>
}
Your cshtml page:
#section ContentRight
{
#if (mycollection != null && mycollection.Count() > 0)
{
<h2>
Stuff
</h2>
<ul class="stuff">
#foreach (MyCollectionType item in mycollection )
{
<li class="stuff-item">#item.Name</li>
}
</ul>
}
}