Skip to main content
Actions are the link between what a user does in the shop and what the shop executes in response. Whether a customer adds a product to the basket, registers, or logs in — there is an action behind each of these operations. Actions accept the user’s input, process it in the background, and return to the frontend whether everything worked or an error occurred.

Basic principle

The shop provides a set of predefined actions. They are defined by the system and cannot be named freely. Each action has a fixed name, for example BasketItemAdd (add a product to the basket), Login (log in user), or AccountRegister (create a new account). Actions can be executed in three ways:
  • Via a form — the most common case. The user fills out a form and submits it.
  • Via a link — for actions without user input, for example logging out.
  • Via AJAX — when the page should not be reloaded after execution.
Regardless of how an action is executed, the same process always runs in the background: the shop receives the request, processes it, and reports back whether the action was successful or an error occurred. Some actions behave differently and return data directly instead of a new page, for example as JSON within the checkout. This deviating behavior is documented for the affected actions.

Preparing actions

Before an action can be used in a template, a so-called action object must be created. You can think of it as a container that holds everything needed to execute the action and, after submission, also contains the shop’s response. The action object is created with $wsActions.create(), where the name of the desired action is passed:
{{ var $myAction = $wsActions.create("BasketItemAdd") }}
Optionally, the target page to which the user is redirected after the action runs successfully can already be passed here:
{{ var $myAction = $wsActions.create("BasketItemAdd", target=$wsViews.viewUrl('basket.htm')) }}
The object does not trigger anything yet; it merely prepares the action. The action itself is executed only when the user submits the form, clicks a link, or an AJAX request is sent. After execution, the shop automatically fills the action object with feedback. In the template, this can then be accessed via the variable $myAction, for example with $myAction.success or $myAction.errors. A complete description of all available properties can be found in the $wsActions module reference.
PropertyDescription
$action.idThe ID of the action, which is required in the form so the shop knows what to execute.
$action.csrfA security token that protects every request (more on this here).
$action.successIs true when the action was executed successfully.
$action.errorIs true when an error occurred.
$action.errorsA list of all errors that occurred, with error code, affected field, and optional detail.
$action.paramsThe most recently entered values. Useful for displaying a form pre-filled after an error so the user doesn’t have to re-enter everything.

Executing actions

Execution via forms

The most common way to execute an action is via an HTML form. The prepared action object is embedded into the form, and the action is executed as soon as the user submits the form. The form is always sent to the URL of the current page so that the shop can rebuild the same page after execution and display the response:
<form method="POST" action="{{= $wsViews.current.url() }}">
Every form that executes an action requires three mandatory fields:
FieldValueDescription
wsact{{= $action.id }}Tells the shop which action should be executed.
wscsrf{{= $action.csrf }}A security token to protect against unauthorized access (see INSERT LINK HERE).
wstargete.g., {{= $wsViews.viewUrl('basket.htm') }}The page the user is redirected to after successful execution. The redirect also prevents a page reload from resubmitting the form.
In addition to the mandatory fields, the form contains the action-specific fields. Which ones these are is described in the documentation of the respective action. The following example shows a complete form for adding a product to the basket:
{{ var $myAction = $wsActions.create("BasketItemAdd") }}
<form method="POST" action="{{= $wsViews.current.url() }}">
  <input type="hidden" name="wsact"     value="{{= $myAction.id }}">
  <input type="hidden" name="wscsrf"    value="{{= $myAction.csrf }}">
  <input type="hidden" name="wstarget"  value="{{= $wsViews.viewUrl('basket.htm') }}">
  <input type="hidden" name="productId" value="12345">
  <input type="number" name="quantity"  value="1">
  <button type="submit">Add to basket</button>
</form>

Tags - distinguishing multiple forms of the same action type

If a page contains multiple forms of the same action type, for example an “Add to basket” button in each product box on a category page, all of these forms share the same response by default. This means: if an error occurs for one product, all forms on the page would display this error. A tag allows each action to be uniquely distinguished. The product ID, for example, is well-suited as a tag:
{{ var $action = $wsActions.create("BasketItemAdd", tag=$product.id) }}
From this, the shop generates a unique ID in the format BasketItemAdd:42 (where 42 would be the product ID in this example). This gives each product its own action object, and errors appear only on the affected form. For some actions, particularly in the checkout, the tag is used to specify the address type directly in the action name:
{{ var $commitBillAction     = $wsActions.create("CheckoutCommitDraftAddress:bill") }}
{{ var $commitShippingAction = $wsActions.create("CheckoutCommitDraftAddress:shipping") }}

Securing actions

Actions can be protected against unauthorized access in two ways — via a CSRF token, which is mandatory for every action, and optionally via a captcha.

CSRF protection

CSRF (Cross-Site Request Forgery) refers to an attack in which an external website unnoticeably sends requests to the shop on behalf of a logged-in user, for example to trigger an order or change account data. CSRF protection prevents the shop from accepting such requests. For this purpose, a one-time CSRF token is generated for each user session. Because the token is bound to the current session, an external website cannot know it and therefore cannot trigger the action unnoticed. The token is available directly on the action object and is embedded as follows:
<input type="hidden" name="wscsrf" value="{{= $action.csrf }}">

Captcha protection

For certain actions, for example registration or a contact form, a captcha can additionally be enabled. A captcha is a security check that ensures the form is filled out by a real human and not by an automated bot. If captcha protection is active for an action, the action is only executed if the user has solved the captcha successfully. If the check fails, the action is not executed. For protection to take effect, two prerequisites are required:
  1. The action must be entered under security.actionGuard in the shop configuration for captcha protection.
  2. The captcha widget must be included in the template of the corresponding form. More on this here.
