Asp.net MVC Text Editor pass long html code to controller - c#

I wants to make a text editor for a site to allow user to write his own text or past text from another source to create pages (like wordpress for example).
To do that i chose Jquery-Te as text editor.
My problem is that when i copy/paste a text from wikipedia for example, i got a 404.15 error. I have read that i can do some changes on the RequestLimit but i can't know how long user text can be. I can set a limit to 400000 but if user enter 500000 he will get an error. Is there another way to allow user to pass a lot of text?
My second problem is Jquery-Te generates html and my text is in fact an html code. Sometimes i cant get error like "A potentially dangerous Request.Form value was detected from the client".
Can someone help me to do what i want?
I give you my code :
View :
<form action="SaveArticle" method="post">
#Html.TextBoxFor(x => x.Article.Titre)
#Html.TextBoxFor(x => x.Article.Contenu, new { #class = "editor" })
<button type="submit" class="btn btn-info btn-circle">
<i class="fa fa-search"></i>
</button>
</form>
<script>
$(".editor").jqte();
</script>
Controller :
public ActionResult GestionDesPages(GestionDesPagesViewModel gdpvm)
{
return View(gdpvm);
}
[HttpPost]
public ActionResult SaveArticle(GestionDesPagesViewModel gdpvm)
{
Articles article = gdpvm.Article;
article.Date = DateTime.Now;
article.Auteur = "Pascal";
db.Articles.Add(article);
db.SaveChanges();
return View("GestionDesPages");
}
ViewModel :
public class GestionDesPagesViewModel
{
public Articles Article{get; set;}
}
Model :
public partial class Articles
{
public int Id { get; set; }
public string Titre { get; set; }
public string Contenu { get; set; }
public string Auteur { get; set; }
public Nullable<System.DateTime> Date { get; set; }
public Nullable<bool> Actif { get; set; }
}
Sorry For Bad English.
Thanks.
EDIT :
Thank You Nava.
The solution given by Nava helped me to resolve all my problems.
I didn't said that i uses EntityFramework. To add [AllowHttp], I used the procedure defined in this theAdd annotation to Entity Framework Generated class

To allow HTML to be inputted in your form and not get the "potentially dangerous" message add the [AllowHtml] attribute to Contenu
[AllowHtml]
public string Contenu { get; set; }
Keep in mind that when you display that field, you will probably want to Html encode to prevent a Cross site scripting problem

Related

Why can I not submit a form with a navigation property with Entity Framework?

