Widget in orchard won't save into database - asp.net-mvc

I've a problem with orchard.
I've created a widget that is a video player, it's a simple widget with the class ContentPartRecord:
public class VideoPlayerPartRecord : ContentPartRecord
{
public virtual string MediaFile { get; set; }
public virtual int Width { get; set; }
public virtual int Height { get; set; }
public virtual bool AutoStart { get; set; }
public virtual bool Repeat { get; set; }
}
and the class ContentPart:
public class VideoPlayerPart : ContentPart<VideoPlayerPartRecord>
{
[Required(ErrorMessage = "Media File required")]
[Display(Name = "Media File: ")]
public string MediaFile
{
get { return Record.MediaFile; }
set { Record.MediaFile = value; }
}
[Required(ErrorMessage = "Width required")]
[Display(Name = "Width: ")]
public int Width
{
get { return Record.Width; }
set { Record.Width = value; }
}
[Required(ErrorMessage = "Height required")]
[Display(Name = "Height: ")]
public int Height
{
get { return Record.Height; }
set { Record.Height = value; }
}
[Display(Name = "Auto Start: ")]
public bool AutoStart
{
get { return Record.AutoStart; }
set { Record.AutoStart = value; }
}
[Display(Name = "Repeat: ")]
public bool Repeat
{
get { return Record.Repeat; }
set { Record.Repeat = value; }
}
}
this is the file migration:
public class Migrations : DataMigrationImpl {
public int Create() {
// Creating table default_Raise_VideoPlayer_VideoPlayePartRecord
SchemaBuilder.CreateTable("default_Raise_VideoPlayer_VideoPlayePartRecord", table => table
.ContentPartRecord()
.Column("MediaFile", DbType.String)
.Column("Width", DbType.Int32)
.Column("Height", DbType.Int32)
.Column("AutoStart", DbType.Boolean)
.Column("Repeat", DbType.Boolean)
);
ContentDefinitionManager.AlterPartDefinition(typeof(VideoPlayerPart).Name, cfg => cfg
.Attachable());
return 1;
}
public int UpdateFrom1() {
ContentDefinitionManager.AlterTypeDefinition("VideoPlayerWidget", cfg => cfg
.WithPart("VideoPlayerPart")
.WithPart("WidgetPart")
.WithPart("CommonPart")
.WithSetting("Stereotype", "Widget"));
return 2;
}
}
the problem is that when I insert the widget it is added but I can't see it. Why??

You need to add a Handler for your widget to persist the item, and a placement.info entry to present it.
http://docs.orchardproject.net/Documentation/Writing-a-content-part
(code taken from article)
using Maps.Models;
using Orchard.ContentManagement.Handlers;
using Orchard.Data;
namespace Maps.Handlers {
public class MapHandler : ContentHandler {
public MapHandler(IRepository<MapRecord> repository) {
Filters.Add(StorageFilter.For(repository));
}
}
}
and Placement.info
<Placement>
<Place Parts_Map="Content:10"/>
<Place Parts_Map_Edit="Content:7.5"/>
</Placement>
If it doesn't save into a database - its probably the handler. If it doesn't display on screen, it's probably placement.info
Also you didn't mention having a driver or a view, but I'd guess it is the Handler or the placement info you are missing. Also, check spelling and convention very carefully when relating the driver, migration and placement info, as there are several places you can use the wrong text string if you create your parts manually.

Related

Setting default value in MVC database first

Hi is there a cleaner way to set default value in database first other than initialize in the partial class like below?
[MetadataType(typeof(PersonAttribute))]
public partial class person {
public person() {
isMale = true;
}
}
public class PersonAttribute
{
[Display(Name = "Title")]
[Required(ErrorMessage = "Please enter the news title.")]
public string UserTitle { get; set; }
public bool isMale { get; set; }
}

Exception: Multiplicity constraint violated