If an action is executed via a confirmation link in an email (opt-in link), no captcha check takes place. In this case, the link itself serves as proof that a human has acted.
Actions without user input, for example logging out or removing an item from the basket, can be triggered via a link without the need for a form. For this purpose, a URL is generated with $wsActions.url(). The method expects three arguments: the action name, the target page after execution, and optional parameters as a list. If no optional parameters are needed, an empty list {} is passed:
<a href="{{= $wsActions.url('Logout', $wsViews.viewUrl('home.htm'), {}) }}">Log out</a>

Execution via AJAX

By default, the entire page is reloaded after a form is submitted. Sometimes this is not desired, however, for example when only a small area of the page should be updated without the user experiencing a visible reload. In such cases, the action can be executed via AJAX. The data is sent to the shop in the background and the response (success or error) is processed directly via JavaScript without reloading the page. CSRF protection is required just as for forms; the token can be retrieved via $wsActions.csrfToken. A typical use case:
The user adds a product to the basket and only the basket counter in the header updates; the rest of the page remains unchanged.
$.post("{{= $wsViews.viewUrl('ajax/actionResponse.json') }}", {
    "wscsrf":    "{{= $wsActions.csrfToken }}",
    "wsact":     "BasketItemAdd",
    "productId": "12345",
    "quantity":  2
}, function(data) {
    if (data.success) {
        // Action successful – e.g., update basket counter in the header
    } else {
        // Display error
        console.log(data.errors);
    }
});

Executing multiple actions simultaneously

Sometimes multiple actions should be executed in sequence with a single click on “Submit”. A typical example is the last step in the checkout, where the billing address and shipping address are confirmed and the order is completed. For this purpose, the wsmultiact field is used instead of the normal wsact field, once per action. wsact and wsmultiact cannot be combined in the same form. The actions are executed in the order in which the wsmultiact fields are listed in the form.\

Assigning fields to an action

Because multiple actions in the same form accept fields, each field must be clearly assigned to a specific action. This is done by a prefix before the field name, separated by |. In the template it looks like this:
<input type="hidden" name="{{= $commitBillAction.id }}|addressType" value="bill">
This means: the field addressType with the value bill belongs to the action $commitBillAction.\

An action’s error should stop the next one

By default, all actions are executed, even if one of them fails. If a failed action should abort the subsequent one, the parameter wsmultiactabortonerror can be set to on:
<input type="hidden" name="wsmultiactabortonerror" value="on">

Ignoring an action’s error

With wsmultiactignoreerror, individual actions can be marked as non-critical. If an action marked this way fails, this is not treated as a fatal error. The parameter can be specified multiple times, once per action:
<input type="hidden" name="wsmultiactignoreerror" value="{{= $action1.id }}">
<input type="hidden" name="wsmultiactignoreerror" value="{{= $action2.id }}">
Ignoring an error affects two situations:
  • Redirect despite error
    By default, the user remains on the current page in case of an error and is not redirected to the target page specified in wstarget. If the failed action is marked via wsmultiactignoreerror, the redirect still takes place.
  • Subsequent actions continue running
    If wsmultiactabortonerror=on is also set, an error would normally abort all subsequent actions. Actions marked via wsmultiactignoreerror do not trigger this abort — execution continues.
Example: completing the checkout
The following example shows the last step of a checkout. With a click on “Order now”, three actions are executed in sequence:
{{ var $commitBillAction     = $wsActions.create("CheckoutCommitDraftAddress:bill") }}
{{ var $commitShippingAction = $wsActions.create("CheckoutCommitDraftAddress:shipping") }}
{{ var $checkoutConfirm      = $wsActions.create("CheckoutConfirm") }}

<form method="POST" action="{{= $wsViews.current.url() }}">
  <input type="hidden" name="wscsrf"     value="{{= $commitBillAction.csrf }}">
  <input type="hidden" name="wsmultiact" value="{{= $commitBillAction.id }}">
  <input type="hidden" name="wsmultiact" value="{{= $commitShippingAction.id }}">
  <input type="hidden" name="wsmultiact" value="{{= $checkoutConfirm.id }}">
  <input type="hidden" name="wstarget"   value="{{= $wsViews.viewUrl('order-confirmation.htm') }}">
  <input type="hidden" name="{{= $commitBillAction.id }}|addressType"     value="bill">
  <input type="hidden" name="{{= $commitShippingAction.id }}|addressType" value="shipping">
  <button type="submit">Order now</button>
</form>
Execution order of the example:
  1. CheckoutCommitDraftAddress:bill - apply the billing address
  2. CheckoutCommitDraftAddress:shipping - apply the shipping address
  3. CheckoutConfirm - complete the order

Result of execution

Success

If the action was successful, $myAction.success equals true. In this case, a success message can be displayed or the user can be redirected to another page via wstarget.

Error

If the action fails, for example because a required field is missing or an email address is invalid, $myAction.error equals true. The error messages should be output in the template so the user knows what needs to be corrected:
{{ if $myAction.error }}
  <div class="alert alert-danger">
    Please correct the following entries:
    <ul>
      {{ foreach $error in $myAction.errors }}
        {{ if $error.text }}
          <li>{{= $error.text }} ({{= $error.field }}){{ if $error.subCode }} – {{= $error.subCode }}{{ /if }}</li>
        {{ else }}
          <li>{{= $error.code }} ({{= $error.field }}){{ if $error.subCode }} – {{= $error.subCode }}{{ /if }}</li>
        {{ /if }}
      {{ /foreach }}
    </ul>
  </div>
{{ /if }}
The error codes that a specific action can return are described in the documentation of the respective action. An explanation of all error properties can be found in the $wsActions module reference.

Redirect

After an action has been executed successfully, the user can be redirected to a target page. This also prevents a page reload from resubmitting the form. More on this here.

Actions overview

A complete overview of all available actions can be found in the respective topic areas: