├── .gitignore ├── view ├── frontend │ ├── templates │ │ ├── form │ │ │ ├── additional_info.phtml │ │ │ ├── blocks_container.phtml │ │ │ ├── success.phtml │ │ │ ├── action.phtml │ │ │ └── tab.phtml │ │ └── widget_form.phtml │ ├── web │ │ ├── css │ │ │ └── source │ │ │ │ └── _module.less │ │ └── js │ │ │ └── widget-form.js │ └── layout │ │ └── alekseon_widgetforms_form_success.xml └── adminhtml │ └── layout │ └── alekseon_customformsbuilder_form_edit.xml ├── registration.php ├── Block ├── Form │ ├── BlocksContainer.php │ ├── Action.php │ ├── AdditionalInfo.php │ ├── Tab.php │ └── Success.php ├── Adminhtml │ └── Form │ │ └── Edit │ │ └── Tab │ │ ├── NewsletterSettings.php │ │ └── WidgetSettings.php └── WidgetForm.php ├── Model ├── Attribute │ └── Source │ │ └── TextFormAttributes.php └── Config │ └── Source │ └── AvailableForms.php ├── etc ├── frontend │ ├── routes.xml │ └── events.xml ├── module.xml ├── di.xml ├── adminhtml │ ├── events.xml │ └── di.xml └── widget.xml ├── composer.json ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── workflows │ ├── coding-standard.yml │ └── integration.yml ├── Observer ├── FormRecordAttributeSaveAfter.php ├── AddFrontendInputBlockOptions.php └── SubscribeToNewsletter.php ├── Plugin ├── RemoveWidgetGroupAttributesFromGeneralTabPlugin.php ├── RemoveAttributesFromGeneralTabPlugin.php ├── AddFormFieldsetWarningPlugin.php ├── FromAttributeGroupCodesPlugin.php └── NewsletterEmailValidatorPlugin.php ├── LICENCE ├── README.md ├── Controller └── Form │ ├── Success.php │ └── Submit.php ├── CHANGELOG.md └── Setup └── Patch └── Data └── CreateWidgetFormsAttributesPatch.php /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .DS_Store 3 | Thumbs.db -------------------------------------------------------------------------------- /view/frontend/templates/form/additional_info.phtml: -------------------------------------------------------------------------------- 1 | 9 | getChildHtml() ?> 10 | -------------------------------------------------------------------------------- /view/frontend/templates/form/blocks_container.phtml: -------------------------------------------------------------------------------- 1 | 11 | getChildHtml() ?> 12 | -------------------------------------------------------------------------------- /registration.php: -------------------------------------------------------------------------------- 1 | getForm(); 13 | ?> 14 | 15 | getSuccessMessage(); ?> 16 | 17 | -------------------------------------------------------------------------------- /Block/Form/Action.php: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /etc/frontend/events.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /etc/module.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /view/frontend/templates/form/action.phtml: -------------------------------------------------------------------------------- 1 | 12 |
13 | 16 |
17 | -------------------------------------------------------------------------------- /etc/di.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "alekseon/widget-forms", 3 | "description": "N/A", 4 | "require": { 5 | "alekseon/custom-forms-frontend": "^100.1.0", 6 | "alekseon/widget-forms-re-captcha": "100.2.*" 7 | }, 8 | "type": "magento2-module", 9 | "version": "102.5.6", 10 | "license": [ 11 | "OSL-3.0", 12 | "AFL-3.0" 13 | ], 14 | "autoload": { 15 | "files": [ 16 | "registration.php" 17 | ], 18 | "psr-4": { 19 | "Alekseon\\WidgetForms\\": "" 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Block/Form/AdditionalInfo.php: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /etc/adminhtml/events.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Magento Version:** 27 | Example: Magento ver. 2.4.3-p3 28 | 29 | **Extension versions** 30 | Example: alekseon/widget-forms 102.2.0 31 | 32 | **Additional context** 33 | Add any other context about the problem here. 34 | -------------------------------------------------------------------------------- /.github/workflows/coding-standard.yml: -------------------------------------------------------------------------------- 1 | name: M2 Coding Standard 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | phpcs: 6 | name: M2 Code Analysis 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: extdn/github-actions-m2/magento-coding-standard/8.1@master 11 | with: 12 | phpcs_severity: 10 13 | phpcs_report: full 14 | 15 | phpmd: 16 | name: M2 Mess Detection 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v4 20 | - uses: extdn/github-actions-m2/magento-mess-detector@master 21 | 22 | phpstan: 23 | name: M2 PHPStan 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v4 27 | - uses: extdn/github-actions-m2/magento-phpstan/8.3@master 28 | with: 29 | composer_name: alekseon/widget-forms 30 | -------------------------------------------------------------------------------- /Observer/FormRecordAttributeSaveAfter.php: -------------------------------------------------------------------------------- 1 | getEvent()->getAttribute(); 25 | if ($frontendInputBlock = $attribute->getFrontendInputBlock()) { 26 | $attribute->setAttributeExtraParam('frontend_input_block', $frontendInputBlock); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Plugin/RemoveWidgetGroupAttributesFromGeneralTabPlugin.php: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Alekseon Ltd. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | © 2020 GitHub, Inc. -------------------------------------------------------------------------------- /view/adminhtml/layout/alekseon_customformsbuilder_form_edit.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | form_widget_settings_tab 14 | form_widget_settings_tab 15 | 16 | 17 | 18 | newsletter_settings_tab 19 | newsletter_settings_tab 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /view/frontend/templates/form/tab.phtml: -------------------------------------------------------------------------------- 1 | 12 |
getTab()->getIsFirstTab()): ?>style="display: none" 16 | > 17 | getWidgetFormBlock()->getFormTitle()): ?> 18 | escapeHtmlAttr($block->getWidgetFormBlock()->getFormTitle()) ?> 19 | 20 | getWidgetFormBlock()->getFormDescription()): ?> 21 |
escapeHtmlAttr($block->getWidgetFormBlock()->getFormDescription()) ?>
22 | 23 | getChildHtml() ?> 24 |
25 |
getTab()->getIsFirstTab()): ?>style="display: none"> 28 | getSubmitButtonHtml() ?> 29 |
30 | -------------------------------------------------------------------------------- /.github/workflows/integration.yml: -------------------------------------------------------------------------------- 1 | name: M2 Integration Tests 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | 8 | jobs: 9 | integration-tests: 10 | name: Magento 2 Integration Tests 11 | runs-on: ubuntu-latest 12 | services: 13 | mysql: 14 | image: mysql:5.7 15 | env: 16 | MYSQL_ROOT_PASSWORD: root 17 | ports: 18 | - 3306:3306 19 | options: --tmpfs /tmp:rw --tmpfs /var/lib/mysql:rw --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 20 | es: 21 | image: docker.io/wardenenv/elasticsearch:7.8 22 | ports: 23 | - 9200:9200 24 | env: 25 | 'discovery.type': single-node 26 | 'xpack.security.enabled': false 27 | ES_JAVA_OPTS: "-Xms64m -Xmx512m" 28 | options: --health-cmd="curl localhost:9200/_cluster/health?wait_for_status=yellow&timeout=60s" --health-interval=10s --health-timeout=5s --health-retries=3 29 | steps: 30 | - uses: actions/checkout@v4 31 | - name: M2 Integration Tests with Magento 2 (Php 8.3) 32 | uses: extdn/github-actions-m2/magento-integration-tests/8.3@master 33 | with: 34 | module_name: Alekseon_WidgetForms 35 | composer_name: alekseon/widget-forms 36 | ce_version: '2.4.0' 37 | -------------------------------------------------------------------------------- /Plugin/AddFormFieldsetWarningPlugin.php: -------------------------------------------------------------------------------- 1 | getDataObject(); 27 | 28 | if ($form->getCanUseForWidget()) { 29 | $frontendInputTypeConfig = $attribute->getFrontendInputTypeConfig(); 30 | if ($frontendInputTypeConfig) { 31 | $frontendBlocks = $frontendInputTypeConfig->getFrontendBlocks(); 32 | 33 | if (!isset($frontendBlocks['default'])) { 34 | $formFieldSettings['warnings'][] = __( 35 | 'Frontend input %1 is not supported by form widgets.', 36 | $frontendInputTypeConfig->getLabel() 37 | ); 38 | } 39 | } 40 | } 41 | 42 | return $formFieldSettings; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Plugin/FromAttributeGroupCodesPlugin.php: -------------------------------------------------------------------------------- 1 | formRepository = $formRepository; 30 | } 31 | 32 | /** 33 | * @param $attribute 34 | * @param $result 35 | * @return bool 36 | * @throws \Magento\Framework\Exception\NoSuchEntityException 37 | */ 38 | public function afterGetCanUseGroup($attribute, $result) 39 | { 40 | $form = $this->formRepository->getById($attribute->getFormId()); 41 | return $form->getCanUseForWidget() || $result; 42 | } 43 | 44 | /** 45 | * @param $attribute 46 | * @param $result 47 | * @return bool 48 | * @throws \Magento\Framework\Exception\NoSuchEntityException 49 | */ 50 | public function afterGetIsGroupEditable($attribute, $result) 51 | { 52 | $form = $this->formRepository->getById($attribute->getFormId()); 53 | if ($form->getCanUseForWidget()) { 54 | return false; 55 | } 56 | return $result; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /view/frontend/templates/widget_form.phtml: -------------------------------------------------------------------------------- 1 | 12 | getForm()): ?> 13 |
14 |
19 | 20 | 21 | getBlocksContainer('tabs_container')->toHtml() ?> 22 |
23 | getBlocksContainer('after_form')->toHtml() ?> 24 |
25 | 38 | 39 | -------------------------------------------------------------------------------- /Block/Form/Tab.php: -------------------------------------------------------------------------------- 1 | getLayout()->createBlock( 31 | \Alekseon\WidgetForms\Block\Form\Action::class, 32 | 'form_' . $this->getForm()->getId() . '_action_' . $this->getTab()->getId(), 33 | [ 34 | 'data' => [ 35 | 'submit_button_label' => $this->getSubmitButtonLabel() 36 | ], 37 | ] 38 | )->toHtml(); 39 | } 40 | 41 | /** 42 | * @return \Magento\Framework\Phrase 43 | */ 44 | private function getSubmitButtonLabel() 45 | { 46 | if (!$this->getTab()->getIsLastTab()) { 47 | return __('Next'); 48 | } 49 | 50 | $form = $this->getForm(); 51 | if ($form && $form->getSubmitButtonLabel()) { 52 | return $form->getSubmitButtonLabel(); 53 | } 54 | 55 | return __('Submit'); 56 | } 57 | 58 | /** 59 | * @return \Alekseon\WidgetForms\Block\WidgetForm 60 | */ 61 | public function getWidgetFormBlock() 62 | { 63 | return $this->getParentBlock()->getParentBlock(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Plugin/NewsletterEmailValidatorPlugin.php: -------------------------------------------------------------------------------- 1 | formRepository = $formRepository; 40 | $this->emailValidatorFactory = $emailValidatorFactory; 41 | } 42 | 43 | /** 44 | * @param Attribute $attribute 45 | * @param $validator 46 | */ 47 | public function afterGetInputValidators(Attribute $attribute, $validators) 48 | { 49 | $formId = $attribute->getFormId(); 50 | $form = $this->formRepository->getById($formId); 51 | 52 | if ($form->getSubscribeToNewsletter() && $form->getNewsletterEmail() == $attribute->getAttributeCode()) { 53 | $emailValidator = $this->emailValidatorFactory->create(); 54 | if (!isset($validators[$emailValidator->getCode()])) { 55 | $validators[] = $emailValidator; 56 | } 57 | } 58 | 59 | return $validators; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Block/Form/Success.php: -------------------------------------------------------------------------------- 1 | formRepository = $formRepository; 47 | parent::__construct($context, $data); 48 | } 49 | 50 | /** 51 | * @return \Alekseon\CustomFormsBuilder\Model\Form|false 52 | */ 53 | public function getForm() 54 | { 55 | if ($this->form === null) { 56 | $formId = (int)$this->getData('form_id'); 57 | $form = false; 58 | if ($formId) { 59 | try { 60 | $form = $this->formRepository->getById($formId, null, true); 61 | } catch (\Exception $e) { 62 | } 63 | } 64 | 65 | $this->form = $form; 66 | } 67 | 68 | return $this->form; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Observer/AddFrontendInputBlockOptions.php: -------------------------------------------------------------------------------- 1 | getEvent()->getFormFieldId(); 25 | $fieldset = $observer->getEvent()->getFieldset(); 26 | $fieldSettings = $observer->getEvent()->getFieldSettings(); 27 | 28 | if (!isset($fieldSettings['attribute'])) { 29 | return; 30 | } 31 | 32 | $attribute = $fieldSettings['attribute']; 33 | $attribute->setFrontendInputBlock($attribute->getAttributeExtraParam('frontend_input_block')); 34 | 35 | $frontendInputTypeConfig = $attribute->getFrontendInputTypeConfig(); 36 | if ($frontendInputTypeConfig) { 37 | $frontendBlocks = $frontendInputTypeConfig->getFrontendBlocks(); 38 | } else { 39 | $frontendBlocks = []; 40 | } 41 | 42 | if (is_array($frontendBlocks) && count($frontendBlocks) > 1 ) { 43 | 44 | $options = []; 45 | foreach ($frontendBlocks as $code => $data) { 46 | $options[] = [ 47 | 'value' => $code, 48 | 'label' => __($data['label']), 49 | ]; 50 | } 51 | 52 | $fieldset->addField('form_field_' . $formFieldId . '_frontend_input_block', 'select', 53 | [ 54 | 'label' => __('Input Block Type'), 55 | 'name' => 'form_fields[' . $formFieldId . '][frontend_input_block]', 56 | 'values' => $options, 57 | ] 58 | )->addCustomAttribute("data-fieldcode", "frontend_input_block"); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /etc/widget.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 11 | 12 | Widget Form 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 34 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /Model/Config/Source/AvailableForms.php: -------------------------------------------------------------------------------- 1 | formCollectionFactory = $formCollectionFactory; 34 | } 35 | 36 | /** 37 | * @return array 38 | * @throws \Magento\Framework\Exception\LocalizedException 39 | */ 40 | public function toOptionArray() 41 | { 42 | $optionArray = [ 43 | '' => __('-- Not Selected --'), 44 | ]; 45 | $options = $this->toArray(); 46 | foreach ($options as $optionId => $optionLabel) 47 | { 48 | $optionArray[$optionId] = $optionLabel; 49 | } 50 | return $optionArray; 51 | } 52 | 53 | /** 54 | * @return array|null 55 | * @throws \Magento\Framework\Exception\LocalizedException 56 | */ 57 | public function toArray() 58 | { 59 | if ($this->options === null) { 60 | $this->options = []; 61 | $formCollection = $this->formCollectionFactory->create(); 62 | $formCollection->addAttributeToSelect('title'); 63 | $formCollection->addAttributeToFilter('can_use_for_widget', true); 64 | 65 | foreach ($formCollection as $form) { 66 | $this->options[$form->getId()] = $form->getTitle(); 67 | } 68 | } 69 | return $this->options; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Block/Adminhtml/Form/Edit/Tab/NewsletterSettings.php: -------------------------------------------------------------------------------- 1 | getDataObject()->getCanUseForWidget(); 43 | } 44 | 45 | /** 46 | * {@inheritdoc} 47 | */ 48 | public function isHidden() 49 | { 50 | return false; 51 | } 52 | 53 | /** 54 | * @return mixed 55 | */ 56 | public function getDataObject() 57 | { 58 | return $this->_coreRegistry->registry('current_form'); 59 | } 60 | 61 | /** 62 | * @return $this 63 | * @throws \Magento\Framework\Exception\LocalizedException 64 | */ 65 | protected function _prepareForm() 66 | { 67 | $dataObject = $this->getDataObject(); 68 | 69 | /** @var \Magento\Framework\Data\Form $form */ 70 | $form = $this->_formFactory->create(); 71 | $newsletterFieldset = $form->addFieldset('newsletter_settings_fieldset', 72 | [ 73 | 'legend' => __('Newsletter Settings') 74 | ] 75 | ); 76 | $this->addAllAttributeFields($newsletterFieldset, $dataObject,['included' => ['newsletter']]); 77 | $this->setForm($form); 78 | 79 | return parent::_prepareForm(); 80 | } 81 | 82 | 83 | /** 84 | * Initialize form fileds values 85 | * 86 | * @return $this 87 | */ 88 | protected function _initFormValues() 89 | { 90 | $this->getForm()->addValues($this->getDataObject()->getData()); 91 | return parent::_initFormValues(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Block/Adminhtml/Form/Edit/Tab/WidgetSettings.php: -------------------------------------------------------------------------------- 1 | getDataObject()->getCanUseForWidget(); 43 | } 44 | 45 | /** 46 | * {@inheritdoc} 47 | */ 48 | public function isHidden() 49 | { 50 | return false; 51 | } 52 | 53 | /** 54 | * @return mixed 55 | */ 56 | public function getDataObject() 57 | { 58 | return $this->_coreRegistry->registry('current_form'); 59 | } 60 | 61 | /** 62 | * @return $this 63 | * @throws \Magento\Framework\Exception\LocalizedException 64 | */ 65 | protected function _prepareForm() 66 | { 67 | $dataObject = $this->getDataObject(); 68 | 69 | /** @var \Magento\Framework\Data\Form $form */ 70 | $form = $this->_formFactory->create(); 71 | $widgetFieldset = $form->addFieldset('widget_settings_fieldset', ['legend' => __('Widget Settings')]); 72 | $this->addAllAttributeFields($widgetFieldset, $dataObject, ['included' => ['widget_form_attribute']]); 73 | $this->setForm($form); 74 | 75 | return parent::_prepareForm(); 76 | } 77 | 78 | 79 | /** 80 | * Initialize form fileds values 81 | * 82 | * @return $this 83 | */ 84 | protected function _initFormValues() 85 | { 86 | $this->getForm()->addValues($this->getDataObject()->getData()); 87 | return parent::_initFormValues(); 88 | } 89 | 90 | 91 | /** 92 | * @inheritDoc 93 | */ 94 | protected function _addAdditionalFormElementData(AbstractElement $element) 95 | { 96 | if (in_array($element->getId(), ['form_submit_success_message', 'form_submit_success_title'])) { 97 | $element->setNote( 98 | '' 99 | . __('Template Variables') 100 | . ' ' 101 | . __( ' are allowed.')); 102 | } 103 | 104 | return parent::_addAdditionalFormElementData($element); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /view/frontend/web/js/widget-form.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright © Alekseon sp. z o.o. 3 | * http://www.alekseon.com/ 4 | */ 5 | define([ 6 | 'jquery', 7 | 'Magento_Ui/js/modal/alert' 8 | ], function ($, alert) { 9 | 'use strict'; 10 | 11 | $.widget('mage.alekseonWidgetForm', { 12 | 13 | options: { 14 | currentTab: 1, 15 | tabs: [], 16 | form: null, 17 | formSubmitUrl: 'formSubmitUrl', 18 | formId: '', 19 | success_mode: '', 20 | formSuccessUrl: '' 21 | }, 22 | 23 | _create: function () { 24 | let self = this; 25 | this.options.form = $('#' + this.options.formId)[0]; 26 | $(this.options.form).submit(function (event) { 27 | self.submitFormAction(); 28 | event.preventDefault(event); 29 | }); 30 | }, 31 | 32 | openTab: function (form, tabIndex) { 33 | $(this.options.form).find('#form-tab-fieldset-' + this.options.currentTab).slideUp(); 34 | $(this.options.form).find('#form-tab-actions-' + this.options.currentTab).hide(); 35 | 36 | setTimeout(() => { 37 | this.options.currentTab = tabIndex; 38 | 39 | $(this.options.form).find('#form-tab-fieldset-' + this.options.currentTab).slideDown(); 40 | $(this.options.form).find('#form-tab-actions-' + this.options.currentTab).show(); 41 | }, "100"); 42 | }, 43 | 44 | submitFormAction: function () { 45 | if ($(this.options.form).validation && !$(this.options.form).validation('isValid')) { 46 | return false; 47 | } 48 | 49 | if (this.options.tabs[this.options.currentTab + 1] !== undefined) { 50 | this.openTab(this.options, this.options.currentTab + 1); 51 | return; 52 | } 53 | 54 | let self = this; 55 | const formData = new FormData(this.options.form); 56 | 57 | $.ajax({ 58 | url: this.options.formSubmitUrl, 59 | type: 'POST', 60 | data: formData, 61 | processData: false, 62 | contentType: false, 63 | dataType: 'json', 64 | showLoader: true 65 | }).done(function (response) { 66 | if (response.errors) { 67 | self.onError(response); 68 | } else { 69 | self.onSuccess(response); 70 | } 71 | }).fail(function (error) { 72 | self.onError(error.responseJSON); 73 | }).always(function() { 74 | self.onComplete(); 75 | }); 76 | }, 77 | 78 | onComplete: function() { 79 | }, 80 | 81 | onError: function(response) { 82 | alert({ 83 | title: $.mage.__('Error'), 84 | content: response.message 85 | }); 86 | }, 87 | 88 | onSuccess: function(response) { 89 | if (this.options.success_mode === 'form') { 90 | this.options.form.parentElement.innerHTML = response.message; 91 | } else if (this.success_mode === 'success_page') { 92 | window.location.href = this.formSuccessUrl; 93 | return; 94 | } else { 95 | alert({ 96 | title: response.title, 97 | content: response.message 98 | }); 99 | } 100 | this.options.form.reset(); 101 | if (this.options.currentTab !== 1) { 102 | this.openTab(this.options.form, 1); 103 | } 104 | } 105 | }); 106 | 107 | return $.mage.alekseonWidgetForm; 108 | }); 109 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |

3 |
4 | Magento 2 Widget Form 5 |
6 | Alekseon_WidgetForms 7 |
8 |

9 | 10 |

Place forms as widgets to CMS page in Magento 2

11 | 12 |

Let you add forms to CMS pages

13 | 14 | ## Demo 15 | 16 | https://widget-forms.alekseon.com/ 17 | 18 | ## Examples of usage 19 | 20 | - Ask About Product
21 | - Multi-step Survey (soon)
22 | 23 | ## Videos 24 | 25 |
26 | 27 | How to modify contact us form in Magento Open Source 28 | 29 | 30 | 31 | How to modify contact us form in Magento Open Source 32 | 33 |
34 | 35 | ## Installation 36 | 37 | In your Magento2 root directory, you may install this package via composer: 38 | 39 | ``` 40 | composer require alekseon/widget-forms 41 | bin/magento setup:upgrade 42 | ``` 43 | 44 | 45 | ## Support 46 | 47 | CJM Ver. | Magento 2.0 | Magento 2.1 | Magento 2.2 | Magento 2.3 | Magento 2.4 48 | --- | :---: | :---: | :---: | :---: | :---: 49 | 1.x | :x: | :x: | :white_check_mark: | :white_check_mark: | :white_check_mark: 50 | 51 | 52 | ## Features 53 | 54 | ### Add Forms to CMS Pages 55 | 56 | You can place any Form you will create by [Alekseon_CustomFormsBuilder](https://github.com/Alekseon/magento2-custom-forms-builder) extension to CMS page. 57 | 58 | Visit our [alekseon.com](https://alekseon.com/en/blog/post/module-widget-forms/) website for more information about the Widget. 59 | 60 | 61 | 62 | ## Hyvä Theme compatibility 63 | 64 | [Hyvä compatibility](https://hyva.io/) 65 |
66 | Alekseon_WidgetForms is also available as a [Compatibility module](https://gitlab.hyva.io/hyva-public/module-tracker/-/issues/236) for [Hyvä Themes](https://hyva.io/) 67 | 68 | ## Issue Tracking / Upcoming Features 69 | 70 | For issues, please use the [issue tracker](https://github.com/Alekseon/widget-forms/issues). 71 | 72 | Issues keep this project alive and strong, so let us know if you find anything! 73 | 76 | ### Development / Contribution 77 | 78 | If you want to contribute please follow the below instructions: 79 | 80 | 1. Create an issue and describe your idea 81 | 2. [Fork this repository](https://github.com/Alekseon/widget-forms/fork) 82 | 3. Create your feature branch (`git checkout -b my-new-feature`) 83 | 4. Commit your changes 84 | 5. Publish the branch (`git push origin my-new-feature`) 85 | 6. Submit a new Pull Request for review 86 | 87 | ## Wiki 88 | 89 | https://github.com/Alekseon/magento2-widget-forms/wiki 90 | 91 | ## Maintainers 92 | 93 | Current maintainers: 94 | 95 | * [Linek](https://github.com/Linek) 96 | * [marcinfr](https://github.com/marcinfr) 97 | 98 | See also our [contributors](https://github.com/Alekseon/widget-forms/graphs/contributors) 99 | 100 | 101 | ## License 102 | 103 | [The Open Software License 3.0 (OSL-3.0)](https://opensource.org/licenses/OSL-3.0) 104 | -------------------------------------------------------------------------------- /Observer/SubscribeToNewsletter.php: -------------------------------------------------------------------------------- 1 | subscriptionManager = $subscriptionManager; 62 | $this->storeManager = $storeManager; 63 | $this->customerSession = $customerSession; 64 | $this->customerAccountManagement = $customerAccountManagement; 65 | $this->eventManager = $eventManager; 66 | } 67 | 68 | /** 69 | * Execute 70 | * 71 | * @param Observer $observer 72 | */ 73 | public function execute(Observer $observer) 74 | { 75 | $formRecord = $observer->getEvent()->getFormRecord(); 76 | $form = $formRecord->getForm(); 77 | 78 | if ($form->getSubscribeToNewsletter()) { 79 | $emailField = $form->getNewsletterEmail(); 80 | $email = $formRecord->getData($emailField); 81 | $storeId = (int) $this->storeManager->getStore()->getId(); 82 | if ($email) { 83 | $this->validateEmailAvailable($email); 84 | $customerId = $this->getCurrentCustomerId(); 85 | if ($customerId) { 86 | $subscriber = $this->subscriptionManager->subscribeCustomer($customerId, $storeId); 87 | } else { 88 | $subscriber = $this->subscriptionManager->subscribe($email, $storeId); 89 | } 90 | $this->eventManager->dispatch( 91 | 'alekseon_widget_form_after_subscribe', 92 | [ 93 | 'form_record' => $formRecord, 94 | 'subscriber' => $subscriber, 95 | ] 96 | ); 97 | } 98 | } 99 | } 100 | 101 | /** 102 | * @return false | int 103 | */ 104 | private function getCurrentCustomerId() 105 | { 106 | if ($this->customerSession->isLoggedIn()) { 107 | return $this->customerSession->getCustomerDataObject()->getId(); 108 | } 109 | 110 | return false; 111 | } 112 | 113 | /** 114 | * @param $email 115 | */ 116 | private function validateEmailAvailable($email) 117 | { 118 | $websiteId = $this->storeManager->getStore()->getWebsiteId(); 119 | if ($this->customerSession->isLoggedIn() 120 | && ($this->customerSession->getCustomerDataObject()->getEmail() !== $email 121 | && !$this->customerAccountManagement->isEmailAvailable($email, $websiteId)) 122 | ) { 123 | throw new LocalizedException( 124 | __('This email address is already assigned to another user.') 125 | ); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Controller/Form/Success.php: -------------------------------------------------------------------------------- 1 | request = $context->getRequest(); 61 | $this->resultPageFactory = $resultPageFactory; 62 | $this->formRepository = $formRepository; 63 | $this->templateFilter = $templateFilter; 64 | $this->customerSession = $customerSession; 65 | $this->resultForwardFactory = $resultForwardFactory; 66 | } 67 | 68 | /** 69 | * @return \Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface|void 70 | */ 71 | public function execute() 72 | { 73 | $customerFormId = $this->customerSession->getWidgetFormsSubmitFormId(); 74 | $form = $this->getForm(); 75 | if (!$form || !$customerFormId) { 76 | return $this->resultForwardFactory->create()->forward('noroute'); 77 | } 78 | 79 | if ($form->getId() != $customerFormId) { 80 | return $this->resultForwardFactory->create()->forward('noroute'); 81 | } 82 | $this->customerSession->setWidgetFormsSubmitFormId(null); 83 | 84 | $resultPage = $this->resultPageFactory->create(); 85 | $resultPage->initLayout(); 86 | $successBlock = $resultPage->getLayout()->getBlock( 87 | 'widgetforms_success' 88 | ); 89 | 90 | if ($successBlock) { 91 | $successBlock->setData('form_id', $form->getId()); 92 | $successBlock->setData('success_message', $this->getSuccessMessage($form)); 93 | } 94 | $resultPage->getConfig()->getTitle()->append($form->getTitle()); 95 | $resultPage->getConfig()->getTitle()->set($this->getSuccessTitle($form)); 96 | return $resultPage; 97 | } 98 | 99 | /** 100 | * @param $form 101 | * @return string 102 | */ 103 | public function getSuccessMessage($form) 104 | { 105 | $successMessage = $form->getFormSubmitSuccessMessage(); 106 | if ($successMessage) { 107 | $successMessage = $this->templateFilter->filter($successMessage); 108 | } else { 109 | $successMessage = __('Thank You!'); 110 | } 111 | return (string) $successMessage; 112 | } 113 | 114 | /** 115 | * @param $form 116 | * @return string 117 | */ 118 | public function getSuccessTitle($form) 119 | { 120 | $successTitle = $form->getFormSubmitSuccessTitle(); 121 | if (!$successTitle) { 122 | $successTitle = __('Success'); 123 | } 124 | return (string) $successTitle; 125 | } 126 | 127 | /** 128 | * @return \Alekseon\CustomFormsBuilder\Model\Form|false 129 | * @throws NoSuchEntityException 130 | */ 131 | public function getForm() 132 | { 133 | $formId = $this->getRequest()->getParam('form_id'); 134 | $form = false; 135 | if ($formId) { 136 | try { 137 | $form = $this->formRepository->getByIdentifier($formId, null, true); 138 | } catch (\Exception $e) {} 139 | } else { 140 | try { 141 | $form = $this->formRepository->getById($formId, null, true); 142 | } catch (\Exception $e) {} 143 | } 144 | 145 | if ($form && $form->getCanUseForWidget()) { 146 | return $form; 147 | } else { 148 | return false; 149 | } 150 | } 151 | 152 | /** 153 | * @return \Magento\Framework\App\RequestInterface 154 | */ 155 | public function getRequest() 156 | { 157 | return $this->request; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | ### Changed 9 | ### Fixed 10 | ### Added 11 | ### Removed 12 | 13 | ## [102.5.6] - 2025-06-17 14 | ### Fixed 15 | - set form_id as required in widget 16 | ### Added 17 | - "Not Selected" option in available forms options 18 | 19 | ## [102.5.5] - 2025-01-22 20 | ### Fixed 21 | - widget option - Success display mode - some CS fixes 22 | 23 | ## [102.5.4] - 2025-01-17 24 | ### Added 25 | - widget option - Success display mode - redirect to Success Page 26 | 27 | ## [102.5.3] - 2024-11-28 28 | ### changed 29 | - revert last changes (use prepareLayout) - when widget has been added by admin/content/widgets, widget data was not set yet 30 | 31 | ## [102.5.2] - 2024-11-17 32 | ### changed 33 | - use prepareLayout method instead of toHtml 34 | 35 | ## [102.5.1] - 2024-11-14 36 | ### Fixed 37 | - fix for missing form title and description 38 | 39 | ## [102.5.0] - 2024-11-07 40 | ### Changed 41 | - group tabs in tabs_container 42 | ### Added 43 | - after_form blocks caontainer 44 | - form object in alekseon_widget_form_after_submit event 45 | 46 | ## [102.4.4] - 2024-09-25 47 | ### Changed 48 | - removed success cms block from widget configuration and allow cms block in Form Submit Success Message 49 | ### Added 50 | - alekseon_widget_form_after_subscribe event 51 | 52 | ## [102.4.3] - 2024-06-24 53 | ### Added 54 | - added success display mode option 55 | 56 | ## [102.4.2] - 2024-06-24 57 | ### Changed 58 | - Translate labels (https://github.com/Alekseon/AlekseonEav/issues/45) 59 | 60 | ## [102.4.1] - 2024-02-26 61 | ### Fixed 62 | - fix ajax error: "complete is not a function" 63 | 64 | ## [102.4.0] - 2023-12-03 65 | ### Changed 66 | - refactoring - removed UI component on frontend 67 | 68 | ## [102.3.0] - 2023-06-21 69 | ### Changed 70 | - frontend inputs moved from Alekseon_WidgetForms to Alekseon_CustonFormsFrontend 71 | ### Added 72 | - form wrapper class 73 | 74 | ## [102.2.7] - 2023-05-02 75 | ### Changed 76 | - changes for tests in github actions 77 | 78 | ## [102.2.6] - 2023-05-01 79 | ### Added 80 | - added email validation for newsletter email input in admin 81 | ### Fixed 82 | - code quality fixes (https://github.com/Alekseon/magento2-widget-forms/issues/17) 83 | 84 | ## [102.2.3] - 2023-03-28 85 | ### Added 86 | - field containers css classes (for hyva) 87 | 88 | ## [102.2.2] - 2023-03-12 89 | ### Fixed 90 | - fix for cache issue 91 | - removed gap and fix label displaye in image input template 92 | 93 | ## [102.2.1] - 2023-03-12 94 | ### Added 95 | - "*" for required fields 96 | - cache tags 97 | ### Fixed 98 | - select default value on radio button input 99 | - stop opening first tab after submit if there is only one tab 100 | 101 | ## [102.2.0] - 2023-03-04 102 | ### Added 103 | - rating type input (stars) 104 | ### Fixed 105 | - default values for select 106 | - fix widget html code 107 | 108 | ## [102.1.6] - 2023-02-27 109 | ### Fixed 110 | - fix for backward compatibility 111 | 112 | ## [102.1.5] - 2022-11-22 113 | ### Added 114 | - class "widget-form-fields-container" for div container 115 | 116 | ## [102.1.4] - 2022-11-19 117 | ### Changed 118 | - modifications for native magento captcha 119 | ### Fixed 120 | - decreased gap between form and submit button 121 | 122 | ## [102.1.3] - 2022-10-27 123 | ### Added 124 | - email validation for logged in customers 125 | - subsribe to newsletter as customer if logged in 126 | ### Fixed 127 | - fix for install data 128 | 129 | ## [102.1.2] - 2022-10-22 130 | ### Changed 131 | - change widget block parameter identifier to form_identifier 132 | 133 | ## [102.1.1] - 2022-10-22 134 | ### Removed 135 | - removed FormSubmitFailureMessage attribute 136 | ### Added 137 | - added FormSubmitSuccessTitle attribute 138 | ### Changed 139 | - changes in displaying success/error messages on form submit 140 | 141 | ## [102.1.0] - 2022-10-22 142 | ### Fixed 143 | - fix infinite loading on submit where returned server error 144 | ## Added 145 | - current product default value 146 | - customer email default value 147 | - hidden text input type block 148 | - form identifier parameter for widget 149 | - create getter methods for success and failure message 150 | - current cms page default value provider 151 | - default text defaul value provider 152 | 153 | ## [102.0.23] 154 | ### Fixed 155 | - fix for warning in isSelected method for Select Field 156 | - fix validation for newsletter email 157 | ### Changed 158 | - TextFormAttribute source extends from CustomFormBuilder module 159 | 160 | ## [102.0.22] - 2022-10-06 161 | ### Fixed 162 | - compatibility to attribute default values 163 | 164 | ## [102.0.21] - 2022-10-05 165 | ### Added 166 | - compatibility to attribute default values 167 | 168 | ## [102.0.19] - 2022-10-04 169 | ### Changed 170 | - changed composer requirements 171 | - modification for input validations 172 | 173 | ## [102.0.18] - 2022-09-27 174 | ### Added 175 | - images input 176 | - max length for textarea 177 | 178 | ## [102.0.17] - 2022-09-02 179 | ### Added 180 | - new template checkbox_vertical for multiselected checkbox 181 | 182 | ## [102.0.16] - 2022-09-02 183 | ### Added 184 | - allow to set tempate on frontend block by di.xml 185 | 186 | ## [102.0.15] - 2022-08-04 187 | ### Fixed 188 | - fixed Magento 2.4.4 issue with closing the short tag in knockout templates 189 | 190 | ## [102.0.14] - 2022-07-07 191 | ### Fixed 192 | - form_key is now passed to Form from frontend 193 | 194 | ## [102.0.13] - 2022-07-07 195 | ### Added 196 | - added input=date field to frontend 197 | 198 | ## [102.0.12] - 2022-06-15 199 | ### Fixed 200 | - changed web template to render title and description as html 201 | 202 | ## [102.0.11] - 2022-06-14 203 | ### Fixed 204 | - template rendering checkbox input when it is boolean type 205 | 206 | ## [102.0.10] - 2022-06-01 207 | ### Added 208 | - added css className to the form wrapper element 209 | 210 | ## [102.0.9] - 2022-05-31 211 | ### Added 212 | - added form submit failure message 213 | ### Changed 214 | - form submit success message is now wysiwyg field 215 | ### Fixed 216 | - some typos 217 | 218 | ## [102.0.8] - 2022-05-02 219 | ### Added 220 | - checkbox intup block type for multiselect 221 | - subscribe to newsetter 222 | ### Changed 223 | - Removed widget forms from menu. Now its done by CustomFormsBuilder module 224 | 225 | ## [102.0.7] - 2022-04-14 226 | ### Fixed 227 | - fix for widgets with non existing forms 228 | - fix for non validated items in admin menu, for example with too long title 229 | 230 | ## [102.0.5] - 2022-04-01 231 | ### Added 232 | - multiselect input 233 | 234 | ## [102.0.4] - 2022-04-01 235 | ### Added 236 | - require for checkbox input 237 | 238 | ## [102.0.3] 239 | ### Changed 240 | - rewrited widget forms to ui component on frontend 241 | - changes to be campatible for recaptcha module 242 | ### Added 243 | - empty option for select input 244 | - log error during form submition 245 | - form input types options (checkbox and radio buttons) 246 | 247 | ## [102.0.2] - 2020-10-15 248 | ### Added 249 | - Added compatibility with Magento 2.4.0 250 | 251 | ## [101.0.0] - 2020-04-27 252 | ### Added 253 | - init 254 | 255 | -------------------------------------------------------------------------------- /Controller/Form/Submit.php: -------------------------------------------------------------------------------- 1 | request = $context->getRequest(); 79 | $this->response = $context->getResponse(); 80 | $this->eventManager = $context->getEventManager(); 81 | $this->formRecordFactory = $formRecordFactory; 82 | $this->jsonFactory = $jsonFactory; 83 | $this->formRepository = $formRepository; 84 | $this->formKeyValidator = $formKeyValidator; 85 | $this->templateFilter = $templateFilter; 86 | $this->logger = $logger; 87 | $this->customerSession = $customerSession; 88 | } 89 | 90 | /** 91 | * @return \Magento\Framework\App\ResponseInterface|\Magento\Framework\Controller\ResultInterface|void 92 | */ 93 | public function execute() 94 | { 95 | $resultJson = $this->jsonFactory->create(); 96 | 97 | try { 98 | $form = $this->getForm(); 99 | $this->validateData(); 100 | $post = $this->getRequest()->getPost(); 101 | $formRecord = $this->formRecordFactory->create(); 102 | $formRecord->getResource()->setCurrentForm($form); 103 | $formRecord->setStoreId((int) $form->getStoreId()); 104 | $formRecord->setFormId($form->getId()); 105 | $formFields = $form->getFieldsCollection(); 106 | foreach ($formFields as $field) { 107 | $fieldCode = $field->getAttributeCode(); 108 | $value = $post[$fieldCode] ?? $field->getDefaultValue(); 109 | $formRecord->setData($fieldCode, $value); 110 | } 111 | 112 | $formRecord->getResource()->save($formRecord); 113 | $this->eventManager->dispatch( 114 | 'alekseon_widget_form_after_submit', 115 | [ 116 | 'form_record' => $formRecord, 117 | 'form' => $form, 118 | ] 119 | ); 120 | $resultJson->setData( 121 | [ 122 | 'errors' => false, 123 | 'title' => $this->getSuccessTitle($formRecord), 124 | 'message' => $this->getSuccessMessage($formRecord), 125 | ] 126 | ); 127 | $this->customerSession->setWidgetFormsSubmitFormId($form->getId()); 128 | } catch (LocalizedException $e) { 129 | $resultJson->setData( 130 | [ 131 | 'errors' => true, 132 | 'message' => $e->getMessage() 133 | ] 134 | ); 135 | } catch (\Exception $e) { 136 | $this->logger->error('Widget Form Error during submit action: ' . $e->getMessage()); 137 | $resultJson->setData( 138 | [ 139 | 'errors' => true, 140 | 'message' => __('We are unable to process your request. Please, try again later.'), 141 | ] 142 | ); 143 | } 144 | 145 | return $resultJson; 146 | } 147 | 148 | /** 149 | * @param $form 150 | * @return string 151 | */ 152 | public function getSuccessMessage($formRecord) 153 | { 154 | $successMessage = $formRecord->getForm()->getFormSubmitSuccessMessage(); 155 | if ($successMessage) { 156 | $successMessage = $this->templateFilter->filter($successMessage); 157 | } else { 158 | $successMessage = __('Thank You!'); 159 | } 160 | return (string) $successMessage; 161 | } 162 | 163 | /** 164 | * @param $form 165 | * @return string 166 | */ 167 | public function getSuccessTitle($formRecord) 168 | { 169 | $successTitle = $formRecord->getForm()->getFormSubmitSuccessTitle(); 170 | if (!$successTitle) { 171 | $successTitle = __('Success'); 172 | } 173 | return (string) $successTitle; 174 | } 175 | 176 | /** 177 | * @return void 178 | * @throws LocalizedException 179 | */ 180 | public function validateData() 181 | { 182 | if (!$this->formKeyValidator->validate($this->getRequest())) { 183 | throw new LocalizedException(__('Invalid Form Key. Please refresh the page.')); 184 | } 185 | 186 | if ($this->getRequest()->getParam('hideit')) { 187 | throw new LocalizedException(__('Interrupted Data')); 188 | } 189 | } 190 | 191 | /** 192 | * 193 | */ 194 | public function getForm() 195 | { 196 | $formId = $this->getRequest()->getParam('form_id'); 197 | $form = $this->formRepository->getById($formId); 198 | return $form; 199 | } 200 | 201 | /** 202 | * @return \Magento\Framework\App\RequestInterface 203 | */ 204 | public function getRequest() 205 | { 206 | return $this->request; 207 | } 208 | 209 | /** 210 | * @return \Magento\Framework\App\ResponseInterface 211 | */ 212 | public function getResponse() 213 | { 214 | return $this->response; 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /Setup/Patch/Data/CreateWidgetFormsAttributesPatch.php: -------------------------------------------------------------------------------- 1 | moduleDataSetup = $moduleDataSetup; 47 | $this->eavSetupFactory = $eavSetupFactory; 48 | $this->formAttributeRepository = $formAttributeRepository; 49 | $this->formFactory = $formFactory; 50 | } 51 | 52 | /** 53 | * @inheritdoc 54 | */ 55 | public function apply() 56 | { 57 | $this->moduleDataSetup->getConnection()->startSetup(); 58 | 59 | $eavSetup = $this->eavSetupFactory->create(); 60 | $eavSetup->setAttributeRepository($this->formAttributeRepository); 61 | 62 | /** 63 | * remove attributes used in old versions of module 64 | */ 65 | $eavSetup->deleteAttribute('form_submit_failure_message'); 66 | 67 | $this->createWidgetFormAttributes($eavSetup); 68 | $this->createNewsletterAttributes($eavSetup); 69 | $this->createMultistepAttribites($eavSetup); 70 | 71 | $this->moduleDataSetup->getConnection()->endSetup(); 72 | return $this; 73 | } 74 | 75 | /** 76 | * @param $eavSetup 77 | * @return void 78 | */ 79 | private function createWidgetFormAttributes($eavSetup) 80 | { 81 | $eavSetup->createAttribute( 82 | 'can_use_for_widget', 83 | [ 84 | 'frontend_input' => 'boolean', 85 | 'frontend_label' => 'Can use as frontend widget', 86 | 'visible_in_grid' => true, 87 | 'is_required' => true, 88 | 'sort_order' => 50, 89 | 'scope' => Scopes::SCOPE_GLOBAL, 90 | ] 91 | ); 92 | 93 | $eavSetup->createAttribute( 94 | 'frontend_form_description', 95 | [ 96 | 'frontend_input' => 'textarea', 97 | 'frontend_label' => 'Frontend Form Description', 98 | 'visible_in_grid' => false, 99 | 'is_required' => false, 100 | 'sort_order' => 10, 101 | 'group_code' => 'widget_form_attribute', 102 | 'scope' => Scopes::SCOPE_STORE, 103 | ] 104 | ); 105 | 106 | $eavSetup->createAttribute( 107 | 'submit_button_label', 108 | [ 109 | 'frontend_input' => 'text', 110 | 'frontend_label' => 'Submit Button Label', 111 | 'visible_in_grid' => false, 112 | 'is_required' => false, 113 | 'sort_order' => 20, 114 | 'group_code' => 'widget_form_attribute', 115 | 'scope' => Scopes::SCOPE_STORE, 116 | ] 117 | ); 118 | 119 | $eavSetup->createOrUpdateAttribute( 120 | 'form_submit_success_message', 121 | [ 122 | 'frontend_input' => 'textarea', 123 | 'frontend_label' => 'Form Submit Success Message', 124 | 'visible_in_grid' => false, 125 | 'is_required' => false, 126 | 'sort_order' => 30, 127 | 'group_code' => 'widget_form_attribute', 128 | 'scope' => Scopes::SCOPE_STORE, 129 | 'is_wysiwyg_enabled' => true, 130 | ] 131 | ); 132 | 133 | $eavSetup->createOrUpdateAttribute( 134 | 'form_submit_success_title', 135 | [ 136 | 'frontend_input' => 'text', 137 | 'frontend_label' => 'Form Submit Success Title', 138 | 'visible_in_grid' => false, 139 | 'is_required' => false, 140 | 'sort_order' => 29, 141 | 'group_code' => 'widget_form_attribute', 142 | 'scope' => Scopes::SCOPE_STORE, 143 | ] 144 | ); 145 | } 146 | 147 | private function createNewsletterAttributes($eavSetup) 148 | { 149 | $eavSetup->createAttribute( 150 | 'subscribe_to_newsletter', 151 | [ 152 | 'frontend_input' => 'boolean', 153 | 'frontend_label' => 'Subscribe to newletter', 154 | 'visible_in_grid' => false, 155 | 'is_required' => false, 156 | 'sort_order' => 10, 157 | 'scope' => Scopes::SCOPE_GLOBAL, 158 | 'group_code' => 'newsletter', 159 | ] 160 | ); 161 | 162 | $eavSetup->createAttribute( 163 | 'newsletter_email', 164 | [ 165 | 'frontend_input' => 'select', 166 | 'frontend_label' => 'Email field', 167 | 'backend_type' => 'varchar', 168 | 'source_model' => 'Alekseon\WidgetForms\Model\Attribute\Source\TextFormAttributes', 169 | 'visible_in_grid' => false, 170 | 'is_required' => false, 171 | 'sort_order' => 20, 172 | 'scope' => Scopes::SCOPE_GLOBAL, 173 | 'group_code' => 'newsletter', 174 | ] 175 | ); 176 | } 177 | 178 | /** 179 | * @param $eavSetup 180 | * @return void 181 | * @throws \Magento\Framework\Exception\NoSuchEntityException 182 | */ 183 | private function createMultistepAttribites($eavSetup) 184 | { 185 | $eavSetup->createOrUpdateAttribute( 186 | 'enable_multiple_steps', 187 | [ 188 | 'frontend_input' => 'boolean', 189 | 'frontend_label' => 'Enable Multiple Steps', 190 | 'visible_in_grid' => false, 191 | 'is_required' => true, 192 | 'sort_order' => 50, 193 | 'group_code' => 'widget_form_attribute', 194 | 'scope' => Scopes::SCOPE_GLOBAL, 195 | 'note' => 'If Yes, display tabs as separated steps', 196 | ] 197 | ); 198 | 199 | // this attribute "enable_multiple_steps" was created with typo in code in old version of module 200 | $wrongAttribute = $this->formAttributeRepository->getByAttributeCode('enable_multpiple_steps', true); 201 | if ($wrongAttribute && $wrongAttribute->getId()) { 202 | $formsWithEnabledSteps = $this->formFactory->create()->getCollection() 203 | ->addAttributeToFilter('enable_multpiple_steps', 1); 204 | 205 | foreach ($formsWithEnabledSteps as $form) { 206 | $form->setEnableMultipleSteps(1); 207 | $form->saveAttributeValue('enable_multiple_steps'); 208 | } 209 | 210 | $eavSetup->deleteAttribute('enable_multpiple_steps'); 211 | } 212 | } 213 | 214 | /** 215 | * @inheritdoc 216 | */ 217 | public static function getDependencies() 218 | { 219 | return []; 220 | } 221 | 222 | /** 223 | * @inheritdoc 224 | */ 225 | public function revert() 226 | { 227 | $this->moduleDataSetup->getConnection()->startSetup(); 228 | 229 | $eavSetup = $this->eavSetupFactory->create(); 230 | $eavSetup->setAttributeRepository($this->formAttributeRepository); 231 | 232 | $eavSetup->deleteAttribute('can_use_for_widget'); 233 | $eavSetup->deleteAttribute('frontend_form_description'); 234 | $eavSetup->deleteAttribute('submit_button_label'); 235 | $eavSetup->deleteAttribute('form_submit_success_title'); 236 | $eavSetup->deleteAttribute('form_submit_success_message'); 237 | $eavSetup->deleteAttribute('subscribe_to_newsletter'); 238 | $eavSetup->deleteAttribute('newsletter_email'); 239 | $eavSetup->deleteAttribute('enable_multiple_steps'); 240 | 241 | $this->moduleDataSetup->getConnection()->endSetup(); 242 | } 243 | 244 | /** 245 | * @inheritdoc 246 | */ 247 | public function getAliases() 248 | { 249 | return []; 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /Block/WidgetForm.php: -------------------------------------------------------------------------------- 1 | formRepository = $formRepository; 82 | $this->formKey = $formKey; 83 | $this->eventManager = $eventManager; 84 | $this->jsonHexTag = $jsonHexTag; 85 | parent::__construct($context, $data); 86 | } 87 | 88 | /** 89 | * @inheritDoc 90 | */ 91 | protected function _toHtml() 92 | { 93 | $form = $this->getForm(); 94 | if ($form) { 95 | $this->addTabs(); 96 | $this->addFields(); 97 | $this->eventManager->dispatch( 98 | 'alekseon_widget_form_prepare_layout', 99 | [ 100 | 'widget_block' => $this, 101 | 'form' => $this->getForm(), 102 | ] 103 | ); 104 | } 105 | return parent::_toHtml(); 106 | } 107 | 108 | /** 109 | * @param string $alias 110 | * @return BlocksContainer 111 | */ 112 | public function getBlocksContainer(string $alias) 113 | { 114 | $containerAlias = 'form_' . $this->getForm()->getId() . '_' . $alias; 115 | /** @var BlocksContainer $blocksContainer */ 116 | $blocksContainer = $this->getChildBlock($containerAlias); 117 | if (!$blocksContainer) { 118 | $blocksContainer = $this->addChild( 119 | $containerAlias, 120 | BlocksContainer::class 121 | ); 122 | } 123 | return $blocksContainer; 124 | } 125 | 126 | /** 127 | * @return void 128 | */ 129 | private function addTabs() 130 | { 131 | if ($this->tabBlocks == null) { 132 | $this->tabBlocks = []; 133 | $tabs = []; 134 | 135 | if ($this->getForm()->getEnableMultipleSteps()) { 136 | $formTabs = $this->getForm()->getFormTabs(); 137 | /** @var FormTab $tab */ 138 | foreach ($formTabs as $tab) { 139 | $tabs[$tab->getId()] = $tab; 140 | } 141 | } 142 | 143 | if (empty($tabs)) { 144 | // backward compatible, to be sure there is always at least one tab 145 | $tab = $this->getForm()->addFormTab(); 146 | $tab->setId(1); 147 | $tabs[$tab->getId()] = $tab; 148 | } 149 | 150 | $firstTab = reset($tabs); 151 | $firstTab->setIsFirstTab(true); 152 | 153 | $lastTab = end($tabs); 154 | $lastTab->setIsLastTab(true); 155 | $tabsContainer = $this->getBlocksContainer('tabs_container'); 156 | 157 | $tabsCounter = 0; 158 | /** @var FormTab $tab */ 159 | foreach ($tabs as $tabCode => $tab) { 160 | $tabsCounter ++; 161 | $fieldBlockAlias = 'form_' . $this->getForm()->getId() . '_tab_' . $tabCode; 162 | $tab->setTabSequenceNumber($tabsCounter); 163 | $this->tabBlocks[$tabCode] = $tabsContainer->addChild( 164 | $fieldBlockAlias, 165 | Tab::class, 166 | [ 167 | 'form' => $this->getForm(), 168 | 'tab' => $tab, 169 | ] 170 | ); 171 | $this->tabSequence[$tabsCounter] = $tabCode; 172 | } 173 | } 174 | } 175 | 176 | /** 177 | * @return void 178 | */ 179 | private function addFields() 180 | { 181 | $fields = $this->getFormFieldsCollection(); 182 | /** @var Attribute $attribute */ 183 | foreach ($fields as $attribute) { 184 | $tabCode = $attribute ? $attribute->getGroupCode() : ''; 185 | /** @var Tab $tabBlock */ 186 | $tabBlock = $this->tabBlocks[$tabCode] ?? reset($this->tabBlocks); 187 | $fieldBlockAlias = 'form_field_' . $attribute->getAttributeCode(); 188 | $tabBlock->addChild( 189 | $fieldBlockAlias, 190 | \Alekseon\CustomFormsFrontend\Block\Form\Field::class, 191 | [ 192 | 'attribute' => $attribute 193 | ] 194 | ); 195 | } 196 | } 197 | 198 | /** 199 | * @return \Alekseon\CustomFormsBuilder\Model\ResourceModel\FormRecord\Attribute\Collection 200 | */ 201 | private function getFormFieldsCollection() 202 | { 203 | if ($this->formFieldsCollection === null) { 204 | $form = $this->getForm(); 205 | $this->formFieldsCollection = $form->getFieldsCollection(); 206 | } 207 | return $this->formFieldsCollection; 208 | } 209 | 210 | /** 211 | * @param $tabSequenceNumber 212 | * @return false|mixed 213 | */ 214 | public function getTabBlock($tabSequenceNumber) 215 | { 216 | $tabCode = $this->tabSequence[$tabSequenceNumber] ?? false; 217 | return $tabCode ? $this->tabBlocks[$tabCode] : false; 218 | } 219 | 220 | /** 221 | * @return int|null 222 | */ 223 | public function getTabsCounter() 224 | { 225 | return count($this->tabSequence); 226 | } 227 | 228 | /** 229 | * @return false|string 230 | */ 231 | public function getTabsJson() 232 | { 233 | return $this->jsonHexTag->serialize($this->tabSequence); 234 | } 235 | 236 | /** 237 | * @return \Alekseon\CustomFormsBuilder\Model\Form|false 238 | */ 239 | public function getForm() 240 | { 241 | if ($this->form === null) { 242 | $identifier = $this->getData('form_identifier'); 243 | $form = false; 244 | if ($identifier) { 245 | try { 246 | $form = $this->formRepository->getByIdentifier($identifier, null, true); 247 | } catch (\Exception $e) {} 248 | } else { 249 | $formId = (int)$this->getData('form_id'); 250 | if ($formId) { 251 | try { 252 | $form = $this->formRepository->getById($formId, null, true); 253 | } catch (\Exception $e) {} 254 | } 255 | } 256 | 257 | if ($form && $form->getCanUseForWidget()) { 258 | $this->form = $form; 259 | } else { 260 | $this->form = false; 261 | } 262 | } 263 | 264 | return $this->form; 265 | } 266 | 267 | /** 268 | * @return false|string 269 | */ 270 | public function getFormTitle() 271 | { 272 | if ($this->getHideTitle()) { 273 | return false; 274 | } 275 | 276 | return $this->getForm()->getTitle(); 277 | } 278 | 279 | /** 280 | * @return false|string 281 | */ 282 | public function getFormDescription() 283 | { 284 | if ($this->getHideDescription()) { 285 | return false; 286 | } 287 | 288 | return $this->getForm()->getFrontendFormDescription(); 289 | } 290 | 291 | /** 292 | * @return string 293 | */ 294 | public function getFormKey() 295 | { 296 | return $this->formKey->getFormKey(); 297 | } 298 | 299 | /** 300 | * @return int 301 | */ 302 | public function getCacheLifetime(): int 303 | { 304 | return 86400; 305 | } 306 | 307 | /** 308 | * @return array 309 | */ 310 | public function getCacheKeyInfo(): array 311 | { 312 | $cacheKeyInfo = parent::getCacheKeyInfo(); 313 | if ($this->getForm()) { 314 | $cacheKeyInfo['widget_data'] = $this->serialize(); 315 | } 316 | return $cacheKeyInfo; 317 | } 318 | 319 | /** 320 | * @return string[] 321 | */ 322 | public function getIdentities(): array 323 | { 324 | return $this->getForm() ? $this->getForm()->getIdentities() : []; 325 | } 326 | 327 | /** 328 | * @return string 329 | */ 330 | public function getSubmitFormUrl() 331 | { 332 | return $this->getUrl('Alekseon_WidgetForms/form/submit', [ 333 | 'form_id' => $this->getForm()->getId(), 334 | ]); 335 | } 336 | 337 | /** 338 | * @return string 339 | */ 340 | public function getFormWrapperClass(): string 341 | { 342 | return $this->getForm()->getIdentifier() 343 | ? 'alekseon-widget-' . $this->getForm()->getIdentifier() . '-form--wrapper' 344 | : ''; 345 | } 346 | 347 | /** 348 | * @return string 349 | */ 350 | public function getSuccessFormUrl() 351 | { 352 | $id = $this->getForm()->getIdentifier(); 353 | if (!$id) { 354 | $id = $this->getForm()->getId(); 355 | } 356 | return $this->getUrl('Alekseon_WidgetForms/form/success', [ 357 | 'form_id' => $id, 358 | ]); 359 | } 360 | } 361 | --------------------------------------------------------------------------------