After changing relationship of Question and PossibleAnswer from Many-to-many to One-to-many I got an exception: "The changes to the database were committed successfully, but an error occurred while updating the object context. The ObjectContext might be in an inconsistent state. Inner exception message: Multiplicity constraint violated. The role 'PossibleAnswer_Question_Source' of the relationship 'WebUI.Models.PossibleAnswer_Question' has multiplicity 1 or 0..1." What does it mean?
Here is my model:
public class Question
{
public int ID { get; set; }
public string Text { get; set; }
public bool IsAssociatedWithProfessor { get; set; }
public bool IsAssociatedWithAssistant { get; set; }
public virtual ICollection<PossibleAnswer> PossibleAnswers { get; set; }
public virtual ICollection<Results> Results { get; set; }
}
public class PossibleAnswer
{
public int ID { get; set; }
public string Text { get; set; }
public virtual Question Question { get; set; }
}
And view model for Question (I know that ToQuestion should be in controller, I will rearrange it later):
public class QuestionVM
{
public QuestionVM() {
}
public QuestionVM(Question question) : this()
{
ID = question.ID;
Text = question.Text;
IsAssociatedWithProfessor = question.IsAssociatedWithProfessor;
IsAssociatedWithAssistant = question.IsAssociatedWithAssistant;
}
public int? ID { get; set; }
public string Text { get; set; }
public bool IsAssociatedWithProfessor { get; set; }
public bool IsAssociatedWithAssistant { get; set; }
private IEnumerable<string> _possibleAnswers;
public IEnumerable<string> PossibleAnswers
{
get
{
return _possibleAnswers ?? new List<string>(){"", "", "", "", ""};
}
set
{
_possibleAnswers = value;
}
}
public Question ToQuestion()
{
Question question = new Question
{
Text = this.Text,
IsAssociatedWithProfessor = this.IsAssociatedWithProfessor,
IsAssociatedWithAssistant = this.IsAssociatedWithAssistant,
PossibleAnswers = new List<PossibleAnswer>()
};
//ID will be null if creating new question
if(ID != null)
{
question.ID = (int) ID;
}
foreach (string possibleAnswer in this.PossibleAnswers)
{
if (!String.IsNullOrEmpty(possibleAnswer))
{
question.PossibleAnswers.Add(new PossibleAnswer { Text = possibleAnswer });
}
}
return question;
}
}
Here is my post method for creating new question:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AddQuestion(QuestionVM questionVM)
{
try
{
if (ModelState.IsValid)
{
Question question = questionVM.ToQuestion();
context.Questions.Add(question);
context.SaveChanges();
return RedirectToAction("Questions");
}
}
catch (DataException /* dex */)
{
//Log the error (uncomment dex variable name and add a line here to write a log.
ModelState.AddModelError("", "Trenutno nije moguće snimiti promjene, pokušajte ponovo.");
}
return View(questionVM);
}
Line:question.PossibleAnswers.Add(new PossibleAnswer { Text = possibleAnswer });
causes the exception since I didn't save possible answers in database before I am adding them to question... But how can I add them to database without use of DbContext (since it is not a good practice to use DbContext in method which converts view model to model)?
Error is coming because PossibleAnswers are not being marked as added entity.
So update your QuestionVM like below:
public class QuestionVM
{
public QuestionVM() {
}
public QuestionVM(Question question) : this()
{
ID = question.ID;
Text = question.Text;
IsAssociatedWithProfessor = question.IsAssociatedWithProfessor;
IsAssociatedWithAssistant = question.IsAssociatedWithAssistant;
}
public int? ID { get; set; }
public string Text { get; set; }
public bool IsAssociatedWithProfessor { get; set; }
public bool IsAssociatedWithAssistant { get; set; }
private IEnumerable<string> _possibleAnswers;
public IEnumerable<string> PossibleAnswers
{
get
{
return _possibleAnswers ?? new List<string>(){"", "", "", "", ""};
}
set
{
_possibleAnswers = value;
}
}
public Question ToQuestion()
{
Question question = new Question
{
Text = this.Text,
IsAssociatedWithProfessor = this.IsAssociatedWithProfessor,
IsAssociatedWithAssistant = this.IsAssociatedWithAssistant,
PossibleAnswers = new List<PossibleAnswer>()
};
//ID will be null if creating new question
if(ID != null)
{
question.ID = (int) ID;
}
return question;
}
public List<PossibleAnswer> GetPosibleAnswers()
{
var listOfPossibleAnswers = new List<PossibleAnswer>();
foreach (string possibleAnswer in this.PossibleAnswers)
{
if (!String.IsNullOrEmpty(possibleAnswer))
{
listOfPossibleAnswers.Add(new PossibleAnswer { Text = possibleAnswer });
}
}
return listOfPossibleAnswers;
}
}
and then update your code like below.
if (ModelState.IsValid)
{
Question question = questionVM.ToQuestion();
context.Questions.Add(question);
context.SaveChanges();
question.PossibleAnswers.AddRange(questionVM.GetPosibleAnswers());
context.SaveChanges();
return RedirectToAction("Questions");
}

