Ticket (Solved)

Import/Export not working for Cust. Module, nor Driver - really confused?

Hi guys, I've made a very simple module that doesnt appear to be wired right? I can't import/export by content successfully, nor does my Driver even appear to be working. Ultimately I wanted to add a Migration/Handler/Driver/Models and have Orchard handle the rest, but have found I've resorted to CRUD via repository to edit a simple record in the DB. Could anyone take a quick look as to what I maybe missing to successfully use Orchards contentManager instead? Thanks in advance...

MIGRATION

public class Migrations : DataMigrationImpl {

    public int Create() {
        SchemaBuilder.CreateTable("NewBookPartRecord", builder => builder
            .ContentPartRecord()
            .Column<string>("UserName", col => col.WithLength(100))
            .Column<string>("InstanceToken", col => col.WithLength(100))
            .Column<string>("Password", col => col.WithLength(100))
            );

        ContentDefinitionManager.AlterTypeDefinition("NewBook", builder => builder
            .DisplayedAs("NewBook Settings")
            .WithPart("NewBookPart")
            .WithPart("CommonPart")
            .WithPart("IdentityPart")
            );
        return 1;
    }
}

HANDLER

public class NewBookPartHandler : ContentHandler
{
    public NewBookPartHandler(IRepository<NewBookPartRecord> repository)
    {
        Filters.Add(StorageFilter.For(repository));
    }
}

DRIVER

public class NewBookPartDriver : ContentPartDriver<NewBookPart>
{
    //GET
    protected override DriverResult Editor(NewBookPart part, dynamic shapeHelper)
    {
        return ContentShape("Parts_NewBook_Edit",
            () => shapeHelper.EditorTemplate(
                TemplateName: "Parts/NewBook",
                Model: part,
                Prefix: Prefix));
    }

    //POST
    protected override DriverResult Editor(NewBookPart part, IUpdateModel updater, dynamic shapeHelper)
    {
        updater.TryUpdateModel(part, Prefix, null, null);
        return Editor(part, shapeHelper);
    }

    protected override void Importing(NewBookPart part, ImportContentContext context)
    {
        context.ImportAttribute(part.PartDefinition.Name, "UserName", x => part.UserName = x);
        context.ImportAttribute(part.PartDefinition.Name, "InstanceToken", x => part.InstanceToken = x);
        context.ImportAttribute(part.PartDefinition.Name, "Password", x => part.Password = x);
    }

    protected override void Exporting(NewBookPart part, ExportContentContext context)
    {
        context.Element(part.PartDefinition.Name).SetAttributeValue("UserName", part.UserName);
        context.Element(part.PartDefinition.Name).SetAttributeValue("InstanceToken", part.InstanceToken);
        context.Element(part.PartDefinition.Name).SetAttributeValue("Password", part.Password);
    }
}

MODEL

public class NewBookPartRecord : ContentPartRecord {
    public virtual string UserName { get; set; }
    public virtual string InstanceToken { get; set; }
    public virtual string Password { get; set; }
}

public class NewBookPart : ContentPart<NewBookPartRecord> {
    [Required]
    public string UserName
    {
        get { return Record.UserName; }
        set { Record.UserName = value; }
    }
    [Required]
    public string InstanceToken
    {
        get { return Record.InstanceToken; }
        set { Record.InstanceToken = value; }
    }
    [Required]
    public string Password
    {
        get { return Record.Password; }
        set { Record.Password = value; }
    }
}

CONTROLLER

[Admin]
public class AdminController : Controller {
    private readonly IOrchardServices _orchardServices;
    private readonly IRepository<NewBookPartRecord> _nbRepository;
    private readonly IContentManager _contentManager;

    public AdminController(IOrchardServices orchardServices, IRepository<NewBookPartRecord> nbRepository, IContentManager contentManager)
    {
        _orchardServices = orchardServices;
        _nbRepository = nbRepository;
        _contentManager = contentManager;
    }

    public ActionResult Edit() {
        var nb = _nbRepository.Get(1);
        var viewModel = new NewBookPartViewModel();
        viewModel.UserName = nb.UserName;
        viewModel.InstanceToken = nb.InstanceToken;
        viewModel.Password = nb.Password;

        return View("Edit", viewModel);
    }

    [HttpPost]
    public ActionResult Edit(NewBookPartViewModel vm) {
        var nbRec = new NewBookPartRecord {
            Id = 1,
            UserName = vm.UserName,
            InstanceToken = vm.InstanceToken,
            Password = vm.Password
        };

        _nbRepository.Update(nbRec);
        return View("Edit", vm);
    }
}

Re: Import/Export not working for Cust. Module, nor Driver - really confused?

