请注意,本站并不支持低于IE8的浏览器,为了获得最佳效果,请下载最新的浏览器,推荐下载 Chrome浏览器
欢迎光临。交流群:166852192

Adding Dynamic Forms Content to Lists using Workflows


In this post we'll see how to write a custom Workflows activity that adds content items to a list. The idea is that when a content item is created, it automatically gets added to the configured list. this could be useful when for example setting up a form with Dynamic Forms that binds input fields to new content items.

Writing the AddToList Activity

The AddToListActivity will have just one setting to configure: the List to add the content item to.
The following listing provides a complete implementation for an activity that:
  1. Enables the user to configure a list to add content to.
  2. When executed, adds the current content item to the configured list.
using System.Collections.Generic;
using Orchard.ContentManagement;
using Orchard.Core.Containers.Models;
using Orchard.Core.Containers.Services;
using Orchard.Localization;
using Orchard.Workflows.Models;
using Orchard.Workflows.Services;

namespace IDeliverable.Demos.AddToListWorkflow.Activities {
    public class AddToListActivity : Task {
        private readonly IContentManager _contentManager;
        private readonly IContainerService _containerService;

        public AddToListActivity(IContentManager contentManager, IContainerService containerService) {
            _contentManager = contentManager;
            _containerService = containerService;
            T = NullLocalizer.Instance;
        }

        public Localizer T { get; set; }
        public override string Name => "AddToList";
        public override LocalizedString Category => T("Content");
        public override LocalizedString Description => T("Add content items to the specified list.");

        // The Form to display when editing this activity.
        public override string Form => "AddToListForm";

        public override bool CanExecute(WorkflowContext workflowContext, ActivityContext activityContext) {
            // If there is no current content item, there is nothing to add to the list, so exit.
            if(workflowContext.Content == null)
                return false;

            // We can only add the content item if it has the ContainablePart attached.
            if(!workflowContext.Content.Is<ContainablePart>())
                return false;

            // We can only add the current content item to the list if a list was specified.
            return GetTargetList(activityContext) != null;
        }

        public override IEnumerable<LocalizedString> GetPossibleOutcomes(WorkflowContext workflowContext, ActivityContext activityContext) {
            yield return T("Done");
        }

        public override IEnumerable<LocalizedString> Execute(WorkflowContext workflowContext, ActivityContext activityContext) {
            // Load the specified list.
            var targetList = GetTargetList(activityContext);

            // Add the current content item to the list.
            _containerService.MoveItem(workflowContext.Content.As<ContainablePart>(), targetList);

            // All set.
            yield return T("Done");
        }

        /// <summary>
        /// Reads the selected list ID from the activity context.
        /// If an ID was provided, the List is loaded via the content manager and returned.
        /// If no ID was specified, or no List was found by the specified ID, null is returned.
        /// </summary>
        private ContainerPart GetTargetList(ActivityContext activityContext) {
            var targetListId = activityContext.GetState<string>("TargetListId");

            if (targetListId == null)
                return null;

            var targetListIdentity = new ContentIdentity(targetListId);
            var targetList = _contentManager.ResolveIdentity(targetListIdentity);

            return targetList?.As<ContainerPart>();
        }
    }
}
The most interesting part of the activity is the code that acually moves the content item to the selected list.
The following listing shows the complete implementation for the associated form:
using System.Linq;
using System.Web.Mvc;
using Orchard;
using Orchard.ContentManagement;
using Orchard.Core.Containers.Services;
using Orchard.Forms.Services;

namespace IDeliverable.Demos.AddToListWorkflow.Forms {
    public class AddToListForm : Component, IFormProvider {
        private readonly IContainerService _containerService;
        private readonly IContentManager _contentManager;

        public AddToListForm(IContainerService containerService, IContentManager contentManager) {
            _containerService = containerService;
            _contentManager = contentManager;
        }

