I created a new Content Part. I attached the Content Part to a Page Content Item. When editing the Page Content item, the custom Content Part Editor (that I created) shows correctly, having two fields only.
When I fill in data and hit "Save", the Driver's POST Editor action is receiving empty values for the custom part. I am making use of InfoSet in my Part class. However, I am also creating a migration to create a Table for the part record.
Here is some code:
using Orchard.ContentManagement;
using Orchard.ContentManagement.Records;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
namespace SlideShare.Models
{
public class SlideSharePartRecord : ContentPartRecord
{
public virtual string SlideShareId { get; set; }
public virtual int StartFromSlide { get; set; }
}
public class SlideSharePart : ContentPart<SlideSharePartRecord>
{
[Required]
public string SlideShareId
{
get { return Retrieve(x => x.SlideShareId); }
set { Store(x => x.SlideShareId, value); }
}
[Required]
[Range(1,20)]
public int StartFromSlide
{
get { return Retrieve(x => x.StartFromSlide); }
set { Store(x => x.StartFromSlide, value); }
}
}
}
using Orchard.ContentManagement.Handlers;
using Orchard.Data;
using System;
using System.Collections.Generic;
using SlideShare.Models;
namespace SlideShare.Handlers
{
public class SlideShareHandler : ContentHandler
{
public SlideShareHandler(IRepository<SlideSharePartRecord> repository)
{
Filters.Add(StorageFilter.For(repository));
}
}
}
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using SlideShare.Models;
using System;
using System.Collections.Generic;
namespace SlideShare.Drivers
{
public class SlideShareDriver : ContentPartDriver<SlideSharePart>
{
protected override string Prefix { get { return "SlideShare"; } }
protected override DriverResult Display(SlideSharePart part, string displayType, dynamic shapeHelper)
{
return ContentShape("Parts_SlideShare", () => shapeHelper.Parts_SlideShare(
SlideShareId: part.SlideShareId,
StartsFromSlide: part.StartFromSlide));
}
// GET
protected override DriverResult Editor(SlideSharePart part, dynamic shapeHelper)
{
return ContentShape("Parts_SlideShare_Edit", () => shapeHelper.EditorTemplate(
TemplateName: "Parts.SlideShare",
Model: part,
Prfix: Prefix));
}
// POST
protected override DriverResult Editor(SlideSharePart part, IUpdateModel updater, dynamic shapeHelper)
{
updater.TryUpdateModel(part, Prefix, null, null);
return Editor(part, shapeHelper);
}
}
}
#using System.Web.Mvc.Html
#model SlideShare.Models.SlideSharePart
<fieldset>
<legend>#T("SlideShare Presentation Fields")</legend>
<div class="editor-field">
#Html.LabelFor(x => x.SlideShareId, T("SlideShare Id"))
#Html.EditorFor(x => x.SlideShareId)
#Html.ValidationMessageFor(x => x.SlideShareId)
</div>
<div class="hint">#T("Enter the SlideShare Presentation Id")</div>
<div class="editor-field">
#Html.LabelFor(x => x.StartFromSlide, T("Start From Slide"))
#Html.EditorFor(x => x.StartFromSlide)
#Html.ValidationMessageFor(x => x.StartFromSlide)
</div>
<div class="hint">#T("Enter the Start From Slide")</div>
</fieldset>
Related
I tried to use AutoMapper NullSubstitute feature with source member and destination member of type string.
It doesn't seem to work with Projection.
As an example, I adapted src/UnitTests/Projection/NullSubstitutes.cs.
namespace AutoMapper.UnitTests.Projection
{
using System.Collections.Generic;
using System.Linq;
using QueryableExtensions;
using Xunit;
public class NullSubstitutesWithMapFrom
{
private List<Dest> _dests;
public class Source
{
public string Value { get; set; }
}
public class Dest
{
public string ValuePropertyNotMatching { get; set; }
}
[Fact]
public void Can_substitute_null_values()
{
MapperConfiguration Configuration = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Source, Dest>().ForMember(m => m.ValuePropertyNotMatching, opt =>
{
opt.MapFrom(src => src.Value);
opt.NullSubstitute("5");
});
});
var source = new[] { new Source { Value = null } }.AsQueryable();
_dests = source.ProjectTo<Dest>(Configuration).ToList();
Assert.Equal("5", _dests[0].ValuePropertyNotMatching);
}
}
}
Expected : "5" equals "5"
Actual : "5" equals null
With Map everything is ok
namespace AutoMapper.UnitTests.Projection
{
using System.Collections.Generic;
using System.Linq;
using Xunit;
public class NullSubstitutesWithMapFrom
{
private List<Dest> _dests;
public class Source
{
public string Value { get; set; }
}
public class Dest
{
public string ValuePropertyNotMatching { get; set; }
}
[Fact]
public void Can_substitute_null_values()
{
MapperConfiguration Configuration = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Source, Dest>().ForMember(m => m.ValuePropertyNotMatching, opt =>
{
opt.MapFrom(src => src.Value);
opt.NullSubstitute("5");
});
});
var source = new[] { new Source { Value = null } }.ToList();
_dests = Configuration.CreateMapper().Map<List<Dest>>(source);
Assert.Equal("5", _dests[0].ValuePropertyNotMatching);
}
}
}
It looks a bit like what was described in https://github.com/AutoMapper/AutoMapper/issues/642
I need authorization attribute for action which allows all but specific role.
something like
[!Authorize(Roles = "SuperUser")]
public ActionResult PaySuperUser.....
Anything built in?
Or any suggestion for custom attribute?
I think a custom attribute is a way to go.
Here is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Controllers;
namespace YourFancyNamespace
{
public class AuthorizeExtended : AuthorizeAttribute
{
private string _notInRoles;
private List<string> _notInRolesList;
public string NotInRoles
{
get
{
return _notInRoles ?? string.Empty;
}
set
{
_notInRoles = value;
if (!string.IsNullOrWhiteSpace(_notInRoles))
{
_notInRolesList = _notInRoles
.Split(new[] {","}, StringSplitOptions.RemoveEmptyEntries)
.Select(r => r.Trim()).ToList();
}
}
}
public override void OnAuthorization(HttpActionContext actionContext)
{
base.OnAuthorization(actionContext);
if (_notInRolesList != null && _notInRolesList.Count > 0)
{
foreach (var role in _notInRolesList)
{
if (actionContext.RequestContext.Principal.IsInRole(role))
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
}
}
}
}
}
}
And here is how you can use it:
// A AuthorizeExtended equals Authorize(with Role filter) + exclude all pesky users
[AuthorizeExtended(Roles = "User", NotInRoles="PeskyUser")]
[HttpPost]
[Route("api/Important/DoNotForgetToUpvote")]
public async Task<IHttpActionResult> DoNotForgetToUpvote()
{
return Ok("I did it!");
}
// Б AuthorizeExtended equals plain Authorize + exclude all pesky users
[AuthorizeExtended(NotInRoles="PeskyUser")]
[HttpPost]
[Route("api/Important/DoNotForgetToUpvote")]
public async Task<IHttpActionResult> DoNotForgetToUpvote()
{
return Ok("I did it!");
}
// В AuthorizeExtended equals Authorize
[AuthorizeExtended]
[HttpPost]
[Route("api/Important/DoNotForgetToUpvote")]
public async Task<IHttpActionResult> DoNotForgetToUpvote()
{
return Ok("I did it!");
}
Walking through this tutorial everything is fine but the data doesn't save.
Tables has been created and edit page shows the fields but no record has been saved. After clicking the save button I get this message:
Your Car ContentType has been created.
Not found
The page you are looking for does not exist.
Any idea?
Models:
public class CarPart : ContentPart<CarPartRecord> {
public string Name {
get { return Record.Name; }
set { Record.Name = value; }
}
public string Description {
get { return Record.Description; }
set { Record.Description = value; }
}
public bool IsPublished {
get { return Record.IsPublished; }
set { Record.IsPublished = value; }
}
}
public class CarPartRecord : ContentPartRecord {
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual bool IsPublished { get; set; }
}
Handlers:
public class CarPartHandler : ContentHandler {
public CarPartHandler(IRepository<CarPartRecord> repository) {
Filters.Add(StorageFilter.For(repository));
}
}
Drivers:
public class CarPartDriver : ContentPartDriver<CarPart> {
protected override string Prefix { get { return "Car"; } }
protected override DriverResult Editor(CarPart part, dynamic shapeHelper) {
return ContentShape("Parts_Car_Edit", () => shapeHelper.EditorTemplate(TemplateName: "Parts/Car", Model: part, Prefix: Prefix));
}
protected override DriverResult Editor(CarPart part, IUpdateModel updater, dynamic shapeHelper) {
updater.TryUpdateModel(part, Prefix, null, null);
return Editor(part, shapeHelper);
}
}
Migrations:
public int Create(){
SchemaBuilder.DropTable("CarPartRecord");
SchemaBuilder.CreateTable("CarPartRecord", table => table
.ContentPartRecord()
.Column<string>("Name",column => column.WithLength(50).NotNull().Unique())
.Column<string>("Description",column => column.WithLength(500))
.Column<bool>("IsPublished",column => column.WithDefault(true).NotNull()));
return 1;
}
public int UpdateFrom1() {
ContentDefinitionManager.AlterPartDefinition("CarPart",part => part.Attachable());
return 2;
}
Errors:
Orchard.Exceptions.DefaultExceptionPolicy - Default - An unexpected exception was caught
http...../OrchardLocal/Admin/Contents/Create/Car
NHibernate.Exceptions.GenericADOException: could not execute batch command.[SQL: SQL not available] ---> System.Data.SqlClient.SqlException: Cannot insert the value NULL into column 'Name', table 'OrchardDB.dbo.Mega_Car_CarPartRecord'; column does not allow nulls. INSERT fails.
NHibernate.AssertionFailure - Default - An AssertionFailure occurred - this may indicate a bug in NHibernate or in your custom types.
http..../OrchardLocal/Admin/Contents/Create/Car
NHibernate.AssertionFailure: null id in Orchard.ContentManagement.Records.ContentTypeRecord entry (don't flush the Session after an exception occurs)
Orchard.Web\Modules\M.Car\Views\EditorTemplates\Parts
#using System.Web.Mvc.Html
#model M.Car.Models.CarPart
<fieldset>
<legend>Car Fields</legend>
<div class="editor-label">#Html.LabelFor(x => x.Name)</div>
<div class="editor-field">
#Html.EditorFor(x => x.Name)
#Html.ValidationMessageFor(x => x.Name)
</div>
<div class="editor-label">#Html.LabelFor(x => x.Description)</div>
<div class="editor-field">
#Html.EditorFor(x => x.Description)
#Html.ValidationMessageFor(x => x.Description)
</div>
<div class="editor-label">#Html.LabelFor(x => x.IsPublished)</div>
<div class="editor-field">
#Html.EditorFor(x => x.IsPublished)
#Html.ValidationMessageFor(x => x.IsPublished)
</div>
</fieldset>
I have created a custom content type called 'AccessFolder'. I can see it in the list of content types and can create a new one. When I create a new AccessFolder, I get my editor template that I created for it. After I enter the information and click save, I'm directed to a Not Found page however the indicator message tells me my AccessFolder was created successfully.
In the driver, I can see the model after it is bound using the updater.TryUpdateModel. The correct values are assigned to the model's properties.
It just never gets to the database.
AccessFolderPart:
public class AccessFolderPart : ContentPart<AccessFolderPartRecord>
{
public virtual string Name
{
get { return Record.Name; }
set { Record.Name = value; }
}
public virtual IEnumerable<RoleRecord> DownloadRoles
{
get { return Record.DownloadRoles.Select(x => x.RoleRecord); }
}
}
AccessFolderPartRecord
public class AccessFolderPartRecord : ContentPartRecord
{
public virtual string Name { get; set; }
public virtual List<ContentAccessFolderRoleRecord> DownloadRoles { get; set; }
}
Relevant Pieces of AccessFolderPartDriver
protected override DriverResult Editor(AccessFolderPart part, dynamic shapeHelper)
{
var viewModel = new AccessFolderViewModel(part, _roleService.GetRoles());
return ContentShape("Parts_AccessFolder_Edit", () =>
shapeHelper.EditorTemplate(TemplateName: templateName, Model: viewModel, Prefix: Prefix));
}
protected override DriverResult Editor(AccessFolderPart part, Orchard.ContentManagement.IUpdateModel updater, dynamic shapeHelper)
{
var viewModel = new AccessFolderViewModel { Part = part };
updater.TryUpdateModel(viewModel, Prefix, null, null);
if (part.ContentItem.Id != 0)
{
_roleService.UpdateRolesForAccessFolder(part.ContentItem, part.DownloadRoles);
}
return Editor(part, shapeHelper);
}
I've been stuck on this since Friday. I've created custom types before and never had any problems with it. I can't see what I've done wrong here.
Update - Added content Handler class
Here's the one line for the handler:
public class AccessFolderPartHandler : ContentHandler
{
public AccessFolderPartHandler(IRepository<AccessFolderPartRecord> repository)
{
Filters.Add(StorageFilter.For(repository));
}
}
I Think you are missing the proper mapping on your driver:
if (updater.TryUpdateModel(viewModel, Prefix, null, null))
{
part.Name= viewModel.Name;
if (part.ContentItem.Id != 0)
{
_roleService.UpdateRolesForAccessFolder(part.ContentItem, part.DownloadRoles);
}
}
I've been having a little problem creating a simple widget. All I want to do is create a widget that has the a MediaPickerField and 2 int and 2 string fields that are stored in the database using a part. Everything works as advertised. ContentRecord is created and the image field is properly stored, except that the four fields are not being stored. There are no error in the logs and cannot seem to see why this is happening. Has anyone come across this before? Thank you.
Migration.cs
public int UpdateFrom1()
{
SchemaBuilder.CreateTable("SchoenNavButtonPartRecord",
table => table.ContentPartRecord()
.Column<string>("Url", col=> col.WithLength(2140))
.Column<string>("Text")
.Column<int>("ButtonWidth")
.Column<int>("ButtonHeight"));
ContentDefinitionManager.AlterTypeDefinition("SchoenNavButton", builder =>
builder.WithPart("CommonPart")
.WithPart("SchoenNavButtonPart")
.WithPart("TitlePart")
.Creatable());
ContentDefinitionManager.AlterPartDefinition("SchoenNavButtonPart", builder =>
builder.WithField("ButtonImage", field=>
field.OfType("MediaPickerField")
.WithDisplayName("Button Image")
.WithSetting("Hint", "Select Image for Button")));
return 2;
}
PartRecord
public class SchoenNavButtonPartRecord : ContentPartRecord
{
public virtual string Url { get; set; }
public virtual string Text { get; set; }
public virtual int ButtonWidth { get; set; }
public virtual int ButtonHeight { get; set; }
}
Part
public class SchoenNavButtonPart : ContentPart<SchoenNavButtonPartRecord>
{
public string Url { get; set; }
public string Text { get; set; }
[DefaultValue(296)]
public int ButtonWidth { get; set; }
[DefaultValue(188)]
public int ButtonHeight { get; set; }
}
Handler
public class SchoenNavButtonHandler : ContentHandler
{
public SchoenNavButtonHandler(IRepository<SchoenNavButtonPartRecord> buttonImageLinkRepository)
{
Filters.Add(StorageFilter.For(buttonImageLinkRepository));
}
}
Driver
public class SchoenNavButtonPartDriver : ContentPartDriver<SchoenNavButtonPart>
{
public SchoenNavButtonPartDriver()
{
}
protected override string Prefix
{
get
{
return "SchoenNavButton";
}
}
protected override DriverResult Display(SchoenNavButtonPart part, string displayType, dynamic shapeHelper)
{
var fields = ((ContentPart) part).Fields.OfType<MediaPickerField>();
MediaPickerField mediaPickerField = null;
if(fields.Any())
{
mediaPickerField = fields.ElementAt(0);
}
return ContentShape("Parts_SchoenNavButton",
() => shapeHelper.Parts_SchoenNavButton(
SchoenNavButtonPart: part,
ImageUrl: mediaPickerField == null ? "#" : mediaPickerField.Url
));
}
protected override DriverResult Editor(SchoenNavButtonPart part, dynamic shapeHelper)
{
return ContentShape("Parts_SchoenNavButton_Edit", () =>
shapeHelper.EditorTemplate(
TemplateName: "Parts/SchoenNavButton",
Model: part,
Prefix: Prefix));
}
protected override DriverResult Editor(SchoenNavButtonPart part, Orchard.ContentManagement.IUpdateModel updater, dynamic shapeHelper)
{
updater.TryUpdateModel(part, Prefix, null, null);
return Editor(part, shapeHelper);
}
}
EditorTemplage
#model FishySoftware.SchoenBuilders.Models.SchoenNavButtonPart
<fieldset>
<legend>Button Details</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Url, T("Url"))
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.Url)
#Html.ValidationMessageFor(model => model.Url)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Text, T("Text"))
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.Text)
#Html.ValidationMessageFor(model => model.Text)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ButtonWidth, T("Button Width"))
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.ButtonWidth)
#Html.ValidationMessageFor(model => model.ButtonWidth)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ButtonHeight, T("Button Height"))
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.ButtonHeight)
#Html.ValidationMessageFor(model => model.ButtonHeight)
</div>
</fieldset>
Orchard is always using content of Record classes to save data to the database. You, on the other hand, are saving data from the browser inside the Part class and you haven't properly connected your Part and Record classes.
To do that, you'll have to change your ShoenNavButtonPart to this:
public class SchoenNavButtonPart : ContentPart<SchoenNavButtonPartRecord>
{
public string Url {
get { return Record.Url; }
set { Record.Url = value; }
}
public string Text {
get { return Record.Text; }
set { Record.Text = value; }
}
[DefaultValue(296)]
public int ButtonWidth {
get { return Record.ButtonWidth; }
set { Record.ButtonWidth = value; }
}
[DefaultValue(188)]
public int ButtonHeight {
get { return Record.ButtonHeight; }
set { Record.ButtonHeight = value; }
}
}