this question is similar to this one but it targets Orchard CMS 1.8, where Site Settings were redesigned.
I've implemented HomeSettingsPart to store my custom settings for Home page.
public class HomeSettingsPart : ContentPart {
public int NewsCount {
get { return this.Retrieve(x => x.NewsCount); }
set { this.Store(x => x.NewsCount, value); }
}
public int MaterialsCount {
get { return this.Retrieve(x => x.MaterialsCount); }
set { this.Store(x => x.MaterialsCount, value); }
}
public string WelcomeText {
get { return this.Retrieve(x => x.WelcomeText); }
set { this.Store(x => x.WelcomeText, value); }
}
}
Also I've added a handler for this part:
public class HomeSettingsHandler : ContentHandler {
public HomeSettingsHandler() {
T = NullLocalizer.Instance;
Filters.Add(new ActivatingFilter<HomeSettingsPart>("Site"));
Filters.Add(new TemplateFilterForPart<HomeSettingsPart>("HomeSettings", "Parts.HomeSettings", "HomePage"));
}
public Localizer T { get; set; }
protected override void GetItemMetadata(GetContentItemMetadataContext context) {
if (context.ContentItem.ContentType != "Site")
return;
base.GetItemMetadata(context);
context.Metadata.EditorGroupInfo.Add(new GroupInfo(T("HomePage")));
}
}
Everything is like in this tutorial and this code works fine.
Now I want to add an MediaLibraryPickerField to this content part. I've added this field in migrations as usual and I see it in AdminUI ContentManagement/Parts section.
ContentDefinitionManager.AlterPartDefinition("HomeSettingsPart", x => x
.WithField("HomeSlider", y => y
.OfType("MediaLibraryPickerField")
.WithDisplayName("...")
.WithSetting("MediaLibraryPickerFieldSettings.Multiple", "true")
)
);
But, when I open my Settings/HomeSettings I see only 3 editors for my HomeSettingsPart and nothing is rendered for picker field.
So, how do I add this field correctly?
thanks.
You can also see this page in the documentation site : http://docs.orchardproject.net/Documentation/Adding-custom-settings
Related
I am trying to register a ICustomVirtualPathProvider in one of my modules. This is what I am trying to use:
public class AzureVirtualPathProvider : VirtualPathProvider, ICustomVirtualPathProvider
{
public IStaticDataStorageProvider StaticDataStorageProvider { get; set; }
public VirtualPathProvider Instance
{
get
{
return this;
}
}
public AzureVirtualPathProvider(IStaticDataStorageProvider staticDataStorageProvider)
{
StaticDataStorageProvider = staticDataStorageProvider;
}
public override bool FileExists(string virtualPath)
{
if (!virtualPath.Contains("StaticData")) return base.FileExists(virtualPath);
return true;
}
public override VirtualFile GetFile(string virtualPath)
{
if (!virtualPath.Contains("StaticData") || !StaticDataStorageProvider.IsCloud()) return base.GetFile(virtualPath);
return new CustomVirtualFile(StaticDataStorageProvider, virtualPath);
}
}
so in Module.Load I am setting:
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<AzureVirtualPathProvider>().PropertiesAutowired().As<ICustomVirtualPathProvider>();
}
but this has not been picked up when Orchard calls this line in OrchardStartup.cs (in Orchard.Framework)
if (HostingEnvironment.IsHosted) {
foreach (var vpp in container.Resolve<IEnumerable<ICustomVirtualPathProvider>>()) {
HostingEnvironment.RegisterVirtualPathProvider(vpp.Instance);
}
}
I haver tried calling HostingEnvironment.RegisterVirtualPathProvider directly thus:
HostingEnvironment.RegisterVirtualPathProvider(new AzureVirtualPathProvider());
and tried to inject the dependency using property injecction:
builder.Register(c => new AzureVirtualPathProvider { StaticDataStorageProvider = c.Resolve<IStaticDataStorageProvider>() });
however the value for StaticDataStorageProvider is always null when AzureVirtualPathProvider is run.
I have tried moving AzureVirtualPathProvider to OrchardFramework but then it does not resolve StaticDataStorageProvider.
How do I get Orchard to load my CustomVirtualPathProvider?
In the end I did this:
public class OrchardShellEvents : IOrchardShellEvents
{
readonly ICustomVirtualPathProvider _customVirtualPathProvider;
public OrchardShellEvents(ICustomVirtualPathProvider customVirtualPathProvider)
{
_customVirtualPathProvider = customVirtualPathProvider;
}
public void Activated()
{
HostingEnvironment.RegisterVirtualPathProvider(_customVirtualPathProvider.Instance);
}
public void Terminating()
{
}
}
I don't know if this is the best solution but it worked and might help someone else.
I am trying to build a module with the following in a migration:
public class XyzzyPartRecord : ContentPartRecord
{
public virtual string Plugh { get; set; }
}
public class XyzzyPart : ContentPart<XyzzyPartRecord>
{
public string Plugh {
get { return Retrieve( r => r.Plugh ); }
set { Store( r => r.Plugh, value ); }
}
}
public int Create() {
SchemaBuilder.CreateTable( "XyzzyPartRecord", table => table
.ContentPartRecord()
.Column<string>( "Plugh" )
);
ContentDefinitionManager.AlterPartDefinition( "XyzzyPart", cfg => cfg
.WithDescription( "XyzzyPart" ) );
ContentDefinitionManager.AlterTypeDefinition( "XyzzyItem", cfg => cfg
.WithPart( "XyzzyPart" )
);
return 1;
}
When accessing an XyzzyItem, there is no XyzzyPart in the Parts collection. Instead there is a ContentPart.
How do I get my Content Part to allow it to be added to a Content Item's Parts collection?
I had neglected to create either a Driver or a Handler for my part. Once those were in place my code worked as expected.
namespace MyProject.Drivers {
public class XyzzyPartDriver : ContentPartDriver<XyzzyPart> {
protected override DriverResult Display( XyzzyPart part, string displayType, dynamic shapeHelper ) {
return ContentShape( "Parts_Xyzzy",
() => shapeHelper.Parts_Xyzzy(
Xyzzy: part ) );
}
}
}
namespace MyProject.Handlers {
public class XyzzyPartHandler : ContentHandler {
public XyzzyPartHandler( IRepository<XyzzyPartRecord> repository ) {
Filters.Add( StorageFilter.For( repository ) );
}
}
}
I'm having trouble getting my custom tokens to work with my ContentPart. My problem is the same as what is described here:
Is it possible to create an orchard autoroute using contents of a custom type property?
I have created my tokens:
namespace MyNS.Types.Providers
{
public class BioPartTokens : ITokenProvider
{
public BioPartTokens() {
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
public void Describe(dynamic context) {
context.For("Bio", T("Bio"), T("Tokens for the Bio content type"))
.Token("FirstName", T("FirstName"), T("First name of person."))
.Token("LastName", T("LastName"), T("Last name of person."));
}
public void Evaluate(dynamic context) {
context.For<BioPart>("Bio")
.Token("FirstName", (Func<BioPart, object>) (f => f.ContentItem.Parts.OfType<BioPart>().First().FirstName.ToLower()))
.Chain("FirstName", "FirstName", (Func<BioPart, object>)(f => f.ContentItem.Parts.OfType<BioPart>().First().FirstName.ToLower()))
.Token("LastName", (Func<BioPart, object>)(f => f.ContentItem.Parts.OfType<BioPart>().First().LastName.ToLower()))
.Chain("LastName", "LastName", (Func<BioPart, object>)(f => f.ContentItem.Parts.OfType<BioPart>().First().LastName.ToLower()))
;
}
}
}
My model:
namespace MyNS.Types.Models
{
public class BioPart: ContentPart<BioPartRecord>
{
public string FirstName {
get { return Record.FirstName; }
set { Record.FirstName = value; }
}
public string LastName
{
get { return Record.LastName; }
set { Record.LastName = value; }
}
public RoleInSchool RoleInSchool
{
get { return Record.RoleInSchool; }
set { Record.RoleInSchool = value; }
}
public bool IsBlogger {
get { return Record.IsBlogger; }
set { Record.IsBlogger = value; }
}
}
}
Though I've tried URL patterns with all of the following tokens, I've not been able to get a value back from the form that I've submitted:
{Content.Bio.FirstName}
{Content.BioPart.FirstName}
{Bio.FirstName}
{BioPart.FirstName}
No errors are being logged.
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'm new with orchard.
To learn orchard module development, I am following documentation to try to create a commerce module.
The module consists of product part and product type, which has product part.
When I try to save data in following method:
public ActionResult Create(FormCollection input)
{
var product = contentManager.New<ProductPart>("Product");
product.Description = input["Description"];
product.Sku = input["Sku"];
product.Price =Convert.ToDecimal(input["Price"]);
if (!ModelState.IsValid)
{
return View(product);
}
contentManager.Create(product);
return RedirectToAction("Index");
}
I am getting an error that specific cast is Invalid and part(ContentPart) is null.
public static T New<T>(this IContentManager manager, string contentType)
where T : class, IContent {
var contentItem = manager.New(contentType);
if (contentItem == null)
return null;
var part = contentItem.Get<T>();
if (part == null)
throw new InvalidCastException();
return part;
}
I used content type Product and I have ProductRecord class for storage data, as below:
public class ProductRecord:ContentPartRecord
{
// public virtual int Id { get; set; }
public virtual string Sku { get; set; }
public virtual string Description { get; set; }
public virtual decimal Price { get; set; }
}
public class ProductPart : ContentPart<ProductRecord>
{
/*
public int Id
{
get { return Record.Id; }
set{Record.Id = value;}
}
*/
[Required]
public string Sku
{
get { return Record.Sku; }
set { Record.Sku = value; }
}
[Required]
public string Description
{
get { return Record.Description; }
set{ Record.Description = value;}
}
[Required]
public decimal Price
{
get { return Record.Price; }
set { Record.Price = value; }
}
}
Can anybody tell me what my problem is?
I'm just guessing, but did you declare your record and your ContentType in migration.cs? If you didn't, the content management will be unable to create a content item with your type as it will not know the type in question.
Your migration.cs should look somehow like that:
public class Migrations : DataMigrationImpl
{
public int Create()
{
SchemaBuilder.CreateTable("ProductRecord",
table =>
{
table.ContentPartRecord()
.Column<string>("Sku")
.Column<string>("Description")
.column<decimal>("Price");
});
ContentDefinitionManager.AlterTypeDefinition("Product", cfg => cfg.WithPart("ProductPart"));
return 1;
}
}
On a side note, the naming convention in Orchard is to name the record for a part XXXPartRecord. I don't think your problem lies there though.
I have mentioned this in you other thread.. Orchard Content Type is null
you need
Migrations
public class Migrations : DataMigrationImpl {
public int Create() {
SchemaBuilder.CreateTable("ProductRecord",
table => table
.ContentPartRecord()
.COLUMNS NEED TO BE SPECIFIED
);
ContentDefinitionManager.AlterTypeDefinition("Forum",
cfg => cfg
.WithPart("ProductPart")
.WithPart("CommonPart")
);
Repository
public class ProductPartHandler : ContentHandler {
public ProductPartHandler(IRepository repository) {
Filters.Add(StorageFilter.For(repository));
}
Hope this helps
You could try generating a similar part using the command line utility by pszmyd and see whats different.
http://www.szmyd.com.pl/blog/generating-orchard-content-parts-via-command-line