Setting authorization on a specific ASP .NET MVC 3 field? - asp.net-mvc-3

Is it possible to set authorization on a specific field in MVC 3?
My initial thought (and MSDN research) indicates that the [Authorize] tag is only for controller level actions (create,edit,index,etc). I can do this on the controller action:
[Authorize(Roles = "RoleA,RoleB")]
public ActionResult Create()
{
return View(new Tracking());
}
The scenario is that two roles (RoleA and RoleB) can access the 'Edit' controller. But only RoleA can change the first field. The other role (B) can only view the field.
I would like to do something like this on a specific field:
[Required]
[Range(1, 99)]
[Authorize(Roles = "RoleA")]
public int Sequence { get; set; }
UPDATE1:
A little more research down the StackOverflow rabbit roles reveals that I need to use partial views.
So in my view I add this code:
<div>
#if (Context.User.IsInRole("RoleA"))
{
#Html.Partial("_SequenceEdit")
}
else
{
#Html.Partial("_SequenceView")
}
</div>
So if the user is RoleA they get a partial view that allows editing of the 'sequence' field. Otherwise they get a view only of the 'sequence' field.
My view only partial view looks like this:
<div class="editor-label">
#Html.LabelFor(model => model.Sequence)
</div>
<div class="editor-field">
#Html.DisplayFor(model => model.Sequence)
#Html.HiddenFor(model => model.Sequence)
#Html.ValidationMessageFor(model => model.Sequence)
</div>

I see that you've already figured out how to modify the view in order to not show a text box to users in Role B. But you should also do server-side validation to make sure only users in Role A can edit the field.
[Authorize(Roles = "RoleA,RoleB")]
[HttpPost]
public ActionResult Edit(int trackingID, Tracking newTrackingObject)
{
// grab the current version of the tracking object from your data repo
var oldTrackingObject = trackingRepo.GetByID(trackingID);
// check if the user is in role A and edit the sequence number
if(Context.User.IsInRole("RoleA"))
oldTrackingObject.Sequence = newTrackingObject.Sequence;
// continue processing the new tracking object
// after all processing is done, persist the edited tracking object back to the repo
trackingRepo.Update(oldTrackingObject);
trackingRepo.SaveChanges();
}
This will prevent users in Role B from changing the sequence field by manually editing the hidden form field (eg. with FireBug or a similar tool.)

Related

Save and retrieve checkbox values asp.net mvc3

I am a newbie to MVC3 technology and trying to workout my way get through a small problem.
I simply need to get checked checkbox values to be saved in database and on Edit view check them back.
<input type="checkbox" value="Photo" name="DocSub" /> Photograph<br />
<input type="checkbox" value="BirthCertificate" name="DocSub" /> Copy Of Birth Certificate<br />
<input type="checkbox" value="School Leaving Certificate" name="DocSub" /> School Leaving Certificate<br />
When the Submit button is clicked, the [HTTPPOST] Action method of the desired controller is called. There I receive the selected values in this form :
var selectedCheckBoxValues = Request.Form["DocSub"];
I am getting the all the checked checkbox values in comma separated form and able to store them to the database, but wondering if this is the right approach to go by.
Also I need to know to retrieve checkbox values from database on Edit view in already checked form.
the typical apporoach to these problems is to use a view with a model
ie, suppose this is view Documents.cshtml
#model DocumentViewModel
#Html.LabelFor(m => m.Photo)
#Html.CheckBoxFor( m => m.Photo )
#Html.LabelFor(m => m.BirthCertificate)
#Html.CheckBoxFor( m => m.BirthCertificate )
#Html.LabelFor(m => m.SchoolLeavingCertificate)
#Html.CheckBoxFor( m => m.SchoolLeavingCertificate )
and use a viewmodel to pass data to the view
the viewmodel is a class where you have the data your going to send to the view, ie.
public class DocumentViewModel{
public bool Photo {get;set;}
public bool BirthCertificate { get; set; }
public bool SchoolLeavingCertificate {get;set;}
}
and you'd have a controller that populates the viewmodel and calls the view
public ActionResult Documents()
{
var modelData = new DocumentViewModel();
//or retrieve from database at this point
// ie. modelData.Photo = some database value
return View(modelData);
}
[HttpPost]
public ActionResult Documents(DocumentViewModel documentsVM)
{
if (ModelState.IsValid)
{
//update the database record, save to database... (do stuff with documentsVM and the database)
return RedirectToAction("NextAction");
}
//else, if model is not valid redirect back to the view
return View(documentsVM);
}
look for tutorials out there on mvc basics. read code.

How to keep the same data when return to the view?

