Skip to content

Commit 4231e92

Browse files
committed
Initial commit
0 parents  commit 4231e92

File tree

110 files changed

+6954
-0
lines changed

Some content is hidden

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

110 files changed

+6954
-0
lines changed

.gitignore

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/.settings
2+
/.buildpath
3+
/.project
4+
/.idea
5+
composer.lock
6+
/.vagrant

LICENSE

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Copyright (c) 2019 Daniel Nahrebecki <[email protected]>
2+
3+
The Open Software License version 3.0
4+
5+
Full license is at: http://opensource.org/licenses/OSL-3.0

README.md

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# B2Bcodext - Cms Form Builder
2+
3+
CMS Form Builder is a flexible OroCommerce extension that allows you to easily create
4+
forms via UI.
5+
6+
No longer need to have a dev team in order to add a form to your storefront. With this extension you can create forms in minutes without writing even a single line of code.
7+
8+
## Installation
9+
10+
The easiest way to install CMS Form Builder is by using [Composer](https://getcomposer.org):
11+
12+
```bash
13+
curl -sS https://getcomposer.org/installer | php
14+
php composer.phar require b2bcodext/cms-form-builder
15+
```
16+
17+
or if you have Composer already installed, just run:
18+
19+
```bash
20+
composer require b2bcodext/cms-form-builder
21+
```
22+
23+
After installation and running `oro:platform:update`, forms are accessible under Marketing > Cms Forms in the back-office menu. Read more on [how you can create your first form.](./src/B2bCode/Bundle/CmsFormBundle/Resources/doc/user_doc.md#how-to-create-your-first-form)
24+
25+
26+
## Features
27+
28+
- Easily embed your forms in landing pages.
29+
- Email notifications sent on every form response.
30+
- Possibility to set different custom email templates per email.
31+
- Export form responses to CSV.
32+
- Possibility to add additional CSS to form fields directly from UI.
33+
- Many field types supported out of the box, like Email (with validation support) or Hidden field. [Full list](./src/B2bCode/Bundle/CmsFormBundle/Resources/doc/field_types.md).
34+
- Especially useful when creating special forms for marketing campaign which gives you possibility to embed custom values (like campaign code) without exposing that to end users
35+
- Seamless integration with ORO Reports engine. Powerful tool for creating custom reports based on form responses.
36+
37+
### Features for developers
38+
39+
> Flexibility Driven Development
40+
41+
- Easy custom validation rules setup via YAML or events. [More info](./src/B2bCode/Bundle/CmsFormBundle/Resources/doc/dev_doc.md#validation).
42+
- E.g. check if a given email already exists in a system or validate product SKU from Request for Quote form
43+
- Easily create your own form field types with custom type-specific options and/or custom validation. [Have a look at the example.](./src/B2bCode/Bundle/CmsFormBundle/Resources/doc/dev_doc.md#how-to-add-new-field-type)
44+
- Most of the functionality is covered either with interfaces, events or DI tags which gives you a possibility to inject your code on every single stage of the process.
45+
46+
## Documentation
47+
48+
- [For developers](./src/B2bCode/Bundle/CmsFormBundle/Resources/doc/dev_doc.md)
49+
- [For users](./src/B2bCode/Bundle/CmsFormBundle/Resources/doc/user_doc.md)
50+
51+
## License
52+
53+
[OSL-3.0](./LICENSE) Copyright (c) 2019 Daniel Nahrebecki <[email protected]>

composer.json

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "b2bcodext/cms-form-builder",
3+
"description": "B2Bcodext - CMS Form Builder for OroCommerce to easily create forms via UI",
4+
"license": "OSL-3.0",
5+
"authors": [
6+
{
7+
"name": "Daniel Nahrebecki",
8+
"email": "[email protected]"
9+
}
10+
],
11+
"autoload": {
12+
"psr-4": {
13+
"": "src/"
14+
},
15+
"exclude-from-classmap": [
16+
"**/Tests/"
17+
]
18+
},
19+
"require": {
20+
"oro/commerce": "3.1.*"
21+
},
22+
"minimum-stability": "dev"
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the B2Bcodext CMS Form Builder.
5+
*
6+
* (c) Daniel Nahrebecki <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace B2bCode\Bundle\CmsFormBundle;
13+
14+
use B2bCode\Bundle\CmsFormBundle\DependencyInjection\Compiler\TwigSandboxConfigurationPass;
15+
use Symfony\Component\DependencyInjection\ContainerBuilder;
16+
use Symfony\Component\HttpKernel\Bundle\Bundle;
17+
18+
class B2bCodeCmsFormBundle extends Bundle
19+
{
20+
/**
21+
* {@inheritdoc}
22+
*/
23+
public function build(ContainerBuilder $container)
24+
{
25+
parent::build($container);
26+
27+
$container->addCompilerPass(new TwigSandboxConfigurationPass());
28+
}
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the B2Bcodext CMS Form Builder.
5+
*
6+
* (c) Daniel Nahrebecki <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace B2bCode\Bundle\CmsFormBundle\Builder;
13+
14+
use B2bCode\Bundle\CmsFormBundle\Entity\CmsForm;
15+
use B2bCode\Bundle\CmsFormBundle\Entity\CmsFormField;
16+
use B2bCode\Bundle\CmsFormBundle\Exception\CmsFormNotFound;
17+
use B2bCode\Bundle\CmsFormBundle\Form\Type\CmsFormType;
18+
use B2bCode\Bundle\CmsFormBundle\Provider\FieldTypeRegistry;
19+
use B2bCode\Bundle\CmsFormBundle\Validator\Config\FormConstraintCollection;
20+
use B2bCode\Bundle\CmsFormBundle\Validator\ConstraintProviderInterface;
21+
use Doctrine\Common\Persistence\ManagerRegistry;
22+
use Symfony\Component\Form\FormBuilderInterface as SymfonyFormBuilderInterface;
23+
use Symfony\Component\Form\FormFactoryInterface;
24+
use Symfony\Component\Form\FormInterface;
25+
use Symfony\Component\Routing\RouterInterface;
26+
27+
class FormBuilder implements FormBuilderInterface
28+
{
29+
/** @var FormFactoryInterface */
30+
protected $formFactory;
31+
32+
/** @var ManagerRegistry */
33+
protected $managerRegistry;
34+
35+
/** @var FieldTypeRegistry */
36+
protected $fieldTypeRegistry;
37+
38+
/** @var RouterInterface */
39+
protected $router;
40+
41+
/** @var ConstraintProviderInterface */
42+
protected $constraintProvider;
43+
44+
/**
45+
* @param FormFactoryInterface $formFactory
46+
* @param ManagerRegistry $managerRegistry
47+
* @param FieldTypeRegistry $fieldTypeRegistry
48+
* @param RouterInterface $router
49+
* @param ConstraintProviderInterface $constraintProvider
50+
*/
51+
public function __construct(
52+
FormFactoryInterface $formFactory,
53+
ManagerRegistry $managerRegistry,
54+
FieldTypeRegistry $fieldTypeRegistry,
55+
RouterInterface $router,
56+
ConstraintProviderInterface $constraintProvider
57+
) {
58+
$this->formFactory = $formFactory;
59+
$this->managerRegistry = $managerRegistry;
60+
$this->fieldTypeRegistry = $fieldTypeRegistry;
61+
$this->router = $router;
62+
$this->constraintProvider = $constraintProvider;
63+
}
64+
65+
/**
66+
* {@inheritdoc}
67+
*/
68+
public function getForm(string $alias, array $options = []): FormInterface
69+
{
70+
$repository = $this->managerRegistry->getManagerForClass(CmsForm::class)->getRepository(CmsForm::class);
71+
/** @var CmsForm $cmsForm */
72+
$cmsForm = $repository->findOneBy(['alias' => $alias]);
73+
74+
if ($cmsForm === null) {
75+
throw new CmsFormNotFound(sprintf('CmsForm with alias %s not found', $alias));
76+
}
77+
78+
if (!array_key_exists('action', $options) || $options['action'] === null || $options['action'] === '') {
79+
$options['action'] = $this->router->generate('b2b_code_cms_frontend_ajax_respond', [
80+
'uuid' => $cmsForm->uuid()
81+
]);
82+
}
83+
84+
return $this->buildForm($cmsForm, $options);
85+
}
86+
87+
/**
88+
* Builds form containing only one field. Useful for the field preview.
89+
*
90+
* @param CmsFormField $field
91+
* @return FormInterface
92+
*/
93+
public function buildField(CmsFormField $field): FormInterface
94+
{
95+
$formBuilder = $this->formFactory->createBuilder(CmsFormType::class, null, ['csrf_protection' => false]);
96+
$this->addField($formBuilder, $field);
97+
98+
return $formBuilder->getForm();
99+
}
100+
101+
/**
102+
* @param CmsForm $cmsForm
103+
* @param array $options
104+
*
105+
* @return FormInterface
106+
*/
107+
protected function buildForm(CmsForm $cmsForm, array $options = []): FormInterface
108+
{
109+
$formBuilder = $this->formFactory->createBuilder(CmsFormType::class, null, $options);
110+
$constraintCollection = $this->constraintProvider->getConstraintsForForm($cmsForm);
111+
112+
// configure fields
113+
foreach ($cmsForm->getFields() as $field) {
114+
$this->addField($formBuilder, $field, $constraintCollection);
115+
}
116+
117+
return $formBuilder->getForm();
118+
}
119+
120+
/**
121+
* @param SymfonyFormBuilderInterface $formBuilder
122+
* @param CmsFormField $field
123+
* @param FormConstraintCollection $constraintCollection
124+
*/
125+
protected function addField(
126+
SymfonyFormBuilderInterface $formBuilder,
127+
CmsFormField $field,
128+
?FormConstraintCollection $constraintCollection = null
129+
): void {
130+
if ($constraintCollection === null) {
131+
$constraintCollection = new FormConstraintCollection(new CmsForm());
132+
}
133+
134+
$formType = $this->fieldTypeRegistry->getByKey($field->getType());
135+
if ($formType === null) {
136+
return;
137+
}
138+
$fieldOptions = array_merge($formType->getFormOptions(), $field->getOptions());
139+
$constraints = $this->buildConstraintsForField($field, $constraintCollection, $fieldOptions);
140+
if (count($constraints) > 0) {
141+
$fieldOptions['constraints'] = $constraints;
142+
}
143+
144+
$formBuilder->add($field->getName(), $formType->getFormType(), $fieldOptions);
145+
}
146+
147+
/**
148+
* @param CmsFormField $field
149+
* @param FormConstraintCollection $constraintCollection
150+
* @param array $fieldOptions
151+
* @return array
152+
*/
153+
protected function buildConstraintsForField(
154+
CmsFormField $field,
155+
FormConstraintCollection $constraintCollection,
156+
array $fieldOptions
157+
): array {
158+
$constraints = [];
159+
160+
if (array_key_exists('constraints', $fieldOptions)) {
161+
$constraints = $fieldOptions['constraints'];
162+
}
163+
164+
if ($constraintCollection->hasFieldAnyConstraints($field->getName())) {
165+
return array_merge($constraints, $constraintCollection->getConstraintsForField($field->getName()));
166+
}
167+
168+
return $constraints;
169+
}
170+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the B2Bcodext CMS Form Builder.
5+
*
6+
* (c) Daniel Nahrebecki <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace B2bCode\Bundle\CmsFormBundle\Builder;
13+
14+
use B2bCode\Bundle\CmsFormBundle\Exception\CmsFormNotFound;
15+
use Symfony\Component\Form\FormInterface;
16+
17+
interface FormBuilderInterface
18+
{
19+
/**
20+
* @param string $alias
21+
* @param array $options
22+
* @throws CmsFormNotFound
23+
* @return mixed
24+
*/
25+
public function getForm(string $alias, array $options = []): FormInterface;
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the B2Bcodext CMS Form Builder.
5+
*
6+
* (c) Daniel Nahrebecki <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace B2bCode\Bundle\CmsFormBundle\Cache;
13+
14+
use B2bCode\Bundle\CmsFormBundle\Validator\Loader\ValidationRuleLoader;
15+
use Symfony\Component\HttpKernel\CacheClearer\CacheClearerInterface;
16+
use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface;
17+
18+
class CacheClearer implements CacheWarmerInterface, CacheClearerInterface
19+
{
20+
/** @var ValidationRuleLoader */
21+
protected $ruleLoader;
22+
23+
/**
24+
* @param ValidationRuleLoader $ruleLoader
25+
*/
26+
public function __construct(ValidationRuleLoader $ruleLoader)
27+
{
28+
$this->ruleLoader = $ruleLoader;
29+
}
30+
31+
/**
32+
* {@inheritdoc}
33+
*/
34+
public function warmUp($cacheDir)
35+
{
36+
$this->ruleLoader->getForForm('dummy-call');
37+
}
38+
39+
/**
40+
* {@inheritdoc}
41+
*/
42+
public function isOptional()
43+
{
44+
return true;
45+
}
46+
47+
/**
48+
* {@inheritdoc}
49+
*/
50+
public function clear($cacheDir)
51+
{
52+
$this->ruleLoader->clearCache();
53+
}
54+
}

0 commit comments

Comments
 (0)