Testing issues with MVC Controller using IOC

I am testing my controller code using IOC Unity. The issue is with the initialisation of the model that I am passing to constructor of my controller.
Here is my code in the test project
[TestMethod]
public void TestHomeControllerIndexMethod()
{
HomeController controller = new HomeController(new stubPeopleService());
ViewResult result = controller.Index() as ViewResult;
Assert.AreEqual(0, result);
}
Below is the code of the stubPeopleService that I have created which I pass to my controller constructor above
public class stubPeopleService : IPeople
{
public int Age
{
get;
set;
}
public string BirthPlace
{
get;
set;
}
public DateTime DateOfBirth
{
get;
set;
}
public int GetAge(DateTime reference, DateTime birthday)
{
int age = reference.Year - birthday.Year;
if (reference < birthday.AddYears(age)) age--;
return age + 1;
}
public int Height
{
get;
set;
}
public List<People> listPeople { get { return GetPeople(); } }
public string Name
{
get;
set;
}
public int Weight
{
get;
set;
}
private List<People> GetPeople()
{
List<People> list = new List<People>();
list.Add(new People
{
Name = "Ranjit Menon",
DateOfBirth = DateTime.Today,
BirthPlace = "London",
Age = 25,
Height = 175,
Weight = 85
});
return list.OrderBy(x => x.Name).ToList();
}
}
When I debug my test , I notice that the all the properties do not contain any value. The only property that contains value is listPeople property. The listpeople property does initialise the other properties but throws an object cannot be created error.Let me know if I am doing the test correctly. I need to do a test initialising the model with some values.
Code from my home controller
private IPeople peopleService;
public HomeController(IPeople people)
{
this.peopleService = people;
}
public ActionResult Index()
{
return View(peopleService);
}
Please find the IPeople interface below
public interface IPeople
{
int Age { get; set; }
string BirthPlace { get; set; }
DateTime DateOfBirth { get; set; }
int GetAge(DateTime reference, DateTime birthday);
int Height { get; set; }
List<People> listPeople { get; }
string Name { get; set; }
int Weight { get; set; }
}

how to get ALL errors from viewdata.modelstate

