In my application I want to show user profile image in layout page (_LoginPartial).
In AspNet Identity membership their is a AspNerUser table . I want to customize this AspNerUser table to maintain image field.
then show that image in Layout page (_LoginPartial) view.
How can I do this ? Really appreciate can suggest a way to do this
EDIT
I generated my DataBaseContext name using ADO.NET entity model , database Context name is ProjectNameEntities
then I tried to enable migration using following command on PMC
Enable-Migrations -ContextTypeName myProject.Models.ProjectNameEntities
but then I'm getting following error
Creating a DbModelBuilder or writing the EDMX from a DbContext created
using Database First or Model First is not supported. EDMX can only be
obtained from a Code First DbContext created without using an existing
DbCompiledModel.
is this possible to do with edmx model ?
Add Field Model (Code First)
First thing you need to do is modify the ApplicationUser Model that the database table is built from. This class is usually located in the IdentityModels.cs file. Add a new field to hold the image:
public class ApplicationUser : IdentityUser
{
// maybe be other code in this model
public byte[] ProfilePicture { get; set; }
}
Next you need to update your database to reflect the changes (assuming you are using Code First). You can find detailed information on the process in this article.
Enable-Migration
Add-Migration "Added user profile"
Update-Database (will apply any pending migrations to the database)
Return profile picture
Now add an action to a controller similar to:
public FileContentResult Photo(string userId)
{
// get EF Database (maybe different way in your applicaiton)
var db = HttpContext.GetOwinContext().Get<ApplicationDbContext>();
// find the user. I am skipping validations and other checks.
var user = db.Users.Where(x => x.Id == userId).FirstOrDefault();
return new FileContentResult(user.ProfilePicture, "image/jpeg");
}
Finally in your _LoginPartial add the following call to the Action we just created where ever you want the image to show up. You will need to change the Controller name to what ever controller you put your action on.
<img src="#Url.Action("Photo", "Account" , new { UserId=User.Identity.GetUserId() })" />
Save profile picture
First you need to create a page to upload the image. Create an action to return the form:
[HttpGet]
public ActionResult Profile()
{
ViewBag.Message = "Update your profile";
return View();
}
The Razor view would be called Profile.cshtml and look have a form on it that looks like: (note that the Action and controller location may be different for you depending on how you structure your project)
#using (Html.BeginForm("Profile", "Manage", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<fieldset>
<legend>Photo</legend>
<div class="editor-label">
<label for="profile">FileName:</label>
</div>
<div class="editor-field">
<input name="Profile" id="profile" type="file" />
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
The form will post back to an action so you need to create one that looks like:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Profile(HttpPostedFileBase Profile)
{
// get EF Database (maybe different way in your applicaiton)
var db = HttpContext.GetOwinContext().Get<ApplicationDbContext>();
// find the user. I am skipping validations and other checks.
var userid = User.Identity.GetUserId();
var user = db.Users.Where(x => x.Id == userid).FirstOrDefault();
// convert image stream to byte array
byte[] image = new byte[Profile.ContentLength];
Profile.InputStream.Read(image, 0, Convert.ToInt32(Profile.ContentLength));
user.ProfilePicture = image;
// save changes to database
db.SaveChanges();
return RedirectToAction("Index", "Home");
}
Note that there needs to be validations and checks put in place according to your rules but this is the basic idea on how it works.
Created a GitHub project that shows the basics above in a working sample: https://github.com/jsturtevant/mvc-aspnet-identity2-profile-picture-sample
Related
This is maybe a little bit of an open ended question.
Basically, I have an MVC project, people can log in and create a profile. If they are logged in and on their profile page they display Edit buttons to enable them to be able to edit sections of their profile.
This is done via an attribute in the View Model that checks if the profile username matches the logged in username.
What I'm having a little trouble with is when posting back to update details via ajax, how can I ensure the user is updating his profile and no one else's. I obviously want to make sure no one can hack the site and update someone else's profile.
Do I just have a check in the action that checks if the logged in user matches the profile they are on... if it doesn't then return a 401? Is there a sexier way of doing this? Custom Attribute etc...?
First of all, you should use the AuthorizeAttribute (or a subclass of it) to protect your controller actions from unauthorized access.
[HttpPost]
[Authorize]
public ActionResult Edit(Model model)
{
// edit model here
}
Secondly, to ensure that the user doesn't update somebody else's profile, you should design your action method to use the current user instead of putting it into the URL.
[HttpPost]
[Authorize]
public ActionResult Edit(Model model)
{
// Only allow the logged in user to edit their own profile
// TODO: Update the user's database record by using the user variable as the key.
var user = User.Identity.Name;
// edit model here
}
Third, you should use the ValidateAntiForgeryToken in conjunction with the AntiForgeryToken HTML helper to ensure your pages are posted to the server by the same browser that requested them.
[HttpPost]
[Authorize]
[ValidateAntiForgeryToken]
public ActionResult Edit(Model model)
{
// Only allow the logged in user to edit their own profile
// TODO: Update the user's database record by using the user variable as the key.
var user = User.Identity.Name;
// edit model here
}
#using (Html.BeginForm("Edit", "Account")) {
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<fieldset>
<legend>Change Password Form</legend>
<ol>
<li>
#Html.LabelFor(m => m.OldPassword)
#Html.PasswordFor(m => m.OldPassword)
</li>
<li>
#Html.LabelFor(m => m.NewPassword)
#Html.PasswordFor(m => m.NewPassword)
</li>
<li>
#Html.LabelFor(m => m.ConfirmPassword)
#Html.PasswordFor(m => m.ConfirmPassword)
</li>
</ol>
<input type="submit" value="Change password" />
</fieldset>
}
Another thing you could do is to add a second identifier to the user table that is not available outside of the application and require that any post that edits data have a one-way hash of this second identifier in it or it will be rejected. This identifier should of course be unique per user (best to do a Guid) and then use a one-way hash algorithm followed by URL encoding.
/Account/Edit/?hash=AKXHAonyrOtruBO%2FVI%2FGr%2FM%2B4ZadjrS3YRt21ILSLntssu23l%2FN6hpUilZM8Hkgn%2Bg%3D%3D
[HttpPost]
[Authorize]
[ValidateAntiForgeryToken]
public ActionResult Edit(Model model, string hash)
{
// Only allow the logged in user to edit their own profile
// TODO: Update the user's database record by using the user variable as the key.
var user = User.Identity.Name;
// After looking up the user, hash the hidden ID field using the same algorithm
// and ensure that the hashes match. If not, throw a 401 error.
// edit model here
}
This may help if you have a record with a sequential ID in the URL and you don't want the user to just change the ID to some other value and post it.
It helps to analyze the Account controller that is generated when you make a brand new MVC project from the VS template to see how it is put together.
I am a newbie and is making web application in Visual Studio 2010 using MVC2 + Entity framework.
I have a situation in which I want to put both operations i.e create user / update user at same view, I have also tried attaching relevant picture where I have made two portions one for create user and second for manage users.
My 'create user' fields are at top of website and when user click 'create button' page got refreshed and all enlisted users gets displayed on same view under second portion 'manage users' showing link to edit/delete them.
I want that when I click on edit link, that particular entity fields get populated on same view in first portion 'create user' where I can modify them and press 'update button'
VIEW
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Admin.Master" Inherits="System.Web.Mvc.ViewPage" %>
<%# Import Namespace="MyNamespace" %>
<h4>Create New User</h4>
<form method="post" action="/Lignum/CUser">
<label for="inputEmail3">Full Name</label>
<input type="text" name="Fullname" id="txtFullname" >
<label for="inputEmail3">Email</label>
<input type="email" name="Email" id="Email1">
<button id="btnCUser" class="btn btn-primary">Create</button>
</form>
<h4>Manage Users</h4>
<table>
<tr>
<td>Sr#</td><td>Name</td><td>Email</td><td></td>
</tr>
<% int i=0;
foreach (MyWebsite.Models.User objUser in ViewData.Model as IEnumerable<MyWebsite.Models.User>)
{%>
<tr>
<td><%= ++i%></td>
<td><%= objUser.Fullname%></td>
<td><%= objUser.Email%></td>
<td>
Edit
</td>
</tr>
<%}%>
</table>
CONTROLLER
public ActionResult Index()
{
return View("UserMgt", _repositoryUser.SelectAll());
}
public ActionResult Edit(object Id)
{
if (Id != null && Id.ToString().Trim().Length > 0)
{
int param = int.Parse(Id.ToString());
return View("UserMgt", _repositoryUser.SelectByID(Id));
}
return View("404");
}
You will need to make use of JQuery & Ajax to achieve this. Your page is getting refreshed most likely because your are submitting a form. Instead of form submit, you need to attach a function to handle onclick event.
In that function you will know which item is clicked, load the data to be edited from the server sending an ajax request with item id.
When request return you can then open a JQuery popup window or update page's html to display data. User will be allowed to make changes and on Ok button click you can again send the data back to server to save.
I am looking for an example online to refer to you as my code is little complex. You can also look for an example online.
UPDATE:
i want that when i click on edit link, that particular entity fields
get populated on same view in 1st portion 'create user' where i can
modify them and press 'update button'
Ok, looked at your code. As I said earlier you will need to define an "id" for each html element, the value will be objUser.UserId (you can prefix something if you want). Now define a click event for all html elements i.e. .
For a working example refer this link.
I suggest you progress as you gain some insight and post updated code. We will suggest what's needed for next step. This way you would learn more.
You can try following:
Create a View model with whatever you need on the create page i.e. user details
strongly bind your view with this view model
Have three action methods in controller "Create","Populate" and "Update" with Update and Populate taking Id of the entity as input (you can choose better names)
Initially call Create method which will just return an empty view model with your View
Have a hidden variable in view which will store the Id of the entity (in case of create this will be zero)
on click of create just take the value of this hidden variable and do a post to Update action method.In this case if it is new entity id will be zero
On click of edit go call Populate method with id of the entity which again will return ViewModel with entity details loaded to the same create view (also set the hidden variable with id)
In your update method based on the id perform create or update operation i.e. Create for zero and Update for 1
If you post your code or other details I can give some more details using code.
EDIT: OK few more details in terms of code.
//This is the view model you need to bind to your view
public class UserViewModel
{
public int UserId { get; set; }
public string Email { get; set; }
public string FullName { get; set; }
public List<Users> UserList {get;set;} //For binding to the grid
}
Below are the action methods in controller.
public ActionResult Create()
{
var viewModel = new UserViewModel();
//Logic: Create empty view model for create
return View("UserMgt", viewModel);
}
public ActionResult Edit(int id)
{
var viewModel = new UserViewModel();
//Logic: populate the view model based on the id
return View("UserMgt", viewModel);
}
// Call this method using Jquery ajax
public bool Update(UserViewModel user)
{
if (user.Id == 0)
//Logic : Create the user
else
//Logic : Edit the user
return Json(status); //Status = true if successful else false
}
Initially call create.On click of edit call Edit method.On click of save call Update.
For using jquery ajax follow below link
http://api.jquery.com/jquery.ajax/
Im kinda new in MVC4 and im not able to figure it out.
"CustomViewMOdel" "CustomViewMOdel"
"ControllerX" ----------------> "VIEW" -----------------> "ControllerY"
My problem is that i want to pass my customviewmodel to view (which is working just fine!). In the View im showing some of model's fields to users (which is working fine also). BUT Now i want user, to change ONE field of the models fields and then PASS the WHOLE model to Controller X (with all fields filled, including the field what user was able to change AND other fields what were just shown)
Can anyone give a very simple code example of how to do this?
You can just create a form that posts to another controller:
ControllerX:
public ActionResult DoSomething()
{
return View(new CustomVM());
}
ViewA
#Model CustomViewModel
#using Html.BeginForm("DoSomethingElse", "ControllerY")
{
#Html.EditorFor(vm => vm.SomeProperty)
<input type="submit" value="OK" />
}
ControllerY
public ActionResult DoSomethingElse(CustomViewModel vm)
{
// do something else
}
You can use #Html.HiddenFor(o => o.Property) on the form.
This will not show a property on it.
But the advanced user may change the property through a development console. So you should check all the changes in the ControllerY
Example:
#Html.HiddenFor(o => o.Id)
#Html.HiddenFor(o => o.Name)
#Html.EditorFor(o => o.Description)
<input type="submit" value="OK" />
This will only let the user change a description but still have "id" and "name" on the FormCollection.
I'm creating some user profile edit forms in MVC4 at the moment and for testing I was rendering the UserId property into a readonly textbox on the form like this:
<li>
#Html.LabelFor(model => model.UserId)
#Html.TextBoxFor(model => model.UserId, new { #readonly="readonly"})
</li>
As I'm nearing completion of the edit form I removed this textbox as it's just using up real estate. Once I had done this the model sent back to the controller when saving had the integer default value of 0 and then the Entity Framework blows up as it cannot update any rows. So I added this to the form:
<li>
#Html.HiddenFor(model => model.UserId, new { #readonly="readonly"})
</li>
Is this a safe move? Should I be using the ViewBag for things like this? On the profile details page I render an edit button like this:
#Html.ActionLink("Edit", "Edit", new { id=Model.UserId })
Meaning that the UserId is rendered in the link. Is this safe and secure or do I need to rethink how I move the models and ids around the UI?
TIA,
Is this a safe move?
This will do the job of sending the id to the server. Just get rid of the readonly="readonly" attribute which makes very little sense for a hidden input.
Should I be using the ViewBag for things like this?
This doesn't change anything in terms of security. Any user could still put whatever id he wants. Whether you are using a hidden field or an ActionLink you are still sending the id as plain text to the server and anyone could forge a request and put whatever id he wants. So if you site uses some form of authentication you must absolutely check on the server side that the id that you received actually is a resource that belongs to the currently authenticated user before attempting to perform any actions on it. Otherwise some authenticated user could supply the id of a resource that belongs to another user and be able to update it. Of course that's just a hypothetical scenario, it's not clear at all if this is your case and whether this id needs to be secured.
If UserId is sensitive, then there are other options
Keep UserId server side only with Session state (if your architecture allows for Session)
Put it in an encrypted cookie. Note as per Darin, that these can be compromised.
If it isn't sensitive, then your HiddenFor is fine - post it back with the rest of the form.
Don't put it in your ActionLink Querystring unless this is part of your route (i.e. /Controller/Action/id)
I would strongly suggest using ValueInjecter. Here is a code snippet doing the same thing
[HttpGet]
public new ActionResult Profile()
{
var model = new ProfileModel();
model.InjectFrom<UnflatLoopValueInjection>(this.GetCurrentUser());
return View(model);
}
[HttpPost]
public new ActionResult Profile(ProfileModel model)
{
if (ModelState.IsValid)
{
this.GetCurrentUser().InjectFrom<UnflatLoopValueInjection>(model);
try
{
_userService.SaveOrUpdate(this.GetCurrentUser());
TempData["Success"] = "User was successfully updated.";
return RedirectToAction("Profile");
}
catch (Exception)
{
ModelState.AddModelError("Exception", "Unexpected error");
}
}
return View(model);
}
And here is the view...
#using (Html.BeginForm("Profile", "Account", FormMethod.Post, new { #class = "form-horizontal" }))
{
#Html.ValidationSummary(true, "Unable to update profile. Please correct the errors and try again.", new { #class = "alert alert-block alert-error" })
#Html.EditorForModel()
<div class="form-actions">
<input type="submit" value="Update" class="btn btn-primary" />
</div>
}
I am playing with the Scaffolding that is in Asp.net mvc
I have a property on my view model for countries
public IEnumerable<SelectListItem> Countries { get; set; }
Yet, when I create a view and specify the viewmodel it doesn't scaffold a drop down list as I expected it to. In fact it get completely ignored.
I have compiled the project before doing it
I also tried adding a property like this
public int CountryId { get; set; }
As this article suggested there is a convention at work
http://blog.stevensanderson.com/2011/01/28/mvcscaffolding-one-to-many-relationships/
I am using the Add view option you have when right clicking in a controller action method
Any ideas what is wrong?
In my current project, i faced this problem and couldn't find a quick way to scaffold the Dropdown list of a one-many relation inside one of my Entities.
What I ended up doing was like the following:
1- Create the normal AddView=>Create way.
2- If i had a ID property in my model class, the defaul;t template will generate something like this to represent this property in my view:
<div class="editor-label">
#Html.LabelFor(model => model.CityID)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.CityID)
#Html.ValidationMessageFor(model => model.CityID)
</div>
3- Now I need to replace this default template with a working one, so I wrote this code in the CREATE method:
IEnumerable<SelectListItem> cityItems = dataContext.Cities.AsEnumerable().Select(c => new SelectListItem()
{
Text = c.Name,
Value = c.ID.ToString(),
Selected = false,
});
SelectList cityList = new SelectList(cityItems, "Value", "Text");
ViewBag.CityList = cityList;
this will fetch the Cities table and create a Selectlist that i can pass to my view and work with it to provide the DrobDown with it's items.
4- replace the default code in my View by one like the following:
<div class="Post-label">
<div class="editor-label">
#Html.LabelFor(model => model.CityID)
</div>
<div class="editor-field">
#Html.DropDownListFor(m => m.CityID, ViewData["CityList"] as SelectList)
#Html.ValidationMessageFor(model => model.CityID)
</div>
</div>
The reason I've used ViewData["CityList"] instead of ViewBag.CityList is that this one worked but the other not.
5- now my view is working find and is fetching the City data just like what I expected, and using the same model inside my Edit view resulted in a working one too.
Give it a try and let me know what happened, Thanks.
I have noticed that for a given model, the "Create" scaffold-generated code when creating a new controller is different than if you right-click in an existing controller and say "Add View" and choose the "Create" scaffolding template. In the first case, given you have the correct properties on the child class
public Country Country {get;set;}
public int CountryID {get;set;}
then this case (adding controller with MVC scaffolding read/write and appropriate Model class) WILL generate a #Html.DropDownList for the parent relationship, whereas right-clicking within the controller Create method will not scaffold the drop-down but will instead create an #Html.EditorFor for the relationship.
So the answer if you want scaffolding code to generate the drop-down may be to delete and re-create your controller if possible, otherwise manually add in the appropriate code.
In order to have the option to choose a country with a dropdown the property in your Model should be:
public Country Country{ get; set; } Navigation property used by EF, doesn't involve the database
with
public Country CountryId{ get; set; }
Create the foreign key on the Person table
Each instance/record of a person is associated with a country: the relation is defined with the navigation property via code and with the CountryID for the database.
The scaffholding template will then generate the edit/create methods and views using :
ViewBag.PossibleCountries
Here's a similar scenario.