display selected file after view reload in ASP.NET MVC 5 - asp.net-mvc-5

I am working on a project in ASP.NET MVC 5, there is a requirement to upload image file, I can upload the image to the server, what I am facing is when there is an error in the view submitted I return the view model with some error message to be displayed on the view so that the user is not requested to select the image again, all the form fields data in the view are getting populated except the file which previously selected is not. Please help me the way to achieve this.
Here is the code I have to select the image
#Html.TextBoxFor(model => Model.Logo, null, new { type = "file"})
here is the model field
[DataType(DataType.Upload)]
public HttpPostedFileBase Logo
{
get;
set;
}

For security reasons, you cannot set the value of a file input in the controller, so if you want to avoid having the user select the file again if you return the view, you must first save the file (it could be to a temporary location) and display an indicator to the user that the file has been uploaded. For example, your model might contain the following properties
public HttpPostedFileBase Logo { get; set; }
public string LogoFileName { get; set; }
public string LogoPath { get; set; }
and in the controller method, save the file and set the FileName (i.e. model.LogoFileName = Logo.FileName;) and Path properties. Then in the view, add a conditional statement to render the FileName if it exists, or display the file input if it does not, for example
if(Model.LogoFileName == null)
{
#Html.TextBoxFor(m => m.Logo, new { type = "file" })
}
else
{
#DisplayFor(m => m.LogoFileName)
}

Related

Creating A Per-View Resource File In MVC

I am converting a localized WebForms application to MVC5. Most of the examples I have seen for MVC have a single resource - resx - file (per language) for the entire application.
Is it possible, to have a separate file for each view? If so, is there an example of how to reference the said file?
UPDATE: I would like to leave the resource files uncompiled if possible. This would allow us to edit the RESX files on the fly without having to recompile the site everytime.
Below is the procedure I had in WebForms. I am essentially trying to reproduce this in MVC5.
public string LocalizeText(Page CurrentPage, string resourceKey)
{
string localizedText = string.Empty;
// LOOK FOR LOCALIZED TEXT
String filePath = string.Empty;
if (!String.IsNullOrEmpty(CurrentPage.Request.GetFriendlyUrlFileVirtualPath()))
{
filePath = CurrentPage.Request.GetFriendlyUrlFileVirtualPath(); // FOR FRIENDLY URLS
}
else
{
filePath = CurrentPage.Request.CurrentExecutionFilePath; // FOR "UNFRIENDLY" URLS (THOSE WITH A FILE EXTENSION VISIBLE)
}
try
{
localizedText = Convert.ToString(HttpContext.GetLocalResourceObject(filePath, resourceKey, System.Globalization.CultureInfo.CurrentCulture)).Trim();
}
catch (Exception ex)
{
HttpContext.Current.Response.Write(ex.ToString() + "<br />" + filePath);
}
return localizedText;
}
The resource files would be located in the App_LocalResources folder.
Yes, it is possible to have a separate resource file for a view. As a really simple, and pretty dull example (sorry about that :-)), consider the following view model:
using _31662592.Resources;
using System.ComponentModel.DataAnnotations;
public class HomeViewModel
{
[Display(Name = "WelcomeHeader", ResourceType = typeof(HomeResources))]
public string WelcomeHeader { get; set; }
[Display(Name = "WelcomeMessage", ResourceType = typeof(HomeResources))]
public string WelcomeMessage { get; set; }
}
Here, I'm making use of the Display attribute, which has support for localisation. So in my case, the resource file I created looks like this:
As you can see, the ResourceType property corresponds with the type of your resource file (i.e. HomeResources in my case), and the Name property corresponds with the name of the string in the resource file which you wish to bind the property to.
Nothing fancy in the action:
public ActionResult Index()
{
return View(new HomeViewModel());
}
The view is very simple too:
#model _31662592.Models.HomeViewModel
<h1>#Html.DisplayNameFor(m => m.WelcomeHeader)</h1>
<p>#Html.DisplayNameFor(m => m.WelcomeMessage)</p>
You can even use the resources inline in your views, such as:
#using _31662592.Resources
<h1>#HomeResources.WelcomeHeader</h1>
<p>#HomeResources.WelcomeMessage</p>
In case you have any problems, you should make sure that:
The Build Action for the resource is set to "Embedded Resource".
The Custom Tool for the resource is set to "PublicResXFileCodeGenerator".
Both of these options can be set by right-clicking on the resource file and selecting Properties. Those options will then be shown in the properties dockable window. Finally, if you wish to reference the resource file from another project, make sure it's set to Public rather than internal, which you can set by double-clicking the resource file to open it as normal, then changing its access modifier from internal to public.

