Skip to content

Commit 684dc89

Browse files
authored
Funnel (#365)
* start implementing funnel configuration, #362 * fix setup files, #362 * implement funnel frontend workflow, #362 * respect signal workflow, introduce funnel process resolver, #362 * fix attachment stream: use filestack * improve channel response, simplify funnel runtime data, #362 * add core config section to funnel action, #362 * strict dynamic funnel actions, #362 * fix core configuration flag check * add some docs, require pimcore 10.5 * [Tests] add gc_probability to 0 * remove deprecated Session NamespacedAttributeBag which was unused * remove session from service DI * use eventlistener to boot extjs plugins * fix session declaration * add some upgrade notes
1 parent 9f7aae5 commit 684dc89

File tree

148 files changed

+5710
-293
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

148 files changed

+5710
-293
lines changed

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,18 @@
1010

1111
### Release Plan
1212

13-
| Release | Supported Pimcore Versions | Supported Symfony Versions | Release Date | Maintained | Branch |
14-
|---------|-----------------------------------|----------------------------|--------------|----------------|------------|
15-
| **4.x** | `10.1` - `10.5` | `^5.3` | 13.10.2021 | Feature Branch | master |
16-
| **3.x** | `6.0` - `6.9` | `3.4`, `^4.4` | 17.07.2019 | Bugfix only | [3.x](https://github.com/dachcom-digital/pimcore-formbuilder/tree/3.x) |
17-
| **2.7** | `5.4`, `5.5`, `5.6`, `5.7`, `5.8` | `3.4` | 27.06.2019 | Unsupported | [2.7](https://github.com/dachcom-digital/pimcore-formbuilder/tree/2.7) |
13+
| Release | Supported Pimcore Versions | Supported Symfony Versions | Release Date | Maintained | Branch |
14+
|---------|-----------------------------------|----------------------------|--------------|----------------|----------------------------------------------------------------------------------|
15+
| **4.x** | `10.1` - `10.5` | `^5.4` | 13.10.2021 | Feature Branch | master |
16+
| **3.x** | `6.0` - `6.9` | `3.4`, `^4.4` | 17.07.2019 | Bugfix only | [3.x](https://github.com/dachcom-digital/pimcore-formbuilder/tree/3.x) |
17+
| **2.7** | `5.4`, `5.5`, `5.6`, `5.7`, `5.8` | `3.4` | 27.06.2019 | Unsupported | [2.7](https://github.com/dachcom-digital/pimcore-formbuilder/tree/2.7) |
1818
| **1.5** | `4.0` | -- | 18.03.2017 | Unsupported | [pimcore4](https://github.com/dachcom-digital/pimcore-formbuilder/tree/pimcore4) |
1919

2020
## Installation
2121

2222
```json
2323
"require" : {
24-
"dachcom-digital/formbuilder" : "~4.1.0"
24+
"dachcom-digital/formbuilder" : "~4.2.0"
2525
}
2626
```
2727

@@ -44,9 +44,8 @@ It's also possible to render a form via Twig or even within a controller method.
4444
Nothing to tell here, it's just [Symfony](https://symfony.com/doc/current/templating/overriding.html) standard.
4545

4646
## Further Information
47-
- [SPAM Protection (Honeypot, reCAPTCHA)](docs/03_SpamProtection.md)
4847
- [Usage (Rendering Types, Configuration)](docs/0_Usage.md)
49-
- [Output Workflows](docs/OutputWorkflow/0_Usage.md)
48+
- [SPAM Protection (Honeypot, reCAPTCHA)](docs/03_SpamProtection.md)
5049
- [Output Workflows](docs/OutputWorkflow/0_Usage.md)
5150
- [API Channel](docs/OutputWorkflow/09_ApiChannel.md)
5251
- [Email Channel](docs/OutputWorkflow/10_EmailChannel.md)
@@ -55,6 +54,7 @@ Nothing to tell here, it's just [Symfony](https://symfony.com/doc/current/templa
5554
- [Output Transformer](docs/OutputWorkflow/15_OutputTransformer.md)
5655
- [Field Transformer](docs/OutputWorkflow/16_FieldTransformer.md)
5756
- [Success Management](docs/OutputWorkflow/20_SuccessManagement.md)
57+
- [Funnels](docs/OutputWorkflow/40_Funnels.md)
5858
- [Backend Administration of Forms](docs/01_BackendUsage.md)
5959
- [Export Forms](docs/02_ExportForms.md)
6060
- [Ajax Forms](docs/20_AjaxForms.md)

UPGRADE.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Upgrade Notes
22

3+
## Version 4.2.0
4+
- **[NEW FEATURE]**: Funnel Feature [#362](https://github.com/dachcom-digital/pimcore-formbuilder/issues/362)
5+
- **[FEATURE]**: PIMCORE 10.5 Support only!
6+
- **[ENHANCEMENT]**: Removed deprecated session `NamespacedAttributeBag` which was unused
7+
- **[ENHANCEMENT]**: Remove session from service DI
8+
- **[ENHANCEMENT]**: Use EventListener instead of legacy plugin architecture, to boot ExtJS extensions
9+
- **[DEPRECATION]**: Deprecated Method `assembleViewVars()` in `FormAssembler`, use `assemble()` instead
10+
311
## Version 4.1.4
412
- **[BUGFIX]**: Fix link to translation list [#359](https://github.com/dachcom-digital/pimcore-formbuilder/pull/359)
513
- **[BUGFIX]**: skip dependency if document is null [@JHeimbach](https://github.com/dachcom-digital/pimcore-formbuilder/pull/354)

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
}
3232
},
3333
"require": {
34-
"pimcore/pimcore": "^10.1",
34+
"pimcore/pimcore": "^10.5",
3535
"doctrine/orm": "^2.7"
3636
},
3737
"require-dev": {

docs/0_Usage.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ class DefaultController extends FrontendController
7979

8080
return $this->renderTemplate(
8181
'@FormBuilder/Form/form.html.twig',
82-
$assembler->assembleViewVars($optionBuilder)
82+
$assembler->assemble($optionBuilder)
8383
);
8484
}
8585
}

docs/OutputWorkflow/40_Funnels.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
## Funnels
2+
The Funnel-Feature allows you to create additional user journeys after the form has been submitted.
3+
4+
### Example
5+
In this example, we've implemented a full checkout which allows users to buy goods which they have selected in the initial form builder form.
6+
7+
> 💰 **Attention!** The cart feature itself is not available under the open source licence!
8+
9+
Below you'll find a brief walk through to get the idea behind this powerful feature:
10+
11+
![image](https://user-images.githubusercontent.com/700119/207104368-133d754a-c404-4e62-bfb6-e753bcccb1ad.png)
12+
13+
1. The first Funnel Layer renders a Summary Page (_"Checkout Summary Layer"_) by using a custom snippet. There are two actions:
14+
1. "Buy now" which will lead to the next channel
15+
2. "Back to form", which returns to the (restored) form
16+
2. The Cart Processor Channel processes the Payment itself (Off-Page Payment for example). Every default channel comes with two "virtual funnel actions":
17+
1. "On Success": Which will lead to the next channel
18+
2. "On Error:" "Back to form", which returns to the (restored) form
19+
3. The Email Channel (Which should be familiar to you) triggers an email submission. Since it's also a default chanel, the two virtual funnel actions most be defined:
20+
1. "On Success": Which will lead to the next channel
21+
2. "On Error:" "Back to form", which returns to the (restored) form
22+
4. The last Funnel Layer renders a "Thank You" Page (_"Dynamic Layout Layer"_) by using a custom snippet. There is just one action:
23+
1. "Done": A disabled action, so no button will be rendered
24+
25+
***
26+
27+
## Some important Facts
28+
- An existing workflow cannot be transformed into a funnel, just on creation time
29+
- A Funnel is not able to process the default success management, use a final funnel layer to restore this feature
30+
- The initial form will be stored within a storage provider. By default, a session storage will be shipped, but you're able to create your own storage provider
31+
- Every funnel layer creates a physical URL (Initially a UUID/v4 will be generated, but you're able to rename them)
32+
- Every funnel layer receives the `SubmissionEvent` object, so you're able to process users data which has been collected via form builders root form
33+
- Every funnel layer will be submitted as a form. If a funnel layer provides some additional form data, it will be stored within the storage provider via `FunnelRuntimeData`
34+
- After the last channel has been called OR an exception raised, a `funnel_finished` flag will be added to the url. This indicates, that the workflow is done and the current storage will be flushed
35+
36+
***
37+
38+
## Enable Funnel Feature
39+
Funnels are disabled by default, so let's enable it:
40+
41+
```yaml
42+
form_builder:
43+
funnel:
44+
enabled: true
45+
```
46+
47+
## Add Routes
48+
Import preconfigured funnel routes from the FormBuilder package or copy them into your project.
49+
Don't freak out, we're talking about one tiny route only:
50+
51+
```yaml
52+
# config/routes.yaml
53+
form_builder_routing_funnels:
54+
resource: '@FormBuilderBundle/Resources/config/pimcore/routing_funnels.yml'
55+
56+
# or import routes without the {locale} flag in routes
57+
# form_builder_routing_funnels:
58+
# resource: '@FormBuilderBundle/Resources/config/pimcore/routing_funnels_not_localized.yml'
59+
```
60+
61+
## Further Information
62+
- [Create a Funnel](./Funnel/0_CreateFunnel.md)
63+
- [Funnel Layer & Custom Funnel Layers](./Funnel/10_FunnelLayer.md)
64+
- [Funnel Actions & Custom Funnel Actions](./Funnel/20_FunnelActions.md)
65+
- [Storage Provider & Custom Storage Provider](./Funnel/30_StorageProvider.md)
66+
- [Custom Storage Class](./Funnel/40_CustomStorageClass.md)
67+
- [Root Form Serialization Groups](./Funnel/50_RootForm.md)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
## Funnels | Create a Funnel
2+
![image](https://user-images.githubusercontent.com/700119/207111195-9d2921c4-1cb6-4910-95e8-038533a4b4cf.png)
3+
4+
An existing workflow cannot be transformed into a funnel, just on creation time.
5+
So after you've clicked the "Add Output Workflow" Button, you need to check the "Is Funnel Output Workflow"
6+
7+
### Funnel Layer Naming
8+
![image](https://user-images.githubusercontent.com/700119/207261685-9020243a-3660-4220-add8-8b8d42de9101.png)
9+
Each funnel layer is available within a dedicated route: `/fb/funnel/{funnelId}/{channelId}/{storageToken}`.
10+
After you've added a fresh funnel layer, a UUID/V4 as `{channelId}` will be generated.
11+
You're able to rename the funnel layer after clicking on the blue pencil.
12+
13+
### The Funnel Layer
14+
![image](https://user-images.githubusercontent.com/700119/207261230-c9d7253f-5c55-4753-afee-c455951dd62f.png)
15+
Every Funnel Workflow comes with a new Channel, called "Funnel Layer".
16+
17+
Alright, let's dive deep [into the funnel layers](./10_FunnelLayer.md).
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
## Funnel Layer
2+
A Funnel Layer is basically the same as an output workflow you already know.
3+
The big difference: It's a real frontend page with different actions, triggered by the user itself.
4+
5+
![image](https://user-images.githubusercontent.com/700119/207115270-37c44d6e-c493-45b4-ab0f-9f7c3d97e7b6.png)
6+
7+
### Dynamic Layout Layer
8+
By default, there is only one layer, a so-called "_Dynamic Layout Layer_".
9+
10+
This layer is configured as `dynamicFunnelActionAware`, which means:
11+
- that you need to define you're own actions (See next chapter)
12+
- that you need to place them in your snippet template
13+
14+
So, go ahead, create a snippet and drag it into the "Layout" field.
15+
16+
Next, we need to configure some [funnel actions](./20_FunnelActions.md).
17+
18+
***
19+
20+
### Custom Funnel Layer
21+
It is very easy to configure a custom funnel layer.
22+
23+
#### Configuration
24+
```yaml
25+
services:
26+
App\Funnel\DummyLayer:
27+
tags:
28+
- { name: form_builder.output_workflow.funnel_layer, type: dummyLayer }
29+
```
30+
31+
#### PHP Service
32+
```php
33+
<?php
34+
35+
namespace App\Funnel;
36+
37+
use FormBuilderBundle\Model\FunnelActionDefinition;
38+
use FormBuilderBundle\OutputWorkflow\Channel\Funnel\Layer\FunnelLayerData;
39+
use FormBuilderBundle\OutputWorkflow\Channel\Funnel\Layer\FunnelLayerInterface;
40+
use Symfony\Component\Form\Extension\Core\Type\TextType;
41+
use Symfony\Component\Form\FormBuilderInterface;
42+
43+
class DummyLayer implements FunnelLayerInterface
44+
{
45+
public function getName(): string
46+
{
47+
return 'Dummy Layer';
48+
}
49+
50+
public function getFormType(): array
51+
{
52+
/**
53+
* If you want to configure additional fields in backend, add them here
54+
*/
55+
56+
return [
57+
'type' => TextType::class,
58+
'options' => []
59+
];
60+
}
61+
62+
public function dynamicFunnelActionAware(): bool
63+
{
64+
/**
65+
* If you're returning true,
66+
* user is allowed to define its own funnel actions,
67+
* and you should return an empty array in the method below: getFunnelActionDefinitions()
68+
*/
69+
70+
return false;
71+
}
72+
73+
public function getFunnelActionDefinitions(): array
74+
{
75+
/**
76+
* If you want to define some predefined actions,
77+
* dynamicFunnelActionAware() needs to return false.
78+
* Then you're able to add them in your funnel layer layout (see template below)
79+
*/
80+
81+
return [
82+
new FunnelActionDefinition('dummyButton', 'Top Button')
83+
];
84+
}
85+
86+
87+
public function buildForm(FunnelLayerData $funnelLayerData, FormBuilderInterface $formBuilder): void
88+
{
89+
/**
90+
* If you need additional information from a given user,
91+
* just add some additional fields to the Layer form type.
92+
* You're also able to fetch this submitted data in upcoming channels
93+
* via SubmissionEvent->getFunnelRuntimeData()
94+
*/
95+
96+
$formBuilder->add('consent', CheckboxType::class, ['constraints' => [new IsTrue()]]);
97+
}
98+
99+
public function handleFormData(FunnelLayerData $funnelLayerData, array $formData): array
100+
{
101+
return $formData;
102+
}
103+
104+
public function buildView(FunnelLayerData $funnelLayerData): void
105+
{
106+
$funnelLayerConfiguration = $funnelLayerData->getFunnelLayerConfiguration();
107+
108+
$viewArguments = [
109+
'dummyField' => $funnelLayerConfiguration['dummyField']
110+
];
111+
112+
$funnelLayerData->setFunnelLayerView('/funnel/dummy_layer.html.twig');
113+
$funnelLayerData->setFunnelLayerViewArguments($viewArguments);
114+
}
115+
}
116+
```
117+
118+
#### JS Service
119+
```javascript
120+
pimcore.registerNS('Formbuilder.extjs.formPanel.outputWorkflow.funnelLayer.dummyLayer');
121+
Formbuilder.extjs.formPanel.outputWorkflow.funnelLayer.dummyLayer = Class.create(Formbuilder.extjs.formPanel.outputWorkflow.funnelLayer.abstractLayer, {
122+
123+
getConfigItems: function () {
124+
return [
125+
{
126+
xtype:'textfield',
127+
name: 'dummyField',
128+
fieldLabel: 'My Dummy Field'
129+
}
130+
];
131+
}
132+
});
133+
```
134+
135+
#### Twig Partial
136+
```twig
137+
{% if formTheme is not null %}
138+
{% form_theme form with (formTheme ~ '.html.twig') only %}
139+
{% endif %}
140+
141+
{% block funnel_content %}
142+
143+
{{ dump(dummyField) }}
144+
145+
<div class="row">
146+
{{ form_widget(form.consent) }}
147+
</div>
148+
149+
{% if funnelActions.hasByName('dummyButton') %}
150+
{{ form_widget(form.dummyButton, {attr: {class: 'btn-primary'}, label: 'Do something' }) }}
151+
{% endif %}
152+
153+
{% endblock funnel_content %}
154+
```
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
## Funnel: Funnel Actions
2+
3+
There are two types of general funnel actions.
4+
5+
### Dynamic Actions
6+
![image](https://user-images.githubusercontent.com/700119/207158937-39e05e12-473f-4c5c-a69b-ec2d8e333c3a.png)
7+
If a funnel channel is configured as `dynamicFunnelActionAware`, an admin is able to add 1:n action definitions.
8+
Additionally, template placeholder will be available.
9+
10+
### Preconfigured Actions
11+
![image](https://user-images.githubusercontent.com/700119/207161010-0077440a-317d-4035-9acf-fc2ffbbd3b83.png)
12+
If a funnel channel has preconfigured action definitions by returning an array via `getFunnelActionDefinitions`,
13+
an admin is able to set actions to these exact predefined buttons.
14+
15+
### Global Configuration
16+
- `Allow Invalid Form Submission`: This allows you to define an action, which skips any form constraints
17+
18+
***
19+
20+
### Funnel Action Types
21+
There some predefined funnel action types:
22+
23+
#### Type: Channel Route Action
24+
![image](https://user-images.githubusercontent.com/700119/207159146-4f065da1-dff1-4c38-a515-026969611f54.png)
25+
This action navigates to a channel.
26+
27+
- `Allow Invalid Form Submission`: This allows you to define an action, which skips any form constraints.
28+
29+
#### Type: Form Route Action
30+
![image](https://user-images.githubusercontent.com/700119/207159387-48d82e4a-c21b-44ac-bcfb-a9f351426975.png)
31+
This action navigates back to the form.
32+
33+
- `Populate Form`: Restores form values
34+
35+
#### Type: Disable Action
36+
![image](https://user-images.githubusercontent.com/700119/207159504-8bb6f92e-1e0f-420c-b43d-af0793465e7d.png)
37+
38+
This action skips any further process (mostly used if a given layer comes with some predefined action definitions)
39+
No further configuration needed. This action won't show up in form rendering process.

0 commit comments

Comments
 (0)