        public void Describe(DescribeContext context) {
            // Describe the "AddtoListForm".
            context.Form("AddToListForm", factory => {
                var shapeFactory = (dynamic) factory;

                // Create a Form shape.
                var form = shapeFactory.Form(
                    Id: "AddToListForm",

                    // Add a dropdown list named "TargetListId" that will provide the available
                    // List content items to choose from.
                    _TargetListId: shapeFactory.SelectList(
                        Id: "TargetListId",
                        Name: "TargetListId",
                        Title: T("Target List"),
                        Description: T("Select the target list to add content items to.")));

                // Get all available list content items.
                var lists = _containerService.GetContainers(VersionOptions.Latest).ToList();

                // Populate the dropdown list with available list content items.
                foreach (var list in lists) {
                    var listMetadata = _contentManager.GetItemMetadata(list);
                    var listTitle = listMetadata.DisplayText;

                    // Use the content identity instead of primary key value, so that export/import will work for this form.
                    var listIdentity = listMetadata.Identity.ToString();

                    form._TargetListId.Add(new SelectListItem {Text = listTitle, Value = listIdentity});
                }

                return form;
            });
        }
    }
}
Notice the usage of the IContainerService to fetch all list content items.
In order for this code to compile, make sure to add a project reference to both Orchard.Workflows and Orchard.Forms. Also, add these features to the list of dependencies of your module's manifest:
Module.txt
Name: IDeliverable.Demos.AddToListWorkflow
AntiForgery: enabled
Author: The Orchard Team
Website: http://orchardproject.net
Version: 1.0
OrchardVersion: 1.0
Description: Description for the module
Features:
   IDeliverable.Demos.AddToListWorkflow:
      Description: Description for feature IDeliverable.Demos.AddToListWorkflow.
      Dependencies: Orchard.Workflows, Orchard.Forms

Trying it out

With above activity in place, we can now use it to for example assign dynamicaly created content items using Dynamic Forms to a list, without cluttering the content list view. Let's see an example.
  1. Enable the feature we just created (IDeliverable.Demos.AddToListWorkflow) as well as the Dynamic Forms and Lists feature.
  2. Create a new content type called Contact Form Submission with the following parts and fields:
    1. Title Part
    2. Body Part
    3. Identity Part
    4. Containable Part
    5. Email (Text Field)
    6. Name (Text Field)
  3. Uncheck all of the following content type settings: CreatableListableDraftable and Securable. We don't want any of these content items to appear in the content list. Instead, we want to add them to a List content item that we'll create next.
  4. Create a new List content item called Contact Form Submissions. configure it such that it will only contain items of type Contact Form Submission. Also configure it to appear as a menu item on the admin menu. Use "Contact Form" as the menu item name, and position 6 for example.
  5. Create a new Form content item and add fields for NameEmailSubject and Message. Configure the form to create content items of type Contact Form Submission and configure the field bindings. These items should be saved as a draft in order to prevent them from being publicly accessible.
  6. Create a new Workflow with the following activities:
    1. Content Created -> Configure this to be the start activity and to trigger when items of type Contact Form Submission are created. Remember, the dynamic form is configured to create content items of this type.
    2. Add To List -> Configure this to use the Contact Form Submissions list.
With that in place, you now have a contact form that will generate content items of type Contact Form Submission, which will be added to theContact Form Submissions list.
The following screenshots demonstrate what I just described in a bit more detail:

Figure 1 - Enable the Dynamic Forms and Lists module, and of course the custom feature we're building.

Figure 2 - Create the Contact Form Submission content type. Items of this type will be created by the contact form.

Figure 3 - Create a new List content item that will contain the dynamicly created Contact Form Submission content items.

Figure 4 - Create a new Form content item with a Form element that contain a bunch of fields to be bound against dynamicaly created Contact Form Submission content items.

Figure 5 - The Form element is configured to create content items of type Contact Form Submission. They are saved as a draft to prevent them from being publicly accessible.

Figure 6 - Each field on the Form is bound to a part or field property of the Contact Form Submission type. The above screenshot shows the binding for the Subject text field, which is bound to the Name field on the content type.

Figure 7 - The workflow handling the Content Created event by adding the created content item to a list.

Figure 8 - Trying out the contact form from the front-end.
 
Figure 9 - After submitting the contact form, a new Contact Form Submission was created and added to the Contact Form Submissions list. 

Conclusion

The combination of Dynamic Forms and Workflows is a powerful one. Although we had to write a custom activity, it is easy to imagine that we can implement pretty advanced scenarios without the need for a single line of code once we have a rich set of workflow activities.
The source code can be downloaded here.
The module contains a Setup recipe that you can execute that will enable all of the features mentioned in this post, as well as the Contact Form Submission type, Contact Form Submissions List, Contact Form item and the workflow shown above.



作者原创内容不容易,如果觉得内容不错,请点击右侧“打赏”,赏俩给作者花花,也算是对作者付出的肯定,也可以鼓励作者原创更多更好内容。
更多详情欢迎到QQ群 166852192 交流。