Can't seem to expand an Image Navigation property with Breeze JS

I am using MVC, Entity Framework, Durandal and Breeze JS. I've got a user which looks like such (simplified):
public class User : EntityBase<Guid>, IAggregateRoot
{
public Guid Id { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[ForeignKey("UserImage")]
public virtual Guid? ImageId { get; set; }
public virtual UserImage UserImage { get; set; }
}
The UserImage class looks like such. I know I should limit the size of the Image. (Maybe this is the issue?):
public class UserImage
{
public Guid Id { get; set; }
[MaxLength]
public byte[] Image { get; set; }
}
I've got an api function on the server to get the current user:
public IQueryable<User> GetCurrentUser()
{
IPrincipal principal = HttpContext.Current.User;
var users = _uow.Users.FindBy(u => u.UserName.Equals(principal.Identity.Name));
if (!users.Any())
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Unauthorized));
}
return users;
}
And two calls on the client which get the current user. The first is in the shell:
function loadCurrentUser() {
return uow.CurrentUser.all().then(function (newUser) {
log('Welcome to the Site ' + newUser[0].FullName() + '!', newUser[0], true);
config.CurrentUser(newUser[0]);
return true;
});
}
The second is in a ManageUser viewmodel:
function activate() {
return uow.CurrentUser.all(['UserImage']).then(function (user) {
self.CurrentUser(user[0]);
return $.when(init()).then(boot());
}).fail(function() {
return router.activate('accounts/login');
});
}
Now I can load an Image into the ManageUser page and save and in fiddler it shows that the ImageId and Image are being sent across to the server. Then I checked the BeforeSaveEntity intercept and shows two entities being saved.
Updated User with ImageId set
New UserImage
The data is also visible in the database. Now when I refresh the Manage User page I can see the two GetCurrentUser calls in fiddler.
From the shell call I can see that the User is being returned and an ImageId is set but no UserImage was sent over because didn't expand the query.
From the Manage User call I see the User is returned but only the ImageId is sent over and the Image object was OMITTED from the JSON.
Has anyone come across this issue with images? All my other expands appear to be working correctly. Does anyone have any examples on using breeze to save just the filepath to the image and possibly using windows azure for media storage?
I know this probably won't answer your question but I would propose not sending the byte array to the client and rather have an Image Handler on the server side that takes an ImageId as a parameter and then return the image with the relevant Content Type set. An example of this can be found here.
By using this approach you could reference your image from HTML using an img tag with the source set to the Image Hander with the relevant ImageId.
An example using knockout for data binding would be:
<a data-bind="attr: {href: '/Image/' + User.ImageId()}"></a>
This approach enables you to easily add caching on both the server and client which will improve performance. It also removed the need to convert the byte array to an image on the client side, which may or may not be a pain.
Edit:
When saving the managed user, post the Image to an Upload action on the ImageHandler (have a look at this article). This action must return the new Id of the image. After you've received the new Id, update the User.ImageId on client side and call SaveChanges on breeze.

ContentManager.Create does nothing