I am trying to do a bit of a custom error handler. We have 4 tabs (using JQuery tabs), they are all build from one large model. Say for simplicity the model looks like:
myModel.HomeInfo
myModel.PhoneNumbers
myModel.Addresses
myModel.PersonalDetails
Each part is an object that have various bits of information. They all have attributes on them and validate messages.
At the top of the page (above the tabs) I want to display some top level errors, by that I mean the errors for attributes on the "myModel" object. This works when I do the:
foreach (ModelState state in viewData.ModelState.Values)
When I do:
#Html.ValidationSummary(false)
on my view I get all errors from each of the four objects and all their children, (more than 10). But when I go through the errors my self, (code above), I only get 2 errors, (the errors for "myModel" only, not its child properties).
I tried to use ILSPY to see what the validation summary is doing and replicate it. Believe I had the code pretty much line for line, but it still only got the two errors.
I do not know what magic is going on when I use the #Html.ValidationSummary().
What I want to know is how I can get all the errors for the whole object my self to be able to display some of the errors on each tab.
for clarification here is my basic model:
public class MemberProfileModel
{
[CompanyTabValid]
public CompanyInformationModel CompanyInformation { get; set; }
[ContactTabValid]
public ContactInformationModel ContactInformation { get; set; }
[InvoiceTabValid]
public InvoiceInformationModel InvoiceInformation { get; set; }
[TabProductIdentificationMarkValid]
public ProductIdentificationMarkModel ProductIdentificationMark { get; set; }
}
public class CompanyTabValid : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext context)
{
var model = value as CompanyInformationModel;
if(model == null) throw new ArgumentNullException("value");
var failed = new ValidationResult("Company information incomplete.");
return model.IsValid ? ValidationResult.Success : failed;
}
}
public class ContactInformationModel : BaseModel
{
public ContactInformationModel()
{
PrimarySiteAddress = new AddressInformation();
PrimarySiteContact = new ContactInformation();
RegisteredOfficeAddress = new AddressInformation();
RegisteredOfficeContact = new ContactInformation();
}
public override void Validate()
{
IsValid = PrimarySiteAddress.IsValid &&
PrimarySiteContact.IsValid &&
RegisteredOfficeAddress.IsValid &&
RegisteredOfficeContact.IsValid;
}
public AddressInformation PrimarySiteAddress { get; set; }
public ContactInformation PrimarySiteContact { get; set; }
public AddressInformation RegisteredOfficeAddress { get; set; }
public ContactInformation RegisteredOfficeContact { get; set; }
}
public class AddressInformation : BaseModel
{
public int Id { get; set; }
public Guid MemberId { get; set; }
/// <summary>
/// This property is only here to make EF happy, do not use
/// </summary>
public int LocationTypeValue { get; set; }
public LocationType LocationType { get { return (LocationType) LocationTypeValue; } set { LocationTypeValue = (int) value; } }
[Required(AllowEmptyStrings = false, ErrorMessage = "Address Line 1 required.")]
[Display(Name = "Address Line 1 *")]
public string AddressLine1 { get; set; }
[Display(Name = "Address Line 2")]
public string AddressLine2 { get; set; }
[Display(Name = "Address Line 3")]
public string AddressLine3 { get; set; }
[Required(AllowEmptyStrings = false, ErrorMessage = "Town required.")]
[Display(Name = "Town *")]
public string Town { get; set; }
[Required(AllowEmptyStrings = false, ErrorMessage = "County required.")]
[Display(Name = "County *")]
public string County { get; set; }
[Display(Name = "Country *")]
public string Country { get; set; }
[RequiredOneOfTwo("InterationalPostCode", ErrorMessage="PostCode or international PostCode are required.")]
[Display(Name = "Post Code *")]
public string PostCode { get; set; }
[RequiredOneOfTwo("PostCode", ErrorMessage = "International PostCode or PostCode are required.")]
[Display(Name = "International Post Code *")]
public string InterationalPostCode { get; set; }
public override void Validate()
{
if (string.IsNullOrEmpty(AddressLine1))
{
this.IsValid = false;
return;
}
else if (string.IsNullOrEmpty(Town))
{
this.IsValid = false;
return;
}
else if (string.IsNullOrEmpty(County))
{
this.IsValid = false;
return;
}
else if (string.IsNullOrEmpty(Country))
{
this.IsValid = false;
return;
}
else if (string.IsNullOrEmpty(PostCode) && string.IsNullOrEmpty(InterationalPostCode))
{
this.IsValid = false;
return;
}
this.IsValid = true;
return;
}
}
I have shown an example of a validation attribute (some of ours are custom, some are normal), the top level MemberProfileModel = myModel in this example, and ContactInformationModel is one of its children which in turn has its own objects such as AddressInformation.
Thanks
I found out why this wasn't working for me. As usual it was me being silly. Because the model has multiple layers / levels to it, I.e. model.someobject.someotherobject.someproperty, when I called tryValidateModel it would validate the top level but not the inner layers.
The solution to this was to ensure they are all called:
TryValidateModel(mp);
TryValidateModel(mp.ContactInformation.PrimarySiteAddress);
TryValidateModel(mp.ContactInformation.RegisteredOfficeAddress);
So my solution is to either create a method to call try validate on each object level or create a refelctive method to do it for me.
In the post event of your page, in the controller just add this:
[HttpPost]
public ActionResult Create(TestViewModel testViewModel)
{
// If not Valid
if (!ModelState.IsValid)
{
return this.View(testViewModel)
}
...
}
And don't forget to add your required and/or other validation to your viewmodel access methods:
[Required(ErrorMessageResourceType = typeof(Resources.MyProject), ErrorMessageResourceName = "validation_Test")]
public virtual string HomeInfo { get; set; }
And in your view:
<div class="editor-row">
<div class="editor-label">
#Html.LabelFor(model => model.HomeInfo)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.HomeInfo)
#Html.ValidationMessageFor(model => model.HomeInfo)
</div>
</div>
I got this from here:
http://www.unknownerror.org/opensource/aspnet/Mvc/q/stackoverflow/1352948/how-to-get-all-errors-from-asp-net-mvc-modelstate
public static List<string> GetErrorListFromModelState
(ModelStateDictionary modelState)
{
var query = from state in modelState.Values
from error in state.Errors
select error.ErrorMessage;
var errorList = query.ToList();
return errorList;
}