I am learning C# and I have been running into a lot of issues with my first project around navigation properties. The main hurdle I am having an issue with is a basic four property model (and anything that includes a navigation property).
Here is the one I am currently working with:
Company Model:
public class Company
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
[Display(Name = "Id")]
public int Id { get; set; }
[Display(Name = "Name")]
public string Name { get; set; }
public CompanyType Type { get; set; }
public ICollection<Contact>? Contacts { get; set; }
}
Company Type Model:
public class CompanyType
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
[Display(Name = "Id")]
public int Id { get; set; }
[Display(Name = "Company Type")]
public string Type { get; set; } = "";
}
And here is my context - just simple.
public class SRMContext : DbContext
{
public DbSet<Company> Companies { get; set; }
public DbSet<Contact> Contacts { get; set; }
public DbSet<CompanyType> CompanyTypes { get; set; }
public SRMContext (DbContextOptions<SRMContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Company>().ToTable("Companies");
modelBuilder.Entity<Contact>().ToTable("Contacts");
modelBuilder.Entity<CompanyType>().ToTable("CompanyTypes");
}
}
*I removed irrelevant models in the context.
I first used scaffolding to create the CRUD razor pages for Company. It would not include any navigation properties in it at all - for any model. Not sure if I am doing something wrong, or if this is standard. So I am trying to manually update the scaffolded pages to include CompanyType. I was able to generate a select list, and provide that to the UI. It gives me the options and I click submit. But when I click submit, it says I need to include the Company Type. If I change it to not required, it will submit, but there will be no company type listed.
I know you can just do the Id without doing the navigation property, but further in my code, I have many-to-many navigation properties, so the Id will not work for them. I am having the same issues with them too, but this one is much simpler to work with until I get this figured out.
What am I doing wrong?
public class CreateModel : PageModel
{
private readonly SRMContext _context;
public SelectList CompanyTypes { get; set; }
public CreateModel(SRMContext context)
{
_context = context;
}
public async Task<IActionResult> OnGetAsync()
{
CompanyTypes = new SelectList(_context.CompanyTypes, nameof(CompanyType.Id), nameof(CompanyType.Type));
return Page();
}
[BindProperty]
public Company Company { get; set; }
// To protect from overposting attacks, see https://aka.ms/RazorPagesCRUD
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Companies.Add(Company);
await _context.SaveChangesAsync();
return RedirectToPage("./Index");
}
}
View Page
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Company.Name" class="control-label"></label>
<input asp-for="Company.Name" class="form-control" />
<span asp-validation-for="Company.Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Company.Type" class="control-label"></label>
<select asp-for="Company.Type" asp-items="Model.CompanyTypes" class="form-control"></select>
<span asp-validation-for="Company.Type" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
(I'm sorry, I know its a lot of code, but I want to show everything needed!)
Complex objects are not recognized on a form post, so the type is probably null. Instead, post the foreign key of the type and it should be recognized:
public class Company
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
[Display(Name = "Id")]
public int Id { get; set; }
[Display(Name = "Name")]
public string Name { get; set; }
public int CompanyType_Id { get; set; }
[ForeignKey("CompanyType_Id")]
public CompanyType Type { get; set; }
public ICollection<Contact>? Contacts { get; set; }
}
And in your form:
<select asp-for="Company.CompanyType_Id" asp-items="Model.CompanyTypes" class="form-control"></select>
Haven't tested this, but pretty sure this is the issue.
See my answer to this question (Binding Complex Entities inside complex Entities to Requests) for an explanation of what is happening when you send an entity to a View to serve as it's Model and the issues & risks you can encounter when trying to send that model back.
The general advice I give is to not send entities to the view because while the view engine on the server gets the entity, what comes back from the client browser is not still an entity, it is just data fields that get cast as an entity so they are either incomplete or prone to tampering. (plus generally involve sending far more data over the wire than is needed)
Your Create PageModel is a good start, but consider just embedding the fields from a Customer rather than a Customer entity (Same goes for Update/Edit) This would include a CustomerTypeId column that when you build your Customer entity within the POST call, you fetch the CustomerType reference from the DbContext and assign that to the newly created Customer. This has the benefit of also validating whatever FKs you receive from the client browser to ensure you are dealing with legal data, throwing an exception in a more useful spot for debugging than the final SaveChanges call. The typical rationale for wanting to pass entities around is to avoid additional DB Read hits. Reading related entities like this by ID is extremely fast, and even cases where you might need to load several related entities there are techniques to batch these up into a single read operation. (Select all IDs needed, read in a single hit using a Contains, then assign from that set)

How to use tag helpers when using ViewModel to add multiple models to a view?

I'm building a ASP.NET MVC web application that has several forms in a view. I want the user to be able to fill out each form and capture the data for processing. These forms have a unique model, 3 for this page, that need to be added to the view somehow, so I have tried ViewModel.
ViewModel.cs
public class ViewModel
{
public ConModel ConModel { get; set; }
public CompanyModel CompanyModel { get; set; }
public ComplaintModel ComplaintModel { get; set; }
public KnowledgeModel KnowledgeModel { get; set; }
}
All the models in the ViewModel look similar:
public class ConModel
{
[Required]
[Display(Name = "First Name")]
public string FirstName { get; set; }
. . .
}
I load ViewModel into my view in the controller:
public class FormsController : Controller
{
public IActionResult Index()
{
ViewModelFraud viewModelFraud = new ViewModelFraud();
viewModelFraud.ConModel = new ConModel();
viewModelFraud.CompanyModel = new CompanyModel();
. . .
return View(viewModelFraud);
}
}
In my view, I create a tag helper in my form:
<form asp-page-handler="Con" method="post">
<div class="col-sm-12">
<div class="custom-control custom-switch">
<input type="text" class="custom-control-input" id="FirstName">
<label class="custom-control-label" asp-for="FirstName"></label>
</div>
</div>
. . .
I'm using asp-for="FirstName" tag helper to get a label added to this input element from the model, but I get build errors if I try. The error implies that the tag helper doesn't see the model. Am I loading them incorrectly or at all?
Thanks, #pinkfloydx33 and #Farhad Zamani!
Turns out, you need to include the ViewModel into the view like so:
#model WebApp.Models.Con.ViewModel;
And asp-for="ConModel.FirstName"

MVC Dropdown option displayed as 'null' in debug mode