I am trying to build a service in Orchard that allows me to create content through a custom form on a page. The service and the content type definitions look fine to me, but somehow, eventhough I don't get any errors or other signs in the Orchard log files, creating new content using the IContentManager does nothing for me.
Parts involved
The controller accepting the form values
[HttpPost]
public ActionResult Create(CreateSopViewModel viewModel)
{
if(!ModelState.IsValid)
{
var shape = _shape.CreateContent();
shape.Header = _shape.Parts_Title(Title: "New item");
// Add the original fields to the shape.
shape.Title = viewModel.Title;
shape.Description = viewModel.Description;
shape.InitialComments = viewModel.InitialComments;
return new ShapeResult(this, shape);
}
// Store the new procedure in the database
_service.CreateContentItem(
viewModel.Title,viewModel.Description,viewModel.InitialComments);
// Redirect the user back to the homepage.
return Redirect("~/");
}
The service that contains the CreateContentItem method:
public void CreateContentItem(string title, string description, string initialComments)
{
// Initialize a new content item based on the SOP type
var customPart = _services.ContentManager.New<MyCustomPart>("CustomContentType");
customPart.Description = description;
customPart.Identifier = BuildIdentifier(title);
customPart.ContentItem.As<TitlePart>().Title = title;
_services.ContentManager.Create(customPart.ContentItem);
}
The content part + record
public class MyCustomPart: ContentPart<MyCustomPartRecord>
{
[Required]
public string Identifier
{
get { return Record.Identifier; }
set { Record.Identifier = value; }
}
[Required]
public string Description
{
get { return Record.Description; }
set { Record.Description = value; }
}
}
public class MyCustomPartRecord: ContentPartRecord
{
public virtual string Identifier { get; set; }
public virtual string Description { get; set; }
}
The migration
SchemaBuilder.CreateTable(typeof(MyCustomPartRecord).Name, table => table
.ContentPartRecord()
.Column<string>("Description")
.Column<string>("Identifier"));
ContentDefinitionManager.AlterPartDefinition("StandardOperationalProcedurePart", builder => builder
.Attachable(true));
ContentDefinitionManager.AlterTypeDefinition("CustomContentType", builder => builder
.DisplayedAs("Custom Content Type")
.WithPart("TitlePart")
.WithPart("MyCustomPart")
.Creatable(true));
Question
Again, I don't get any errors, not in the log and not in Visual Studio. However, my new content item doesn't get created or at least, I can't see it in the admin section of the site under Content.
What is going on and how can I debug this behavior?
I had a similar problem, which was solved when I used the overloaded Create method taking a VersionOptions enum value:
content.Create(customPart.ContentItem, VersionOptions.Published);
This should work even if the content item is not creatable, as mine isn't.
I had a similar issue. In my case the item did appear eventually, but not right away.
The solution for me was to do:
_contentManager.Flush();
I was having this issue, in my case it was that I actually had an error in the database (trying to put 100+ characters into a field that would only hold 100!).
I found the error I was getting (null id in Orchard.Indexing.Models.IndexingTaskRecord entry (don't flush the Session after an exception occurs) ), actually masked the issue. I had to go hunt in the logs to find the real problem.
So anyway, my advice is if you see that contentmanager.create seems to be doing nothing, and any errors don't seem to help, check the logs carefully. They can be found in the logs sub-folder of the appdata folder in the main Orchard.Web project. Because as I've found in the last 48 hours, often the answer is there.

Orchard CMS, Merging templates

I am developing a Widget to show Content pushes on the home page. The push model is as below.
public class PushRecord : ContentPartRecord
{
public virtual string Header { get; set; }
public virtual string Text { get; set; }
public virtual string Url { get; set; }
}
On the admin, I modified the ContentType of the Push Widget to add Media Picker Field. I would like to make a hyper link around the image with Url provided by PushPart. Npw the widget is rendered by two templates, Parts.Push.cshtml and Fields.MediaPicker-PushWidget-Image.cshtml. How do I merge these two and make my Push rendering possible? Any help is greatly appreciated.
Maybe try suppressing display of mediapickerfield via placement.info, and then explicitly render the image with the hyperlink from the .cshtml of the PushPart. You can access the MediaPickerField url like this:
#{
var pushPart = Model.ContentPart;
var photoUrl = pushPart.MediaPickerFieldName.Url;
}
<img src="#photoUrl" ... />

ASP.NET MVC 4 Remember page index

In a ASP.NET MVC 4 app I have a view with a paged list (just a simple table, no telerik grid or anything like that). New values are fetched from the database when the user pages through the list.
On every row in that table there is an edit button, when clicking the button you are presented with an edit view and when you click save in that view you are redirected back to the view with the paged list.
The urls for the list view looks like this
http://localhost/Items/Page/1
http://localhost/Items/Page/2
The route looks like this
routes.MapRoute(
name: "ItemsList",
url :"Items/Page/{page}",
defaults: new { controller = "Items", action = "Index", page = 1 },
constraints: new {page = #"\d+"}
);
My question is this: what is the preferred, most common way to store away the referring url, so when done editing an item, I can redirect the user back to the correct url
http://localhost/Items/Page/2
and not just to
http://localhost/Items
I've tried splitting up
Request.UrlReferrer.PathAndQuery
and storing those values around, and then build the url from those values but I have a feeling there is a much better solution to this problem. Any suggestions?
Update
Right now I'm thinking that I could put the UrlReferrer.PathAndQuery (if there are any values) as a property on the view model for the edit screen and then use that when deciding on where to redirect after a save.
Any thoughts out there on that approach?
Here is my final solution to the problem, it's not super elegant but it works.
I added a property to the View model that could store the url. That value get's stored in a hidden field.
public class SkillEditModel
{
public int Id { get; set; }
public string Name { get; set; }
public string RedirectBackToUrl { get; set; }
}
In the controller Edit(GET) method I store the value with the view model
if (!Request.UrlReferrer == null)
{
model.RedirectBackToUrl = Request.UrlReferrer.PathAndQuery;
}
And finally after saving the changes in Edit (POST) I did this
if (!string.IsNullOrWhiteSpace(model.RedirectBackToUrl))
{
return new RedirectResult(model.RedirectBackToUrl);
}
return RedirectToAction("Index");

Resources