I am using angularjs with Asp.net MVC to check the write access of a folder for users. If the user has the write access then I want to show a div which has a link. I have a Div in SampleView.Html and I have a method which checks for user's write access in MVC Controller called ReportController.cs. what will be the code for Angular Controller that I can use to pass value from MVC controller to Angularjs View?
SampleView.html:
<div id="DivPackerTemplate" class="cp-btn cp-btn-primary pull-right"><a ng-href="\\Samplefolder" >Edit Template</a></div>
ReportController.cs:
public void AccessPackerPlanTemplate(string folderPath)
{
string path = #"\\sample";
string NtAccountName = #"sampleuser";
DirectoryInfo di = new DirectoryInfo(path);
DirectorySecurity acl = di.GetAccessControl(AccessControlSections.All);
AuthorizationRuleCollection rules = acl.GetAccessRules(true, true, typeof(NTAccount));
//Go through the rules returned from the DirectorySecurity
foreach (AuthorizationRule rule in rules)
{
//If we find one that matches the identity we are looking for
if (rule.IdentityReference.Value.Equals(NtAccountName, StringComparison.CurrentCultureIgnoreCase))
{
//Cast to a FileSystemAccessRule to check for access rights
if ((((FileSystemAccessRule)rule).FileSystemRights & FileSystemRights.WriteData) > 0)
{
//Show the link
{
DivPackerTemplate.Visible = false; \\This is not working is there a alternative for this?
}
}
}
}
If your using angular you should make SampleView.Html a directive and inject a service that can call your mvc AccessPackerPlanTemplate method to get the information or better yet create an angular rule service that can wrap and all your rule logic and cache results.
Step 1: create the directive to wrap DivPackerTemplate
Directive doc -> https://docs.angularjs.org/guide/directive
Step 2: create an angular service that wraps your calls to rules logic (rules should be in webapi but you can use regular mvc actions if you must) -> https://docs.angularjs.org/guide/services
Then you simply wire them up by injecting your rules service into the directive and using it to populate the template data, caching is optional.
In the angular world you do not "pass value's" from the server into a view, your views use angular services to "pull values" into views/directives
I'm not an angular expert but something along these lines, omitted the rules service implementation ($rules) as angular services are fairly easy to create.
angular.module('moduleA', [])
.controller('SimpleDirectiveController', ['$rules','$scope',function($rules,$scope) {
$scope.show = function() {
return $rules.yourmethodtogetrulesresult();//for this case return either 'hidden' or 'visible'
}; }])
.directive('PackerTemplate', function() {
return {
template: function($scope) {
var templatevar = '<a ng-href="\\Samplefolder" visibility=\'[XX]\'>Edit Template</a>'
return templatevar.replace('[XX]',$scope.show())
}
};
});
From your MVC application you can return an asp view. Inside the view you can use your angular controller like this:
#model dynamic
#{
Layout = "";
}
<div ng-controller="PackerListController as vm">
<h1 class="page-header">
<button ng-click="vm.editTemplate()">Edit Template</button>
</h1>
</div>
Related
I am using angularjs with Asp.net MVC to check the write access of a folder for users. If the user has the write access then I want to show a div which has a link. I have a div in SampleView.Html and I have a method which checks for user's write access in MVC Controller called ReportController.cs. What will be the code for Angular Controller that I can use to pass value from MVC controller to Angularjs View?
SampleView.html:
<div id="DivPackerTemplate" class="cp-btn cp-btn-primary pull-right">
<a ng-href="\\Samplefolder" >Edit Template</a></div>
ReportController.cs:
public void AccessPackerPlanTemplate(string folderPath)
{
string path = #"\\sample";
string NtAccountName = #"sampleuser";
DirectoryInfo di = new DirectoryInfo(path);
DirectorySecurity acl = di.GetAccessControl(AccessControlSections.All);
AuthorizationRuleCollection rules = acl.GetAccessRules(true, true, typeof(NTAccount));
//Go through the rules returned from the DirectorySecurity
foreach (AuthorizationRule rule in rules)
{
//If we find one that matches the identity we are looking for
if (rule.IdentityReference.Value.Equals(NtAccountName, StringComparison.CurrentCultureIgnoreCase))
{
//Cast to a FileSystemAccessRule to check for access rights
if ((((FileSystemAccessRule)rule).FileSystemRights & FileSystemRights.WriteData) > 0)
{
//Show the link
{
DivPackerTemplate.Visible = false; \\This is not working is there a alternative for this?
}
}
}
}
I might not be understanding the question exactly correctly, but I you case it might be best just to show or hide it using razor syntax, rather than exposing a variable and handling that in the JavaScript. If you're not returning a Model from your ASP.NET controller, then in your ASP.NET controller set the ViewBag variable like this:
ViewBag.DivPackerTemplateVisible = false;
And in your view:
#if(ViewBag.DivPackerTemplateVisible) {
<div id="DivPackerTemplate" class="cp-btn cp-btn-primary pull-right"><a ng-href="\\Samplefolder" >Edit Template</a></div>
}
You should also make sure that ViewBag.DivPackerTemplateVisible always has a value of true or false, possibly by declaring it to true at the top of your ASP.NET controller.
Question background:
I am implementing some basic 'shopping cart' logic to an MVC app. Currently when I click a link - denoted as 'Add To Cart' on the screen shot below this calls to an 'AddToCart' method in the 'ProductController' as shown:
Product.cshtml code:
#Html.ActionLink("Add To Cart", "AddToCart")
'AddToCart' method in the ProductController:
public void AddToCart()
{
//Logic to add item to the cart.
}
The issue:
Not an issue as such but currently when I click the 'Add To Cart' button on the ActionLink on the ProductDetail.cshtml view the page calls the 'AddToCart' method on the ProductController and gives a blank view on the page - as shown below. I want the view to stay on 'ProductDetail.cshtml' and just call the 'AddToCart' method, how do I do this?
Basically #Html.ActionLink() or <a></a> tag uses get request to locate the page. Hence whenever you clicked it, you request to your AddToCart action method in ProductController and if that action method returns null or void so a blank or empty page is shown as you experienced (because or #Html.ActionLink() get request by Default).
So if you want to add your value to cart then call AddToCart method using ajax i.e:
HTML:
#Html.ActionLink("Add To Cart", "AddToCart", null, new { id="myLink"})
Jquery or Javascript:
$("#myLink").click(function(e){
e.preventDefault();
$.ajax({
url:$(this).attr("href"), // comma here instead of semicolon
success: function(){
alert("Value Added"); // or any other indication if you want to show
}
});
});
'AddToCart' method in the ProductController:
public void AddToCart()
{
//Logic to add item to the cart.
}
Now this time when the call goes to AddToCart method it goes by using ajax hence the whole page will not redirect or change, but its an asynchronous call which execute the AddToCart action method in your ProductController and the current page will remains same. Hence the product will also added to cart and page will not change to blank.
Hope this helps.
The answer of Syed Muhammad Zeeshan is what you are looking for, however you may return an EmptyResult.
public ActionResult AddToCart()
{
//Logic to add item to the cart.
return new EmptyResult();
}
According to this it has no impact on your code ASP.Net MVC Controller Actions that return void
But maybe sometime you want to return data and then you could do something like this:
if (a)
{
return JSon(data);
}
else
{
return new EmptyResult();
}
As many people mentioned here you will need to use AJAX if your using asp.net MVC to hit a controller POST function without having to leave your view.
A good use case for this is if you want to upload a file without refreshing the page and save that on the server.
All of the
return new EmptyResult();
Wont work, they will still redirect you.
Here is how you do it, in your view have the follow form as an example:
<form enctype="multipart/form-data" id="my-form">
<p>
The CSV you want to upload:
</p>
<input type="file" class="file-upload" name="FileUpload" />
</div>
<div>
<button type="submit" class="btn btn-default" name="Submit" value="Upload">Upload</button>
</div>
</form>
Then in the JavaScript side you need to add this to your view with within Script tags.
$("#my-form").on('submit', function (event) {
event.preventDefault();
// create form data
var formData = new FormData();
//grab the file that was provided by the user
var file = $('.file-upload')[0].files[0];
// Loop through each of the selected files.
formData.append('file', file);
if (file) {
// Perform the ajax post
$.ajax({
url: '/YourController/UploadCsv',
data: formData,
processData: false,
contentType: false,
type: 'POST',
success: function (data) {
alert(data);
}
});
}
});
Your controller might look something like this to process this type of file:
[HttpPost]
public void UploadCsv()
{
var listOfObjects = new List<ObjectModel>();
var FileUpload = Request.Files[0]; //Uploaded file
//check we have a file
if (FileUpload.ContentLength > 0)
{
//Workout our file path
string fileName = Path.GetFileName(FileUpload.FileName);
string path = Path.Combine(Server.MapPath("~/App_Data/"), fileName);
//Try and upload
try
{
//save the file
FileUpload.SaveAs(path);
var sr = new StreamReader(FileUpload.InputStream);
string csvData = sr.ReadToEnd();
foreach (string r in csvData.Split('\n').Skip(1))
{
var row = r;
if (!string.IsNullOrEmpty(row))
{
//do something with your data
var dataArray = row.Split(',');
}
}
}
catch (Exception ex)
{
//Catch errors
//log an error
}
}
else
{
//log an error
}
}
There are many ways to accomplish what you want, but some of them require a lot more advanced knowledge of things like JavaScript than you seem aware of.
When you write ASP.NET MVC applications, you are required to have more intimate knowledge of how browsers interact with the web server. This happens over a protocol called HTTP. It's a simple protocol on the surface, but it has many subtle details that you need to understand to successfully write ASP.NET MVC apps. You also need to know more about Html, CSS, and JavaScript.
In your case, you are creating an anchor tag (<a href="..."/>), which when click upon, instructs the browser to navigate to the url in the href. That is why you get a different page.
If you don't want that, there are a number of ways change your application. The first would be, instead of using an ActionLink, you instead simply have a form and post values back to your current controller. And call your "add to cart" code from your post action method.
Another way would be have your AddToCart method look at the referrer header (again, part of that more subtle knowledge of http) and redirect back to that page after it has processed its work.
Yet another way would be to use Ajax, as suggested by Syed, in which data is sent to your controller asynchronously by the browser. This requires that you learn more about JavaScript.
Another option is to use an embedded iframe and have your "add to cart" be it's own page within that iframe. I wouldn't necessarily suggest that approach, but it's a possibility.
Controller should return ActionResult. In this case a redirect to the caller page.
using System.Web.Mvc;
using System.Web.Mvc.Html;
public ActionResult Index()
{
HtmlHelper helper = new HtmlHelper(new ViewContext(ControllerContext, new WebFormView(ControllerContext, "Index"), new ViewDataDictionary(), new TempDataDictionary(), new System.IO.StringWriter()), new ViewPage());
helper.RenderAction("Index2");
return View();
}
public void Index2(/*your arg*/)
{
//your code
}
I was struggling with this and couldn't get it working with ajax.
Eventually got a working solution by making my controller method return type ActionResult rather than void and returning a RedirectToAction() and inputting the action relating to the view I wanted to remain on when calling the controller method.
public ActionResult Method()
{
// logic
return RedirectToAction("ActionName");
}
My View uses #Html.Action(…) to reflect various functional blocks. In opening the page, the site shows Authorization dialog box for users that do not have role pointed in controller method. (e.g. “manager” or “caller”) On pressing “cancel” get:
401 - Unauthorized: Access is denied due to invalid credentials.
May I achieve changes of my application behavior in case a user has no required roles, #Html.Action is ignored or nothing is shown?
My VIew:
#model InApp.ViewModel.ListViewModel
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div id="CallList">
#Html.Action("List","Call",new {id=Model.id})
</div>
<div class="Order">
#Html.Action("Create","Order",new {id=Model.id})
</div>
Controllers:
[Authorize(Roles = "manager, caller")] //if a user is not 'manager' or 'caller'
public PartialViewResult List() // nothing is shown
{
//...private
return PartialView();
}
[Authorize(Roles = "manager, admin")]
public PartialViewResultCreate()
{
//...private
return PartialView();
}
Trying to find the correct solution I have found similar questions:
Ignore #Html.Action() if user not in Role
and asp.net MVC3 razor: display actionlink based on user role
But I do not like “if” condition in my View. I am looking for a complex solution to hide and show separate parts using only AuthorizeAttribute and to avoid if – else in View or Controller. Thanks for any help!
I can suggest using this extension method:
This is a wrapper for #Html.Action which checks the user rights by using reflection.
public static MvcHtmlString ActionBaseRole(this HtmlHelper value, string actionName, string controllerName, object routeValues , IPrincipal user)
{
bool userHasRequeredRole = false;
Type t = Type.GetType((string.Format("MyProject.Controllers.{0}Controller",controllerName))); // MyProject.Controllers... replace on you namespace
MethodInfo method = t.GetMethod(actionName);
var attr = (method.GetCustomAttribute(typeof(AuthorizeAttribute), true) as AuthorizeAttribute);
if (attr != null)
{
string[] methodRequeredRoles = attr.Roles.Split(',');
userHasRequeredRole = methodRequeredRoles.Any(r => user.IsInRole(r.Trim())); // user roles check in depends on implementation authorization in you site
// In a simple version that might look like
}
else userHasRequeredRole = true; //method don't have Authorize Attribute
return userHasRequeredRole ? value.Action(actionName, controllerName, routeValues) : MvcHtmlString.Empty;
}
Using in view:
#Html.ActionBaseRole("List","Call",new {id=Model.id},User)
If you do not like this logic in the view - move it somewhere else. For example you can make your own extension method and use it like #Html.ActionOrNothing(...).
And the implementation should check if user has permissing to view something and return an empty string/view otherwise.
I am trying to build a dynamic menu for my ASP.NET MVC4 web application. As I am constructing the menu I want to make sure that menu items for which a user should not have access are not displayed in the menu.
I am using forms authentication and the [Authorize] attribute with each page requiring a given a role.
Given two strings (Controller and Action), and a logged in user, how can I determine if a user will have access to that Controller Action?
All of my menu data is stored in a database. My plan to render the menu is to construct a JSON object of the menu data and embed that into the View. Then client side I will use Handlebars.js and plug the menu JSON object into a template.
What I am trying to do is check permissions on a given Controller/Action for a user as I am rendering the menu data. My initial thought was to use reflection and look up the controller action method and check for the existence of an Authorize attribute and check to see if the current logged in user has the necessary role access that page. If not, then the menu item would not be rendered.
I am always reluctant to use reflection however, there usually tends to be an easier way of doing things.
public static IEnumerable<MethodInfo> GetActions(string controller, string action)
{
return Assembly.GetExecutingAssembly().GetTypes()
.Where(t =>(t.Name == controller && typeof(Controller).IsAssignableFrom(t)))
.SelectMany(
type =>
type.GetMethods(BindingFlags.Public | BindingFlags.Instance)
.Where(a => a.Name == action && a.ReturnType == typeof(ActionResult))
);
}
then
var roles = ((AuthorizeAttribute) (GetActions("ControllerName" + "Controller", "ActionName").First().GetCustomAttributes(typeof (AuthorizeAttribute), false)[0])).Roles;
if(roles.Contains("admin or smth"))
{
doSomsing();
}
I have implemented a very similar scenario.
The only difference is that my menus are stored in XML files.
Since you have your menu data stored in a database, I suggest you either add an xml field that contains security info to each menu record; or, have a new table that maps menu items to users.
The table can look like this:
MenuItemName (id) User
---------------------------------------
ViewVacationHistory (12) firstuser
ViewVacationHistory (12) seconduser
ApproveVacationRequest (10) seconduser
Now when your controller receives the request that would result in displaying menu items, and since your controller is decorated with the Authorize attribute, it will receive the user in the HttpContext. Here you simply query the database for menu items that match, then render the menu accordingly.
Use ActionFilter Attribute to filter the users based on Role
http://www.asp.net/mvc/tutorials/hands-on-labs/aspnet-mvc-4-custom-action-filters
create the class called Rolevalidation and add the code as below
public class AuthorizeRoles : AuthorizeAttribute
{
List<string> roles = new List<string>(“your list of roles”);
bool isAuthenticated = false;
for (int i = 0; i < roles.Count(); i++)
{
if (u.Role.Name == roles[i])
{
isAuthenticated = true;
break;
}
}
if (isAuthenticated)
{
SetCachePolicy(filterContext);
}
else
{
filterContext.Result = new RedirectResult("~/Error");
}
}
Add this code in every controller’s begining* [AuthorizeRoles(Roles = "SuperAdmin")]
Assuming you have a view that contains all the menu html code, probably will be better just using:
<ul class="menu">
<li>
#Html.ActionLink("Home", "Index", "Home")
</li>
#if (System.Web.Security.Roles.IsUserInRole(User.Identity.Name, "Administrator"))
{
<li>
#Html.ActionLink("Statistics", "Index", "Stats")
</li>
}
</ul>
Hope this helps!
The links will be generated by the json object which comes from an action and controller.
The json object should have a list of links(or what ever required to implement the menu item), the list should be generated by sort of settings stored in the database, tells each user what links to show.
Beside that what if the user has the URL so in this case you need to use
https://msdn.microsoft.com/en-us/library/system.web.mvc.authorizeattribute.users(v=vs.118).aspx
You can use an Authorize attribute with which to decorate an action from the controller [Authorize(AuthorizationContext)] or you can extend the authorize attribute for custom authorization;
You can also define some conventions in a dictionary in which you define that a Create action of a controller needs a create type authorization a.s.o.
Dictionary<string, Right> actionConventions = new Dictionary<string, Right>
{
{ "Index", Right.View },
{ "List", Right.View },
{ "Open", Right.View },
{ "Create", Right.Create},
{ "Edit", Right.Edit },
{ "Delete", Right.Delete }
}
and override OnAuthorization and Authorize methods of AuthorizeAttribute and either check the convention if the action sticks to the conventions defined in the dictionary or check a specific condition where you either Authorize(AuthorizationContext) for the action or HandleUnauthorizedRequest(AuthorizationContext).
Handling the menu you can create an authorization service in which you return the rights of the current user and add a class containing the rights of the user and you check the Model when rendering the model, if a menu item should be rendered or not.
Authorize Attribute MSDN
I'm a web developer new to using the MVC3 framework. We're building a site that implements a lot of sub folders for different segments of our audience. This routing concept is throwing a wrench in our structure for SEO.
In my global.asax file under the routing section we have:
routes.MapRoute("test", "test/{testFirst}/{testSecond}",
new { controller = "test", action = "RouteTest", testSecond = UrlParameter.Optional });
and in my controller we have:
public ActionResult RouteTest(string testFirst, string testSecond)
{
return View(testFirst, testSecond);
}
When I run the site and try to go to /test/test/index it won't pull up the view. It's stuck looking for test.cshtml which doesn't exist because it's a folder not a file.
Any ideas on to how make nested folders work?
EDIT:
Here's a branch of the structure we want and maybe it will help with what I'm trying to accomplish.
This is kind of hard to show but it should get the idea across. We have 5 different audiences that come to the site. I broke down 1 audience and what the flow of that audience is.
Not all segments will have products some are just content other segments have that 3rd level and have products to view
audience
segment
segment
products
segment
products
segment
This is the basic structure that we want the URLs to take
domain.com/audience/segment/products/(productsname)
Suggestions on how to make this possible
You are using the wrong overload for the View() method. Here's what you're using when you call View(testFirst, testSecond):
protected internal ViewResult View(
string viewName,
string masterName
)
MSDN Reference.
By putting "test" for the viewName, you're telling the Controller to render a View called Test (test.cshtml). Which you don't have.
It sounds to me like you are trying to correlate WebForms with MVC. It is not the same, and you are seeing a prime example with routing. ASP.NET MVC doesn't work off of the NTFS structure (folders and files). It relies on routing through route definitions.
If you are looking to render the View "RouteTest", then do something like this:
public ActionResult RouteTest(string testFirst, string testSecond)
{
ViewBag.testFirst = testFirst;
ViewBag.testSecond = testSecond;
return View();
}
This will render the "RouteTest" view and in your dynamic object ViewBag you will have access to two properties: testFirst and testSecond. In your view you can pull those values. (Although I highly recommend strongly-typed Views using a ViewModel)
Example Solution
ViewModel
public class TestData
{
public string testFirst { get ; set ; }
public string testSecond { get ; set ; }
}
Controller
public ActionResult RouteTest(string testFirst, string testSecond)
{
TestData td = new TestData();
td.testFirst = testFirst;
td.testSecond = testSecond;
return View(td);
}
Strongly-Typed View
#model TestData
#Html.Label(Model.testFirst)
#Html.Label(Model.testSecond)