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.
Related
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
Setup
Models
POCOs, virtual is required by Glass Mapper.
using System.Collections.Generic;
using Glass.Mapper.Sc.Configuration.Attributes;
using Glass.Mapper.Sc.Fields;
namespace Sample
{
public class Parent<T>
{
[SitecoreId]
public virtual Guid Id { get; set; }
public virtual string Title { get; set; }
public virtual IEnumerable<T> Children { get; set; }
}
public class Article
{
[SitecoreId]
public virtual Guid Id { get; set; }
public virtual string Title { get; set; }
public virtual string Text { get; set; }
}
public class Teaser
{
[SitecoreId]
public virtual Guid Id { get; set; }
public virtual string Title { get; set; }
public virtual Image Banner { get; set; }
}
}
Views
Referenced by Sitecore as view renderings, with the model pointing to Sample.Parent (see below for Sitecore model definitions).
#inherits Glass.Mapper.Sc.Web.Mvc.GlassView<Sample.Parent<Sample.Article>>
<h1>#Editable(x => x.Title)</h1>
<div class="article-list">
#foreach (var article in Model.Children)
{
<article class="article">
<h2 class="article-title">#Editable(article, x => x.Title)</h2>
<div class="article-content">#Editable(article, x => x.Text)</div>
</article>
}
</div>
#inherits Glass.Mapper.Sc.Web.Mvc.GlassView<Sample.Parent<Sample.Teaser>>
<h1>#Editable(x => x.Title)</h1>
<div class="teaser-list">
#foreach (var teaser in Model.Children)
{
<article class="teaser">
<h2 class="teaser-title">#Editable(teaser, x => x.Title)</h2>
<div class="teaser-banner">#RenderImage(teaser, x => x.Banner)</div>
</article>
}
</div>
Sitecore model definitions
Here's where I'm not sure if I did it right. These are the model types I defined as the Sitecore models (under /sitecore/layout/models).
Sample.Parent`1[T], Sample
Also tried (without success):
Sample.Parent, Sample
Sample.Parent`1[Sample.Article, Sample], Sample
Sample.Parent<Sample.Article>, Sample)
Sample.Article, Sample
Sample.Teaser, Sample
Is this possible?
The example code is simplified, but should capture what I'm trying to do. Basically I want to be able to use the generic type as a way to reuse more code. Because of external restrictions I'm unable to use anything but Glass Mapper 3. The errors I'm seeing are either that Sitecore cannot find the type, or a "object reference not set" (it appears to use Sitecore.Mvc.Presentation.RenderingModel, Sitecore.Mvc as a model when this happens).
Or am I being crazy? :) Is there a better way to accomplish this?
I think there is probably difficulties in the way that Glass tries to process the generic string (To be honest I never designed it to handle generic strings).
If you are using V4 then you don't need to define a model in Sitecore. Leave the model field blank and Glass should resolve the model from the #inherits deceleration in the cshtml file.
I am relying heavily on EditorTemplates in my application, but I've run into a problem which I can not seem to solve, without not moving away from EditorTemplates for drop down lists.
Consider this (View)Model:
public class CreateStudentViewModel
{
public DropDownList StudentTypes { get; set; }
public CreateStudent Command { get; set; }
}
public class DropDownList {
public string SelectedValue { get; set; }
public IList<SelectListItem> Items { get; set; }
}
public class CreateStudent {
public string Name { get; set; }
public int StudentTypeId { get; set; }
}
I use this to provide a way for the frontend user to set the student type, this is done with the following EditorTemplate:
#model DropDownList
<div class="form-group#(Html.ValidationErrorFor(m => m.SelectedValue, " has-error"))">
#Html.LabelFor(m => m)
#Html.DropDownListFor(m => m.SelectedValue, Model.Items)
#Html.ValidationMessageFor(m => m.SelectedValue, null)
</div>
And used within my view:
#Html.EditorFor(m => m.StudentTypes)
Now this EditorTemplate is binding to the StudentTypes.SelectedValue on DropDownList, which is good in some cases - but I need to bind this to my Model.Command.StudentTypeId here.
I know I can move all this code directly to the view and directly bind it, instead of having it inside a EditorTemplate, but I will try my best to avoid this.
Ideally I am thinking of extending the EditorFor to provide a way like:
#Html.EditorFor(m => m.StudentTypes, new { selectedValue = Model.Command.StudentTypeId });
But I can not seem to translate this to something like:
#Html.DropDownList(#ViewBag.selectedValue.ToString(), Model.Items);
As this just places the value (int) as the field name. Any suggestions is welcome! :-)
Your chief problem here is encapsulating your drop down list in a class in order to rely on the C# type editor template convention. Instead, just use your model directly and use UIHint to tell Razor to use a particular template. Here's a simplified version of what I use:
View Model
[UIHint("Choice")]
public int SelectedFoo { get; set; }
public IEnumerable<SelectListItem> FooChoices { get; set; }
Views\Shared\EditorTemplates\Choice.cshtml
#{
var choices = ViewData["choices"] as IEnumerable<SelectListItem> ?? new List<SelectListItem>();
if (typeof(System.Collections.IEnumerable).IsAssignableFrom(ViewData.ModelMetadata.ModelType) && ViewData.ModelMetadata.ModelType != typeof(string))
{
#Html.ListBox("", choices)
}
else
{
#Html.DropDownList("", choices)
}
}
View
#Html.EditorFor(m => m.SelectedFoo, new { choices = Model.FooChoices })
In case it's not obvious, the conditional in the editor template determines if the property is a value or list type, and either uses a drop down list control or listbox control, respectively.
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.
How do I bind such a complex model with multiple layers that contain multiple objects?
Right now I pass the model to the view - (populating a form / a check box tree) and I would like the exact model back (SubjectSelectionModel) but it's not binding correctly.
Could anyone elaborate on the process I need to take in order to bind these correctly in my view?
View Model:
public class SubjectSelectionModel
{
public IList<Subject> Subjects { get; set; }
}
Subject Class:
public class Subject
{
public String Name { get; set; }
public IList<Bin> Bins { get; set; }
public Subject()
{
}
public Subject(IList<Course> courses)
{
}
}
Bin Class:
public class Bin
{
public Subject Subject { get; set; }
public int Amount { get; set; }
public IList<Foo> Foos { get; set; }
}
Foo Class:
public class Foo
{
public int Number { get; set; }
}
This is where Editor Templates come in handy. Rather than messing around with this, you can use simple editor templates to handle all the grunt work for you.
You would create several templates in ~/Views/Shared/EditorTemplates, and then in your primary view it should look like this:
View.cshtml
#model SubjectSelectionModel
#using(Html.BeginForm()) {
#EditorFor(m => m.Subjects)
<input type="submit" />
}
Subject.cshtml
#model Subject
#Html.EditorFor(m => m.Name)
#Html.EditorFor(m => m.Bins)
Bin.cshtml (I assume you don't want to render Subject, this would be an infinite loop)
#model Bin
#Html.EditorFor(m => m.Amount)
#Html.EditorFor(m => m.Foos)
Foo.cshtml
#model Foo
#Html.EditorFor(m => m.Number)
Obviously, you may want to change the html formatting to whatever you want, but that's essentially it.
You need a for loop for the objects so MVC can bind using the index in the collection.
Example:
for (int subjectIndex = 0; subjectIndex < Model.Subjects.Count; subjectIndex++) {
#Html.TextBoxFor(x => x.Subjects[subjectIndex].Name)
for (int binIndex = 0; binIndex < Model.Subjects.Bins.Count; binIndex++) {
#Html.TextBoxFor(x => x.Subjects[subjectIndex].Bins[binIndex].Amount)
}
}
..etc.
I gave a similar response to a similar question, here: Generating an MVC RadioButton list in a loop