Question

I want to serve different views which use different ViewModel objects depending on Actions. This can be achieved in a conventional ASP.NET MVC paradigm.

[HttpGet]
public ActionResult Create() {
    return View(new CreateViewModel()); //this serves Create.cshtml View
}

[HttpGet, ActionName("Create")]
public ActionResult CreatePOST(CreateViewModel viewModel) {
    if (!ModelState.IsValid) {
        return View(viewModel); //if error, re-serve Create.cshtml View
    }

    // Create new model into database

    return RedirectToAction("Index");
}

[HttpGet]
public ActionResult Edit(int Id) {
    var model = RetriveModel(Id);
    var viewModel = new EditViewModel { Id = model.Id, Name = model.Name };
    return View(viewModel); //this serves Edit.cshtml
}

[HttpPost, ActionName("Edit")]
public ActionResult EditPOST(EditViewModel viewModel) {
    if (!ModelState.IsValid) {
        return View(viewModel); //if error, re-serve Edit.cshtml View
    }

    // Update model in database

    return RedirectToAction("Index");
}

How do I do the same to Orchard Content Part? It seems that the overridable Editor method in a ContentPartDriver fused both Create and Update actions together. How do I tell if the method is creating or updating? Something like

// GET
protected override DriverResult Editor(CustomContentPart part, dynamic shapeHelper) {
    if (IsNewRecord) {
        return ContentShape("Parts_CustomContentPart_Create" () =>
            shapeHelper.EditorTemplate(TemplateName: "Parts/CreateTemplate", Model: new CreateViewModel(), Prefix: Prefix)
        );
    } else {
        return ContentShape("Parts_CustomContentPart_Edit" () =>
            shapeHelper.EditorTemplate(TemplateName: "Parts/EditTemplate", Model: BuildEditViewModel(part), Prefix: Prefix)
        );
    }
}

// POST
protected override DriverResult Editor(CustomContentPart part, IUpdateModel updater, dynamic shapeHelper) {
    object viewModel;
    if (IsNewRecord) {
        viewModel = new CreateViewModel();
    } else {
        viewModel = new EditViewModel();
    }

    update.TryUpdateModel(viewModel, Prefix, null, null);
    return Editor(part, shapeHelper);
}

I'm a beginner in Orchard still learning the ropes on how Orchard does things. Pardon me if my questions are too trivial.

Was it helpful?

Solution 2

Although this question has been asked and answered, just thought of posting my findings so I can find it later.

ContentItem.Id is indeed 0 when the content item isn't created yet. For example, when you're about to create a new Page, ContentItem.Id == 0. Now just click on the Save button without filling up the form and validation will fail since the required field Title wasn't provided and we're getting back the same view with an error. Since validation failed, technically we don't consider the content item to be created yet. However, at this point Orchard already treating it as an existing content item. Orchard even went as far as obtaining and increasing the Identity counter of the Content Item Record table (Orchard_Framework_ContentItemRecord) from the database and assigning it as an Id to the content item.

Orchard even wired up all the Version Records, making it pretty much a full-fledged content item. All these for a content item that failed validation during creation and facing possibility of being discarded altogether. The only thing Orchard hasn't done is inserting it into the database (it's only residing in memory at this point). Therefore there's really no other ways to tell if a content item is an existing one or one that was about to be created other than checking it against the database and see if the content item was really there.

var contentItemRepository = _workContext.Resolve<IRepository<ContentItemRecord>>();
var contentItemRecord = contentItemRepository.Get(Model.ContentItem.Id);

if (contentItemRecord == null) {
    isNew = true;
}

or we could also use the IContentManager to do the same thing

var contentManager = Model.ContentItem.ContentManager;
var contentItem = contentManager.Get(Model.ContentItem.Id, VersionOptions.AllVersions);

if (contentItem == null) {
    isNew = true;
}


Edit:

Apparently I spoke too soon. When I said above that Orchard hasn't inserted the content item into the database yet and it still resides in memory, it actually already in the database, in a yet to be committed Transaction. In the case above where validation fails, the transaction will be rolled back at the end. The correctness of the above code depends on when it was executed. If it was executed before the transaction was cancelled and rolled back, the content item is still in the database and won't yield an accurate result. If it was executed after transaction rollback (eg. in a View), then it'll behave as expected.

How Orchard handles content item creation can be seen in Orchard.Core.Contents.Controllers.AdminController.CreatePOST(string, string, Action<ContentItem>):

_contentManager.Create(contentItem, VersionOptions.Draft);

var model = _contentManager.UpdateEditor(contentItem, this);

if (!ModelState.IsValid) {
    _transactionManager.Cancel();
    return View(model);
}

The content item was being created first before it was being fed into IContentManager.UpdateEditor() to validate.


Update:

Filed a bug at https://github.com/OrchardCMS/Orchard/issues/6534

OTHER TIPS

Check for a content item id, if it is null, or possibly 0, I forget, then you are in the process of creating a content item. If it does have a value then you are editing. You can also use this in your view, can be quite handy.

If you need custom functionality to be called on creation/updating then you could consider using handler methods?

So in your parts handler add something like

OnCreated<MyPart>((ctx, part) => CreateItems(part));

Where CreateItems is a method with your part as a parameter. There are a bunch of content item events you can hook into, there is a neat list in the docs: http://docs.orchardproject.net/Documentation/Understanding-content-handlers

As always, check the source code for good examples of their usage.

EDIT

Apparently checking for null id doesn't work, I checked in some of my modules were I used it and have used the following check:

Model.ContentItem.VersionRecord == null || !Model.ContentItem.VersionRecord.Published
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top