Afternoon Folks,
Im new to MVC 5 and C# and have a simple form with several fields and a dropdown box. I have used the CRUD method for entity framework and can successfully view and create new records in the system.
The only issue that I have is I have a 'Title' dropdown that links to the entity framework and populates these titles into a list. when I load my web page I can now see the titles available in the dropdown list, but when submitting the form, all the values but the 'Title' field are submitted into the database.
When I debug my program, this field regardless of what I select displays null.
I have followed the following tutorial to get this wo work and looked around the net but im struggeling to find a solution.
Link to tutorial
I have two tables in my model, one named 'Title' and the other named 'Client Record'.
As I have used the database first approach and not code first I have combined these two database models into one:
namespace EDT_Test.Models.ViewModels
{
public partial class Marie_Testing
{
[Display(Name = "Client Ref:")]
public int id { get; set; }
[Display(Name = "Created By:")]
public string CreatedBy { get; set; }
public List<Title> allTitles { get; set; }
[Required]
[Display(Name = "Surname:")]
public string Surname { get; set; }
[Display(Name = "Additional Surname:")]
public string Surname2 { get; set; }
[Required]
[Display(Name = "Forename:")]
public string Forename1 { get; set; }
[Display(Name = "Additional Forename:")]
public string Forename2 { get; set; }
The generated Entity Framework model looks like this:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace EDT_Test.Models
{
using System;
using System.Collections.Generic;
public partial class ClientRecord
{
public int id { get; set; }
public string CreatedBy { get; set; }
public string Title { get; set; }
public string Surname { get; set; }
public string Surname2 { get; set; }
public string Forename1 { get; set; }
public string Forename2 { get; set; }
}
}
The only difference for the Title field between the auto created model and Marie_Testing model is I have changed the Title field from a string to a list item.
My Create.cshtml holds a div for the Title dropdown that looks like this (This links to my model named Marie_Testing and not the auto generated ones created by the Entity Framework:
<div class="form-group">
#Html.LabelFor(model => model.allTitles, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<select id="titleid" name="titlename" class="form-control">
#foreach (var item in Model.allTitles)
{
<option value="#item.id">#item.Title1</option>
}
</select>
</div>
</div>
My code for the ClientRecordsController is:
// GET: ClientRecords/Create
public ActionResult Create()
{
////set the defaults (dropdown) of the page for the ceaton of a new record.
Marie_Testing vmPopulateData = new Marie_Testing();
List<Title> titles = (from t in db.Titles select t).ToList();
//List<Title> titles = Title.Select(t => new{t.id, t.Title}.ToString.ToList());
vmPopulateData.allTitles = titles;
return View(vmPopulateData);
}
// POST: ClientRecords/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "id,CreatedBy,Title,Surname,Surname2,Forename1,Forename2")] ClientRecord clientRecord)
{
if (ModelState.IsValid)
{
db.ClientRecords.Add(clientRecord);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(clientRecord);
}
Any help is much appreciated as I don't understand how can see the dropdown list in the web page but cannot seem to grab the selected value and post this to the database.
Regards
Betty B
Why not try the #Html.DropDownListFor?
Instead of this:
<select id="titleid" name="titlename" class="form-control">
#foreach (var item in Model.allTitles)
{
<option value="#item.id">#item.Title1</option>
}
</select>
try:
#Html.DropDownListFor(x => x.PropertyToBindTo, new SelectList(Model.allTitles, "id", "Title1"), new { #class = "form-control", id = "Title", name = "Title" })
Where x.PropertyToBindTo is whatever value that you need to get from that select list. Try it out... you may have to play with it a little in order to really understand how you need to work it.
You need to have a string field to hold the value of the selected from the dropdown so your view would change from
<select id="titleid" name="titlename" class="form-control">
to
<select id="Title" name="Title" class="form-control">
And you will also have a Title property on your view model like this
public string Title{get;set;}
You need to have a read on how MVC binds forms to models to get an understanding of why this happens.
Hope this helps..
well I prefer to use this
C#
List<Titles> oTitlesList = TitlesService.getTitles();
ViewBag.DropDownListBag = new SelectList(oTitlesList , "Id", "Name");
Razor
#Html.DropDownListFor(model => model.yourModelAtt, #ViewBag.DropDownListBag as SelectList, "Select.....", new { #class = "form-control" })

Passing IENumerable integer values to controller via ViewModel

Models
public class IntegerList
{
public int IntegerListID { get; set; }
public string Direction { get; set; }
public long Performance { get; set; }
public virtual ICollection<Integer> Integers { get; set; }
}
public class Integer
{
public int IntegerID { get; set; }
[Required(ErrorMessage = "An integer is Required")]
[Range(0, 9999999, ErrorMessage = "Enter an integer")]
public int IntegerValue { get; set; }
public int IntegerListID { get; set; }
public virtual IntegerList IntegerList { get; set; }
}
The above models are for an application that sorts a range of integers into ascending or descending order and calculates the time taken to perform the sort.
The integers are entered into textboxes by the user who can add or remove them using jquery.
I've got the application working by passing formcollection to the controller, splitting the string of integers into an array of integer values to be added to IntegerList.Integers.
However, I'm looking for a more elegant strongly-typed solution.
ViewModel
public class IntegerViewModel
{
[UIHint("Integers")]
public IEnumerable<Integer> Integers { get; set; }
public string Direction { get; set; }
}
Where I'm struggling is adding Integers.IntegerValues to the view so they can be passed to the controller via the viewmodel. I also need to increment/decrement each IntegerValue textbox value as it is added/removed by jquery. I'm not sure I'm approaching this from the right angle.
I hope that's clear. Thanks for your assistance.
Razor
<form method="post">
<div id="integers">
#Html.Label("Enter Integers")
<div class="integer">
#Html.EditorFor(model => model.Integers)
Add Integer
</div>
</div>
#Html.Label("Sort Direction")
<div>
#Html.DropDownListFor(model => model.Direction, new[] {
new SelectListItem() {Text = "Ascending", Value = "Ascending"},
new SelectListItem() {Text = "Descending", Value = "Descending"}
})
<input class="button" type="submit" value="Submit" />
</div>
</form>
Editor Template (for Integers)
#model Integer
#Html.EditorFor(m => m.IntegerValue)
Let me summarize everything.
You will not find any strongly-typed solution there as far as you are adding and removing values (integers) using JavaScript (jQuery).
So, when you are adding integer editor using jquery you should render approppriate HTML according to ASP.NET MVC contracts.
David's comment is very valuable. Check out this link. http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
Here you can see how you should render your HTML to receive different types of data on the server (ASP.NET MVC action).
Also you can check your EditorFor output and render HTML like it was rendered using MVC.
And finally, here is the question about dynamic form fields in ASP.NET MVC. ASP.NET MVC Dynamic Forms
Hope this helps.

Piranha CMS Image Extenion in Custom Region

I have created a custom Region and I now would like to add an Image to it.
How can implement the Image selector into this custom region?
I have seen an example here which shows you how to do this to the core extensions, but I can't seem to get it to work with a custom region.
public class AccordionItem
{
public string Title { get; set; }
public string Body { get; set; }
}
[Export(typeof(IExtension))]
[ExportMetadata("InternalId", "AccordionRegions")]
[ExportMetadata("Name", "Accordion")]
[ExportMetadata("ResourceType", typeof(Piranha.Resources.Extensions))]
[ExportMetadata("Type", ExtensionType.Region)]
[Serializable]
public class AccordionRegion : Extension
{
[Display(Name = "Title")]
public string Title { get; set; }
public IList<AccordionItem> Items { get; set; }
public AccordionRegion()
{
Items = new List<AccordionItem>();
}
}
I've looked at your code and found a quite easy solution on how to incorporate images into your SlideItems. But first another thing.
[ExportMetadata("ResourceType", typeof(Piranha.Resources.Extensions))]
This meta data field is only valid if you want to fetch the key entered in the Name meta field to enable different languages based on culture. Since the key Accordion doesn't exist in the core it messes it up, at least for me.
Now onto the the image. To get it working I simply modified your slider item to the following:
public class AccordionItem
{
public string Title { get; set; }
public string Body { get; set; }
public ImageRegion Image { get ; set ; }
public AccordionItem() {
Image = new ImageRegion() ;
}
}
This way we can reuse the standard functionality from the ImageRegion that is available in the core project. Since you didn't provide the source code for your views I mocked them up like this.
AccordionRegion.cshtml
#model AccordionRegion
<ul class="form">
<li>#Html.LabelFor(m => m.Title)
<div class="input">#Html.TextBoxFor(m => m.Title)</div>
</li>
</ul>
#Html.EditorFor(m => m.Items)
EditorTemplates/AccordionItem.cshtml
#model AccordionItem
<fieldset>
<legend>An item</legend>
<ul class="form">
<li>#Html.LabelFor(m => m.Title)
<div class="input">#Html.TextBoxFor(m => m.Title)</div>
</li>
<li>#Html.LabelFor(m => m.Body)
<div class="input">#Html.TextAreaFor(m => m.Body)</div>
</li>
</ul>
#Html.PartialFor("ImageRegion", m => m.Image)
</fieldset>
Since the ImageRegion renders quite a lot of HTML, I would implement some kind of server side code that generates a new AccordionItem and ships it back to the client with AJAX instead of hardcoding a lot of HTML/JS stuff in the view.

Categories