Creating a custom module for Orchard

I created a custom module using this guide from Orchard documentation, but for some reason I can't see the fields in the content type when I want to create a new one.
this is my model:
public class CustomerPartRecord : ContentPartRecord
{
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual int PhoneNumber { get; set; }
public virtual string Address { get; set; }
public virtual string Profession { get; set; }
public virtual string ProDescription { get; set; }
public virtual int Hours { get; set; }
}
public class CustomerPart : ContentPart<CustomerPartRecord>
{
[Required(ErrorMessage="you must enter your first name")]
[StringLength(200)]
public string FirstName { get { return Record.FirstName; } set { Record.FirstName = value; } }
[Required(ErrorMessage = "you must enter your last name")]
[StringLength(200)]
public string LastName { get { return Record.LastName; } set { Record.LastName = value; } }
[Required(ErrorMessage = "you must enter your phone number")]
[DataType(DataType.PhoneNumber)]
public int PhoneNumber { get { return Record.PhoneNumber; } set { Record.PhoneNumber = value; } }
[StringLength(200)]
public string Address { get { return Record.Address; } set { Record.Address = value; } }
[Required(ErrorMessage = "you must enter your profession")]
[StringLength(200)]
public string Profession { get { return Record.Profession; } set { Record.Profession = value; } }
[StringLength(500)]
public string ProDescription { get { return Record.ProDescription; } set { Record.ProDescription = value; } }
[Required(ErrorMessage = "you must enter your hours")]
public int Hours { get { return Record.Hours; } set { Record.Hours = value; } }
}
this is the Handler:
class CustomerHandler : ContentHandler
{
public CustomerHandler(IRepository<CustomerPartRecord> repository)
{
Filters.Add(StorageFilter.For(repository));
}
}
the Driver:
class CustomerDriver : ContentPartDriver<CustomerPart>
{
protected override DriverResult Display(CustomerPart part, string displayType, dynamic shapeHelper)
{
return ContentShape("Parts_Customer", () => shapeHelper.Parts_Customer(
FirstName: part.FirstName,
LastName: part.LastName,
PhoneNumber: part.PhoneNumber,
Address: part.Address,
Profession: part.Profession,
ProDescription: part.ProDescription,
Hours: part.Hours));
}
//GET
protected override DriverResult Editor(CustomerPart part, dynamic shapeHelper)
{
return ContentShape("Parts_Customer", () => shapeHelper.EditorTemplate(
TemplateName:"Parts/Customer",
Model: part,
Prefix: Prefix));
}
//POST
protected override DriverResult Editor(CustomerPart part, IUpdateModel updater, dynamic shapeHelper)
{
updater.TryUpdateModel(part, Prefix, null, null);
return Editor(part, shapeHelper);
}
the migration:
public class Migrations : DataMigrationImpl
{
public int Create()
{
// Creating table CustomerPartRecord
SchemaBuilder.CreateTable("CustomerPartRecord", table => table
.ContentPartRecord()
.Column("FirstName", DbType.String)
.Column("LastName", DbType.String)
.Column("PhoneNumber", DbType.Int32)
.Column("Address", DbType.String)
.Column("Profession", DbType.String)
.Column("ProDescription", DbType.String)
.Column("Hours", DbType.Int32)
);
return 1;
}
public int UpdateFrom1()
{
ContentDefinitionManager.AlterPartDefinition("CustomerPart",
builder => builder.Attachable());
return 2;
}
public int UpdateFrom2()
{
ContentDefinitionManager.AlterTypeDefinition("Customer", cfg => cfg
.WithPart("CommonPart")
.WithPart("RoutePart")
.WithPart("BodyPart")
.WithPart("CustomerPart")
.WithPart("CommentsPart")
.WithPart("TagsPart")
.WithPart("LocalizationPart")
.Creatable()
.Indexed());
return 3;
}
}
Can someone please tell me if I am missing something?
Did you add an entry in the placement.info file? I forget that sometimes. Without placement, it won't know where to put your content so it just doesn't get rendered.

Resources