How to keep the same data when return to the view?
I tried to put return the form to the view, but it did not work.
Is there any good and simple way to do this?
[HttpPost]
public ActionResult Register(FormCollection form)
{
string name = form["Name"].Trim();
if (string.IsNullOrEmpty(name))
{
TempData["TempData"] = "Please provide your name ";
return View(form);
}
string email = form["Email"].Trim();
var isEmail = Regex.IsMatch(email, #"(\w+)#(\w+)\.(\w+)");
if (!isEmail)
{
TempData["TempData"] = "Sorry, your email is not correct.";
return View(form);
}
//do some things
}
Not sure why you would be using FormCollection in the post but maybe you come from a WebForms background. In MVC you should use ViewModels for the transport of your data to and from the Views.
By default the Register method in an MVC 3 app uses a ViewModel in the Register View. You should simply post it back. In fact, the default app has that already created for you if you didn't know as part of the Internet template.
The standard pattern is to have a ViewModel that represents your data that you will use in your View. For example, in your case:
public class RegisterViewModel {
[Required]
public string Name { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
[Display(Name = "Email address")]
public string Email { get; set; }
}
Your controller the should contain 2 actions, a Get and a Post. The Get renders the View and is ready for the user to enter data. upon submitting the View the Post action is then called. The View sends the ViewModel to the action and the method then takes action to validate and save the data.
If there is a validation error with the data, it's very simple to return the ViewModel back to the View and display the error messages.
Here is the Get action:
public ActionResult Register() {
var model = new RegisterViewModel();
return View(model);
}
And here is the Post action:
[HttpPost]
public ActionResult Register(RegisterViewModel model) {
if(ModelState.IsValid) { // this validates the data, if something was required, etc...
// save the data here
}
return View(model); // else, if the model had validation errors, this will re-render the same view with the original data
}
Your view would look something like this
#model RegisterViewModel
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<div class="editor-label">
#Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.Name) <br />
#Html.ValidationMessageFor(model => model.Name)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Email)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.Email) <br />
#Html.ValidationMessageFor(model => model.Email)
</div>
}
Using other strategies to capture and save data in an MVC app is absolutely possible, it's a very extensible framework. But there is a specific pattern that makes MVC what it is and working against that pattern can sometimes prove difficult. For a beginner it is best to understand the preferred patterns and strategies first and then once understood very well, you can then adopt some of your own custom strategies to meet your needs. By then you should understand the system well enough to know what you need to change and where.
Happy coding!!

ASP.NET MVC3 passing data between primary view and a search view via Ajax link

I have just started learning ASP.NET MVC3.
I have the following scenario. In a create view for a certain model the user can lookup code/description by clicking on a link (rendered with Html.ActionLink helpers). The lookup values are retrieved from lookup tables in a database and presented in a separate view. The two views are handled by two different controllers. When the user selects a lookup value in the latter view that value (code+description) should be copied back to the create view.
How can data be passed between the two views? Is this not possible due to the stateless nature of Http requests?
I tried that with an Ajax link, but it didn't worked out.
code snippet Create view:
<fieldset>
<legend>Z-Info</legend>
<div class="editor-label">
#Html.LabelFor(model => model.ZZL_U_CODE)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ZZL_U_CODE)
#Html.ValidationMessageFor(model => model.ZZL_U_CODE)
</div>
<div class="editor-label">
#Ajax.ActionLink("Land code test", "Index", "Domein", new {name = "lan" },
new AjaxOptions {
HttpMethod = "Get",
Url = Url.Action("Index", "Domein", new {name = "lan" }),
OnBegin = "OnBegin",
OnSuccess = "InsertCodeNaam",
OnFailure = "OnFailure",
OnComplete = "OnComplete"
})
</div>
When the user select a code/description the following Select action is called which returns Json data back.
Select action:
public class DomeinController : Controller
{
private ZZLEntities db = new ZZLEntities();
//
// GET: /Domein/
public ViewResult Index(string name)
{
DomeinViewModel model = DomeinRepositry.GetAll(name);
return View(model);
}
GET: /Domein/Select/5
public JsonResult Select(int id, string naam)
{
return Json(new DomCodeNaam { codeValue = id, naamValue = naam }, JsonRequestBehavior.AllowGet);
}
Are there other solutions possible? Can partial views be an option?
Well you have two options:
Just post back lookup values and then internally redirect to the
first ("create") view, but this time passing (internally) the values
chosen by user so the view can be rendered with chosen values. Maybe
not fancy but very easy to implement. You will loose data that user have already entered into first form though, unless you post it too or you make this a 2 step process.
If you want to use Ajax, you need update appropriate parts of the
form in the first "create" view on the client side, depending on the
actions of user (i.e. what lookup values they have chosen).
I am however a bit confused with what you exactly mean by "separate view"

My controller viewmodel isn't been populated with my dynamic views model

Im creating an application that allows me to record recipes. Im trying to create a view that allows me to add the basics of a recipe e.g. recipe name,date of recipe, temp cooked at & ingredients used.
I am creating a view that contains some jquery to load a partial view clientside.
On post im having a few troubles trying to get the values from the partial view that has been loaded using jquery.
A cut down version of my main view looks like (I initially want 1 partial view loaded)
<div id="ingredients">
#{ Html.RenderPartial("_AddIngredient", new IngredientViewModel()); }
</div>
<script type="text/javascript">
$(document).ready(function () {
var dest = $("#ingredients");
$("#add-ingredient").click(function () {
loadPartial();
});
function loadPartial() {
$.get("/Recipe/AddIngredient", {}, function (data) { $('#ingredients').append(data); }, "html");
};
});
</script>
My partial view looks like
<div class="ingredient-name">
#Html.LabelFor(x => Model.IngredientModel.IngredientName)
#Html.TextBoxFor(x => Model.IngredientModel.IngredientName)
</div>
<div class="ingredient-measurementamount">
#Html.LabelFor(x => Model.MeasurementAmount)
#Html.TextBoxFor(x => Model.MeasurementAmount)
</div>
<div class="ingredient-measurementtype">
#Html.LabelFor(x => Model.MeasurementType)
#Html.TextBoxFor(x => Model.MeasurementType)
</div>
Controller Post
[HttpPost]
public ActionResult Create(RecipeViewModel vm,IEnumerable<string>IngredientName, IEnumerable<string> MeasurementAmount, IEnumerable<string> MeasurementType)
{
Finally my viewmodel looks like
public class IngredientViewModel
{
public RecipeModel RecipeModel { get; set; }
public IEnumerable<IngredientModel> Ingredients { get; set; }
}
My controller is pretty ugly......im using Inumerble to get the values for MeasurementAmount & MeasurementType (IngredientName always returns null), Ideally I thought on the httppost Ingredients would be populated with all of the on I would be able Ingredients populated
What do I need to do to get the values from my partial view into my controller?
Why don't you take a look at the MVC Controlstoolkit
I think they would do what you want.
Without getting in too much detail. Can you change the public ActionResult Create to use FormCollection instead of a view model? This will allow you to see what data is coming through if any. It would help if you could post it then.
Your view model gets populated by using Binding - if you haven't read about it, it might be a good idea to do that. Finally I would consider wrapping your lists or enums into a single view model.
Possible Problem
The problem could lay with the fact that the new Partial you just rendered isn't correctly binded with your ViewModel that you post later on.
If you inspect the elements with firebug then the elements in the Partial should be named/Id'ed something like this: Ingredients[x].Property1,Ingredients[x].Property2 etc.
In your situation when you add a partial they are probably just called Property1,Property2.
Possible Solution
Give your properties in your partial the correct name that corresponds with your List of Ingredients. Something like this:
#Html.TextBox("Ingredients[x].Property1","")
Of, after rendering your partial just change all the names en ID's with jquery to the correct value.
It happens because fields' names from partial view do not fit in default ModelBinder convention. You should analyze what names fields have in your partial view.
Also you should implement correct way of binding collections to MVC controller. You could find example in Phil's Haack post
Assuming RecipeViewModel is the model being supplied to the partial view, try just accepting that back in your POST controller like this:
[HttpPost]
public ActionResult Create(RecipeViewModel vm)
{
//
}
You should get the model populated with all the values supplied in the form.

Adding profile fields to registration form asp.net mvc3

I am new to mvc and would like to add an additional field to my registration page that is simply a dropdownlist bound to a table in my model (a table of organization names and IDs). However, in my default application I see that the AccountController is using the RegisterModel model to create the register form view. This is fine, I don't want to disturb this. But I want to add a new select box on the page bound to a different model (my model with the organizations). How do I accomplish this?
I've found other posts that suggest I create a wrapper model for both my model and the RegisterModel, but this isn't working. My wrapper model looks like this:
public class RegisterPeopleModel
{
public RegisterModel reg { get; set; }
public fwfEntities fwf { get; set; }
}
And now the field validator for password is no longer working. The code in the view:
<div class="editor-field">
#Html.PasswordFor(m => m.reg.ConfirmPassword)
#Html.ValidationMessageFor(m => m.reg.ConfirmPassword)
</div>
Now renders this:
<div class="editor-field">
<input data-val="true" data-val-equalto="The password and confirmation password do not match." data-val-equalto-other="*.Password" id="reg_ConfirmPassword" name="reg.ConfirmPassword" type="password" />
<span class="field-validation-valid" data-valmsg-for="reg.ConfirmPassword" data-valmsg-replace="true"></span>
</div>
Notice that the IDs of the span and input no longer match. The form no longer works at all. This is leading me to believe I'm taking the wrong approach. Is there a better way of getting my select list on the page bound to a different model?
Thanks in advance.
Is there a better way of getting my select list on the page bound to a
different model?
If the model doesn't meet the requirements of the view you should modify this model. In ASP.NET MVC it is a good practice to design view models and pass only view models to views and not domain models (like for example EF autogenerated classes which is what this fwfEntities type seem to be). So design a view model which contains only the properties needed by your view and have your controller action query the database in order to fetch the model then map the model to a view model and finally pass this view model to the view.
Without seeing RegisterModel my guess is that ConfirmPassword has not got a required attribute for example;
[Required(ErrorMessage = "A password is required")]
public string ConfirmPassword { get; set; }
oh I believe the period in the id is replaced with an underscore so as not to cause issues with Jquery.

Resources