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

Customizing User Registration and Login with Dynamic Forms and Workflows


In this tutorial we are gonna checkout some of the new features that were introduced with the advent of Orchard 1.9.
Specifically, we are going to see how we can leverage Dynamic Forms and Workflows to create our own Login and Registration screens without the need for a custom module.
So shutdown Visual Studio (unless you're using it to launch IISExpress) and let's get started!


Customizing User Registration and Login

The goal of this tutorial is demonstrate how you can create a custom Registration and Login screen usingDynamic Forms and Workflows without the need for a custom module.
We will extend the User type to store additional information about the user: first name and last name, just to demonstrate how to collect and store additional information about the user during the registration process.
As we shall see, there are two approaches to using a form for the creation of a user. The first approach uses a method called binding, while the second approach uses a Workflow activity called CreateUser.
We will look at both of them.

Exending the User type

Oftentimes there are cases where you need to store additional information as part of a user. This can be done in a variety of ways. For our sample, we will simply attach two content fields to the User content type:FirstName and LastName.
To attach these fields to the User content type, go to Content Definition, edit the User type, and add twoTextFields. Name them FirstName and LastName, respectively. The Display Name for these fields can be anything you like. I named them "First Name" and "Last Name".

Creating The Registration Form

The next thing we need to do is create a new Form content item. Enable the Dynamic Forms feature if you haven't already done so, and click on the "Form" menu item in the "New" section of the admin menu.
When we create a new Form content item, we will start out with a single Form and Button element. The first thing we will want to do is give this form element a name and enable client validation.
Specify "Registration" for the form's name and enabled client validation.
Note: the Form content item itself will be called "Register". Don't confuse this with the Form element, which will be called "Registration". We could use the same names if we wanted to, but I think "register" makes more sense for the content item and its URL, whereas submissions of the form element itself kind of represent a registration. Anyway, up to you.
We will not store the submitted form, but we will want to create a new content item when the form is submitted: we will create a new User content item.
The Form Element properties should look like this:

Next, we will add the neccessary form elements to our form.
For our registration form, we want the user to provide the following fields:
FieldElement Type
First nameText Field
Last nameText Field
EmailEmail Field
PasswordPassword Field
Confirm passwordPassword Field
Accept terms and conditionsCheckbox
We will also add a Validation Summary element to show any validation errors to the user.
The form will look like the following in design mode:

For each element, we can configure their name, whether or not to show a label, configure bindings, validation, and more.
The settings should be pretty much self-explanatory, but let's have a look at the Bindings section for the First Name field.

Bindings


What we see here is a list of bindings. A binding enables you to route an incoming form value to a part or field on the content item being created. The incoming form value is provided by the form element, which in the case of the First Name field is a TextField. Since the name of this field is "FirstText", the form value with that key will be supplied to the selected bindings.
Since we configured the form element to create a new content item of type User, the bindings section of a given form field element will enumerate all content fields and parts of that type for which a binding is available.
At the moment of this writing, the following bindings are available:
  • BodyPartBindings
  • TitlePartBindings
  • UserPartBindings
  • TextFieldBindings
If the User content type would have a BodyPart attached, then we would see its bindings as well.
As seen in the above picture, we checked the FirstName.Text binding, which means that any value submitted for this First Name text field element will be set to the Text property of the FirstName content field on the User content type.
We'll do the same for the Last Name, UserName, Email and Password fields.
However, when we try and select the bindings for the UserPart, we will find that these bindings don't appear in the list of bindings. What's going on?
The way the Bindings screen works is by inspecting the Content Type definition of the configured content type. Since the UserPart is welded onto the User content item dynamically at runtime, it won't find this part on the User type.
This is easily fixed by following these steps:
  1. Go to Content Definition -> Content Parts and create a new part called UserPart. Make sure this part is Attachable. Although the Description field is optional, it is considered good practice to always provide a description to parts. I specified Turns your content type into a user." as its description.
  2. Go to the User type edit screen and attach the UserPart.
  3. Optional: go back to the UserPart edit screen and uncheck the "Attachable" checkbox. This step is not necessary, but since we won't be attaching this part to any other content type, I'd like to keep things tidy.
Now when you go back to the Form editor and edit the UserName field element, you'll see the UserPart bindings as seen in the previous image.

Validation

An important aspect of most forms is input validation. Some examples of input validation are:
  • A value is required;
  • A value must have a minimum length and may not exceed a certain maximum;
  • A value must fall within a certain numerical range;
  • A value must match a certain pattern;
  • A value must match the value of another field;
With Dynamic Forms, each element type provides its own validation settings that we can configure. For example, the Text Field element supports a Required, Minimum Length and Maximum Length setting.

We can optionally provide a custom validation message that will be displayed when any of these rules add a validation error. If we leave that field empty, a default validation error will be presented to the user.
The "Show Validation Message" provides us with a way to automatically render the validation error message close to the input field. Since elements are rendered using shapes, this can be customized on a theme level.
Alternatively, we could add a Validation Message element to our form and position it anywhere we like, just as long as it is a child of the same Form element.
In this tutorial, we simply added the Validation Summary element to the beginning of our form. For this element to work properly, it too must be a child of the Form element.

Client Validation

Dynamic Forms supports client validation. To enable this, simply go to the Form element's properties, and check the "Enable Client Validation" option.
Now the form fields will be validated on the client side (in addition to serverside validation).
For the "Confirm Password" field (which is a Password Field element) we will use the Compare validator:

All we have to do is specify the name of the element whos value to compare with.
And that is basically all it takes to create a custom Registration form!
But wait a minute, what happens if the user tries to register with a username or email that is already taken?
Oh dear, that would be a problem indeed. We need unique logins. Also, we want to inform the user if he tries to register with an already existing username or email address.
And what if we want to assign the newly created user a role and send a welcome email?
Do we need a custom module after all?
Not if Orchard Workflows has any say in the matter!

Workflows

Orchard Workflows is a feature introduced with 1.7, and provides us with a way to visually program rules. They are like Orchard Rules, but way more powerful.
Dynamic Forms comes with a small set of workflow activities that will enable us to control what happens when a form is submitted. This enables us to prevent any from being submitted by adding a model error.
Let's define what our workflow should do when a Registration form is submitted:
  • Before the User content item is actually created, we want to validate the username and email. If the user name or email address is already in use, we want to add a model error. That will both notify the user of the error as well as prevent the form from actually creating the User content item.
  • If the user name and email are available, we want to automatically approve the user, create an authentication cookie, assign them to the Contributor role, send them a welcome email, and redirect him to the Admin dashboard.
Such a workflow would look like this:



Note that the start activity is the "Dynamic Form Validating" event (which is configured to only capture this event for the "Registration" form).
This event is triggered before the "Dynamic Form Submitted" event, and gives us a chance to perform custom validation and prevent the form from submitting data / creating content by adding a model error.
We use the Verify User Unicity activity to validate the specified user name and email like this:

What we see here is the usage of a new token provided by the Dynamic Forms module: {FormSubmission.Field:*}, where the  * is to be substituted with the form field name.
However, there is one problem with this workflow: the Approve User activity works only if the content item associated with the workflow is a User, but when a form is being validated, no content item is created yet.
This means that we need to devide our workflow into two: one workflow to validate the form, and another workflow to approve the user, login the user, assign a role, and send the welcome email.
After the modifications, the first workflow will look like this:

The second workflow, the one that will execute only after a user has been created, will look like this:

Here, we listen for the Content Published event (configured to trigger only for new published User events).
The Approve User activity will approve this user, then we assign the user the "Contributer" role, sigin in the user, send a welcome message, display a "Welcome" notification, and redirect to the administration dashboard.

An Alternative Approach

Although the approach we've taken works nicely, I want to show you a slightly different approach.
This time, we won't be creating a new User content item via binding, but by using the Create User activity.
The idea here is that we will simply listen for the Form Submitted event, and execute the Create User activity. The Create User activity has two branches: if the specified user name and or email is already in use, we add a model error. Otherwise, we continue and assign the user a role, send a welcome message, etc.
This enables us to use a single workflow.
Let's see how that works.
First of all, we need to change our Form element configuration by not creating a User content item anymore:

Next, we'll delete the two workflows created earlier, and create a new one:



This time, we listen for the "Form Submitted" event (specifically for the "Registration" form to be submitted).
When that happens, we execute the Create User activity, which is configured as follows:

The Create User activity requires a user name, email and password, which we provide using the FormSubmission.Fields:* token.
We also checked the "Approved" checkbox so that the user is autmatically approved, so no need to use the Approve User activity.
Now, we are also expecting the First Name and Last Name fields, which we want to store as part of the new user.
With the bindings approach, we were able to bind these form fields with the new user. How to do it without?
Meet the Decision activity. This activity, provided by the "C# Scripting" feature, enables as to execute C# code.
By lack of a "Bind Form" activity, we shall use the Decision activity with the following script:

We configured the Decision activity with a single outcome: "Done".
The script updates the two TextFields with the values provided by the {FormSubmission.Field:*} token. Note that we are using the new token syntax (#{}) and that these tokens are enclosed with quotation marks; the Decision activity will first tokenize the script, then execute.
The Sign In User activity does not need to be configured; it will sign in the workflow's current content item if that content item is a user. The Create User activity not only creates a user, but also sets the workflow's content item context to that user.
The rest of the workflow should be self-explanatory.

Login

Great! We have seen how we can create a custom registration screen, now let's see how we can create a custom login screen.
We will take a similar approach: we design a form and setup a workflow to handle its submission.
 Let's create a new Form content item that looks like this:

Remember to also configure the Form element by setting its Name and optionally enabling Client validation:

Now that we have a form, all we need to do is setup a workflow to handle its submission event.
When the Login form is submitted, we will want to:
  • Sign in the user, creating a persistent cookie if the "Remember Me" checkbox was checked;
  • Show a validation error if login failed and log this attempt;
  • Redirect the user to the dashboard if login succeeded and show a "Welcome back!" notification.
Such a workflow would look like this:

The most interesting activity here us the Sign In User activity, which is configured as follows:

Once again, we are providing the required values using the all-awesome{FormSubmission.Field:*} token.
Note: the "Create Persistent Cookie" value must evaluate to a value that is non-empty, and not equal to "false", "no" and "off". All other values will be interpreted as "true".
And that's it!
Now, a more advanced scenario would enable you to require a user to activate his account using some activation link.
To do that, you would update the Registration workflow to not automatically approve and sign in the user, but by just sending the Welcome email and an activation link.
This activation link can be generated using the {Workflow.TriggerUrl:*} token, which generates a nonce based on the current content item ID and provided signal name.
When the user clicks this activation link, they would be directed back to the website, triggering theSignal activity. This activity could either be the starting activity of a separate workflow, or it could resume the workflow that also created the user.
Let's see how that works.

Implementing the Activation Link

First of all, let's update our Registration workflow so that it does not approve and login the user. The updated workflow looks like this:

This looks almost exactly the same (we removed the Sign In User activity), but let's have a look at the updated activities.

Create User Activity

This activity was updated to not automatically approve the user:

Send Email Activity

This activity now looks as follows:

Notice that we're using the {Workflow.TriggerUrl:*} token to generate the URL for the activation link and that we are specifying "ActivateAccount" as the signal name.
This token will generate a URL and a nonce querystring parameter that contains the content item ID and signal name, encrypted.
Because this value is encrypted, no one will be able to tamper with it - only the recipient of the email will be able to trigger the "ActivateAccount" signal for his user account (which ID is part of the nonce).
All that's left is setup the workflow to listen for the signal event.

Activating the User Account

As mentioned earlier, when the user clicks on the activation link, they are directed to some URL on the website that will trigger a Signal event.
We shall create a new workflow that specifically listens for this event. When this event is triggered, we want to perform the following actions:
  • Activate the user's account;
  • Automatically login the user;
  • Show a notification confirming the user that their account is now activated;
  • Redirect to the dashboard.
Such a workflow would look like this:

The Signal activity is configured as follows:



Note that this is the same value as was used when generating the activation link ({Workflow.TriggerUrl:ActivateAccount}).

Conclusion

And there we have it! We have seen how we can create a custom Registration and Login form using the Dynamic Forms and Workflows modules without the need for a a custom module.
The two modules combined allow for a great amount of configuration and flexibility.
This article didn't show every step of the way as that would add a significant amount of images, so instead we created a screencast that demonstrates the construction of the various forms and workflows, as well as demonstrating how they look and function on the front end.
Enjoy!

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