The reason that neither your driver nor handler are invoked is because you are not working with your content type and part through the IContentManager service. Instead, you bypass the content item's entire lifetime bu accessing the repository directly. In fact, you're not even dealing with content items in your controller.

The way to work with content items (with your custom part attached) is to work with the IContentManager service. This service lets you retrieve, store and display content items, and a few other things.

In your example, you could go about it in two ways:

  1. Use the ContentManager to build the editor shape for your content item, or:
  2. Manually create the view and its view model yourself (as you currently did).

I actually recommend going with the second approach when displaying entry forms on the front-end, since the content editor shapes are intended to be displayed on the back end anyway.

With that being said, this is how your controller should be updated (just updating the two Edit actions):

public ActionResult Edit() {
        var nb = _contentManager.Get<NewBookPart>(1);
        var viewModel = new NewBookPartViewModel();
        viewModel.UserName = nb.UserName;
        viewModel.InstanceToken = nb.InstanceToken;
        viewModel.Password = nb.Password;

        return View("Edit", viewModel);
    }

    [HttpPost]
    public ActionResult Edit(NewBookPartViewModel vm) {
        var nb = _contentManager.Get<NewBookPart>(vm.Id, VersionOptions.RequireDraft); // Request a new draft version.
        nb.UserName = vm.UserName;
            InstanceToken = vm.InstanceToken;
            Password = vm.Password;

        // Optionally publish the new draft. If you don't want the item to be published immediately, simply remove the next line. The draft will be updated automatically at the end of the HTTP request.
        _contentManager.Publish(nb);

        return View("Edit", vm);
    }
Friday, July 8, 2016 11:08:30 AM bysfmskywalker

Re: Import/Export not working for Cust. Module, nor Driver - really confused?

Thanks a lot sfmskywalker - I had something similar as above before going via the repository directly, but have since realized that I missed ' _contentManager.Publish(nb.ContentItem);', I just had ' _contentManager.Publish(nb);

So my updated code:

    public ActionResult Edit()
    {
        var nb = _contentManager.Query<NewBookPart>()
            .List()
            .FirstOrDefault();
        var viewModel = new NewBookPartViewModel();

        if (nb==null)
        {
            return View(viewModel);
        }
        viewModel.UserName = nb.UserName;
        viewModel.InstanceToken = nb.InstanceToken;
        viewModel.Password = nb.Password;

        return View("Edit", viewModel);
    }

    [HttpPost]
    public ActionResult Edit(NewBookPartViewModel vm) {
        var nb = _contentManager.Query<NewBookPart>()
            .List()
            .FirstOrDefault();
        if (nb == null) {
            nb = _contentManager.Create<NewBookPart>("NewBook");
        }
        nb.UserName = vm.UserName;
        nb.InstanceToken = vm.InstanceToken;
        nb.Password = vm.Password;

        _contentManager.Publish(nb.ContentItem);

        return View("Edit", vm);
    }

As this is intended for backend/admin only, how does one go about letting orchard handle the views directly as per your 'Use the ContentManager to build the editor shape for your content item...'?

I also see that I should have added this to Orchard's settings, but I will stay on topic to consolidate what I'm learning. Thanks for you great support, really appreciated :)

Monday, July 11, 2016 4:05:53 AM bypug
  • pug
  • Lv. 04 Rookie
  • Total EXP: 144

Re: Import/Export not working for Cust. Module, nor Driver - really confused?

No problem!

In the case of letting Orchard handle the editor view, it becomes even simpler:

  1. Get rid of the NewBookPartViewModel.
  2. Use the Content Manager service to build the editor shape and return that to the view. Have your view simply display that shape.
  3. In the post-back action method, invoke the UpdateEditor method on the Content Manager service (Note that your controller will have to implement the IUpdateModelinterface in order to). If model validation fails, make sure to cancel the transaction, because otherwise some content part changes may be persisted at the end of the request.

You'll find a perfect example of how this works in the Contents module in the Orchard.Core project when you look for the AdminController (look for the Edit and EditPOST methods).

Regarding the settings question, there are primarily two ways to do this:

  1. Instead of introducing a new content type, simply attach your custom part to the Site content type (which represents all of the site settings) using the ActivatingFilter (look at the SmtpSettings part and handler in Orchard.Email).
  2. Instead of implementing a driver, there is an even simpler way that does the plumbing for you using a filter called TemplateFilterForPart. This is basically a generic implementation of what you would normally do in a custom driver. Check out the AkismetSettingsPartHandler implementation in the Orchard.AntiSpam module for an example.

Please feel free to create another issue if you need more help with this.

Friday, July 15, 2016 12:28:53 PM bysfmskywalker

Post a reply

You need to be signed in to post a reply.

Sign In