├── .github └── workflows │ ├── coding-standard.yml │ ├── integration.yml │ └── unit.yml ├── .gitignore ├── .magento └── dev │ └── tests │ └── unit │ └── phpunit.xml ├── Block └── Adminhtml │ ├── Form.php │ ├── Form │ ├── Edit.php │ ├── Edit │ │ ├── Form.php │ │ ├── Tab │ │ │ ├── Fields.php │ │ │ ├── Fields │ │ │ │ └── Form.php │ │ │ └── General.php │ │ └── Tabs.php │ ├── Grid.php │ └── Grid │ │ └── Renderer │ │ ├── Actions.php │ │ └── CreatedFrom.php │ ├── FormField │ ├── Edit.php │ └── Edit │ │ ├── Form.php │ │ └── Tabs.php │ ├── FormRecord.php │ └── FormRecord │ ├── Edit.php │ ├── Edit │ ├── Form.php │ ├── FormInfo.php │ ├── Tab │ │ └── Fields.php │ └── Tabs.php │ └── Grid.php ├── CHANGELOG.md ├── Controller └── Adminhtml │ ├── Form.php │ ├── Form │ ├── Delete.php │ ├── Edit.php │ ├── Index.php │ ├── NewAction.php │ └── Save.php │ ├── FormField.php │ ├── FormField │ ├── Edit.php │ └── Save.php │ ├── FormRecord.php │ └── FormRecord │ ├── Delete.php │ ├── Edit.php │ ├── ExportCsv.php │ ├── ExportExcel.php │ ├── Index.php │ ├── MassDelete.php │ ├── NewAction.php │ └── Save.php ├── LICENCE ├── Model ├── Attribute │ ├── InputValidator │ │ └── PostalCode.php │ └── Source │ │ ├── SelectFormAttributes.php │ │ └── TextFormAttributes.php ├── FieldOptionSources.php ├── Form.php ├── Form │ ├── Attribute.php │ └── AttributeRepository.php ├── FormRecord.php ├── FormRecord │ ├── Attribute.php │ └── AttributeRepository.php ├── FormRepository.php ├── FormTab.php └── ResourceModel │ ├── Form.php │ ├── Form │ ├── Attribute.php │ ├── Attribute │ │ └── Collection.php │ └── Collection.php │ ├── FormRecord.php │ ├── FormRecord │ ├── Attribute.php │ ├── Attribute │ │ └── Collection.php │ └── Collection.php │ ├── FormTab.php │ └── FormTab │ └── Collection.php ├── Plugin ├── AddCustomFormsToAclTreePlugin.php └── AddCustomFormsToAdminMenuPlugin.php ├── README.md ├── Setup └── Patch │ ├── Data │ └── CreateWidgetFormsAttributesPatch.php │ └── Schema │ └── CreateEavTablesV2.php ├── Test └── Unit │ └── Model │ └── FormRepositoryTest.php ├── composer.json ├── etc ├── acl.xml ├── adminhtml │ ├── di.xml │ ├── menu.xml │ ├── routes.xml │ └── system.xml ├── db_schema.xml ├── db_schema_whitelist.json ├── di.xml └── module.xml ├── registration.php └── view ├── adminhtml ├── layout │ ├── alekseon_customformsbuilder_form_edit.xml │ ├── alekseon_customformsbuilder_form_index.xml │ ├── alekseon_customformsbuilder_formfield_edit.xml │ ├── alekseon_customformsbuilder_formrecord_edit.xml │ └── alekseon_customformsbuilder_formrecord_index.xml ├── templates │ ├── form │ │ ├── edit │ │ │ ├── field │ │ │ │ └── identifier.phtml │ │ │ └── tab │ │ │ │ └── fields.phtml │ │ └── wikiLink.phtml │ └── formRecord │ │ └── edit │ │ └── formInfo.phtml └── web │ └── js │ └── form │ └── edit │ ├── formFields.js │ └── formTabs.js └── base ├── requirejs-config.js └── web └── js └── validation-mixin.js /.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/custom-forms-builder 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_CustomFormsBuilder 35 | composer_name: alekseon/custom-forms-builder 36 | ce_version: '2.4.7' 37 | -------------------------------------------------------------------------------- /.github/workflows/unit.yml: -------------------------------------------------------------------------------- 1 | name: M2 Unit Tests 2 | on: [push] 3 | jobs: 4 | unit-tests: 5 | name: Magento 2 Unit Tests 6 | runs-on: ubuntu-latest 7 | env: 8 | MODULE_NAME: Alekseon_CustomFormsBuilder 9 | COMPOSER_NAME: alekseon/custom-forms-builder 10 | REPOSITORY_URL: https://repo.magento.com/ 11 | MAGENTO_MARKETPLACE_USERNAME: ${{ secrets.MAGENTO_MARKETPLACE_USERNAME }} 12 | MAGENTO_MARKETPLACE_PASSWORD: ${{ secrets.MAGENTO_MARKETPLACE_PASSWORD }} 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: extdn/github-actions-m2/magento-unit-tests/8.1@master 16 | env: 17 | MAGENTO_VERSION: '2.4.7' 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .DS_Store 3 | Thumbs.db -------------------------------------------------------------------------------- /.magento/dev/tests/unit/phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | ../../../vendor/alekseon/custom-forms-builder/Test/Unit 11 | 12 | 13 | 14 | ../../../vendor/alekseon/custom-forms-builder 15 | 16 | ../../../vendor/alekseon/custom-forms-builder/Test 17 | 18 | 19 | 20 | 21 | . 22 | testsuite 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Block/Adminhtml/Form.php: -------------------------------------------------------------------------------- 1 | _controller = 'adminhtml_form'; 22 | $this->_blockGroup = 'Alekseon_CustomFormsBuilder'; 23 | $this->_headerText = __('Custom Forms'); 24 | $this->_addButtonLabel = __('Add New Form'); 25 | parent::_construct(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Block/Adminhtml/Form/Edit.php: -------------------------------------------------------------------------------- 1 | _objectId = 'entity_id'; 29 | $this->_controller = 'adminhtml_form'; 30 | 31 | parent::_construct(); 32 | 33 | if (!$this->_authorization->isAllowed('Alekseon_CustomFormsBuilder::manage_custom_forms')) { 34 | $this->removeButton('delete'); 35 | } 36 | 37 | $this->addButton( 38 | 'save_and_continue', 39 | [ 40 | 'label' => __('Save and Continue'), 41 | 'class' => 'save', 42 | 'data_attribute' => [ 43 | 'mage-init' => [ 44 | 'button' => ['event' => 'saveAndContinueEdit', 'target' => '#edit_form'], 45 | ], 46 | ] 47 | ] 48 | ); 49 | } 50 | 51 | /** 52 | * Retrieve URL for save 53 | * 54 | * @return string 55 | */ 56 | public function getSaveUrl() 57 | { 58 | return $this->getUrl( 59 | '*/*/save', 60 | ['_current' => true] 61 | ); 62 | } 63 | 64 | /** 65 | * @inheritdoc 66 | */ 67 | public function getBackUrl() 68 | { 69 | if ($this->getRequest()->getParam('back_to_records')) { 70 | return $this->getUrl('*/formRecord/index', ['id' => $this->getRequest()->getParam('entity_id')]); 71 | } else { 72 | return parent::getBackUrl(); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Block/Adminhtml/Form/Edit/Form.php: -------------------------------------------------------------------------------- 1 | _formFactory->create( 32 | [ 33 | 'data' => [ 34 | 'id' => 'edit_form', 35 | 'action' => $this->getData('action'), 36 | 'method' => 'post', 37 | 'enctype' => 'multipart/form-data', 38 | ] 39 | ] 40 | ); 41 | 42 | $this->setForm($form); 43 | $this->getForm()->setUseContainer(true); 44 | 45 | return parent::_prepareForm(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Block/Adminhtml/Form/Edit/Tab/Fields.php: -------------------------------------------------------------------------------- 1 | registry = $registry; 36 | parent::__construct($context); 37 | } 38 | 39 | /** 40 | * {@inheritdoc} 41 | */ 42 | public function getTabLabel() 43 | { 44 | return __('Form Fields'); 45 | } 46 | 47 | /** 48 | * {@inheritdoc} 49 | */ 50 | public function getTabTitle() 51 | { 52 | return __('Form Fields'); 53 | } 54 | 55 | /** 56 | * {@inheritdoc} 57 | */ 58 | public function canShowTab() 59 | { 60 | return true; 61 | } 62 | 63 | /** 64 | * {@inheritdoc} 65 | */ 66 | public function isHidden() 67 | { 68 | return false; 69 | } 70 | 71 | 72 | /** 73 | * @return \Magento\Backend\Block\Template 74 | */ 75 | protected function _prepareLayout() 76 | { 77 | $this->addChild( 78 | 'add_new_field_button', 79 | \Magento\Backend\Block\Widget\Button::class, 80 | [ 81 | 'label' => __('Add Field'), 82 | 'data_attribute' => ['action' => 'add-form-field'], 83 | 'class' => 'add', 84 | 'id' => 'add_field_button', 85 | ] 86 | ); 87 | 88 | return parent::_prepareLayout(); 89 | } 90 | 91 | /** 92 | * @return mixed|null 93 | */ 94 | public function getCurrentForm() 95 | { 96 | return $this->registry->registry('current_form'); 97 | } 98 | 99 | /** 100 | * @return array[] 101 | */ 102 | public function getFormTabs() 103 | { 104 | $jsonHelper = $this->getData('jsonHelper'); 105 | $formTabs = $this->getCurrentForm()->getFormTabs(); 106 | $tabsData = []; 107 | foreach ($formTabs as $tab) { 108 | $tabsData[] = [ 109 | 'label' => $tab->getLabel(), 110 | 'code' => $tab->getId() 111 | ]; 112 | $this->lastTab = $tab; 113 | } 114 | return $jsonHelper->jsonEncode($tabsData); 115 | } 116 | 117 | /** 118 | * @return mixed 119 | */ 120 | public function getActiveFormTab() 121 | { 122 | return $this->getCurrentForm()->getFirstFormTab(); 123 | } 124 | 125 | /** 126 | * @return int | null 127 | */ 128 | public function getLastTabId() 129 | { 130 | return $this->lastTab->getId(); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /Block/Adminhtml/Form/Edit/Tab/General.php: -------------------------------------------------------------------------------- 1 | dataObject) { 62 | return $this->_coreRegistry->registry('current_form'); 63 | } 64 | return $this->dataObject; 65 | } 66 | 67 | /** 68 | * @inheritdoc 69 | */ 70 | protected function _prepareForm() 71 | { 72 | $dataObject = $this->getDataObject(); 73 | 74 | /** @var \Magento\Framework\Data\Form $form */ 75 | $form = $this->_formFactory->create(); 76 | $baseFieldset = $form->addFieldset('base_fieldset', ['legend' => __('Form Properties')]); 77 | 78 | if ($dataObject->getId()) { 79 | $baseFieldset->addField('entity_id', 'hidden', ['name' => 'entity_id']); 80 | } 81 | 82 | $this->addAllAttributeFields($baseFieldset, $dataObject); 83 | 84 | $baseFieldset->addField('identifier', 85 | 'text', 86 | [ 87 | 'name' => 'identifier', 88 | 'label' => __('Form Identifier'), 89 | 'title' => __('Form Identifier'), 90 | 'note' => __( 91 | 'This is used internally. Make sure you don\'t use spaces or more than %1 symbols.', 92 | 255 93 | ), 94 | ] 95 | ); 96 | 97 | $baseFieldset->addField('group_fields_in', 98 | 'select', 99 | [ 100 | 'name' => 'group_fields_in', 101 | 'label' => __('Group Fields In'), 102 | 'title' => __('Group Fields In'), 103 | 'options' => [ 104 | Form::GROUP_FEILDS_IN_FIELDSETS_OPTION => __('Fieldsets'), 105 | Form::GROUP_FIELDS_IN_TABS_OPTION => __('Tabs'), 106 | ], 107 | ] 108 | ); 109 | 110 | $adminNoteFieldset = $form->addFieldset('admin_note_fieldset', ['legend' => __('Admin Note')]); 111 | 112 | $adminNoteFieldset->addField('admin_note', 113 | 'textarea', 114 | [ 115 | 'name' => 'admin_note', 116 | 'label' => __('Admin Note'), 117 | 'title' => __('Admin Note'), 118 | 'note' => __('Note is not visible on frontend.'), 119 | ] 120 | ); 121 | 122 | $this->setForm($form); 123 | 124 | return parent::_prepareForm(); 125 | } 126 | 127 | 128 | /** 129 | * Initialize form fileds values 130 | * 131 | * @return $this 132 | */ 133 | protected function _initFormValues() 134 | { 135 | $this->getForm()->addValues($this->getDataObject()->getData()); 136 | return parent::_initFormValues(); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /Block/Adminhtml/Form/Edit/Tabs.php: -------------------------------------------------------------------------------- 1 | setId('form_tabs'); 23 | $this->setDestElementId('edit_form'); 24 | $this->setTitle(__('Form Information')); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Block/Adminhtml/Form/Grid.php: -------------------------------------------------------------------------------- 1 | _collectionFactory = $collectionFactory; 37 | parent::__construct($context, $backendHelper, $data); 38 | } 39 | 40 | /** 41 | * @return void 42 | */ 43 | protected function _construct() 44 | { 45 | parent::_construct(); 46 | $this->setUseAjax(true); 47 | } 48 | 49 | /** 50 | * @return $this 51 | */ 52 | protected function _prepareCollection() 53 | { 54 | $collection = $this->_collectionFactory->create(); 55 | $this->setCollection($collection); 56 | 57 | return parent::_prepareCollection(); 58 | } 59 | 60 | /** 61 | * @return $this 62 | * @throws \Exception 63 | */ 64 | protected function _prepareColumns() 65 | { 66 | parent::_prepareColumns(); 67 | 68 | $this->addColumn( 69 | 'entity_id', 70 | [ 71 | 'header' => __('Form Id'), 72 | 'index' => 'entity_id', 73 | ] 74 | ); 75 | 76 | $this->addAttributeColumns(); 77 | 78 | $this->addColumn( 79 | 'admin_note', 80 | [ 81 | 'header' => __('Admin Note'), 82 | 'index' => 'admin_note', 83 | ] 84 | ); 85 | 86 | $this->addColumn( 87 | 'actions', 88 | [ 89 | 'header' => __('Actions'), 90 | 'sortable' => false, 91 | 'filter' => false, 92 | 'renderer' => \Alekseon\CustomFormsBuilder\Block\Adminhtml\Form\Grid\Renderer\Actions::class, 93 | 'header_css_class' => 'col-actions', 94 | 'column_css_class' => 'col-actions' 95 | ] 96 | ); 97 | 98 | return $this; 99 | } 100 | 101 | /** 102 | * @param \Magento\Framework\DataObject $row 103 | * @return string 104 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 105 | */ 106 | public function getRowUrl($row) 107 | { 108 | return false; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Block/Adminhtml/Form/Grid/Renderer/Actions.php: -------------------------------------------------------------------------------- 1 | getUrl('*/formRecord', ['id' => $row->getId()]) . '">' . __('Show Records') . '', 24 | '' . __('Manage Form') . '' 25 | ]; 26 | return implode(' | ', $actions); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Block/Adminhtml/Form/Grid/Renderer/CreatedFrom.php: -------------------------------------------------------------------------------- 1 | getCreatedFromStoreId() === '0') { 22 | return __('All Store Views'); 23 | } 24 | return parent::render($row); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Block/Adminhtml/FormField/Edit.php: -------------------------------------------------------------------------------- 1 | _objectId = 'attribute_code'; 29 | $this->_controller = 'adminhtml_formField'; 30 | 31 | parent::_construct(); 32 | 33 | $this->removeButton('back'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Block/Adminhtml/FormField/Edit/Form.php: -------------------------------------------------------------------------------- 1 | _formFactory->create( 26 | [ 27 | 'data' => [ 28 | 'id' => 'edit_form', 29 | 'action' => $this->getData('action'), 30 | 'method' => 'post' 31 | ] 32 | ] 33 | ); 34 | 35 | $this->setForm($form); 36 | $this->getForm()->setUseContainer(true); 37 | 38 | return parent::_prepareForm(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Block/Adminhtml/FormField/Edit/Tabs.php: -------------------------------------------------------------------------------- 1 | setId('attribute_tabs'); 23 | $this->setDestElementId('edit_form'); 24 | $this->setTitle(__('Form Field Information')); 25 | } 26 | 27 | /** 28 | * @return $this 29 | * @throws \Exception 30 | */ 31 | protected function _beforeToHtml() 32 | { 33 | $this->addTab( 34 | 'main', 35 | [ 36 | 'label' => __('Properties'), 37 | 'title' => __('Properties'), 38 | 'content' => $this->getChildHtml('general'), 39 | 'active' => true 40 | ] 41 | ); 42 | 43 | $this->addTab( 44 | 'frontend_labels', 45 | [ 46 | 'label' => __('Frontend Labels'), 47 | 'title' => __('Frontend Labels'), 48 | 'content' => $this->getChildHtml('frontend_labels'), 49 | ] 50 | ); 51 | 52 | return parent::_beforeToHtml(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Block/Adminhtml/FormRecord.php: -------------------------------------------------------------------------------- 1 | coreRegistry = $coreRegistry; 33 | parent::__construct($context, $data); 34 | } 35 | 36 | /** 37 | * @return void 38 | */ 39 | protected function _construct() 40 | { 41 | $this->_controller = 'adminhtml_formRecord'; 42 | $this->_blockGroup = 'Alekseon_CustomFormsBuilder'; 43 | $this->_headerText = __('Custom Form Records'); 44 | $this->_addButtonLabel = __('Add New Record'); 45 | parent::_construct(); 46 | 47 | if (!$this->getCurrentForm()->getManageFormAllowedDisallowedFlag()) { 48 | $this->addButton( 49 | 'manage_form', 50 | [ 51 | 'label' => __('Manage Form'), 52 | 'onclick' => 'setLocation(\'' . $this->getManageFormUrl() . '\')', 53 | ] 54 | ); 55 | } 56 | 57 | if (!$this->isAddNewRecordAllowed()) { 58 | $this->removeButton('add'); 59 | } 60 | } 61 | 62 | /** 63 | * @return string 64 | */ 65 | public function getCreateUrl() 66 | { 67 | return $this->getUrl('*/*/new', ['form_id' => $this->getCurrentForm()->getId()]); 68 | } 69 | 70 | /** 71 | * @return string 72 | */ 73 | private function getManageFormUrl() 74 | { 75 | return $this->getUrl('*/form/edit', [ 76 | 'entity_id' => $this->getCurrentForm()->getId(), 77 | 'back_to_records' => true, 78 | ]); 79 | } 80 | 81 | /** 82 | * @return mixed 83 | */ 84 | public function getCurrentForm() 85 | { 86 | return $this->coreRegistry->registry('current_form'); 87 | } 88 | 89 | /** 90 | * 91 | */ 92 | protected function isAddNewRecordAllowed() 93 | { 94 | $manageResource = 'Alekseon_CustomFormsBuilder::manage_custom_forms'; 95 | if ($this->_authorization->isAllowed($manageResource)) { 96 | return true; 97 | } 98 | 99 | $resource = 'Alekseon_CustomFormsBuilder::custom_form_' . $this->getCurrentForm()->getId() . '_save'; 100 | return $this->_authorization->isAllowed($resource); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Block/Adminhtml/FormRecord/Edit.php: -------------------------------------------------------------------------------- 1 | coreRegistry = $coreRegistry; 42 | parent::__construct($context, $data); 43 | } 44 | 45 | /** 46 | * @return void 47 | */ 48 | protected function _construct() 49 | { 50 | $this->_objectId = 'id'; 51 | $this->_controller = 'adminhtml_formRecord'; 52 | 53 | parent::_construct(); 54 | 55 | $this->addButton( 56 | 'save_and_continue', 57 | [ 58 | 'label' => __('Save and Continue'), 59 | 'class' => 'save', 60 | 'data_attribute' => [ 61 | 'mage-init' => [ 62 | 'button' => ['event' => 'saveAndContinueEdit', 'target' => '#edit_form'], 63 | ], 64 | ] 65 | ] 66 | ); 67 | 68 | if ($this->getCurrentForm()->getSaveRecordDisallowedFlag()) { 69 | $this->removeButton('save'); 70 | $this->removeButton('save_and_continue'); 71 | } 72 | 73 | if ($this->getCurrentForm()->getDeleteRecordDisallowedFlag()) { 74 | $this->removeButton('delete'); 75 | } 76 | 77 | $this->_formInitScripts[] = 'window.alekseonCustomFormsPostalCodes = ' 78 | . $this->getPostCodeConfig()->getSerializedPostCodes() . ';'; 79 | } 80 | 81 | /** 82 | * Retrieve URL for save 83 | * 84 | * @return string 85 | */ 86 | public function getSaveUrl() 87 | { 88 | return $this->getUrl( 89 | '*/*/save', 90 | ['_current' => true] 91 | ); 92 | } 93 | 94 | /** 95 | * @return string 96 | */ 97 | public function getBackUrl() 98 | { 99 | return $this->getUrl('*/*/index', ['id' => $this->getRequest()->getParam('form_id')]); 100 | } 101 | 102 | /** 103 | * @return string 104 | */ 105 | public function getDeleteUrl() 106 | { 107 | return $this->getUrl('*/*/delete', 108 | [ 109 | 'id' => $this->getRequest()->getParam('id'), 110 | 'form_id' => $this->getRequest()->getParam('form_id') 111 | ] 112 | ); 113 | } 114 | 115 | /** 116 | * @return mixed 117 | */ 118 | public function getCurrentForm() 119 | { 120 | return $this->coreRegistry->registry('current_form'); 121 | } 122 | 123 | /** 124 | * @return mixed|null 125 | */ 126 | public function getCurrentRecord() 127 | { 128 | return $this->coreRegistry->registry('current_record'); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /Block/Adminhtml/FormRecord/Edit/Form.php: -------------------------------------------------------------------------------- 1 | _formFactory->create( 29 | [ 30 | 'data' => [ 31 | 'id' => 'edit_form', 32 | 'action' => $this->getData('action'), 33 | 'method' => 'post', 34 | 'enctype' => 'multipart/form-data', 35 | ] 36 | ] 37 | ); 38 | 39 | $this->setForm($form); 40 | $this->getForm()->setUseContainer(true); 41 | 42 | return parent::_prepareForm(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Block/Adminhtml/FormRecord/Edit/FormInfo.php: -------------------------------------------------------------------------------- 1 | getCurrentRecord()->getId()) { 24 | $this->setTitle(__('Record #') . $this->getCurrentRecord()->getId()); 25 | } else { 26 | $this->setTitle(__('New Record')); 27 | } 28 | parent::_construct(); // TODO: Change the autogenerated stub 29 | } 30 | 31 | /** 32 | * @return mixed 33 | * @throws \Magento\Framework\Exception\LocalizedException 34 | */ 35 | public function getCurrentRecord() 36 | { 37 | return $this->getLayout()->getBlock('form_record_edit')->getCurrentRecord(); 38 | } 39 | 40 | /** 41 | * @return string|void 42 | * @throws \Magento\Framework\Exception\LocalizedException 43 | */ 44 | public function getCreatedFrom() 45 | { 46 | $storeId = $this->getCurrentRecord()->getCreatedFromStoreId(); 47 | if ($storeId) { 48 | try { 49 | $store = $this->_storeManager->getStore($this->getCurrentRecord()->getCreatedFromStoreId()); 50 | return $store->getName(); 51 | } catch (\Exception $e) { 52 | // do nothing 53 | } 54 | } elseif ($storeId === '0') { 55 | return __('All Store Views'); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Block/Adminhtml/FormRecord/Edit/Tab/Fields.php: -------------------------------------------------------------------------------- 1 | _formFactory->create(); 27 | $this->setForm($form); 28 | 29 | $formTabs = $this->getDataObject()->getForm()->getFormTabs(); 30 | $currentTabLabel = ''; 31 | foreach ($formTabs as $formTab) { 32 | $this->formTabCodes[$formTab->getId()] = $formTab->getId(); 33 | if ($formTab->getId() == $this->getTabCode()) { 34 | $currentTabLabel = $formTab->getLabel(); 35 | } 36 | } 37 | 38 | if ($this->groupFieldsInTabs()) { 39 | $fieldset = $form->addFieldset('fieldset', ['legend' => $currentTabLabel]); 40 | $this->addFields($fieldset, $this->getTabCode()); 41 | } else { 42 | $this->setIsFirstTab(true); 43 | foreach ($formTabs as $formTab) { 44 | $fieldset = $form->addFieldset('fieldset_' . $formTab->getId(), ['legend' => $formTab->getLabel()]); 45 | $this->addFields($fieldset, $formTab->getId()); 46 | $this->setIsFirstTab(false); 47 | } 48 | } 49 | 50 | return parent::_prepareForm(); 51 | } 52 | 53 | /** 54 | * @return void 55 | */ 56 | protected function addFields($fieldset, $tabCode) 57 | { 58 | $dataObject = $this->getDataObject(); 59 | if ($this->getIsFirstTab()) { 60 | if ($dataObject->getId()) { 61 | $fieldset->addField('entity_id', 'hidden', ['name' => 'entity_id']); 62 | } 63 | $excluded = array_diff($this->formTabCodes, [$tabCode]); 64 | $this->addAllAttributeFields($fieldset, $dataObject, ['excluded' => $excluded]); 65 | } else { 66 | $this->addAllAttributeFields($fieldset, $dataObject, ['included' => [$tabCode]]); 67 | } 68 | } 69 | 70 | /** 71 | * @return mixed 72 | */ 73 | public function getDataObject() 74 | { 75 | return $this->getLayout()->getBlock('form_record_edit')->getCurrentRecord(); 76 | } 77 | 78 | /** 79 | * @return Fields 80 | */ 81 | protected function _initFormValues() 82 | { 83 | $this->getForm()->addValues($this->getDataObject()->getData()); 84 | return parent::_initFormValues(); 85 | } 86 | 87 | /** 88 | * @return bool 89 | */ 90 | public function groupFieldsInTabs() 91 | { 92 | if ($this->getDataObject()->getForm()->getGroupFieldsIn() == Form::GROUP_FIELDS_IN_TABS_OPTION) { 93 | return true; 94 | } 95 | return false; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Block/Adminhtml/FormRecord/Edit/Tabs.php: -------------------------------------------------------------------------------- 1 | setId('form_record_tabs'); 23 | $this->setDestElementId('edit_form'); 24 | } 25 | 26 | /** 27 | * @return Tabs|\Magento\Backend\Block\Widget\Tabs 28 | * @throws \Exception 29 | */ 30 | protected function _beforeToHtml() 31 | { 32 | if ($this->getCurrentForm()->getGroupFieldsIn() == \Alekseon\CustomFormsBuilder\Model\Form::GROUP_FIELDS_IN_TABS_OPTION) { 33 | $formTabs = $this->getCurrentForm()->getFormTabs(); 34 | 35 | $firstFormTab = $this->getCurrentForm()->getFirstFormTab(); 36 | foreach ($formTabs as $tab) { 37 | $isFirstTab = $firstFormTab->getId() == $tab->getId(); 38 | $fieldsBlock = $this->getLayout()->createBlock( 39 | 'Alekseon\CustomFormsBuilder\Block\Adminhtml\FormRecord\Edit\Tab\Fields' 40 | ); 41 | $fieldsBlock->setTabCode($tab->getId()); 42 | $fieldsBlock->setIsFirstTab($isFirstTab); 43 | $this->addTab( 44 | 'form-tab-' . $tab->getId(), 45 | [ 46 | 'label' => $tab->getLabel(), 47 | 'title' => $tab->getLabel(), 48 | 'active' => $isFirstTab, 49 | 'content' => $fieldsBlock->toHtml(), 50 | ] 51 | ); 52 | } 53 | } else { 54 | $fieldsBlock = $this->getLayout()->createBlock( 55 | 'Alekseon\CustomFormsBuilder\Block\Adminhtml\FormRecord\Edit\Tab\Fields' 56 | ); 57 | $this->addTab( 58 | 'form-tab', 59 | [ 60 | 'label' => __('Record Data'), 61 | 'title' => __('Record Data'), 62 | 'active' => true, 63 | 'content' => $fieldsBlock->toHtml(), 64 | ] 65 | ); 66 | } 67 | 68 | return parent::_beforeToHtml(); 69 | } 70 | 71 | /** 72 | * @return mixed 73 | * @throws \Magento\Framework\Exception\LocalizedException 74 | */ 75 | public function getCurrentRecord() 76 | { 77 | return $this->getLayout()->getBlock('form_record_edit')->getCurrentRecord(); 78 | } 79 | 80 | /** 81 | * @return mixed 82 | * @throws \Magento\Framework\Exception\LocalizedException 83 | */ 84 | public function getCurrentForm() 85 | { 86 | return $this->getLayout()->getBlock('form_record_edit')->getCurrentForm(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Block/Adminhtml/FormRecord/Grid.php: -------------------------------------------------------------------------------- 1 | coreRegistry = $coreRegistry; 38 | parent::__construct($context, $backendHelper, $data); 39 | $this->setDefaultSort('created_at'); 40 | $this->setDefaultDir('desc'); 41 | } 42 | 43 | /** 44 | * @return void 45 | */ 46 | protected function _construct() 47 | { 48 | parent::_construct(); 49 | $this->setUseAjax(true); 50 | } 51 | 52 | /** 53 | * @return $this 54 | */ 55 | protected function _prepareCollection() 56 | { 57 | $collection = $this->getCurrentForm()->getRecordCollection(); 58 | $this->setCollection($collection); 59 | return parent::_prepareCollection(); 60 | } 61 | 62 | /** 63 | * @return $this 64 | * @throws \Exception 65 | */ 66 | protected function _prepareColumns() 67 | { 68 | parent::_prepareColumns(); 69 | 70 | $this->addColumn( 71 | 'created_at', 72 | [ 73 | 'header' => __('Created At'), 74 | 'index' => 'created_at', 75 | 'gmtoffset' => true, 76 | 'type' => 'datetime', 77 | 'header_css_class' => 'col-updated col-date', 78 | 'column_css_class' => 'col-updated col-date' 79 | ] 80 | ); 81 | 82 | $this->addColumn( 83 | 'created_from_store_id', 84 | [ 85 | 'header' => __('Created From'), 86 | 'index' => 'created_from_store_id', 87 | 'type' => 'store', 88 | 'renderer' => \Alekseon\CustomFormsBuilder\Block\Adminhtml\Form\Grid\Renderer\CreatedFrom::class 89 | ] 90 | ); 91 | 92 | $this->addAttributeColumns(); 93 | 94 | $this->addExportType('*/*/exportCsv', __('CSV')); 95 | $this->addExportType('*/*/exportExcel', __('Excel XML')); 96 | 97 | return $this; 98 | } 99 | 100 | /** 101 | * @return mixed 102 | */ 103 | protected function getAttributes() 104 | { 105 | return $this->getCurrentForm()->getFieldsCollection(); 106 | } 107 | 108 | /** 109 | * @return Grid|void 110 | */ 111 | protected function _prepareMassaction() 112 | { 113 | if (!$this->getCurrentForm()->getDeleteRecordDisallowedFlag()) { 114 | $this->setMassactionIdField('entity_id'); 115 | $this->getMassactionBlock()->setFormFieldName('records'); 116 | 117 | $this->getMassactionBlock()->addItem( 118 | 'delete', 119 | [ 120 | 'label' => __('Delete'), 121 | 'url' => $this->getUrl( 122 | '*/*/massDelete', 123 | [ 124 | 'id' => $this->getCurrentForm()->getId(), 125 | ] 126 | ), 127 | 'confirm' => __('Are you sure?') 128 | ] 129 | ); 130 | } 131 | } 132 | 133 | /** 134 | * @return Form 135 | */ 136 | protected function getCurrentForm() 137 | { 138 | return $this->coreRegistry->registry('current_form'); 139 | } 140 | 141 | /** 142 | * @param \Magento\Framework\DataObject $row 143 | * @return string 144 | */ 145 | public function getRowUrl($row) 146 | { 147 | return $this->getUrl('*/*/edit', 148 | [ 149 | 'id' => $row->getEntityId(), 150 | 'form_id' => $this->getCurrentForm()->getId() 151 | ] 152 | ); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | ## [Unreleased] 5 | ### Changed 6 | ### Fixed 7 | ### Added 8 | 9 | ## [102.3.14] - 2024-11-17 10 | ### Added 11 | - postal code validator 12 | - file uploader validations (moved from Alekseon_CustomFormBilder) 13 | 14 | ## [102.3.13] - 2024-09-25 15 | ### Fixed 16 | - fix getMappedAttributeCode method 17 | 18 | ## [102.3.12] - 2024-05-24 19 | ### Fixed 20 | - Translate labels (https://github.com/Alekseon/AlekseonEav/issues/45) 21 | 22 | ## [102.3.11] - 2024-05-09 23 | ### Fixed 24 | - make "Admin Note" translatable 25 | 26 | ## [102.3.10] - 2024-02-25 27 | ### Changed 28 | - Raplaced "Is Enabled" by "Input Visibility", it allows to set input visibility to: visible, only for admin or none 29 | 30 | ## [102.3.9] - 2023-12-03 31 | ### Added 32 | - Max Size param (for file input validator) 33 | - note for params 34 | 35 | ## [102.3.8] - 2023-05-22 36 | ### Added 37 | - manage form button on records view + acl permission 38 | - Yes/No option source 39 | - check if source model class exists 40 | ### Fixed 41 | - fix for saving fields in tabs for new forms 42 | 43 | ## [102.3.7] - 2023-05-09 44 | ### Changed 45 | - code quality improvements 46 | - replaced install and upgrade scripts by patches 47 | ### Fixed 48 | - hide delete massaction if no permisstion set 49 | 50 | ## [102.3.6] - 2023-05-08 51 | ### Changed 52 | - code quality improvements 53 | 54 | ## [102.3.5] - 2023-05-01 55 | ### Added 56 | - github actions 57 | - introduced strict_types 58 | ### Fixed 59 | - display default label instead of store label as fieldset label 60 | - stay on same scope view after "save and continue" form 61 | - code quality improvements 62 | 63 | ## [102.3.4] - 2023-04-22 64 | ### Fixed 65 | - fixed mass delete action 66 | - remove image file during mass delete action 67 | 68 | ## [102.3.3] - 2023-04-21 69 | ### Added 70 | - Created From Store Id for Record entity 71 | - system.xml file 72 | ### Fixed 73 | - added missing group_fields_in column to alekseon_custom_form table 74 | 75 | ## [102.3.2] - 2023-03-12 76 | ### Added 77 | - small alekseon logo 78 | - cache tags 79 | - getRecordCollection() and getRecordById() methods in Form class 80 | - filter,sort,select on form records collection by field identifier 81 | - getData from form record by field identifier 82 | 83 | ## [102.3.1] - 2023-03-04 84 | ### Fixed 85 | - removed "Options Source" input from rating and boolean fields 86 | 87 | ## [102.3.0] - 2023-03-03 88 | ### Added 89 | - possibility to disable form field 90 | - check if "is required" field is editable 91 | - scopable records grid and record edit page 92 | - group fields in tabs 93 | - added "alekseon/widget-forms-statistics" in composer 94 | ### Changed 95 | - use ajax on grids 96 | 97 | ## [102.2.3] - 2023-02-27 98 | ### Added 99 | - notification with link to wiki 100 | 101 | ## [102.2.2] - 2022-11-19 102 | ### Fixed 103 | - fix setUp() declaration in tests 104 | ### Added 105 | - admin note field 106 | - mass delete action for form records 107 | 108 | ## [102.2.1] - 2022-10-22 109 | ### Fixed 110 | - fix error during setup:upgrade for fresh installation 111 | 112 | ## [102.2.0] - 2022-10-22 113 | ### Added 114 | - form identifier 115 | - field identifier 116 | - form property in form record attribute 117 | - added alekseon/custom-forms-frontend requirerment to composer 118 | ### Changed 119 | - max length validator modifications 120 | - moved table definition to db_schema.xml 121 | 122 | ## [102.1.2] - 2022-10-13 123 | ### Changed 124 | - chenged composer requirements 125 | - small fix in setup upgrade 126 | 127 | ## [102.1.1] - 2022-10-12 128 | ### Changed 129 | - attribute_code field length in DB to 255 chars 130 | ### Added 131 | - attribute source: TextFormAttributes 132 | 133 | ## [102.1.0] - 2022-10-10 134 | ### Changed 135 | - Move new entity mail notification functionality to alekseon/custom-forms-email-notification 136 | 137 | ## [102.0.8] - 2022-10-05 138 | ### Added 139 | - compability to attribute default values 140 | 141 | ## [102.0.7] - 2022-10-04 142 | ### Changed 143 | - chenged composer requirements 144 | 145 | ## [102.0.6] - 2022-09-27 146 | ### Added 147 | - created at field for form 148 | - hashed images names and directory name for images (needs eav in version 101.0.11) 149 | - input params (needs eav in version 101.0.11) 150 | 151 | ## [102.0.5] - 2022-08-02 152 | ### Fixed 153 | - fix visibility of configuration 154 | 155 | ## [102.0.4] - 2020-05-02 156 | ### Added 157 | - csv and excel exports 158 | - show custome forms in adminhtml menu 159 | - permissions to view and save custom forms 160 | 161 | ## [102.0.3] - 2020-04-14 162 | ### Fixed 163 | - fixed issue with missing created_at column on fresh installation 164 | 165 | ## [102.0.1] - 2020-04-01 166 | ### Added 167 | - new entity notifiaction email 168 | - option sources for select attributes 169 | - form record created at column 170 | - record attribute variables for events 171 | 172 | ## [102.0.0] - 2020-10-15 173 | ### Added 174 | - Added compatibility with Magento 2.4.0 175 | 176 | ## [101.0.0] - 2020-04-27 177 | ### Added 178 | - init 179 | 180 | -------------------------------------------------------------------------------- /Controller/Adminhtml/Form.php: -------------------------------------------------------------------------------- 1 | coreRegistry = $coreRegistry; 40 | $this->formRepository = $formRepository; 41 | parent::__construct($context); 42 | } 43 | 44 | /** 45 | * @param string $requestParam 46 | * @param int | null $storeId 47 | * @return \Alekseon\CustomFormsBuilder\Model\Form 48 | */ 49 | protected function initForm(string $requestParam = 'entity_id', int $storeId = null) 50 | { 51 | $form = $this->coreRegistry->registry('current_form'); 52 | if (!$form) { 53 | $entityId = $this->getRequest()->getParam($requestParam, false); 54 | if ($storeId === null) { 55 | $storeId = $this->getRequest()->getParam('store'); 56 | } 57 | $form = $this->formRepository->getById($entityId, $storeId, true); 58 | $this->coreRegistry->register('current_form', $form); 59 | } 60 | return $form; 61 | } 62 | 63 | /** 64 | * @return $this 65 | */ 66 | protected function _initAction() 67 | { 68 | $this->_view->loadLayout(); 69 | $this->_setActiveMenu( 70 | 'Alekseon_CustomFormsBuilder::custom_form' 71 | )->_addBreadcrumb( 72 | __('CMS'), 73 | __('CMS') 74 | )->_addBreadcrumb( 75 | __('Manage Custom Forms'), 76 | __('Manage Custom Forms') 77 | ); 78 | return $this; 79 | } 80 | 81 | /** 82 | * @param string $path 83 | * @param array $params 84 | * @return mixed 85 | */ 86 | protected function returnResult($path = '', array $params = []) 87 | { 88 | return $this->resultFactory->create(ResultFactory::TYPE_REDIRECT)->setPath($path, $params); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Controller/Adminhtml/Form/Delete.php: -------------------------------------------------------------------------------- 1 | initForm(); 24 | if ($form->getId()) { 25 | try { 26 | $form->delete(); 27 | $this->messageManager->addSuccessMessage(__('You deleted the form.')); 28 | return $this->returnResult('*/*/', []); 29 | } catch (\Exception $e) { 30 | $this->messageManager->addErrorMessage($e->getMessage()); 31 | return $this->returnResult('*/*/', []); 32 | } 33 | } 34 | $this->messageManager->addErrorMessage(__('We can\'t find an form to delete.')); 35 | return $this->returnResult('*/*/'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Controller/Adminhtml/Form/Edit.php: -------------------------------------------------------------------------------- 1 | _initAction(); 24 | $entity = $this->initForm(); 25 | 26 | if ($entity->getId()) { 27 | $this->_view->getPage()->getConfig()->getTitle()->prepend(__('Edit Form'). ' ' . $entity->getTitle()); 28 | } else { 29 | $this->_view->getPage()->getConfig()->getTitle()->prepend(__('New Custom Form')); 30 | } 31 | 32 | $this->_view->renderLayout(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Controller/Adminhtml/Form/Index.php: -------------------------------------------------------------------------------- 1 | _initAction(); 27 | if ($this->getRequest()->getParam('isAjax')) { 28 | $this->getResponse()->setBody( 29 | $this->_view->getLayout()->getBlock('grid')->toHtml() 30 | ); 31 | return; 32 | } else { 33 | $this->_view->getPage()->getConfig()->getTitle()->prepend(__('Custom Forms')); 34 | $this->_view->renderLayout(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Controller/Adminhtml/Form/NewAction.php: -------------------------------------------------------------------------------- 1 | resultForwardFactory = $resultForwardFactory; 34 | } 35 | 36 | /** 37 | * @return \Magento\Backend\Model\View\Result\Forward 38 | */ 39 | public function execute() 40 | { 41 | return $this->resultForwardFactory->create()->forward('edit'); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Controller/Adminhtml/Form/Save.php: -------------------------------------------------------------------------------- 1 | getRequest()->getParam('back', false)) { 27 | $returnToEdit = true; 28 | } 29 | 30 | $form = false; 31 | $data = $this->getRequest()->getPostValue(); 32 | 33 | if ($data) { 34 | $form = $this->initForm(); 35 | $form->addData($data); 36 | $this->processTabs($form, $data); 37 | 38 | try { 39 | $form->getResource()->save($form); 40 | $this->messageManager->addSuccessMessage(__('You saved the form.')); 41 | } catch (\Exception $e) { 42 | $this->messageManager->addErrorMessage($e->getMessage()); 43 | $returnToEdit = true; 44 | } 45 | } 46 | 47 | if ($returnToEdit && $form) { 48 | return $this->returnResult('*/*/edit', ['_current' => true, 'entity_id' => $form->getId()]); 49 | } else { 50 | if ($this->getRequest()->getParam('back_to_records')) { 51 | return $this->returnResult('*/formRecord/index', ['id' => $form->getId()]); 52 | } else { 53 | return $this->returnResult('*/*/'); 54 | } 55 | } 56 | } 57 | 58 | /** 59 | * @param Form $form 60 | * @param array $data 61 | * @return void 62 | */ 63 | private function processTabs(Form $form, array $data = []) 64 | { 65 | if (isset($data['form_tabs'])) { 66 | $fomTabs = $form->getFormTabs(); 67 | /** @var FormTab $tab */ 68 | foreach ($fomTabs as $tab) { 69 | if ($tab->getId() && !isset($data['form_tabs'][$tab->getId()])) { 70 | $tab->setDeleted(true); 71 | } 72 | } 73 | foreach ($data['form_tabs'] as $tabId => $tabData) { 74 | /** @var FormTab $tab */ 75 | $tab = $fomTabs[$tabId] ?? false; 76 | if ($tab) { 77 | $tab->addData($tabData); 78 | } else { 79 | $tabData['tmp_tab_id'] = $tabId; 80 | $form->addFormTab($tabData); 81 | } 82 | } 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Controller/Adminhtml/FormField.php: -------------------------------------------------------------------------------- 1 | resultPageFactory = $resultPageFactory; 45 | $this->coreRegistry = $coreRegistry; 46 | $this->attributeRepository = $attributeRepository; 47 | parent::__construct($context); 48 | } 49 | 50 | /** 51 | * @param string $requestParam 52 | * @return bool|mixed 53 | * @throws \Magento\Framework\Exception\NoSuchEntityException 54 | */ 55 | protected function initAttribute($requestParam = 'id') 56 | { 57 | $attribute = $this->coreRegistry->registry('current_attribute'); 58 | if (!$attribute) { 59 | $attributeId = $this->getRequest()->getParam($requestParam, false); 60 | if ($attributeId) { 61 | $attribute = $this->attributeRepository->getById($attributeId); 62 | } else { 63 | return false; 64 | } 65 | $this->coreRegistry->register('current_attribute', $attribute); 66 | } 67 | return $attribute; 68 | } 69 | 70 | /** 71 | * @param string $path 72 | * @param array $params 73 | * @return mixed 74 | */ 75 | protected function returnResult($path = '', array $params = []) 76 | { 77 | return $this->resultFactory->create(ResultFactory::TYPE_REDIRECT)->setPath($path, $params); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Controller/Adminhtml/FormField/Edit.php: -------------------------------------------------------------------------------- 1 | initAttribute(); 25 | } catch (\Exception $e) { 26 | $this->messageManager->addErrorMessage($e->getMessage()); 27 | return $this->returnResult('*/form/index'); 28 | } 29 | /** @var \Magento\Backend\Model\View\Result\Page $resultPage */ 30 | $resultPage = $this->resultPageFactory->create(); 31 | if ($attribute) { 32 | $resultPage->getConfig()->getTitle()->prepend(__('Edit Form Field') . ' ' . $attribute->getFrontendLabel()); 33 | } else { 34 | $this->messageManager->addErrorMessage(__('Form Field not found.')); 35 | return $this->returnResult('*/form/index'); 36 | } 37 | return $resultPage; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Controller/Adminhtml/FormField/Save.php: -------------------------------------------------------------------------------- 1 | getRequest()->getPostValue(); 25 | if ($data) { 26 | $attribute = $this->initAttribute(); 27 | $attribute->addData($data); 28 | try { 29 | $this->attributeRepository->save($attribute); 30 | $this->messageManager->addSuccessMessage(__('You saved the form field.')); 31 | } catch (\Exception $e) { 32 | $this->messageManager->addErrorMessage($e->getMessage()); 33 | } 34 | return $this->returnResult('*/form/edit', ['entity_id' => $attribute->getFormId()]); 35 | } 36 | return $this->returnResult('*/form/index', []); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Controller/Adminhtml/FormRecord.php: -------------------------------------------------------------------------------- 1 | coreRegistry = $coreRegistry; 58 | $this->formRepository = $formRepository; 59 | $this->formRecordFactory = $formRecordFactory; 60 | $this->fileFactory = $fileFactory; 61 | $this->dataPersistor = $dataPersistor; 62 | parent::__construct($context); 63 | } 64 | 65 | /** 66 | * @return bool 67 | */ 68 | protected function _isAllowed($formRequestParam = 'form_id') 69 | { 70 | $manageResource = 'Alekseon_CustomFormsBuilder::manage_custom_forms'; 71 | if ($this->_authorization->isAllowed($manageResource)) { 72 | return true; 73 | } 74 | 75 | $form = $this->initForm($formRequestParam); 76 | if ($form) { 77 | $resource = $this->getIsAllowedResource($form); 78 | } else { 79 | $resource = static::ADMIN_RESOURCE; 80 | } 81 | 82 | return $this->_authorization->isAllowed($resource); 83 | } 84 | 85 | /** 86 | * @param $form 87 | * @return string 88 | */ 89 | protected function getIsAllowedResource($form) 90 | { 91 | return 'Alekseon_CustomFormsBuilder::custom_form_' . $form->getId(); 92 | } 93 | 94 | /** 95 | * @param string $requestParam 96 | * @return \Alekseon\CustomFormsBuilder\Model\Form 97 | * @throws \Magento\Framework\Exception\NoSuchEntityException 98 | */ 99 | protected function initForm($requestParam = 'id') 100 | { 101 | $form = $this->coreRegistry->registry('current_form'); 102 | if (!$form) { 103 | $fromId = $this->getRequest()->getParam($requestParam, false); 104 | $storeId = $this->getRequest()->getParam('store', null); 105 | $form = $this->formRepository->getById($fromId, $storeId, true); 106 | 107 | if (!$this->isSaveRecordAllowed($form)) { 108 | $form->setSaveRecordDisallowedFlag(true); 109 | } 110 | if (!$this->isDeleteRecordAllowed($form)) { 111 | $form->setDeleteRecordDisallowedFlag(true); 112 | } 113 | if (!$this->isManageFormAllowed($form)) { 114 | $form->setManageFormAllowedDisallowedFlag(true); 115 | } 116 | 117 | $this->coreRegistry->register('current_form', $form); 118 | } 119 | return $form; 120 | } 121 | 122 | /** 123 | * @param string $requestParam 124 | * @param string $formRequestParam 125 | * @return \Alekseon\CustomFormsBuilder\Model\FormRecord 126 | * @throws LocalizedException 127 | */ 128 | protected function initRecord(string $requestParam = 'id', string $formRequestParam = 'form_id') 129 | { 130 | $record = $this->coreRegistry->registry('current_record'); 131 | $form = $this->initForm($formRequestParam); 132 | if (!$record) { 133 | $recordId = $this->getRequest()->getParam($requestParam, false); 134 | $record = $form->getRecordById($recordId, true); 135 | if (!$record->getId()) { 136 | $record->setFormId($form->getId()); 137 | } 138 | 139 | $data = $this->dataPersistor->get('custom_form_record'); 140 | if (!empty($data)) { 141 | $record->addData($data); 142 | $this->dataPersistor->clear('custom_form_record'); 143 | } 144 | 145 | $this->coreRegistry->register('current_record', $record); 146 | } 147 | 148 | if (!$form->getId() || $record->getFormId() != $form->getId()) { 149 | throw new LocalizedException(__('Incorrect Form')); 150 | } 151 | 152 | return $record; 153 | } 154 | 155 | /** 156 | * @return $this 157 | */ 158 | protected function _initAction() 159 | { 160 | $this->_view->loadLayout(); 161 | $this->_setActiveMenu( 162 | 'Alekseon_CustomFormsBuilder::custom_form' 163 | ); 164 | return $this; 165 | } 166 | 167 | /** 168 | * @param string $path 169 | * @param array $params 170 | * @return mixed 171 | */ 172 | protected function returnResult($path = '', array $params = []) 173 | { 174 | return $this->resultFactory->create(ResultFactory::TYPE_REDIRECT)->setPath($path, $params); 175 | } 176 | 177 | /** 178 | * @param \Alekseon\CustomFormsBuilder\Model\Form $form 179 | * @return bool 180 | */ 181 | private function isSaveRecordAllowed(\Alekseon\CustomFormsBuilder\Model\Form $form) 182 | { 183 | $manageResource = 'Alekseon_CustomFormsBuilder::manage_custom_forms'; 184 | if ($this->_authorization->isAllowed($manageResource)) { 185 | return true; 186 | } 187 | 188 | $resource = 'Alekseon_CustomFormsBuilder::custom_form_' . $form->getId() . '_save'; 189 | return $this->_authorization->isAllowed($resource); 190 | } 191 | 192 | /** 193 | * @param \Alekseon\CustomFormsBuilder\Model\Form $form 194 | * @return bool 195 | */ 196 | private function isManageFormAllowed(\Alekseon\CustomFormsBuilder\Model\Form $form) 197 | { 198 | $manageResource = 'Alekseon_CustomFormsBuilder::manage_custom_forms'; 199 | if ($this->_authorization->isAllowed($manageResource)) { 200 | return true; 201 | } 202 | 203 | $resource = 'Alekseon_CustomFormsBuilder::custom_form_' . $form->getId() . '_manage_form'; 204 | return $this->_authorization->isAllowed($resource); 205 | } 206 | 207 | /** 208 | * @param \Alekseon\CustomFormsBuilder\Model\Form $form 209 | * @return bool 210 | */ 211 | private function isDeleteRecordAllowed(\Alekseon\CustomFormsBuilder\Model\Form $form) 212 | { 213 | return $this->isSaveRecordAllowed($form); 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /Controller/Adminhtml/FormRecord/Delete.php: -------------------------------------------------------------------------------- 1 | initForm('form_id'); 24 | try { 25 | $record = $this->initRecord(); 26 | if ($record->getId()) { 27 | $record->delete(); 28 | $this->messageManager->addSuccessMessage(__('You deleted the record.')); 29 | } 30 | return $this->returnResult('*/*', ['id' => $form->getId()]); 31 | } catch (\Exception $e) { 32 | $this->messageManager->addErrorMessage($e->getMessage()); 33 | return $this->returnResult('*/*', ['id' => $form->getId()]); 34 | } 35 | } 36 | 37 | /** 38 | * @param $form 39 | * @return string 40 | */ 41 | protected function getIsAllowedResource($form) 42 | { 43 | return 'Alekseon_CustomFormsBuilder::custom_form_' . $form->getId() . '_save'; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Controller/Adminhtml/FormRecord/Edit.php: -------------------------------------------------------------------------------- 1 | initForm('form_id'); 24 | 25 | try { 26 | $record = $this->initRecord(); 27 | } catch (\Exception $e) { 28 | $this->messageManager->addErrorMessage($e->getMessage()); 29 | return $this->returnResult('*/*', ['id' => $form->getId()]); 30 | } 31 | 32 | $this->_initAction(); 33 | $title = $record->getForm()->getTitle(); 34 | $this->_view->getPage()->getConfig()->getTitle()->prepend($title); 35 | $this->_view->renderLayout(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Controller/Adminhtml/FormRecord/ExportCsv.php: -------------------------------------------------------------------------------- 1 | initForm(); 28 | 29 | if (!$form->getId()) { 30 | $this->_forward('noroute'); 31 | return; 32 | } 33 | 34 | $fileName = 'export_form_' . $form->getId() . '.csv'; 35 | /** @var \Alekseon\CustomFormsBuilder\Block\Adminhtml\FormRecord\Grid $grid */ 36 | $grid = $this->_view->getLayout()->createBlock(\Alekseon\CustomFormsBuilder\Block\Adminhtml\FormRecord\Grid::class); 37 | return $this->fileFactory->create($fileName, $grid->getCsvFile($fileName), DirectoryList::VAR_DIR); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Controller/Adminhtml/FormRecord/ExportExcel.php: -------------------------------------------------------------------------------- 1 | initForm(); 28 | 29 | if (!$form->getId()) { 30 | $this->_forward('noroute'); 31 | return; 32 | } 33 | 34 | $fileName = 'export_form_' . $form->getId() . '.xml'; 35 | $grid = $this->_view->getLayout()->createBlock(\Alekseon\CustomFormsBuilder\Block\Adminhtml\FormRecord\Grid::class); 36 | return $this->fileFactory->create($fileName, $grid->getExcelFile($fileName), DirectoryList::VAR_DIR); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Controller/Adminhtml/FormRecord/Index.php: -------------------------------------------------------------------------------- 1 | initForm(); 38 | 39 | if (!$form->getId()) { 40 | $this->_forward('noroute'); 41 | return; 42 | } 43 | 44 | $this->_initAction(); 45 | 46 | if ($this->getRequest()->getParam('isAjax')) { 47 | $this->getResponse()->setBody( 48 | $this->_view->getLayout()->getBlock('grid')->toHtml() 49 | ); 50 | } else { 51 | $this->_view->getPage()->getConfig()->getTitle()->prepend($form->getTitle()); 52 | $this->_view->renderLayout(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Controller/Adminhtml/FormRecord/MassDelete.php: -------------------------------------------------------------------------------- 1 | initForm(); 27 | 28 | if (!$form->getId()) { 29 | $this->_forward('noroute'); 30 | return; 31 | } 32 | 33 | $recordIds = $this->getRequest()->getParam('records'); 34 | 35 | if (!is_array($recordIds)) { 36 | $this->messageManager->addErrorMessage(__('Please select form record(s).')); 37 | } else { 38 | try { 39 | $counter = 0; 40 | foreach ($this->getCollection() as $model) { 41 | $model->delete(); 42 | $counter ++; 43 | } 44 | $this->messageManager->addSuccessMessage( 45 | __('A total of %1 record(s) have been deleted.', $counter) 46 | ); 47 | } catch (LocalizedException $e) { 48 | $this->messageManager->addErrorMessage($e->getMessage()); 49 | } catch (\Exception $e) { 50 | $this->messageManager->addExceptionMessage($e, __('Something went wrong while deleting these records.')); 51 | } 52 | } 53 | 54 | $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); 55 | $resultRedirect->setPath('*/formRecord', ['id' => $form->getId()]); 56 | return $resultRedirect; 57 | } 58 | 59 | /** 60 | * @return \Alekseon\CustomFormsBuilder\Model\ResourceModel\FormRecord\Collection 61 | * @throws LocalizedException 62 | */ 63 | protected function getCollection() 64 | { 65 | /** @var Form $form */ 66 | $form = $this->coreRegistry->registry('current_form'); 67 | $collection = $form->getRecordCollection(); 68 | $collection->addFieldToFilter( 69 | 'entity_id', 70 | $this->getRequest()->getParam('records') 71 | ); 72 | 73 | return $collection; 74 | } 75 | 76 | /** 77 | * @param $form 78 | * @return string 79 | */ 80 | protected function getIsAllowedResource($form) 81 | { 82 | return 'Alekseon_CustomFormsBuilder::custom_form_' . $form->getId() . '_save'; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Controller/Adminhtml/FormRecord/NewAction.php: -------------------------------------------------------------------------------- 1 | resultForwardFactory = $resultForwardFactory; 34 | } 35 | 36 | /** 37 | * @return \Magento\Backend\Model\View\Result\Forward 38 | */ 39 | public function execute() 40 | { 41 | return $this->resultForwardFactory->create()->forward('edit'); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Controller/Adminhtml/FormRecord/Save.php: -------------------------------------------------------------------------------- 1 | getRequest()->getParam('back', false)) { 24 | $returnToEdit = true; 25 | } else { 26 | $returnToEdit = false; 27 | } 28 | 29 | $form = $this->initForm('form_id'); 30 | $data = $this->getRequest()->getPostValue(); 31 | $record = $this->initRecord(); 32 | 33 | if ($data) { 34 | try { 35 | $record->addData($data); 36 | $record->setFormId($form->getId()); 37 | $record->getResource()->save($record); 38 | $this->dataPersistor->clear('custom_form_record'); 39 | $this->messageManager->addSuccessMessage(__('You saved the record.')); 40 | } catch (\Exception $e) { 41 | $this->dataPersistor->set('custom_form_record', $data); 42 | $this->messageManager->addErrorMessage($e->getMessage()); 43 | $returnToEdit = true; 44 | } 45 | } 46 | 47 | if ($returnToEdit) { 48 | if ($record->getId()) { 49 | return $this->returnResult('*/*/edit', ['_current' => true, 'id' => $record->getId()]); 50 | } else { 51 | return $this->returnResult('*/*/new', ['_current' => true]); 52 | } 53 | } else { 54 | return $this->returnResult('*/*', ['id' => $form->getId()]); 55 | } 56 | } 57 | 58 | /** 59 | * @param $form 60 | * @return string 61 | */ 62 | protected function getIsAllowedResource($form) 63 | { 64 | return 'Alekseon_CustomFormsBuilder::custom_form_' . $form->getId() . '_save'; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /Model/Attribute/InputValidator/PostalCode.php: -------------------------------------------------------------------------------- 1 | optionsSource = $optionsSource; 42 | $this->postCodeConfig = $postCodeConfig; 43 | parent::__construct($data); 44 | } 45 | 46 | /** 47 | * @return string 48 | */ 49 | public function getValidationFieldClass() 50 | { 51 | $countryFieldId = $this->attribute->getInputParam('post_code_country_field_id'); 52 | if ($countryFieldId) { 53 | return 'alekseon-validate-postal-code'; 54 | } 55 | return parent::getValidationFieldClass(); 56 | } 57 | 58 | /** 59 | * @inheritDoc 60 | */ 61 | public function getDataValidateParams() 62 | { 63 | $countryFieldId = $this->attribute->getInputParam('post_code_country_field_id'); 64 | if ($countryFieldId) { 65 | return [ 66 | 'alekseon-validate-postal-code' => $countryFieldId, 67 | ]; 68 | } 69 | return parent::getDataValidateParams(); 70 | } 71 | 72 | /** 73 | * @inheritDoc 74 | */ 75 | public function getAdminDataValidateParams() 76 | { 77 | return $this->attribute->getInputParam('post_code_country_field_id'); 78 | } 79 | 80 | /** 81 | * @inheritDoc 82 | */ 83 | public function validateValue($value) 84 | { 85 | $countryFieldId = $this->attribute->getInputParam('post_code_country_field_id'); 86 | if (!$countryFieldId) { 87 | return true; 88 | } 89 | $entity = $this->getEntity(); 90 | $countryId = $entity->getData($countryFieldId); 91 | if (!$countryId) { 92 | return true; 93 | } 94 | 95 | $postCodes = $this->postCodeConfig->getPostCodes(); 96 | $patterns = $postCodes[$countryId] ?? []; 97 | 98 | if (empty($patterns)) { 99 | return true; 100 | } 101 | 102 | foreach ($patterns as $patternData) { 103 | $pattern = $patternData['pattern'] ?? ''; 104 | if (!$pattern) { 105 | continue; 106 | } 107 | 108 | if (preg_match('/' . $pattern. '/', $value)) { 109 | return true; 110 | } 111 | } 112 | 113 | return false; 114 | } 115 | 116 | /** 117 | * @return mixed 118 | */ 119 | public function getInputParams() 120 | { 121 | $inputParams = parent::getInputParams(); 122 | $inputParams['post_code_country_field_id'] = [ 123 | 'label' => 'Country Field', 124 | 'type' => 'select', 125 | 'options' => $this->optionsSource->getOptionArray(true), 126 | 'note' => 'Used for Postal Code Validation', 127 | ]; 128 | return $inputParams; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /Model/Attribute/Source/SelectFormAttributes.php: -------------------------------------------------------------------------------- 1 | coreRegistry = $coreRegistry; 32 | } 33 | 34 | /** 35 | * @inheritDoc 36 | */ 37 | protected function getEmptyOptionLabel() 38 | { 39 | return __('Not Selected'); 40 | } 41 | 42 | /** 43 | * @return array 44 | */ 45 | public function getOptions() 46 | { 47 | $options = []; 48 | 49 | /** @var Form $form */ 50 | $form = $this->coreRegistry->registry('current_form'); 51 | 52 | if ($form) { 53 | $fields = $form->getFieldsCollection(); 54 | foreach ($fields as $field) { 55 | if ($field->getFrontendInput() == 'select') { 56 | $options[$field->getAttributeCode()] = $field->getFrontendLabel(); 57 | } 58 | } 59 | } 60 | 61 | return $options; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Model/Attribute/Source/TextFormAttributes.php: -------------------------------------------------------------------------------- 1 | coreRegistry = $coreRegistry; 32 | } 33 | 34 | /** 35 | * @return array 36 | */ 37 | public function getOptions() 38 | { 39 | $options = []; 40 | 41 | /** @var Form $form */ 42 | $form = $this->coreRegistry->registry('current_form'); 43 | 44 | if ($form) { 45 | $fields = $form->getFieldsCollection(); 46 | foreach ($fields as $field) { 47 | if ($field->getFrontendInput() == 'text') { 48 | $options[$field->getAttributeCode()] = $field->getFrontendLabel(); 49 | } 50 | } 51 | } 52 | 53 | return $options; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Model/FieldOptionSources.php: -------------------------------------------------------------------------------- 1 | optionSources = $optionSources; 37 | } 38 | 39 | /** 40 | * @param $code 41 | */ 42 | public function getOptionSourceByCode($code) 43 | { 44 | $optionsSources = $this->getOptionSources(); 45 | return $optionsSources[$code] ?? false; 46 | } 47 | 48 | /** 49 | * @param $sourceModel 50 | * @return string | false 51 | */ 52 | public function getCodeBySourceModel($sourceModel) 53 | { 54 | $this->getOptionSources(); 55 | return $this->codesBySourceModel[$sourceModel] ?? false; 56 | } 57 | 58 | /** 59 | * @return array|null 60 | */ 61 | public function getOptionSources() 62 | { 63 | if ($this->optionSourcesByCodes === null) { 64 | $this->optionSourcesByCodes = []; 65 | foreach ($this->optionSources as $code => $data) { 66 | $optionSource = new \Magento\Framework\DataObject($data); 67 | $sourceModel = $optionSource->getSourceModel(); 68 | if (class_exists($sourceModel)) { 69 | $optionSource->setCode($code); 70 | $this->optionSourcesByCodes[$code] = $optionSource; 71 | $this->codesBySourceModel[$sourceModel] = $code; 72 | } 73 | } 74 | } 75 | return $this->optionSourcesByCodes; 76 | } 77 | 78 | /** 79 | * 80 | */ 81 | public function toOptionArray() 82 | { 83 | $options = $this->getOptionSources(); 84 | $result = [[ 85 | 'value' => self::DEFAULT_ATTRIBUTE_SOURCE_VALUE, 86 | 'label' => __('Default Attribute Options'), 87 | ]]; 88 | 89 | foreach ($options as $code => $option) { 90 | $result[] = [ 91 | 'value' => $code, 92 | 'label' => __($option->getLabel()), 93 | ]; 94 | } 95 | return $result; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Model/Form.php: -------------------------------------------------------------------------------- 1 | recordAttributeRepository = $recordAttributeRepository; 73 | $this->formTabFactory = $formTabFactory; 74 | $this->formRecordFactory = $formRecordFactory; 75 | parent::__construct( 76 | $context, 77 | $registry, 78 | $resource, 79 | $resourceCollection 80 | ); 81 | } 82 | 83 | /** 84 | * @return \Alekseon\AlekseonEav\Model\Entity 85 | * @throws \Magento\Framework\Exception\NoSuchEntityException 86 | * @throws \Magento\Framework\Exception\TemporaryState\CouldNotSaveException 87 | */ 88 | public function afterSave() 89 | { 90 | $this->updateFields(); 91 | $this->addNewFields(); 92 | 93 | return parent::afterSave(); 94 | } 95 | 96 | /** 97 | * @return void 98 | */ 99 | private function addNewFields() 100 | { 101 | $formId = $this->getId(); 102 | 103 | $newFieldsData = $this->getNewFields(); 104 | 105 | if (!$formId || !is_array($newFieldsData)) { 106 | return; 107 | } 108 | 109 | $attributeFactory = $this->recordAttributeRepository->getAttributeFactory(); 110 | foreach ($newFieldsData as $id => $newFieldData) { 111 | if (isset($newFieldData['frontend_label']) && $newFieldData['frontend_label']) { 112 | unset($newFieldData['id']); 113 | $attribute = $attributeFactory->create(); 114 | $attribute->setData($newFieldData); 115 | $attribute->setVisibleInGrid(true); 116 | $attribute->setAttributeCode('field_' . $formId . '_' . time() . '_' . $id); 117 | $attribute->setFormId($formId); 118 | $this->updateFieldGroupCode($attribute); 119 | $this->recordAttributeRepository->save($attribute); 120 | } 121 | } 122 | } 123 | 124 | /** 125 | * @return Collection 126 | */ 127 | public function getRecordCollection() 128 | { 129 | /** @var Collection $collection */ 130 | $collection = $this->formRecordFactory->create()->getCollection(); 131 | $collection->setStoreId($this->getStoreId()); 132 | $collection->addFormFilter($this); 133 | $collection->getResource()->setCurrentForm($this); 134 | return $collection; 135 | } 136 | 137 | /** 138 | * @param $id 139 | * @return mixed 140 | */ 141 | public function getRecordById($id, $graceful = false) 142 | { 143 | $record = $this->formRecordFactory->create(); 144 | $record->setStoreId($this->getStoreId()); 145 | $record->getResource()->setCurrentForm($this); 146 | $record->getResource()->load($record, $id); 147 | if (!$graceful && (!$record->getId() || $record->getFormId() != $this->getId())) { 148 | throw new NoSuchEntityException(__('Form record not found.')); 149 | } 150 | 151 | return $record; 152 | } 153 | 154 | /** 155 | * 156 | */ 157 | private function updateFields() 158 | { 159 | $removedFields = explode(',', $this->getFormRemovedFields()); 160 | $formFields = $this->getFormFields(); 161 | 162 | foreach ($removedFields as $attributeId) { 163 | if ($attributeId) { 164 | $attribute = $this->recordAttributeRepository->getById($attributeId); 165 | $attribute->delete(); 166 | } 167 | } 168 | 169 | if (!is_array($formFields)) { 170 | return; 171 | } 172 | 173 | foreach ($formFields as $fieldData) { 174 | $attributeId = isset($fieldData['id']) ? (int) $fieldData['id'] : false; 175 | if (!$attributeId) { 176 | continue; 177 | } 178 | 179 | if (in_array($attributeId, $removedFields)) { 180 | continue; 181 | } 182 | 183 | $attribute = $this->recordAttributeRepository->getById($attributeId); 184 | unset($fieldData['frontend_input']); 185 | $attribute->addData($fieldData); 186 | $this->updateFieldGroupCode($attribute); 187 | $this->recordAttributeRepository->save($attribute); 188 | } 189 | } 190 | 191 | /** 192 | * @param bool $withDisabled 193 | * @return ResourceModel\FormRecord\Attribute\Collection 194 | */ 195 | public function getFieldsCollection(bool $withDisabled = false): ResourceModel\FormRecord\Attribute\Collection 196 | { 197 | if ($this->fieldsCollection === null) { 198 | $isAdmin = $this->_appState->getAreaCode() == \Magento\Framework\App\Area::AREA_ADMINHTML; 199 | $attributeObject = $this->recordAttributeRepository->getAttributeFactory()->create(); 200 | $this->fieldsCollection = $attributeObject->getCollection(); 201 | 202 | if (!$withDisabled) { 203 | if ($isAdmin) { 204 | $this->fieldsCollection->addFieldToFilter( 205 | 'input_visibility', 206 | ['nin' => Attribute::INPUT_VISIBILITY_NONE] 207 | ); 208 | } else { 209 | $this->fieldsCollection->addFieldToFilter( 210 | 'input_visibility', 211 | Attribute::INPUT_VISIBILITY_VISIBILE 212 | ); 213 | } 214 | } 215 | 216 | $this->fieldsCollection->addFieldToFilter('form_id', $this->getId()); 217 | $this->fieldsCollection->setOrder( 218 | 'sort_order', 219 | \Magento\Framework\Data\Collection::SORT_ORDER_ASC 220 | ); 221 | 222 | } 223 | return $this->fieldsCollection; 224 | } 225 | 226 | /** 227 | * @return array 228 | */ 229 | public function getFormTabs(): array 230 | { 231 | if ($this->formTabs === null) { 232 | /** @var ResourceModel\FormTab\Collection $formTabsCollection */ 233 | $formTabsCollection = $this->formTabFactory->create()->getCollection(); 234 | $formTabsCollection->addFormFilter($this); 235 | $this->formTabs = []; 236 | $lastTab = false; 237 | foreach ($formTabsCollection as $tab) { 238 | $this->formTabs[$tab->getId()] = $tab; 239 | $lastTab = $tab; 240 | } 241 | 242 | if (!$lastTab) { 243 | $lastTab = $this->addFormTab( 244 | [ 245 | 'label' => __(self::DEFAULT_FORM_TAB_LABEL), 246 | ] 247 | ); 248 | } 249 | 250 | $lastTab->setIsLastTab(true); 251 | } 252 | 253 | return $this->formTabs; 254 | } 255 | 256 | /** 257 | * @param array $tabData 258 | * @return FormTab 259 | */ 260 | public function addFormTab(array $tabData = []) 261 | { 262 | $this->getFormTabs(); 263 | $tab = $this->formTabFactory->create(); 264 | $tab->setData($tabData); 265 | $this->formTabs[] = $tab; 266 | return $tab; 267 | } 268 | 269 | /** 270 | * @return mixed 271 | */ 272 | public function getFirstFormTab() 273 | { 274 | $this->getFormTabs(); 275 | return reset($this->formTabs); 276 | } 277 | 278 | /** 279 | * @return string[] 280 | */ 281 | public function getIdentities(): array 282 | { 283 | return [ 284 | self::CACHE_TAG . '_' . $this->getId(), 285 | ]; 286 | } 287 | 288 | /** 289 | * @return string[] 290 | */ 291 | public function getCacheTags() 292 | { 293 | return $this->getIdentities(); 294 | } 295 | 296 | /** 297 | * @param Attribute $recordAttribute 298 | * @return void 299 | */ 300 | private function updateFieldGroupCode(Attribute $recordAttribute) 301 | { 302 | if ($this->getTabIdsMap() === null) { 303 | $tabIdsMap = []; 304 | $formTabs = $this->getFormTabs(); 305 | foreach ($formTabs as $tab) { 306 | if ($tab->getTmpTabId()) { 307 | $tabIdsMap[$tab->getTmpTabId()] = $tab->getId(); 308 | }; 309 | } 310 | $this->setTabIdsMap($tabIdsMap); 311 | } 312 | 313 | $tabIdsMap = $this->getTabIdsMap(); 314 | if (isset($tabIdsMap[$recordAttribute->getGroupCode()])) { 315 | $newGroupId = $tabIdsMap[$recordAttribute->getGroupCode()]; 316 | $recordAttribute->setGroupCode($newGroupId); 317 | } 318 | } 319 | } 320 | -------------------------------------------------------------------------------- /Model/Form/Attribute.php: -------------------------------------------------------------------------------- 1 | attributeFactory = $attributeFactory; 29 | } 30 | 31 | /** 32 | * @return AttributeFactory 33 | */ 34 | public function getAttributeFactory() 35 | { 36 | return $this->attributeFactory; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Model/FormRecord.php: -------------------------------------------------------------------------------- 1 | formRepository = $formRepository; 46 | parent::__construct( 47 | $context, 48 | $registry, 49 | $resource, 50 | $resourceCollection 51 | ); 52 | } 53 | 54 | /** 55 | * @throws \Magento\Framework\Exception\NoSuchEntityException 56 | */ 57 | public function getForm() 58 | { 59 | return $this->formRepository->getById($this->getFormId()); 60 | } 61 | 62 | /** 63 | * @param Form $form 64 | * @return $this 65 | */ 66 | public function setFieldIdentifierMap(Form $form) 67 | { 68 | $fields = $form->getFieldsCollection(); 69 | foreach ($fields as $field) { 70 | if ($field->getIdentifier()) { 71 | $this->fieldIdentifierMap[$field->getIdentifier()] = $field->getAttributeCode(); 72 | } 73 | } 74 | return $this; 75 | } 76 | 77 | /** 78 | * @param string $attributeCode 79 | * @return string 80 | */ 81 | protected function getMappedAttributeCode($attributeCode) 82 | { 83 | $notMappedKeys = ['form_id']; 84 | if (in_array($attributeCode, $notMappedKeys)) { 85 | return $attributeCode; 86 | } 87 | if (!$this->getFormId()) { 88 | return $attributeCode; 89 | } 90 | if ($this->fieldIdentifierMap === null) { 91 | $this->setFieldIdentifierMap($this->getForm()); 92 | } 93 | if (isset($this->fieldIdentifierMap[$attributeCode])) { 94 | $attributeCode = $this->fieldIdentifierMap[$attributeCode]; 95 | } 96 | return $attributeCode; 97 | } 98 | 99 | public function getAttribute($attributeCode) 100 | { 101 | $attributeCode = $this->getMappedAttributeCode($attributeCode); 102 | return parent::getAttribute($attributeCode); 103 | } 104 | 105 | /** 106 | * @param $key 107 | * @return mixed|void|null 108 | */ 109 | protected function _getData($key) 110 | { 111 | $key = $this->getMappedAttributeCode($key); 112 | return parent::_getData($key); 113 | } 114 | 115 | /** 116 | * @param $storeId 117 | * @return FormRecord 118 | */ 119 | public function setStoreId($storeId) 120 | { 121 | if (!$this->getId()) { 122 | $this->setCreatedFromStoreId($storeId); 123 | } 124 | return parent::setStoreId($storeId); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /Model/FormRecord/Attribute.php: -------------------------------------------------------------------------------- 1 | fieldOptionSources = $fieldOptionSources; 67 | $this->formRepository = $formRepository; 68 | parent::__construct( 69 | $context, $registry, $inputTypeRepository, $inputValidatorRepository, $resource, $resourceCollection 70 | ); 71 | } 72 | 73 | /** 74 | * @return \Alekseon\AlekseonEav\Model\Attribute 75 | */ 76 | public function beforeSave() 77 | { 78 | if ($this->getAttributeCode() == 'field_' . $this->getForm()->getId() . '_' . $this->getIdentifier()) { 79 | $this->setIdentifier(null); 80 | } 81 | 82 | if ($optionSourceCode = $this->getOptionSourceCode()) { 83 | $optionSource = $this->fieldOptionSources->getOptionSourceByCode($optionSourceCode); 84 | $backendType = $this->getInputTypeModel()->getDefaultBackendType(); 85 | 86 | if ($optionSource) { 87 | $this->setSourceModel($optionSource->getSourceModel()); 88 | if ($optionSource->getBackendType()) { 89 | $backendType = $optionSource->getBackendType(); 90 | } 91 | $this->setBackendType($backendType); 92 | } 93 | 94 | if ($optionSourceCode == FieldOptionSources::DEFAULT_ATTRIBUTE_SOURCE_VALUE) { 95 | $this->setSourceModel(null); 96 | $this->setBackendType($backendType); 97 | } 98 | } 99 | 100 | $this->setInputParams($this->getInputParams()); 101 | 102 | return parent::beforeSave(); 103 | } 104 | 105 | 106 | /** 107 | * @param $form 108 | * @return \Alekseon\CustomFormsBuilder\Model\Form\Attribute 109 | */ 110 | public function setForm(Form $form) 111 | { 112 | $this->form = $form; 113 | return $this; 114 | } 115 | 116 | /** 117 | * @return mixed 118 | */ 119 | public function getForm(): Form 120 | { 121 | if ($this->form === null) { 122 | $this->form = $this->formRepository->getById($this->getFormId()); 123 | } 124 | 125 | return $this->form; 126 | } 127 | 128 | /** 129 | * @return string 130 | */ 131 | public function getInputVisibility() 132 | { 133 | $options = $this->getInputVisibilityOptions(); 134 | $inputVisibility = $this->getData('input_visibility'); 135 | if (!isset($options[$inputVisibility])) { 136 | $inputVisibility = self::INPUT_VISIBILITY_VISIBILE; 137 | } 138 | return $inputVisibility; 139 | } 140 | 141 | /** 142 | * @return array 143 | */ 144 | public function getInputVisibilityOptions() 145 | { 146 | return [ 147 | self::INPUT_VISIBILITY_VISIBILE => __('Visible'), 148 | self::INPUT_VISIBILITY_NONE => __('Not Visible'), 149 | self::INPUT_VISIBILITY_ADMIN_ONLY => __('Only for Admin'), 150 | ]; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /Model/FormRecord/AttributeRepository.php: -------------------------------------------------------------------------------- 1 | attributeFactory = $attributeFactory; 29 | } 30 | 31 | /** 32 | * @return AttributeFactory 33 | */ 34 | public function getAttributeFactory() 35 | { 36 | return $this->attributeFactory; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Model/FormRepository.php: -------------------------------------------------------------------------------- 1 | formFactory = $formFactory; 39 | } 40 | 41 | /** 42 | * @param $formId 43 | * @return Form 44 | * @throws NoSuchEntityException 45 | */ 46 | public function getById($formId, $storeId = null, $graceful = false) 47 | { 48 | $storeKey = $storeId ?? 'null'; 49 | if (!isset($this->loadedFormsByIds[$formId][$storeKey])) { 50 | $form = $this->formFactory->create(); 51 | $form->setStoreId($storeId); 52 | $form->getResource()->load($form, $formId); 53 | if (!$form->getId()) { 54 | if ($graceful) { 55 | return $form; 56 | } else { 57 | throw new NoSuchEntityException(__('Form with id "%1" does not exist.', $formId)); 58 | } 59 | } 60 | $this->addFormToLoaded($form, $storeKey); 61 | } 62 | 63 | return $this->loadedFormsByIds[$formId][$storeKey]; 64 | } 65 | 66 | /** 67 | * @param string $identifier 68 | * @param int|null $storeId 69 | * @param bool $graceful 70 | * @return Form 71 | * @throws NoSuchEntityException 72 | */ 73 | public function getByIdentifier(string $identifier, int $storeId = null, bool $graceful = true) 74 | { 75 | $storeKey = $storeId ?? 'null'; 76 | if (!isset($this->loadedFormsByIdentifiers[$identifier])) { 77 | /** @var Form $form */ 78 | $form = $this->formFactory->create(); 79 | $form->setStoreId($storeId); 80 | $form->getResource()->load($form, $identifier, 'identifier'); 81 | if (!$form->getId()) { 82 | if ($graceful) { 83 | return $form; 84 | } else { 85 | throw new NoSuchEntityException(__('Form with identifier "%1" does not exist.', $identifier)); 86 | } 87 | } 88 | $this->addFormToLoaded($form, $storeKey); 89 | } 90 | 91 | $formId = $this->loadedFormsByIdentifiers[$identifier]; 92 | return $this->loadedFormsByIds[$formId][$storeKey]; 93 | } 94 | 95 | /** 96 | * @param $form 97 | */ 98 | protected function addFormToLoaded($form, $storeKey = 'null') 99 | { 100 | $this->loadedFormsByIds[$form->getId()][$storeKey] = $form; 101 | if ($form->getIdentifier()) { 102 | $this->loadedFormsByIdentifiers[$form->getIdentifier()] = $form->getId(); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Model/FormTab.php: -------------------------------------------------------------------------------- 1 | attributeCollectionFactory = $attributeCollectionFactory; 38 | parent::__construct($context, $connectionName); 39 | } 40 | 41 | /** 42 | * @return void 43 | */ 44 | protected function _construct() // @codingStandardsIgnoreLine 45 | { 46 | $this->_init('alekseon_custom_form', 'entity_id'); 47 | } 48 | 49 | /** 50 | * @param \Magento\Framework\Model\AbstractModel $object 51 | * @return Form 52 | * @throws \Exception 53 | */ 54 | protected function _afterSave(\Magento\Framework\Model\AbstractModel $object) // @codingStandardsIgnoreLine 55 | { 56 | $formTabs = $object->getFormTabs(); 57 | 58 | if ($formTabs !== null) { 59 | foreach ($formTabs as $tab) { 60 | if ($tab->getDeleted()) { 61 | $tab->getResource()->delete($tab); 62 | continue; 63 | } 64 | if (!$tab->getId()) { 65 | $tab->setFormId($object->getId()); 66 | } 67 | $tab->getResource()->save($tab); 68 | } 69 | } 70 | 71 | return parent::_afterSave($object); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Model/ResourceModel/Form/Attribute.php: -------------------------------------------------------------------------------- 1 | _init( 22 | 'Alekseon\CustomFormsBuilder\Model\Form\Attribute', 23 | 'Alekseon\CustomFormsBuilder\Model\ResourceModel\Form\Attribute' 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Model/ResourceModel/Form/Collection.php: -------------------------------------------------------------------------------- 1 | _init( 22 | 'Alekseon\CustomFormsBuilder\Model\Form', 23 | 'Alekseon\CustomFormsBuilder\Model\ResourceModel\Form' 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Model/ResourceModel/FormRecord.php: -------------------------------------------------------------------------------- 1 | attributeCollectionFactory = $attributeCollectionFactory; 52 | parent::__construct($context, $connectionName); 53 | } 54 | 55 | /** 56 | * @return void 57 | */ 58 | protected function _construct() // @codingStandardsIgnoreLine 59 | { 60 | $this->_init('alekseon_custom_form_record', 'entity_id'); 61 | } 62 | 63 | /** 64 | * @return $this 65 | */ 66 | public function loadAllAttributes() 67 | { 68 | if ($this->allAttributesLoaded) { 69 | return $this; 70 | } 71 | 72 | if (!$this->getCurrentForm()) { 73 | return $this; 74 | } 75 | 76 | $attributeCollection = $this->getCurrentForm()->getFieldsCollection(); 77 | 78 | foreach ($attributeCollection as $attribute) { 79 | $this->attributes[$attribute->getAttributeCode()] = $attribute; 80 | } 81 | $this->allAttributesLoaded = true; 82 | return $this; 83 | } 84 | 85 | /** 86 | * 87 | */ 88 | protected function getCurrentForm() 89 | { 90 | return $this->currentForm; 91 | } 92 | 93 | /** 94 | * @param $form 95 | */ 96 | public function setCurrentForm($form) 97 | { 98 | $this->currentForm = $form; 99 | } 100 | 101 | /** 102 | * @param \Magento\Framework\Model\AbstractModel $object 103 | * @param AttributeInterface $attribute 104 | * @param string $fileName 105 | * @return string 106 | */ 107 | public function getNameForUploadedFile( 108 | \Magento\Framework\Model\AbstractModel $object, 109 | AttributeInterface $attribute, 110 | string $fileName 111 | ) { 112 | $fileNameParts = explode('.', $fileName); 113 | $ext = end($fileNameParts); 114 | return hash('sha256', $attribute->getAttributeCode() . $object->getId() . time()) . '.' . $ext; 115 | } 116 | 117 | /** 118 | * @return string 119 | */ 120 | public function getImagesDirName() 121 | { 122 | $imagesDirName = parent::getImagesDirName(); 123 | $form = $this->getCurrentForm(); 124 | if ($form) { 125 | return $imagesDirName . '/' . substr(hash('sha256', $form->getCreatedAt() . $form->getId()), 0, 10); 126 | } 127 | 128 | return $imagesDirName; 129 | } 130 | 131 | /** 132 | * @param \Magento\Framework\Model\AbstractModel $object 133 | * @return Entity 134 | */ 135 | protected function _beforeDelete(\Magento\Framework\Model\AbstractModel $object) 136 | { 137 | $this->setCurrentForm($object->getForm()); 138 | return parent::_beforeDelete($object); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /Model/ResourceModel/FormRecord/Attribute.php: -------------------------------------------------------------------------------- 1 | _init( 22 | 'Alekseon\CustomFormsBuilder\Model\FormRecord\Attribute', 23 | 'Alekseon\CustomFormsBuilder\Model\ResourceModel\FormRecord\Attribute' 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Model/ResourceModel/FormRecord/Collection.php: -------------------------------------------------------------------------------- 1 | _init( 28 | 'Alekseon\CustomFormsBuilder\Model\FormRecord', 29 | 'Alekseon\CustomFormsBuilder\Model\ResourceModel\FormRecord' 30 | ); 31 | } 32 | 33 | /** 34 | * @param Form $form 35 | * @return $this 36 | * @throws \Magento\Framework\Exception\LocalizedException 37 | */ 38 | public function addFormFilter(Form $form) 39 | { 40 | $this->addFieldToFilter('form_id', $form->getId()); 41 | $this->setFieldIdentifierMap($form); 42 | return $this; 43 | } 44 | 45 | public function setFieldIdentifierMap(Form $form) 46 | { 47 | $fields = $form->getFieldsCollection(); 48 | foreach ($fields as $field) { 49 | if ($field->getIdentifier()) { 50 | $this->fieldIdentifierMap[$field->getIdentifier()] = $field->getAttributeCode(); 51 | } 52 | } 53 | return $this; 54 | } 55 | 56 | /** 57 | * @param string $attributeCode 58 | * @return mixed 59 | */ 60 | protected function getMappedAttributeCode($attributeCode) 61 | { 62 | if (isset($this->fieldIdentifierMap[$attributeCode])) { 63 | $attributeCode = $this->fieldIdentifierMap[$attributeCode]; 64 | } 65 | return $attributeCode; 66 | } 67 | 68 | /** 69 | * @param $attribute 70 | * @param $condition 71 | * @param $joinType 72 | * @return string 73 | * @throws \Magento\Framework\Exception\LocalizedException 74 | */ 75 | protected function getAttributeConditionSql($attribute, $condition, $joinType = 'inner') 76 | { 77 | $attribute = $this->getMappedAttributeCode($attribute); 78 | return parent::getAttributeConditionSql($attribute, $condition, $joinType); 79 | } 80 | 81 | /** 82 | * @param $attribute 83 | * @param $direction 84 | * @return Collection 85 | * @throws \Magento\Framework\Exception\LocalizedException 86 | */ 87 | public function addAttributeToSort($attribute, $direction = self::SORT_ORDER_ASC) 88 | { 89 | $attribute = $this->getMappedAttributeCode($attribute); 90 | return parent::addAttributeToSort($attribute, $direction); 91 | } 92 | 93 | /** 94 | * @param $attribute 95 | * @return Collection 96 | * @throws \Magento\Framework\Exception\LocalizedException 97 | */ 98 | public function addAttributeToSelect($attribute) 99 | { 100 | if (!is_array($attribute)) { 101 | $attribute = $this->getMappedAttributeCode($attribute); 102 | } 103 | return parent::addAttributeToSelect($attribute); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Model/ResourceModel/FormTab.php: -------------------------------------------------------------------------------- 1 | _init('alekseon_custom_form_tab', 'entity_id'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Model/ResourceModel/FormTab/Collection.php: -------------------------------------------------------------------------------- 1 | _init( 24 | 'Alekseon\CustomFormsBuilder\Model\FormTab', 25 | 'Alekseon\CustomFormsBuilder\Model\ResourceModel\FormTab' 26 | ); 27 | } 28 | 29 | /** 30 | * @param Form $form 31 | * @return $this 32 | */ 33 | public function addFormFilter(Form $form) 34 | { 35 | $this->addFieldToFilter('form_id', $form->getId()); 36 | return $this; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Plugin/AddCustomFormsToAclTreePlugin.php: -------------------------------------------------------------------------------- 1 | formCollectionFactory = $formCollectionFactory; 37 | } 38 | 39 | /** 40 | * @param Edit $roleTabEdit 41 | * @param $tree 42 | * @return mixed 43 | * @throws \Magento\Framework\Exception\LocalizedException 44 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 45 | */ 46 | public function afterBuild(TreeBuilder $treeBuilder, $result) 47 | { 48 | foreach ($result as $key => $resultElement) { 49 | if ($resultElement['id'] == 'Alekseon_CustomFormsBuilder::custom_forms') { 50 | 51 | $formCollection = $this->formCollectionFactory->create(); 52 | $formCollection->addAttributeToSelect('title'); 53 | $formCollection->addAttributeToFilter('show_in_menu', true); 54 | 55 | foreach ($formCollection as $form) { 56 | $defaultTitle = __('Form #%1', $form->getId()); 57 | 58 | $title = (string) $form->getTitle(); 59 | if (strlen($title) < 3) { 60 | $title = $defaultTitle; 61 | } 62 | if (strlen($title) > 50) { 63 | $title = substr($title, 0, 50); 64 | } 65 | 66 | $result[$key]['children'][] = [ 67 | 'id' => 'Alekseon_CustomFormsBuilder::custom_form_' . $form->getId(), 68 | 'title' => $title, 69 | 'sortOrder' => 1, 70 | 'children' => [ 71 | [ 72 | 'id' => 'Alekseon_CustomFormsBuilder::custom_form_' . $form->getId() . '_save', 73 | 'title' => __('Save'), 74 | 'sortOrder' => 1, 75 | 'children' => [], 76 | ], 77 | [ 78 | 'id' => 'Alekseon_CustomFormsBuilder::custom_form_' . $form->getId() . '_manage_form', 79 | 'title' => __('Manage Form'), 80 | 'sortOrder' => 2, 81 | 'children' => [], 82 | ] 83 | ], 84 | ]; 85 | } 86 | } 87 | } 88 | 89 | return $result; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Plugin/AddCustomFormsToAdminMenuPlugin.php: -------------------------------------------------------------------------------- 1 | menuItemFactory = $menuItemFactory; 36 | $this->formCollectionFactory = $formCollectionFactory; 37 | } 38 | 39 | /** 40 | * @param $builder 41 | * @param $menu 42 | * @return mixed 43 | * @throws \Magento\Framework\Exception\LocalizedException 44 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 45 | */ 46 | public function afterGetResult($builder, $menu) 47 | { 48 | $formCollection = $this->formCollectionFactory->create(); 49 | $formCollection->addAttributeToSelect('title'); 50 | $formCollection->addAttributeToFilter('show_in_menu', true); 51 | 52 | foreach ($formCollection as $form) { 53 | $defaultTitle = __('Form #%1', $form->getId()); 54 | 55 | $title = $form->getTitle(); 56 | if (strlen($title) < 3) { 57 | $title = $defaultTitle; 58 | } 59 | if (strlen($title) > 50) { 60 | $title = substr($title, 0, 50); 61 | } 62 | 63 | $params = [ 64 | 'id' => 'custom_form_' . $form->getId(), 65 | 'title' => $title, 66 | 'resource' => 'Alekseon_CustomFormsBuilder::custom_form_' . $form->getId(), 67 | 'action' => 'alekseon_customFormsBuilder/formRecord/index/id/' . $form->getId(), 68 | ]; 69 | 70 | try { 71 | $item = $this->menuItemFactory->create($params); 72 | } catch (\InvalidArgumentException $e) { 73 | $params['title'] = (string) $defaultTitle; 74 | $item = false; 75 | } 76 | 77 | try { 78 | if (!$item) { 79 | $item = $this->menuItemFactory->create($params); 80 | } 81 | $contentElements = $menu->get('Alekseon_CustomFormsBuilder::custom_forms'); 82 | $contentElements->getChildren()->add( 83 | $item, 84 | null, 85 | 1 86 | ); 87 | } catch (\Exception $e) { 88 | 89 | } 90 | } 91 | 92 | return $menu; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |

