I am using MVC3, and have a Layout file that is common for all of my Views. In the layout file, I would like to show some information regarding the currently logged in user.
I got it working now, but the only way I found out how to is to set some ViewBag fields in the actions methods, and pick them up in the layout file. This means that I will have to the the ViewBag fields from all my action methods, or at least make a method that sets them, and call this method from all my action methods.
Are there any central way to get this done? The absolute best is to do it once for the layout file in one place, but if no other option one place per controller might be good enough.
The right way is to call Html.RenderAction() inside the layout file, where you want the user-specific details. Then create a suitable Action method somewhere which reads the user's account details and returns them as a view (partial view) or as raw html (return Content("...")).
That way, you can also benefit the ability to cache the layout page and keep the user's account details region uncached (since it's obviously different for every user).
Setting a ViewBag is not a good idea, it's not strongly type and it breaks the correct schema of MVC.
Related
I am building an MVC4 app using razor, I have done many in the past but I want to roll some best practice in to this one in terms of dealing with variables.
The situation is that I have a logged in user (logged in meaning a windows authentication, with that user name matched to a user in my user table). That user comes with a set of profile options such as "canViewReports", "canEditPerson" etc etc.
Now, there are two prongs to those profile options. First is that my presentation layer needs to customise itself depending on what is presented to it. So profile 1 has canViewReport set to false so the tab for reports will be hidden. Profile 2 has it true so the tab will be shown.
The second prong is that if my savvy users type in /reports/index, I need to pick that up and block access for profile 1 but allow profile 2.
Ok, so at the moment I am using a base controller that is decorated with a [UserDataFilter], that user data filter calls a method that checks the current session for a set of keys and if they are missing assigns them. I put each of those profile options for the current user in to the session. I can then use the session variables in my presentation layer and also in code.
My concern is that that is messy looking in my code having to put this kind of thing everywhere:
(bool)session["canViewReports"] everywhere in my razor.
That lead me to try using the viewstart.cshtml and setting App variables in there that I can use in my razor a bit cleaner. In viewstart I set:
App.canViewReports = (bool)HttpContext.Current.Session["canViewReports"];
I can then just use App.canViewreports everyhwere in my views without too much trouble.
My questions are:
1) Is App.canViewReports for the entire application at an IIS level, or does each connection to IIS get its own pool of App. variables. The thing I want to avoid is the first user setting the variable and every other user that subsequently uses the application getting that value! (on different computers)
2) Is there a better way of doing this!!!
Many thanks
Iain
I would use User.IsInRole("canViewReports") in my razor logic to hide and show the menus item. If you build you menu in you layout you only need to do this once.
I would then further protect the action method by decorating the method with
[AuthorizeUser("canViewReports")]
You could create an ISessionService that stores the session information you need.In this way,you can implement it however you want and have full control over it.It could be retrieved easily via a DI container and it's easy to mock.
I am trying to catch the save event on listviews, not the list itself. This is so that i can stop saving of certain views.
I have tried List SPListEventReciever, but this seems to only catch events on the list itself, not the views.
EDIT: As mentioned in comment below, the views im trying to stop people saving are the default views on external lists.
In your situation, I'd try the following:
Remove regular users' permission to modify lists at all (if this is acceptable). This should still let them create personal views.
Write code that will take a personal view, analyse it, (delete it?) and (using elevated privileges) create an equivalent public view. (You can't just change the PersonalView property, sadly.) Be careful to capture everything a user can do on the create-a-view UI - this will be the trickiest part.
Optionally write similar code to allow a view to be switched from public back to personal (checking it's not the default view or any other view you're trying to protect.)
Optionally keep track of who "owns" one of these custom views, for example in a hidden list, and only allow the owner to take a view back to personal.
Write an interface to this code, e.g. an application page allowing a user to select from the relevant views, or a custom action on the ribbon.
I have a composable web application where the user enters an item number and one of a number of processors is invoked to handle the action based on what type of item it is. Each "processor" is in a seperate .dll and can be dropped into the application and picked up by the IOC container to allow the site to be extended.
The processors sometimes need to ask for feedback and so pass up an object that is used along with an Html.EditorFor to collect extra information from the user.
My question is whether or not there is a way to embed an EditorFor template in a seperate .dll and have it get picked up in the web application? Ideally the processor would be able to specify its own EditorFor template so that they don't just use the basic EditorFor template as it is rather lacking.
I am using Asp.Net MVC 3.0 and I have my _layout.cshtml in Shared folder. Problem is I want to generate menus in this .cshtml which should be loaded from database. But as I understand _layout.cshtml won't have any action etc associated where I can write logic and I don't want to write all this code in cshtml itself. Are there any options to write logic for cshtml within Shared folder?
You can setup a Controller and a view to render the menu and call it inside the _layout.cshtml.
#{ Html.RenderAction("Index", "Menus"); }
Eranga is correct, but let me expand on his answer, to answer your question specifically.
What you can do is create a new controller ("menus" for example), and create an action called default. Have this action return a view, calling it whatever you would like. Now go to your shared folder and add the view using the name you just specified.
Now for the cool part. By default, the MVC framework will look in the controllername/viewname path first, and if it fails it will then look at your shared/viewname path, which is where the view you just created resides! Neat, huh? ;p
Check out http://www.aspnetmvcninja.com/views/view-search-paths for more info on MVC search paths.
#Eranga has given you a good head start on implementing the feature you requested. I think the below two articles will be helpful as well:
Html.RenderAction and Html.Action:
http://haacked.com/archive/2009/11/18/aspnetmvc2-render-action.aspx
A sample implementation of Html.Action method with caching:
http://www.tugberkugurlu.com/archive/donut-hole-caching-in-asp-net-mvc-by-using-child-actions-and-outputcacheattribute
I'm using ASP.NET MVC 2 & VS 2008 Pro.
I am building a web site where users must go from page A to page B to page C. In other words, the order is very important. I want to guard against a user on page A simply entering a url for page C. If the user attempted to do such a thing I'd like to redirect them back to the page they were on before they entered the url.
It seems like I would make an action filter for this, but I'm honestly not sure what kind I would need. I would need access to the url the user was on and the url they are going to, and would have to validate them.
Thoughts?
EDIT 1
This is ugly, but it looks like I can get the full Uri's for the referring & destination urls inside of the OnActionExecuting method. I'd wager that this could be done from any kind of action filter. I'm currently testing all of this inside of the OnActionExecuting event of a custom action filter initially designed to check session state for expiration.
LogUtil.Write("OnActionExecuting", String.Format("Referring Url: {0} \n Destination Url: {1} ",
filterContext.RequestContext.HttpContext.Request.UrlReferrer.AbsoluteUri.ToString(),
filterContext.RequestContext.HttpContext.Request.Url.AbsoluteUri.ToString() ));
LogUtil is just a custom class I wrote that writes out to a log file.
So far it isn't pretty, but it works. Anyone have a more elegant solution?
EDIT 2
Another take that makes comparing the url's a bit easier is below. I haven't tried this using routes that actually contain parameters. In that situation, this might get thrown off.
String[] referrerSegments = filterContext.RequestContext.HttpContext.Request.UrlReferrer.Segments;
String[] destinationSegments = filterContext.RequestContext.HttpContext.Request.Url.Segments;
Perform action lookup logic to ensure destinationSegments[destinationSegments.length-1] comes after referrerSegments[referrerSegments-1]. This will likely be done with a static string List that contains the names of all the actions in the application in order. The index values of these shouldn't be more than 1 apart (i.e. destination action should have an index of plus or minus 1 of the value of referring action index).
Thoughts?
EDIT 3
Sigh. Apparently the referrer information is lost when a user is on a page and manually types in the url into the address bar. This seems odd to me, but it means I can only get the url for the current page the person is on.
Anyone have any suggestions here aside from session? I really, really want to avoid storing something like this in session if possible.
An Action filter is exactly what you need, with a redirect when they are being naughty. Keep track of their progress in the time honoured fashion, using the Session State.
This keeps the main part of your application relatively stateless (compared to classic ASP.NET) and keeps this logic in the action filter.
Another consideration: If your input is potentially a long-lived process you could save the user's progress to a database or into a Windows Workflow object. Just a thought. This would enable them to start the process on one machine and pick it up elsewhere. This might not apply to your scenario, but it is worth considering in some situations.