3 |
4 | Custom Forms Builder 5 |
6 | Alekseon_CustomFormsBuilder 7 |
8 |

9 | 10 |

A Custom Form Builder for Magento 2

11 | 12 |

Let you create custom forms in Admin panel

13 | 14 |

15 | 16 | Latest Stable Version 18 | 19 | 20 | Total Downloads 22 | 23 | 24 | Travis CI build status 26 | 27 |

28 | 29 | ## Installation 30 | 31 | In your Magento2 root directory, you may install this package via composer: 32 | 33 | ``` 34 | composer require alekseon/custom-forms-builder 35 | bin/magento setup:upgrade 36 | ``` 37 | 38 | ## Dependencies 39 | 40 | This extension gives you possibility to create forms in admin panel. If you want to place them on frontend by CMS content, please check this extension: [Alekseon_WidgetForms](https://github.com/Alekseon/magento2-widget-forms) 41 | 42 | 43 | ## Support 44 | 45 | CJM Ver. | Magento 2.0 | Magento 2.1 | Magento 2.2 | Magento 2.3 | Magento 2.4 46 | --- | :---: | :---: | :---: | :---: | :---: 47 | 1.x | :x: | :x: | :white_check_mark: | :white_check_mark: | :white_check_mark: 48 | 49 | 50 | ## Features 51 | 52 | ### Create fully customized forms in Magento2 Admin Panel 53 | 54 | 55 | Create as many forms as you need. Define the title and the fields. 56 | You can choose from field types: 57 | * Text Field 58 | * Text Area 59 | * Yes/No 60 | * Dropdown 61 | * Multiple Select 62 | * Date 63 | * Image 64 | 65 | 66 | 67 | ### Manage fields as regular attributes 68 | 69 | Thanks to the EAV structure, you can manage every field of your form similarly to how you manage regular attribute in Magento. 70 | 71 | 72 | 73 | 74 | 75 | 76 | ### Add and access your form objects easily 77 | 78 | You can view and manage the form applications by regular Magento grid 79 | 80 | 81 | 82 | ### Add Forms to CMS Pages 83 | 84 | Please refer to [Alekseon_WidgetForms](https://github.com/Alekseon/magento2-widget-forms) if you want to be able to add these forms to CMS pages for your customers. 85 | 86 | ### Email notifications 87 | 88 | @Todo 89 | 90 | 91 | ## Issue Tracking / Upcoming Features 92 | 93 | For issues, please use the [issue tracker](https://github.com/Alekseon/magento2-custom-forms-builder/issues). 94 | 95 | Issues keep this project alive and strong, so let us know if you find anything! 96 | 97 | We're planning on pumping out a ton of new features, which you can follow on our [project page](https://github.com/Alekseon/magento2-custom-forms-builder/projects/1). 98 | 99 | ### Development / Contribution 100 | 101 | If you want to contribute please follow the below instructions: 102 | 103 | 1. Create an issue and describe your idea 104 | 2. [Fork this repository](https://github.com/Alekseon/magento2-custom-forms-builder/fork) 105 | 3. Create your feature branch (`git checkout -b my-new-feature`) 106 | 4. Commit your changes 107 | 5. Publish the branch (`git push origin my-new-feature`) 108 | 6. Submit a new Pull Request for review 109 | 110 | ## Maintainers 111 | 112 | Current maintainers: 113 | 114 | * [Linek](https://github.com/Linek) 115 | * [marcinfr](https://github.com/marcinfr) 116 | 117 | See also our [contributors](https://github.com/Alekseon/magento2-custom-forms-builder/graphs/contributors) 118 | 119 | 120 | ## License 121 | 122 | [The Open Software License 3.0 (OSL-3.0)](https://opensource.org/licenses/OSL-3.0) 123 | -------------------------------------------------------------------------------- /Setup/Patch/Data/CreateWidgetFormsAttributesPatch.php: -------------------------------------------------------------------------------- 1 | moduleDataSetup = $moduleDataSetup; 47 | $this->eavSetupFactory = $eavSetupFactory; 48 | $this->formAttributeRepository = $formAttributeRepository; 49 | } 50 | 51 | /** 52 | * @inheritdoc 53 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 54 | */ 55 | public function apply() 56 | { 57 | $this->moduleDataSetup->getConnection()->startSetup(); 58 | 59 | /** @var EavDataSetup $eavSetup */ 60 | $eavSetup = $this->eavSetupFactory->create(); 61 | $eavSetup->setAttributeRepository($this->formAttributeRepository); 62 | 63 | $this->createFormAttributes($eavSetup); 64 | 65 | $this->moduleDataSetup->getConnection()->endSetup(); 66 | return $this; 67 | } 68 | 69 | /** 70 | * @param $eavSetup 71 | * @return void 72 | */ 73 | private function createFormAttributes($eavSetup) 74 | { 75 | $eavSetup->createAttribute( 76 | 'title', 77 | [ 78 | 'frontend_input' => 'text', 79 | 'frontend_label' => 'Title', 80 | 'visible_in_grid' => true, 81 | 'is_required' => true, 82 | 'sort_order' => 10, 83 | 'scope' => Scopes::SCOPE_STORE, 84 | ] 85 | ); 86 | 87 | $eavSetup->createAttribute( 88 | 'show_in_menu', 89 | [ 90 | 'frontend_input' => 'boolean', 91 | 'frontend_label' => 'Show in adminhtml menu', 92 | 'visible_in_grid' => false, 93 | 'is_required' => false, 94 | 'sort_order' => 40, 95 | 'scope' => Scopes::SCOPE_GLOBAL, 96 | 'note' => __('Menu -> Marketing -> Custom Forms'), 97 | ] 98 | ); 99 | } 100 | 101 | /** 102 | * @inheritdoc 103 | */ 104 | public static function getDependencies() 105 | { 106 | return []; 107 | } 108 | 109 | /** 110 | * @inheritdoc 111 | */ 112 | public function revert() 113 | { 114 | $this->moduleDataSetup->getConnection()->startSetup(); 115 | 116 | $eavSetup = $this->eavSetupFactory->create(); 117 | $eavSetup->setAttributeRepository($this->formAttributeRepository); 118 | 119 | $eavSetup->deleteAttribute('title'); 120 | $eavSetup->deleteAttribute('show_in_menu'); 121 | 122 | $this->moduleDataSetup->getConnection()->endSetup(); 123 | } 124 | 125 | /** 126 | * @inheritdoc 127 | */ 128 | public function getAliases() 129 | { 130 | return []; 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /Setup/Patch/Schema/CreateEavTablesV2.php: -------------------------------------------------------------------------------- 1 | schemaSetup = $schemaSetup; 38 | $this->eavSetupFactory = $eavSetupFactory; 39 | } 40 | 41 | /** 42 | * @return CreateEavTables|void 43 | */ 44 | public function apply() 45 | { 46 | $this->schemaSetup->startSetup(); 47 | $setup = $this->schemaSetup; 48 | 49 | $eavSetup = $this->eavSetupFactory->create(['setup' => $setup]); 50 | 51 | $eavSetup->createFullEavStructure( 52 | 'alekseon_custom_form_attribute', 53 | 'alekseon_custom_form_entity', 54 | null, 55 | 'alekseon_custom_form' 56 | ); 57 | 58 | $eavSetup->createFullEavStructure( 59 | 'alekseon_custom_form_record_attribute', 60 | 'alekseon_custom_form_record_entity', 61 | null, 62 | 'alekseon_custom_form_record' 63 | ); 64 | 65 | // fix for old module version 66 | $this->updateAttributeCodeColumnSize($setup, 'alekseon_custom_form_attribute'); 67 | $this->updateAttributeCodeColumnSize($setup, 'alekseon_custom_form_record_attribute'); 68 | 69 | $this->addFormIdColumn($setup); 70 | $this->addIdentifierColumn($setup); 71 | $this->addInputVisibilityColumn($setup); 72 | 73 | $this->schemaSetup->endSetup(); 74 | } 75 | 76 | 77 | /** 78 | * @param SchemaSetupInterface $setup 79 | * @param string $attributeTableName 80 | * @return void 81 | */ 82 | private function updateAttributeCodeColumnSize(SchemaSetupInterface $setup, string $attributeTableName) 83 | { 84 | $setup->getConnection()->modifyColumn( 85 | $setup->getTable($attributeTableName), 86 | 'attribute_code', 87 | [ 88 | 'type' => Table::TYPE_TEXT, 89 | 'nullable' => false, 90 | 'default' => '', 91 | 'length' => 255, 92 | 'comment' => 'Attribute Code' 93 | ] 94 | ); 95 | } 96 | 97 | /** 98 | * @param SchemaSetupInterface $setup 99 | * @return void 100 | */ 101 | private function addIdentifierColumn(SchemaSetupInterface $setup) 102 | { 103 | $setup->getConnection()->addColumn( 104 | $setup->getTable('alekseon_custom_form_record_attribute'), 105 | 'identifier', 106 | [ 107 | 'type' => Table::TYPE_TEXT, 108 | 'length' => 255, 109 | 'comment' => 'Identifier', 110 | 'nullable' => true, 111 | ] 112 | ); 113 | $setup->getConnection()->addIndex( 114 | $setup->getTable('alekseon_custom_form_record_attribute'), 115 | $setup->getIdxName( 116 | 'alekseon_custom_form_record_attribute', 117 | ['identifier', 'form_id'], 118 | \Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_UNIQUE 119 | ), 120 | ['identifier', 'form_id'], 121 | \Magento\Framework\DB\Adapter\AdapterInterface::INDEX_TYPE_UNIQUE 122 | ); 123 | } 124 | 125 | /** 126 | * @param SchemaSetupInterface $setup 127 | * @return void 128 | */ 129 | private function addInputVisibilityColumn(SchemaSetupInterface $setup) 130 | { 131 | $recordAttributeTable = $setup->getTable('alekseon_custom_form_record_attribute'); 132 | 133 | $columnDefinition = [ 134 | 'type' => Table::TYPE_SMALLINT, 135 | 'comment' => 'Input Visibility', 136 | 'nullable' => false, 137 | 'default' => 1, 138 | ]; 139 | 140 | /** 141 | * rename in_enabled column to input_visibility for previous module versions 142 | * or add new column for new installations 143 | */ 144 | if ($setup->getConnection()->tableColumnExists($recordAttributeTable, 'is_enabled')) { 145 | $setup->getConnection()->changeColumn( 146 | $recordAttributeTable, 147 | 'is_enabled', 148 | 'input_visibility', 149 | $columnDefinition 150 | ); 151 | } else { 152 | $setup->getConnection()->addColumn( 153 | $recordAttributeTable, 154 | 'input_visibility', 155 | $columnDefinition 156 | ); 157 | } 158 | } 159 | 160 | /** 161 | * @param SchemaSetupInterface $setup 162 | * @return void 163 | */ 164 | private function addFormIdColumn(SchemaSetupInterface $setup) 165 | { 166 | $setup->getConnection()->addColumn( 167 | $setup->getTable('alekseon_custom_form_record_attribute'), 168 | 'form_id', 169 | [ 170 | 'type' => Table::TYPE_INTEGER, 171 | 'unsigned' => true, 172 | 'nullable' => false, 173 | 'comment' => 'Form Id' 174 | ] 175 | ); 176 | 177 | $setup->getConnection()->addForeignKey( 178 | $setup->getConnection()->getForeignKeyName( 179 | $setup->getTable('alekseon_custom_form_record_attribute'), 180 | 'form_id', 181 | $setup->getTable('alekseon_custom_form'), 182 | 'entity_id' 183 | ), 184 | $setup->getTable('alekseon_custom_form_record_attribute'), 185 | 'form_id', 186 | $setup->getTable('alekseon_custom_form'), 187 | 'entity_id', 188 | AdapterInterface::FK_ACTION_CASCADE 189 | ); 190 | } 191 | 192 | /** 193 | * @return array 194 | */ 195 | public function getAliases() 196 | { 197 | return []; 198 | } 199 | 200 | /** 201 | * @return array 202 | */ 203 | public static function getDependencies() 204 | { 205 | return []; 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /Test/Unit/Model/FormRepositoryTest.php: -------------------------------------------------------------------------------- 1 | getMockBuilder(\Alekseon\CustomFormsBuilder\Model\FormFactory::class) 41 | ->disableOriginalConstructor() 42 | ->setMethods(['create']) 43 | ->getMock(); 44 | $this->formResource = $this->getMockBuilder(\Alekseon\CustomFormsBuilder\Model\ResourceModel\Form::class) 45 | ->disableOriginalConstructor() 46 | ->getMock(); 47 | $this->form = $this->getMockBuilder(\Alekseon\CustomFormsBuilder\Model\Form::class)->disableOriginalConstructor()->getMock(); 48 | 49 | $formFactory->expects($this->any()) 50 | ->method('create') 51 | ->willReturn($this->form); 52 | 53 | $this->formRepository = new FormRepository( 54 | $formFactory 55 | ); 56 | } 57 | 58 | /** 59 | * @test 60 | */ 61 | public function testGetByIdException() 62 | { 63 | $this->expectException('Magento\Framework\Exception\NoSuchEntityException'); 64 | $formId = '123'; 65 | 66 | $this->form->expects($this->once()) 67 | ->method('getId') 68 | ->willReturn(false); 69 | $this->formResource->expects($this->once()) 70 | ->method('load') 71 | ->with($this->form, $formId) 72 | ->willReturn($this->form); 73 | $this->form->expects($this->once()) 74 | ->method('getResource') 75 | ->willReturn($this->formResource); 76 | $this->formRepository->getById($formId); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "alekseon/custom-forms-builder", 3 | "description": "N/A", 4 | "require": { 5 | "alekseon/alekseon-eav": "^101.2.15", 6 | "alekseon/custom-forms-frontend": "*", 7 | "alekseon/custom-forms-email-notification": "*", 8 | "alekseon/widget-forms-statistics": "*" 9 | }, 10 | "type": "magento2-module", 11 | "version": "102.3.14", 12 | "license": [ 13 | "OSL-3.0", 14 | "AFL-3.0" 15 | ], 16 | "autoload": { 17 | "files": [ 18 | "registration.php" 19 | ], 20 | "psr-4": { 21 | "Alekseon\\CustomFormsBuilder\\": "" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /etc/acl.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /etc/adminhtml/di.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | All Countries 20 | Alekseon\AlekseonEav\Model\Attribute\Source\Country 21 | varchar 22 | 23 | 24 | Website Allow Countries 25 | Alekseon\AlekseonEav\Model\Attribute\Source\WebsiteAllowCountry 26 | varchar 27 | 28 | 29 | Yes/No 30 | Alekseon\AlekseonEav\Model\Attribute\Source\YesNo 31 | int 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /etc/adminhtml/menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /etc/adminhtml/routes.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /etc/adminhtml/system.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | separator-top 15 | 16 | alekseon 17 | Alekseon_CustomForms::config 18 |
19 |
20 |
21 | -------------------------------------------------------------------------------- /etc/db_schema.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 11 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 28 | 30 | 32 | 34 | 35 | 36 | 37 | 40 | 43 |
44 | 45 | 47 | 48 | 50 | 51 | 52 | 53 | 56 |
57 |
58 | -------------------------------------------------------------------------------- /etc/db_schema_whitelist.json: -------------------------------------------------------------------------------- 1 | { 2 | "alekseon_custom_form": { 3 | "column": { 4 | "entity_id": true, 5 | "created_at": true, 6 | "identifier": true 7 | }, 8 | "constraint": { 9 | "PRIMARY": true, 10 | "ALEKSEON_CUSTOM_FORM_IDENTIFIER": true 11 | } 12 | }, 13 | "alekseon_custom_form_record": { 14 | "column": { 15 | "entity_id": true, 16 | "form_id": true, 17 | "created_at": true 18 | }, 19 | "constraint": { 20 | "PRIMARY": true, 21 | "ALEKSEON_CUSTOM_FORM_RECORD_FORM_ID_ALEKSEON_CUSTOM_FORM_ENTT_ID": true 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /etc/di.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | Max Length 14 | Alekseon\AlekseonEav\Model\Attribute\InputValidator\MaxLengthFactory 15 | 16 | text 17 | textarea 18 | 19 | 20 | 21 | Max Length 22 | 23 | 24 | 25 | 26 | Max File Size In MB 27 | Alekseon\AlekseonEav\Model\Attribute\InputValidator\MaxFileSizeFactory 28 | 29 | image 30 | 31 | 32 | 33 | Max Size 34 | In Mb 35 | 36 | 37 | 38 | 39 | Postal Code 40 | Alekseon\CustomFormsBuilder\Model\Attribute\InputValidator\PostalCodeFactory 41 | 42 | text 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /etc/module.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /registration.php: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 1 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | general_tab 34 | form_general_tab 35 | 36 | 37 | fields_tab 38 | form_fields_tab 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /view/adminhtml/layout/alekseon_customformsbuilder_form_index.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /view/adminhtml/layout/alekseon_customformsbuilder_formfield_edit.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /view/adminhtml/layout/alekseon_customformsbuilder_formrecord_edit.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 1 18 | 19 | 20 | 21 | 22 | 23 | 24 | Magento\Customer\Block\DataProviders\PostCodesPatternsAttributeData 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /view/adminhtml/layout/alekseon_customformsbuilder_formrecord_index.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 1 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /view/adminhtml/templates/form/edit/field/identifier.phtml: -------------------------------------------------------------------------------- 1 | 7 | getSettings() ?> 8 | 9 |
13 | : 14 | 15 | 16 | 17 | 22 |
23 | 24 | -------------------------------------------------------------------------------- /view/adminhtml/templates/form/edit/tab/fields.phtml: -------------------------------------------------------------------------------- 1 | 7 | 10 |
11 |
12 |
13 |
    14 | 17 | 20 |
21 |
22 |
23 |
24 | 28 |
29 | getChildHtml('form_fields_tab_form') ?> 30 | getChildHtml('add_new_field_button') ?> 31 |
32 | 44 | 57 | -------------------------------------------------------------------------------- /view/adminhtml/templates/form/wikiLink.phtml: -------------------------------------------------------------------------------- 1 | 7 |
8 |
9 | 10 | Need help? Visit 11 | 12 | WIKI page 13 | 14 | to find more information about custom forms or contact us 15 | Alekseon 16 | 17 |
18 |
19 | 20 | -------------------------------------------------------------------------------- /view/adminhtml/templates/formRecord/edit/formInfo.phtml: -------------------------------------------------------------------------------- 1 |
2 | 10 |
11 | -------------------------------------------------------------------------------- /view/adminhtml/web/js/form/edit/formFields.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright © Alekseon sp. z o.o. 3 | * http://www.alekseon.com/ 4 | */ 5 | define([ 6 | 'jquery', 7 | 'Magento_Ui/js/modal/confirm', 8 | 'mage/translate' 9 | ], function ($, confirm, $t) { 10 | 'use strict'; 11 | 12 | var formFields = { 13 | 14 | init: function (config) { 15 | this.newFieldsCounter = 0; 16 | this.newFieldTemplate = $('#' + config.newFieldTemplateId)[0]; 17 | 18 | this.formContainer = $('#' + config.formContainerId + ' .form-inline')[0]; 19 | this.newFieldButton = $('#' + config.newFieldButtonId); 20 | this.formRemovedFieldsInputId = $('#' + config.formRemovedFieldsInputId); 21 | this.hideNewFieldTemplate(); 22 | this.addNewFieldButtonEvent(); 23 | this.addFieldsEvents(); 24 | }, 25 | 26 | addNewField: function () { 27 | this.newFieldsCounter ++; 28 | var self = this; 29 | var newField = this.newFieldTemplate.clone(true); 30 | 31 | $(newField).find("input, select, textarea").each(function() { 32 | var fieldCode = $(this).data('fieldcode'); 33 | $(this).attr("name", "new_fields[" + self.newFieldsCounter + "][" + fieldCode + "]"); 34 | }); 35 | 36 | var removeButton = $(newField).find('.delete-field-button')[0]; 37 | this.addRemoveFieldEvent(removeButton); 38 | 39 | var changeTabButton = $(newField).find('.form-field-change-tab-button')[0]; 40 | this.addChangeTabButtonEvent(changeTabButton); 41 | 42 | this.formContainer.appendChild(newField); 43 | 44 | $(document).trigger('form-new-field', [newField]); 45 | $(newField).slideDown(); 46 | }, 47 | 48 | addNewFieldButtonEvent: function () { 49 | this.newFieldButton.click(function () { 50 | formFields.addNewField(); 51 | return false; 52 | }); 53 | }, 54 | 55 | hideNewFieldTemplate: function () { 56 | this.newFieldTemplate.hide(); 57 | }, 58 | 59 | addFieldsEvents: function () { 60 | var self = this; 61 | $(this.formContainer).find('.delete-field-button').each(function() { 62 | self.addRemoveFieldEvent(this); 63 | }); 64 | $(this.formContainer).find('.form-field-change-tab-button').each(function() { 65 | self.addChangeTabButtonEvent(this); 66 | }); 67 | }, 68 | 69 | addChangeTabButtonEvent: function (changeTabButton) { 70 | $(changeTabButton).click(function () { 71 | $(document).trigger('form-field-change-tab-click', [this]); 72 | return false; 73 | }); 74 | }, 75 | 76 | addRemoveFieldEvent: function (removeButton) { 77 | var self = this; 78 | $(removeButton).click(function () { 79 | confirm({ 80 | content: $t('Are You Sure?'), 81 | actions: { 82 | confirm: function () { 83 | var fieldsetWrapper = $(removeButton).closest('.fieldset-wrapper'); 84 | var fieldsetId = self.getFieldsetId($(removeButton)); 85 | if (fieldsetId !== 'new_field') { 86 | var removedIds = []; 87 | var removedIdsVal = $(self.formRemovedFieldsInputId).val(); 88 | if (removedIdsVal) { 89 | removedIds = removedIdsVal.split(','); 90 | } 91 | removedIds.push(fieldsetId); 92 | $(self.formRemovedFieldsInputId).val(removedIds.join(',')); 93 | } 94 | 95 | fieldsetWrapper.slideUp("slow", function () { 96 | this.remove(); 97 | }); 98 | } 99 | } 100 | }); 101 | return false; 102 | }); 103 | }, 104 | 105 | getFieldsetId: function (fieldsetChildElement) { 106 | var fieldsetWrapper = fieldsetChildElement.closest('.fieldset-wrapper'); 107 | var fieldsetId = $(fieldsetWrapper.find('fieldset')[0]).attr('id').substr('form_field_'.length); 108 | return fieldsetId; 109 | } 110 | }; 111 | 112 | return function (config) { 113 | $(document).ready(function () { 114 | formFields.init(config); 115 | }); 116 | }; 117 | }); 118 | -------------------------------------------------------------------------------- /view/adminhtml/web/js/form/edit/formTabs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright © Alekseon sp. z o.o. 3 | * http://www.alekseon.com/ 4 | */ 5 | define([ 6 | 'jquery', 7 | 'Magento_Ui/js/modal/confirm', 8 | 'mage/translate' 9 | ], function ($, confirm, $t) { 10 | 'use strict'; 11 | 12 | var formTabs = { 13 | 14 | init: function (config) { 15 | this.formContainer = $('#' + config.formContainerId); 16 | this.tabs = {}; 17 | this.newTabLabel = config.newTabLabel; 18 | this.activeTab = config.activeTab; 19 | this.lastTabNumber = config.lastTabNumber; 20 | this.initTabs(config.formTabs); 21 | this.onTabChange(); 22 | this.initChangeTabButtons(); 23 | }, 24 | 25 | initChangeTabButtons: function() 26 | { 27 | var self = this; 28 | $(document).on( 29 | "form-field-change-tab-click", 30 | function(e, clickedButton) { 31 | self.onChangeTabButtonClick(clickedButton); 32 | } 33 | ); 34 | 35 | $(window).click(function(e) { 36 | if (!$(e.target).hasClass('change-tab-select')) { 37 | self.hideChangeButtonSelects(); 38 | } 39 | }); 40 | }, 41 | 42 | onChangeTabButtonClick: function(clickedButton) 43 | { 44 | var self = this; 45 | this.hideChangeButtonSelects(); 46 | var select = $(clickedButton).parent().find('.change-tab-select'); 47 | select.empty(); 48 | $.each(this.tabs, function () { 49 | select.append(new Option(this.label, this.id)); 50 | }); 51 | select.val(this.activeTab); 52 | select.on('change', function() { 53 | var fieldset = select.closest('.fieldset'); 54 | self.setFieldTab(fieldset, this.value); 55 | self.onTabChange(); 56 | }); 57 | select.show(); 58 | $(clickedButton).parent().find('.form-field-change-tab-button').hide(); 59 | }, 60 | 61 | setFieldTab: function(fieldset, tabId) 62 | { 63 | $(fieldset.find('.group-code')[0]).val(tabId); 64 | fieldset.removeClass('form_tab_' + this.activeTab); 65 | fieldset.addClass('form_tab_' + tabId); 66 | }, 67 | 68 | hideChangeButtonSelects: function() 69 | { 70 | $('.form-field-change-tab-button').show(); 71 | $('.change-tab-select').hide(); 72 | }, 73 | 74 | initTabs: function (tabsJson) { 75 | var self = this; 76 | $.each(tabsJson, function() { 77 | if (!self.firstTabId) { 78 | self.firstTabId = this.code; 79 | } 80 | self.addTab(this.label, this.code); 81 | }); 82 | this.addNewTabClickEvent(); 83 | $(document).on( 84 | "form-new-field", 85 | function(e, newField) { 86 | var fieldset = $(newField).find('.fieldset')[0]; 87 | self.setFieldTab($(fieldset), self.activeTab); 88 | } 89 | ); 90 | }, 91 | 92 | onTabChange: function () { 93 | var self = this; 94 | $(this.formContainer).find('.fieldset' ).each(function() { 95 | $(this).closest('.fieldset-wrapper').hide(); 96 | }); 97 | 98 | $(this.formContainer).find('.fieldset.' + 'form_tab_' + this.activeTab).each(function() { 99 | $(this).closest('.fieldset-wrapper').show(); 100 | }); 101 | 102 | $.each(self.tabs, function() { 103 | this.tab.removeClass('ui-state-active'); 104 | }); 105 | 106 | var tab = self.tabs[self.activeTab]; 107 | tab.tab.addClass('ui-state-active'); 108 | 109 | this.hideChangeButtonSelects(); 110 | $('.tab-settings').hide(); 111 | $('#tab-settings-' + this.activeTab).show(); 112 | }, 113 | 114 | addNewTabClickEvent: function () { 115 | var self = this; 116 | var addNewButton = $('#add_tab_button'); 117 | addNewButton.click(function () { 118 | self.lastTabNumber ++; 119 | var newTab = self.addTab(self.newTabLabel); 120 | self.activeTab = newTab.attr("data-id"); 121 | self.onTabChange(); 122 | return false; 123 | }); 124 | addNewButton.show(); 125 | }, 126 | 127 | addOnClickTabEvent: function (tab) { 128 | var self = this; 129 | tab.click(function () { 130 | self.activeTab = $(this).parent().attr("data-id"); 131 | self.onTabChange(); 132 | return false; 133 | }); 134 | }, 135 | 136 | addTab: function (label, tabId=null) { 137 | var self = this; 138 | var tabTemplate = $('#tab-template')[0]; 139 | var newTab = tabTemplate.clone(true); 140 | $(tabTemplate).parent()[0].insertBefore(newTab, $('#add_tab_button')[0]); 141 | newTab = $(newTab); 142 | if (!tabId) { 143 | tabId = this.lastTabNumber; 144 | } 145 | newTab.attr("data-id", tabId); 146 | newTab.removeAttr('id'); 147 | var tabLink = newTab.find(".tab-item-link"); 148 | tabLink.title = label; 149 | tabLink.text(label); 150 | this.addOnClickTabEvent(tabLink); 151 | var newSettings = $('#tab-settings-template')[0].clone(true); 152 | newSettings.id = "tab-settings-" + tabId; 153 | var labelInput = $(newSettings).find(".label-input"); 154 | labelInput.val(label); 155 | labelInput.attr('name', 'form_tabs[' + tabId + '][label]'); 156 | $("#tabs-settings-container")[0].appendChild(newSettings); 157 | 158 | if (tabId != this.firstTabId) { 159 | var removeTabButton = $(newSettings).find('.form-remove-tab')[0]; 160 | $(removeTabButton).parent().show(); 161 | $(removeTabButton).click(function() { 162 | self.removeTab(tabId); 163 | return false; 164 | }); 165 | } 166 | 167 | this.tabs[newTab.attr("data-id")] = { 168 | "tab": newTab, 169 | "link": tabLink, 170 | "settings": newSettings, 171 | "label": label, 172 | "id": tabId 173 | }; 174 | newTab.show(); 175 | 176 | return newTab; 177 | }, 178 | 179 | removeTab: function(tabId) { 180 | var self = this; 181 | var selectTab = $('