├── .github
└── ISSUE_TEMPLATE
│ ├── bug-report.md
│ └── config.yml
├── LICENCE.md
├── Plugin.php
├── README.md
├── assets
├── css
│ └── records.css
├── imgs
│ └── icon.svg
└── js
│ ├── inline-errors.js
│ └── recaptcha.js
├── classes
├── BackendHelpers.php
├── FilePond
│ ├── FilePond.php
│ ├── FilePondController.php
│ ├── InvalidPathException.php
│ └── LaravelFilepondException.php
├── GDPR.php
├── MagicForm.php
├── Mails
│ ├── AutoResponse.php
│ ├── Mailable.php
│ └── Notification.php
├── ReCaptcha.php
├── ReCaptchaValidator.php
├── SharedProperties.php
└── UnreadRecords.php
├── components
├── EmptyForm.php
├── FilePondForm.php
├── GenericForm.php
├── emptyform
│ └── default.htm
├── filepondform
│ └── default.htm
├── genericform
│ └── default.htm
└── partials
│ ├── filepond.htm
│ ├── flash.htm
│ ├── js
│ ├── recaptcha.htm
│ └── reset-form.htm
│ └── recaptcha.htm
├── composer.json
├── controllers
├── Exports.php
├── Records.php
├── exports
│ ├── config_form.yaml
│ └── index.htm
└── records
│ ├── config_filter.yaml
│ ├── config_list.yaml
│ ├── index.htm
│ ├── partials
│ ├── _action_button.htm
│ ├── _list_toolbar.htm
│ └── _view_toolbar.htm
│ └── view.htm
├── lang
├── de
│ └── lang.php
├── en
│ └── lang.php
├── fr
│ └── lang.php
├── nl
│ └── lang.php
├── pt-br
│ └── lang.php
├── ru
│ └── lang.php
├── tr
│ └── lang.php
└── zh-cn
│ └── lang.php
├── models
├── Record.php
├── Settings.php
├── export
│ ├── _header.htm
│ └── fields.yaml
├── record
│ ├── columns.yaml
│ └── fields
│ │ ├── _files_fields.htm
│ │ └── _stored_fields.htm
└── settings
│ ├── _gdpr_help.htm
│ ├── _plugin_help.htm
│ ├── _recaptcha_help.htm
│ └── fields.yaml
├── phpunit.xml
├── routes.php
├── tests
└── unit
│ └── classes
│ ├── BackendHelpersTest.php
│ ├── GDPRTest.php
│ └── UnreadRecordsTest.php
├── updates
├── add_group_field.php
├── add_unread_field.php
├── create_records_table.php
└── version.yaml
└── views
└── mail
├── autoresponse.htm
└── notification.htm
/.github/ISSUE_TEMPLATE/bug-report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a bug report for Magic Forms
4 |
5 | ---
6 |
15 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Ask a question
4 | url: https://github.com/skydiver/wn-magic-forms/discussions
5 | about: Ask questions and discuss with other community members
6 |
--------------------------------------------------------------------------------
/LICENCE.md:
--------------------------------------------------------------------------------
1 | # MIT license
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7 | of the Software, and to permit persons to whom the Software is furnished to do
8 | so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/Plugin.php:
--------------------------------------------------------------------------------
1 | 'martin.forms::lang.plugin.name',
21 | 'description' => 'martin.forms::lang.plugin.description',
22 | 'author' => 'Martin M.',
23 | 'icon' => 'icon-bolt',
24 | 'homepage' => 'https://github.com/skydiver/'
25 | ];
26 | }
27 |
28 | public function registerNavigation()
29 | {
30 | if (Settings::get('global_hide_button', false)) {
31 | return;
32 | }
33 |
34 | return [
35 | 'forms' => [
36 | 'label' => 'martin.forms::lang.menu.label',
37 | 'icon' => 'icon-bolt',
38 | 'iconSvg' => 'plugins/martin/forms/assets/imgs/icon.svg',
39 | 'url' => BackendHelpers::getBackendURL(['martin.forms.access_records' => 'martin/forms/records', 'martin.forms.access_exports' => 'martin/forms/exports'], 'martin.forms.access_records'),
40 | 'permissions' => ['martin.forms.*'],
41 | 'sideMenu' => [
42 | 'records' => [
43 | 'label' => 'martin.forms::lang.menu.records.label',
44 | 'icon' => 'icon-database',
45 | 'url' => Backend::url('martin/forms/records'),
46 | 'permissions' => ['martin.forms.access_records'],
47 | 'counter' => UnreadRecords::getTotal(),
48 | 'counterLabel' => 'Un-Read Messages'
49 | ],
50 | 'exports' => [
51 | 'label' => 'martin.forms::lang.menu.exports.label',
52 | 'icon' => 'icon-download',
53 | 'url' => Backend::url('martin/forms/exports'),
54 | 'permissions' => ['martin.forms.access_exports']
55 | ],
56 | ]
57 | ]
58 | ];
59 | }
60 |
61 | public function registerSettings()
62 | {
63 | return [
64 | 'config' => [
65 | 'label' => 'martin.forms::lang.menu.label',
66 | 'description' => 'martin.forms::lang.menu.settings',
67 | 'category' => SettingsManager::CATEGORY_CMS,
68 | 'icon' => 'icon-bolt',
69 | 'class' => 'Martin\Forms\Models\Settings',
70 | 'permissions' => ['martin.forms.access_settings'],
71 | 'order' => 500
72 | ]
73 | ];
74 | }
75 |
76 | public function registerPermissions()
77 | {
78 | return [
79 | 'martin.forms.access_settings' => ['tab' => 'martin.forms::lang.permissions.tab', 'label' => 'martin.forms::lang.permissions.access_settings'],
80 | 'martin.forms.access_records' => ['tab' => 'martin.forms::lang.permissions.tab', 'label' => 'martin.forms::lang.permissions.access_records'],
81 | 'martin.forms.access_exports' => ['tab' => 'martin.forms::lang.permissions.tab', 'label' => 'martin.forms::lang.permissions.access_exports'],
82 | 'martin.forms.gdpr_cleanup' => ['tab' => 'martin.forms::lang.permissions.tab', 'label' => 'martin.forms::lang.permissions.gdpr_cleanup'],
83 | ];
84 | }
85 |
86 | public function registerComponents()
87 | {
88 | return [
89 | 'Martin\Forms\Components\GenericForm' => 'genericForm',
90 | 'Martin\Forms\Components\FilePondForm' => 'filepondForm',
91 | 'Martin\Forms\Components\EmptyForm' => 'emptyForm',
92 | ];
93 | }
94 |
95 | public function registerMailTemplates()
96 | {
97 | return [
98 | 'martin.forms::mail.notification' => Lang::get('martin.forms::lang.mails.form_notification.description'),
99 | 'martin.forms::mail.autoresponse' => Lang::get('martin.forms::lang.mails.form_autoresponse.description'),
100 | ];
101 | }
102 |
103 | public function register()
104 | {
105 | $this->app->resolving('validator', function () {
106 | Validator::extend('recaptcha', 'Martin\Forms\Classes\ReCaptchaValidator@validateReCaptcha');
107 | });
108 | }
109 |
110 | public function registerSchedule($schedule)
111 | {
112 | $schedule->call(function () {
113 | GDPR::cleanRecords();
114 | })->daily();
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 |
4 | ## Why Magic Forms?
5 | Almost everyday we do forms for our clients, personal projects, etc
6 |
7 | Sometimes we need to add or remove fields, change validations, store data and at some point, this can be boring and repetitive.
8 |
9 | So, the objective was to find a way to just put the HTML elements on the page, skip the repetitive task of coding and (with some kind of magic) store this data on a database or send by mail.
10 |
11 |
12 | ## Features
13 | * Create any type of form: contact, feedback, registration, uploads, etc
14 | * Write only HTML
15 | * Don't code forms logic
16 | * Laravel validation
17 | * Custom validation errors
18 | * Use multiple forms on same page
19 | * Store on database
20 | * Export data in CSV
21 | * Access database records from backend
22 | * Send mail notifications to multiple recipients
23 | * Auto-response email on form submit
24 | * reCAPTCHA validation
25 | * Support for Translate plugin
26 | * Inline errors with fields (read documentation for more info)
27 | * File uploads using Filepond
28 |
29 |
30 | ## Documentation
31 | Checkout our docs at:
32 | > https://magicforms.vercel.app/
33 |
--------------------------------------------------------------------------------
/assets/css/records.css:
--------------------------------------------------------------------------------
1 | .files-container ul { padding:0; list-style:none; }
2 |
3 | .record-table { width:100%; border-collapse:collapse; box-shadow:5px 5px 10px #AAA; }
4 | .record-table td { padding:10px; border:solid 1px #D4D8DA; }
5 | .record-label { width:20%; background-color:#ECF0F1; text-align:right; }
6 | .record-value { width:80%; background-color:#F9F9F9; }
7 | .record-value ul { margin:0; }
8 | .record-metadata { font-size:0.8em; color:#888; font-weight:bold; }
--------------------------------------------------------------------------------
/assets/imgs/icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/assets/js/inline-errors.js:
--------------------------------------------------------------------------------
1 | $(window).on('ajaxInvalidField', function(event, fieldElement, fieldName, errorMsg, isFirst) {
2 | $(fieldElement).closest('.form-group').addClass('has-error');
3 | });
4 |
5 | $(document).on('ajaxPromise', '[data-request]', function() {
6 | $(this).closest('form').find('.form-group.has-error').removeClass('has-error');
7 | });
--------------------------------------------------------------------------------
/assets/js/recaptcha.js:
--------------------------------------------------------------------------------
1 | var captchas = [];
2 |
3 | var onloadCallback = function() {
4 | jQuery('.g-recaptcha').each(function(index, el) {
5 | captchas[el.id] = grecaptcha.render(el, $(el).data());
6 | });
7 | }
8 |
9 | function resetReCaptcha(id) {
10 | var widget = captchas[id];
11 | grecaptcha.reset(widget);
12 | }
--------------------------------------------------------------------------------
/classes/BackendHelpers.php:
--------------------------------------------------------------------------------
1 | $URL) {
22 | if ($user->hasAccess($permission)) {
23 | return Backend::url($URL);
24 | }
25 | }
26 | return Backend::url($urls[$default]);
27 | }
28 |
29 | /**
30 | * Check if Translator plugin is installed
31 | *
32 | * @return boolean
33 | */
34 | public static function isTranslatePlugin(): bool
35 | {
36 | return class_exists('\RainLab\Translate\Classes\Translator') && class_exists('\RainLab\Translate\Models\Message');
37 | }
38 |
39 | /**
40 | * Render an array|object as HTML list (UL > LI)
41 | *
42 | * @param mixed $data List items
43 | *
44 | * @return string
45 | */
46 | public static function array2ul($data): string
47 | {
48 | $return = '';
49 | foreach ($data as $index => $item) {
50 | if (!is_string($item)) {
51 | $return .= '
' . htmlspecialchars($index, ENT_QUOTES) . '' . self::array2ul($item) . "
";
52 | } else {
53 | $return .= '';
54 | if (is_object($data)) {
55 | $return .= htmlspecialchars($index, ENT_QUOTES) . ' - ';
56 | }
57 | $return .= htmlspecialchars($item, ENT_QUOTES) . '';
58 | }
59 | }
60 | return $return;
61 | }
62 |
63 | /**
64 | * Anonymize an IPv4 address
65 | * (credits: https://github.com/geertw/php-ip-anonymizer)
66 | *
67 | * @param string $address IPv4 address
68 | *
69 | * @return string Anonymized address
70 | */
71 | public static function anonymizeIPv4(string $address): string
72 | {
73 | return inet_ntop(inet_pton($address) & inet_pton("255.255.255.0"));
74 | }
75 |
76 | /**
77 | * Extract string from curly braces
78 | *
79 | * @param string $pattern Pattern to replace
80 | * @param string $replacement Replacement string
81 | * @param string $subject Strings to replace
82 | *
83 | * @return string
84 | */
85 | public static function replaceToken(string $pattern, string $replacement = null, string $subject): string
86 | {
87 | $pattern = '/{{\s*(' . $pattern . ')\s*}}/';
88 | return preg_replace($pattern, $replacement, $subject);
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/classes/FilePond/FilePond.php:
--------------------------------------------------------------------------------
1 | getTempPath();
36 |
37 | if (!Str::startsWith($filePath, $tempPath)) {
38 | throw new InvalidPathException();
39 | }
40 |
41 | return $filePath;
42 | }
43 |
44 | public function getTempPath()
45 | {
46 | $defaultPath = temp_path('magic-forms-temp');
47 |
48 | if (File::exists($defaultPath)) {
49 | return $defaultPath;
50 | }
51 |
52 | File::makeDirectory($defaultPath);
53 | return $defaultPath;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/classes/FilePond/FilePondController.php:
--------------------------------------------------------------------------------
1 | filepond = $filepond;
27 | }
28 |
29 | /**
30 | * Uploads the file to the temporary directory
31 | * and returns an encrypted path to the file
32 | *
33 | * @param Request $request
34 | * @return \Illuminate\Http\Response
35 | */
36 | public function upload(Request $request): \Illuminate\Http\Response
37 | {
38 | $field = $this->getUploadFieldName();
39 | $input = $request->file($field);
40 | $this->file = is_array($input) ? $input[0] : $input;
41 |
42 |
43 | /** VALIDATE UPLOAD FILE SIZE */
44 | if ($this->checkInvalidSize()) {
45 | $error = e(trans('martin.forms::lang.classes.FilePond.error_filesize'));
46 | return Response::make($error, 422, [
47 | 'Content-Type' => 'text/plain',
48 | ]);
49 | }
50 |
51 | /** VALIDATE UPLOAD FILE TYPE */
52 | if ($this->checkInvalidFile()) {
53 | $error = e(trans('martin.forms::lang.classes.FilePond.error_filetype'));
54 | return Response::make($error, 422, [
55 | 'Content-Type' => 'text/plain',
56 | ]);
57 | }
58 |
59 | if ($input === null) {
60 | return Response::make($field . ' is required', 422, [
61 | 'Content-Type' => 'text/plain',
62 | ]);
63 | }
64 |
65 | $filePath = $this->generateTempFilename();
66 | $filePathParts = pathinfo($filePath);
67 |
68 | if (!$this->file->move($filePathParts['dirname'], $filePathParts['basename'])) {
69 | $error = e(trans('martin.forms::lang.classes.FilePond.error_savefile'));
70 | return Response::make($error, 500, [
71 | 'Content-Type' => 'text/plain',
72 | ]);
73 | }
74 |
75 | return Response::make($this->filepond->getServerIdFromPath($filePath), 200, [
76 | 'Content-Type' => 'text/plain',
77 | ]);
78 | }
79 |
80 | /**
81 | * Takes the given encrypted filepath and deletes
82 | * it if it hasn't been tampered with
83 | *
84 | * @param Request $request
85 | * @return mixed
86 | */
87 | public function delete(Request $request): \Illuminate\Http\Response
88 | {
89 | $filePath = $this->filepond->getPathFromServerId($request->getContent());
90 |
91 | if (unlink($filePath)) {
92 | return Response::make('', 200, [
93 | 'Content-Type' => 'text/plain',
94 | ]);
95 | }
96 |
97 | return Response::make('', 500, [
98 | 'Content-Type' => 'text/plain',
99 | ]);
100 | }
101 |
102 | /**
103 | * Get field name used for uploads
104 | *
105 | * @return string
106 | */
107 | private function getUploadFieldName(): string
108 | {
109 | return request()->headers->get('FILEPOND-FIELD');
110 | }
111 |
112 | /**
113 | * Generate unique temporary filename
114 | *
115 | * @return string
116 | */
117 | private function generateTempFilename(): string
118 | {
119 | return vsprintf('%s%s%s__%s', [
120 | $this->filepond->getTempPath(),
121 | DIRECTORY_SEPARATOR,
122 | str_random(8),
123 | $this->file->getClientOriginalName()
124 | ]);
125 | }
126 |
127 | /**
128 | * Check if uploaded file has a valid size
129 | *
130 | * @return boolean
131 | */
132 | private function checkInvalidSize(): bool
133 | {
134 | $max_size = Settings::get('global_allowed_filesize', 10000);
135 |
136 | $field = $this->getUploadFieldName();
137 |
138 | $validator = Validator::make(request()->all(), [
139 | $field . '.*' => 'max:' . $max_size,
140 | ]);
141 |
142 | return $validator->fails();
143 | }
144 |
145 | /**
146 | * Check if uploaded file has a valid mime type
147 | *
148 | * @return boolean
149 | */
150 | private function checkInvalidFile(): bool
151 | {
152 | $field = $this->getUploadFieldName();
153 | $types = $this->allowedFileTypes();
154 |
155 | $validator = Validator::make(request()->all(), [
156 | $field . '.*' => 'mimes:' . $types,
157 | ]);
158 |
159 | return $validator->fails();
160 | }
161 |
162 | /**
163 | * Get a list of allowed files types
164 | *
165 | * @return string
166 | */
167 | private function allowedFileTypes(): string
168 | {
169 | $settings = Settings::get('global_allowed_files', false);
170 |
171 | if ($settings) {
172 | return $settings;
173 | }
174 |
175 | $default = Definitions::get('defaultExtensions');
176 |
177 | return implode(',', $default);
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/classes/FilePond/InvalidPathException.php:
--------------------------------------------------------------------------------
1 | subDays($gdpr_days);
24 | $rows = Record::whereDate('created_at', '<', $days)->forceDelete();
25 | return $rows;
26 | }
27 |
28 | Flash::error(e(trans('martin.forms::lang.classes.GDPR.alert_invalid_gdpr')));
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/classes/MagicForm.php:
--------------------------------------------------------------------------------
1 | page['recaptcha_enabled'] = $this->isReCaptchaEnabled();
32 | $this->page['recaptcha_misconfigured'] = $this->isReCaptchaMisconfigured();
33 |
34 | if ($this->property('uploader_enable')) {
35 | $this->page['allowed_filesize'] = Settings::get('global_allowed_filesize');
36 | }
37 |
38 | if ($this->isReCaptchaEnabled()) {
39 | $this->loadReCaptcha();
40 | }
41 |
42 | if ($this->isReCaptchaMisconfigured()) {
43 | $this->page['recaptcha_warn'] = Lang::get('martin.forms::lang.components.shared.recaptcha_warn');
44 | }
45 |
46 | if ($this->property('inline_errors') == 'display') {
47 | $this->addJs('assets/js/inline-errors.js');
48 | }
49 | }
50 |
51 | public function settings()
52 | {
53 | return [
54 | 'recaptcha_site_key' => Settings::get('recaptcha_site_key'),
55 | 'recaptcha_secret_key' => Settings::get('recaptcha_secret_key'),
56 | ];
57 | }
58 |
59 | public function onFormSubmit()
60 | {
61 | // FLASH PARTIAL
62 | $flash_partial = $this->property('messages_partial', '@flash.htm');
63 |
64 | // CSRF CHECK
65 | if (Config::get('cms.enableCsrfProtection') && (Session::token() != post('_token'))) {
66 | throw new AjaxException(['#' . $this->alias . '_forms_flash' => $this->renderPartial($flash_partial, [
67 | 'status' => 'error',
68 | 'type' => 'danger',
69 | 'content' => Lang::get('martin.forms::lang.components.shared.csrf_error'),
70 | ])]);
71 | }
72 |
73 | // LOAD TRANSLATOR PLUGIN
74 | if (BackendHelpers::isTranslatePlugin()) {
75 | $translator = \RainLab\Translate\Classes\Translator::instance();
76 | $translator->loadLocaleFromSession();
77 | $locale = $translator->getLocale();
78 | \RainLab\Translate\Models\Message::setContext($locale);
79 | }
80 |
81 | // FILTER ALLOWED FIELDS
82 | $allow = $this->property('allowed_fields');
83 | if (is_array($allow) && !empty($allow)) {
84 | foreach ($allow as $field) {
85 | $post[$field] = post($field);
86 | }
87 | if ($this->isReCaptchaEnabled()) {
88 | $post['g-recaptcha-response'] = post('g-recaptcha-response');
89 | }
90 | } else {
91 | $post = post();
92 | }
93 |
94 | // SANITIZE FORM DATA
95 | if ($this->property('sanitize_data') == 'htmlspecialchars') {
96 | $post = $this->array_map_recursive(function ($value) {
97 | return htmlspecialchars($value, ENT_QUOTES);
98 | }, $post);
99 | }
100 |
101 | // VALIDATION PARAMETERS
102 | $rules = (array)$this->property('rules');
103 | $msgs = (array)$this->property('rules_messages');
104 | $custom_attributes = (array)$this->property('custom_attributes');
105 |
106 | // TRANSLATE CUSTOM ERROR MESSAGES
107 | if (BackendHelpers::isTranslatePlugin()) {
108 | foreach ($msgs as $rule => $msg) {
109 | $msgs[$rule] = \RainLab\Translate\Models\Message::trans($msg);
110 | }
111 | }
112 |
113 | // ADD reCAPTCHA VALIDATION
114 | if ($this->isReCaptchaEnabled() && $this->property('recaptcha_size') != 'invisible') {
115 | $rules['g-recaptcha-response'] = 'required';
116 | }
117 |
118 | // DO FORM VALIDATION
119 | $validator = Validator::make($post, $rules, $msgs, $custom_attributes);
120 |
121 | // NICE reCAPTCHA FIELD NAME
122 | if ($this->isReCaptchaEnabled()) {
123 | $fields_names = ['g-recaptcha-response' => 'reCAPTCHA'];
124 | $validator->setAttributeNames(array_merge($fields_names, $custom_attributes));
125 | }
126 |
127 | // VALIDATE ALL + CAPTCHA EXISTS
128 | if ($validator->fails()) {
129 |
130 | // GET DEFAULT ERROR MESSAGE
131 | $message = $this->property('messages_errors');
132 |
133 | // LOOK FOR TRANSLATION
134 | if (BackendHelpers::isTranslatePlugin()) {
135 | $message = \RainLab\Translate\Models\Message::trans($message);
136 | }
137 |
138 | // THROW ERRORS
139 | if ($this->property('inline_errors') == 'display') {
140 | throw new ValidationException($validator);
141 | } else {
142 | throw new AjaxException($this->exceptionResponse($validator, [
143 | 'status' => 'error',
144 | 'type' => 'danger',
145 | 'title' => $message,
146 | 'list' => $validator->messages()->all(),
147 | 'errors' => json_encode($validator->messages()->messages()),
148 | 'jscript' => $this->property('js_on_error'),
149 | ]));
150 | }
151 | }
152 |
153 | // IF FIRST VALIDATION IS OK, VALIDATE CAPTCHA vs GOOGLE
154 | // (this prevents to resolve captcha after every form error)
155 | if ($this->isReCaptchaEnabled()) {
156 |
157 | // PREPARE RECAPTCHA VALIDATION
158 | $rules = ['g-recaptcha-response' => 'recaptcha'];
159 | $err_msg = ['g-recaptcha-response.recaptcha' => Lang::get('martin.forms::lang.validation.recaptcha_error')];
160 |
161 | // DO SECOND VALIDATION
162 | $validator = Validator::make($post, $rules, $err_msg);
163 |
164 | // VALIDATE ALL + CAPTCHA EXISTS
165 | if ($validator->fails()) {
166 |
167 | // THROW ERRORS
168 | if ($this->property('inline_errors') == 'display') {
169 | throw new ValidationException($validator);
170 | } else {
171 | throw new AjaxException($this->exceptionResponse($validator, [
172 | 'status' => 'error',
173 | 'type' => 'danger',
174 | 'content' => Lang::get('martin.forms::lang.validation.recaptcha_error'),
175 | 'errors' => json_encode($validator->messages()->messages()),
176 | 'jscript' => $this->property('js_on_error'),
177 | ]));
178 | }
179 | }
180 | }
181 |
182 | // REMOVE EXTRA FIELDS FROM STORED DATA
183 | unset($post['_token'], $post['g-recaptcha-response'], $post['_session_key'], $post['files']);
184 |
185 | // FIRE BEFORE SAVE EVENT
186 | Event::fire('martin.forms.beforeSaveRecord', [&$post, $this]);
187 |
188 | if (count($custom_attributes)) {
189 | $post = collect($post)->mapWithKeys(function ($val, $key) use ($custom_attributes) {
190 | return [array_get($custom_attributes, $key, $key) => $val];
191 | })->all();
192 | }
193 |
194 | $record = new Record;
195 | $record->ip = $this->getIP();
196 | $record->created_at = date('Y-m-d H:i:s');
197 |
198 | // SAVE RECORD TO DATABASE
199 | if (!$this->property('skip_database')) {
200 | $record->form_data = json_encode($post, JSON_UNESCAPED_UNICODE);
201 | if ($this->property('group') != '') {
202 | $record->group = $this->property('group');
203 | }
204 |
205 | // attach files
206 | $this->attachFiles($record);
207 |
208 | $record->save(null, post('_session_key'));
209 | }
210 |
211 | // SEND NOTIFICATION EMAIL
212 | if ($this->property('mail_enabled')) {
213 | $notification = App::makeWith(Notification::class, [
214 | $this->getProperties(), $post, $record, $record->files
215 | ]);
216 | $notification->send();
217 | }
218 |
219 | // SEND AUTORESPONSE EMAIL
220 | if ($this->property('mail_resp_enabled')) {
221 | $autoresponse = App::makeWith(AutoResponse::class, [
222 | $this->getProperties(), $post, $record
223 | ]);
224 | $autoresponse->send();
225 | }
226 |
227 | // FIRE AFTER SAVE EVENT
228 | Event::fire('martin.forms.afterSaveRecord', [&$post, $this, $record]);
229 |
230 | // CHECK FOR REDIRECT
231 | if ($this->property('redirect')) {
232 | return Redirect::to($this->property('redirect'));
233 | }
234 |
235 | // GET DEFAULT SUCCESS MESSAGE
236 | $message = $this->property('messages_success');
237 |
238 | // LOOK FOR TRANSLATION
239 | if (BackendHelpers::isTranslatePlugin()) {
240 | $message = \RainLab\Translate\Models\Message::trans($message);
241 | }
242 |
243 | // DISPLAY SUCCESS MESSAGE
244 | return ['#' . $this->alias . '_forms_flash' => $this->renderPartial($flash_partial, [
245 | 'status' => 'success',
246 | 'type' => 'success',
247 | 'content' => $message,
248 | 'jscript' => $this->prepareJavaScript(),
249 | ])];
250 | }
251 |
252 | private function exceptionResponse($validator, $params)
253 | {
254 | // FLASH PARTIAL
255 | $flash_partial = $this->property('messages_partial', '@flash.htm');
256 |
257 | // EXCEPTION RESPONSE
258 | $response = ['#' . $this->alias . '_forms_flash' => $this->renderPartial($flash_partial, $params)];
259 |
260 | // INCLUDE ERROR FIELDS IF REQUIRED
261 | if ($this->property('inline_errors') != 'disabled') {
262 | $response['error_fields'] = $validator->messages();
263 | }
264 |
265 | return $response;
266 | }
267 |
268 | private function prepareJavaScript()
269 | {
270 | $code = false;
271 |
272 | /* SUCCESS JS */
273 | if ($this->property('js_on_success') != '') {
274 | $code .= $this->property('js_on_success');
275 | }
276 |
277 | /* RECAPTCHA JS */
278 | if ($this->isReCaptchaEnabled()) {
279 | $code .= $this->renderPartial('@js/recaptcha.htm');
280 | }
281 |
282 | /* RESET FORM JS */
283 | if ($this->property('reset_form')) {
284 | $params = ['id' => '#' . $this->alias . '_forms_flash'];
285 | $code .= $this->renderPartial('@js/reset-form.htm', $params);
286 | }
287 |
288 | return $code;
289 | }
290 |
291 | private function getIP()
292 | {
293 | if ($this->property('anonymize_ip') == 'full') {
294 | return '(Not stored)';
295 | }
296 |
297 | $ip = Request::getClientIp();
298 |
299 | if ($this->property('anonymize_ip') == 'partial') {
300 | return BackendHelpers::anonymizeIPv4($ip);
301 | }
302 |
303 | return $ip;
304 | }
305 |
306 | private function array_map_recursive($callback, $array)
307 | {
308 | $func = function ($item) use (&$func, &$callback) {
309 | return is_array($item) ? array_map($func, $item) : call_user_func($callback, $item);
310 | };
311 |
312 | return array_map($func, $array);
313 | }
314 |
315 | private function attachFiles(Record $record)
316 | {
317 | $files = post('files', null);
318 |
319 | if (!$files) {
320 | return;
321 | }
322 |
323 | foreach ($files as $file) {
324 | $filepond = App::make(FilePond::class);
325 | $filePath = $filepond->getPathFromServerId($file);
326 |
327 | $record->files()->create([
328 | 'data' => $filePath
329 | ], post('_session_key'));
330 | }
331 | }
332 | }
333 |
--------------------------------------------------------------------------------
/classes/Mails/AutoResponse.php:
--------------------------------------------------------------------------------
1 | properties = $properties;
20 | $this->post = $post;
21 | $this->record = $record;
22 | }
23 |
24 | public function send()
25 | {
26 | // SET DEFAULT EMAIL DATA ARRAY
27 | $this->data = [
28 | 'id' => $this->record->id,
29 | 'data' => $this->post,
30 | 'ip' => $this->record->ip,
31 | 'date' => $this->record->created_at
32 | ];
33 |
34 | // CHECK FOR CUSTOM SUBJECT
35 | if (!empty($this->properties['mail_resp_subject'])) {
36 | $this->prepareCustomSubject();
37 | }
38 |
39 | // SET EMAIL PARAMETERS
40 | $response = isset($this->properties['mail_resp_field']) ? $this->properties['mail_resp_field'] : null;
41 | $to = isset($this->post[$response]) ? $this->post[$response] : null;
42 | $name = isset($this->properties['mail_resp_name']) ? $this->properties['mail_resp_name'] : null;
43 | $from = isset($this->properties['mail_resp_from']) ? $this->properties['mail_resp_from'] : null;
44 | $subject = isset($this->properties['mail_resp_subject']) ? $this->properties['mail_resp_subject'] : null;
45 |
46 | if (filter_var($to, FILTER_VALIDATE_EMAIL) && filter_var($from, FILTER_VALIDATE_EMAIL)) {
47 | // CUSTOM TEMPLATE
48 | $template = $this->getTemplate();
49 |
50 | // SEND AUTORESPONSE EMAIL
51 | Mail::sendTo($to, $template, $this->data, function ($message) use ($from, $name, $subject) {
52 | $message->from($from, $name);
53 |
54 | if (isset($subject)) {
55 | $message->subject($subject);
56 | }
57 | });
58 | }
59 | }
60 |
61 | /**
62 | * Returns email template to use
63 | *
64 | * @return string
65 | */
66 | public function getTemplate(): string
67 | {
68 | return !empty($this->properties['mail_resp_template']) && MailTemplate::findOrMakeTemplate($this->properties['mail_resp_template']) ?
69 | $this->properties['mail_resp_template'] :
70 | 'martin.forms::mail.autoresponse';
71 | }
72 |
73 | /**
74 | * Parse custom subject and modify using form variables and custom settings
75 | *
76 | * @return void
77 | */
78 | public function prepareCustomSubject()
79 | {
80 | // SET DATE FORMAT
81 | $dateFormat = $this->properties['emails_date_format'] ?? 'Y-m-d';
82 |
83 | // DATA TO REPLACE
84 | $id = $this->data['id'];
85 | $ip = $this->data['ip'];
86 | $date = date($dateFormat);
87 |
88 | // REPLACE RECORD TOKENS IN SUBJECT
89 | $this->properties['mail_resp_subject'] = BackendHelpers::replaceToken('record.id', $id, $this->properties['mail_resp_subject']);
90 | $this->properties['mail_resp_subject'] = BackendHelpers::replaceToken('record.ip', $ip, $this->properties['mail_resp_subject']);
91 | $this->properties['mail_resp_subject'] = BackendHelpers::replaceToken('record.date', $date, $this->properties['mail_resp_subject']);
92 |
93 | // REPLACE FORM FIELDS TOKENS IN SUBJECT
94 | foreach ($this->data['data'] as $key => $value) {
95 | if (!is_array($value)) {
96 | $token = 'form.' . $key;
97 | $this->properties['mail_resp_subject'] = BackendHelpers::replaceToken($token, $value, $this->properties['mail_resp_subject']);
98 | }
99 | }
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/classes/Mails/Mailable.php:
--------------------------------------------------------------------------------
1 | properties = $properties;
22 | $this->post = $post;
23 | $this->record = $record;
24 | $this->files = $files;
25 | }
26 |
27 | public function send()
28 | {
29 | // CHECK IF THERE IS AT LEAST ONE MAIL ADDRESS
30 | if (!isset($this->properties['mail_recipients'])) {
31 | $this->properties['mail_recipients'] = false;
32 | }
33 |
34 | // CHECK IF THERE IS AT LEAST ONE MAIL ADDRESS
35 | if (!isset($this->properties['mail_bcc'])) {
36 | $this->properties['mail_bcc'] = false;
37 | }
38 |
39 | // EXIT IF NO EMAIL ADDRESSES ARE SET
40 | if (!$this->checkEmailSettings()) {
41 | return;
42 | }
43 |
44 | // CUSTOM TEMPLATE
45 | $template = $this->getTemplate();
46 |
47 | // SET DEFAULT EMAIL DATA ARRAY
48 | $this->data = [
49 | 'id' => $this->record->id,
50 | 'data' => $this->post,
51 | 'ip' => $this->record->ip,
52 | 'date' => $this->record->created_at
53 | ];
54 |
55 | // CHECK FOR CUSTOM SUBJECT
56 | if (!empty($this->properties['mail_subject'])) {
57 | $this->prepareCustomSubject();
58 | }
59 |
60 | // SEND NOTIFICATION EMAIL
61 | Mail::sendTo($this->properties['mail_recipients'], $template, $this->data, function ($message) {
62 | // SEND BLIND CARBON COPY
63 | if (!empty($this->properties['mail_bcc']) && is_array($this->properties['mail_bcc'])) {
64 | $message->bcc($this->properties['mail_bcc']);
65 | }
66 |
67 | // USE CUSTOM SUBJECT
68 | if (!empty($this->properties['mail_subject'])) {
69 | $message->subject($this->properties['mail_subject']);
70 | }
71 |
72 | // ADD REPLY TO ADDRESS
73 | if (!empty($this->properties['mail_replyto'])) {
74 | $message->replyTo($this->properties['mail_replyto']);
75 | }
76 |
77 | // ADD UPLOADS
78 | if (!empty($this->properties['mail_uploads']) && !empty($this->files)) {
79 | foreach ($this->files as $file) {
80 | $message->attach($file->getLocalPath(), ['as' => $file->getFilename()]);
81 | }
82 | }
83 | });
84 | }
85 |
86 | /**
87 | * Check if emails address are set
88 | *
89 | * @return boolean
90 | */
91 | private function checkEmailSettings(): bool
92 | {
93 | return (is_array($this->properties['mail_recipients']) || is_array($this->properties['mail_bcc']));
94 | }
95 |
96 | public function getTemplate(): string
97 | {
98 | return !empty($this->properties['mail_template']) && MailTemplate::findOrMakeTemplate($this->properties['mail_template']) ?
99 | $this->properties['mail_template'] :
100 | 'martin.forms::mail.notification';
101 | }
102 |
103 | public function prepareCustomSubject()
104 | {
105 | // SET DATE FORMAT
106 | $dateFormat = $this->properties['emails_date_format'] ?? 'Y-m-d';
107 |
108 | // DATA TO REPLACE
109 | $id = $this->data['id'];
110 | $ip = $this->data['ip'];
111 | $date = date($dateFormat);
112 |
113 | // REPLACE RECORD TOKENS IN SUBJECT
114 | $this->properties['mail_subject'] = BH::replaceToken('record.id', $id, $this->properties['mail_subject']);
115 | $this->properties['mail_subject'] = BH::replaceToken('record.ip', $ip, $this->properties['mail_subject']);
116 | $this->properties['mail_subject'] = BH::replaceToken('record.date', $date, $this->properties['mail_subject']);
117 |
118 | // REPLACE FORM FIELDS TOKENS IN SUBJECT
119 | foreach ($this->data['data'] as $key => $value) {
120 | if (!is_array($value)) {
121 | $token = 'form.' . $key;
122 | $this->properties['mail_subject'] = BH::replaceToken($token, $value, $this->properties['mail_subject']);
123 | }
124 | }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/classes/ReCaptcha.php:
--------------------------------------------------------------------------------
1 | translator = Translator::instance();
25 | }
26 | }
27 |
28 | private function isReCaptchaEnabled()
29 | {
30 | return ($this->property('recaptcha_enabled') && Settings::get('recaptcha_site_key') != '' && Settings::get('recaptcha_secret_key') != '');
31 | }
32 |
33 | private function isReCaptchaMisconfigured()
34 | {
35 | return ($this->property('recaptcha_enabled') && (Settings::get('recaptcha_site_key') == '' || Settings::get('recaptcha_secret_key') == ''));
36 | }
37 |
38 | private function getReCaptchaLang($lang = '')
39 | {
40 | if (BackendHelpers::isTranslatePlugin()) {
41 | $lang = '&hl=' . $this->activeLocale = $this->translator->getLocale();
42 | } else {
43 | $lang = '&hl=' . $this->activeLocale = app()->getLocale();
44 | }
45 | return $lang;
46 | }
47 |
48 | private function loadReCaptcha()
49 | {
50 | $this->addJs('https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit' . $this->getReCaptchaLang(), ['async', 'defer']);
51 | $this->addJs('assets/js/recaptcha.js');
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/classes/ReCaptchaValidator.php:
--------------------------------------------------------------------------------
1 | [
13 | 'title' => 'martin.forms::lang.components.shared.group.title',
14 | 'description' => 'martin.forms::lang.components.shared.group.description',
15 | 'type' => 'string',
16 | 'showExternalParam' => false,
17 | ],
18 | 'rules' => [
19 | 'title' => 'martin.forms::lang.components.shared.rules.title',
20 | 'description' => 'martin.forms::lang.components.shared.rules.description',
21 | 'type' => 'dictionary',
22 | 'group' => 'martin.forms::lang.components.shared.group_validation',
23 | 'showExternalParam' => false,
24 | ],
25 | 'rules_messages' => [
26 | 'title' => 'martin.forms::lang.components.shared.rules_messages.title',
27 | 'description' => 'martin.forms::lang.components.shared.rules_messages.description',
28 | 'type' => 'dictionary',
29 | 'group' => 'martin.forms::lang.components.shared.group_validation',
30 | 'showExternalParam' => false,
31 | ],
32 | 'custom_attributes' => [
33 | 'title' => 'martin.forms::lang.components.shared.custom_attributes.title',
34 | 'description' => 'martin.forms::lang.components.shared.custom_attributes.description',
35 | 'type' => 'dictionary',
36 | 'group' => 'martin.forms::lang.components.shared.group_validation',
37 | 'showExternalParam' => false,
38 | ],
39 | 'messages_success' => [
40 | 'title' => 'martin.forms::lang.components.shared.messages_success.title',
41 | 'description' => 'martin.forms::lang.components.shared.messages_success.description',
42 | 'type' => 'string',
43 | 'group' => 'martin.forms::lang.components.shared.group_messages',
44 | 'default' => Lang::get('martin.forms::lang.components.shared.messages_success.default'),
45 | 'showExternalParam' => false,
46 | 'validation' => ['required' => ['message' => Lang::get('martin.forms::lang.components.shared.validation_req')]]
47 | ],
48 | 'messages_errors' => [
49 | 'title' => 'martin.forms::lang.components.shared.messages_errors.title',
50 | 'description' => 'martin.forms::lang.components.shared.messages_errors.description',
51 | 'type' => 'string',
52 | 'group' => 'martin.forms::lang.components.shared.group_messages',
53 | 'default' => Lang::get('martin.forms::lang.components.shared.messages_errors.default'),
54 | 'showExternalParam' => false,
55 | 'validation' => ['required' => ['message' => Lang::get('martin.forms::lang.components.shared.validation_req')]]
56 | ],
57 | 'messages_partial' => [
58 | 'title' => 'martin.forms::lang.components.shared.messages_partial.title',
59 | 'description' => 'martin.forms::lang.components.shared.messages_partial.description',
60 | 'type' => 'string',
61 | 'group' => 'martin.forms::lang.components.shared.group_messages',
62 | 'showExternalParam' => false
63 | ],
64 | 'mail_enabled' => [
65 | 'title' => 'martin.forms::lang.components.shared.mail_enabled.title',
66 | 'description' => 'martin.forms::lang.components.shared.mail_enabled.description',
67 | 'type' => 'checkbox',
68 | 'group' => 'martin.forms::lang.components.shared.group_mail',
69 | 'showExternalParam' => false
70 | ],
71 | 'mail_subject' => [
72 | 'title' => 'martin.forms::lang.components.shared.mail_subject.title',
73 | 'description' => 'martin.forms::lang.components.shared.mail_subject.description',
74 | 'type' => 'string',
75 | 'group' => 'martin.forms::lang.components.shared.group_mail',
76 | 'showExternalParam' => false
77 | ],
78 | 'mail_recipients' => [
79 | 'title' => 'martin.forms::lang.components.shared.mail_recipients.title',
80 | 'description' => 'martin.forms::lang.components.shared.mail_recipients.description',
81 | 'type' => 'stringList',
82 | 'group' => 'martin.forms::lang.components.shared.group_mail',
83 | 'showExternalParam' => false
84 | ],
85 | 'mail_bcc' => [
86 | 'title' => 'martin.forms::lang.components.shared.mail_bcc.title',
87 | 'description' => 'martin.forms::lang.components.shared.mail_bcc.description',
88 | 'type' => 'stringList',
89 | 'group' => 'martin.forms::lang.components.shared.group_mail',
90 | 'showExternalParam' => false
91 | ],
92 | 'mail_replyto' => [
93 | 'title' => 'martin.forms::lang.components.shared.mail_replyto.title',
94 | 'description' => 'martin.forms::lang.components.shared.mail_replyto.description',
95 | 'type' => 'string',
96 | 'group' => 'martin.forms::lang.components.shared.group_mail',
97 | 'showExternalParam' => false
98 | ],
99 | 'mail_template' => [
100 | 'title' => 'martin.forms::lang.components.shared.mail_template.title',
101 | 'description' => 'martin.forms::lang.components.shared.mail_template.description',
102 | 'type' => 'string',
103 | 'group' => 'martin.forms::lang.components.shared.group_mail',
104 | 'showExternalParam' => false
105 | ],
106 | 'mail_resp_enabled' => [
107 | 'title' => 'martin.forms::lang.components.shared.mail_resp_enabled.title',
108 | 'description' => 'martin.forms::lang.components.shared.mail_resp_enabled.description',
109 | 'type' => 'checkbox',
110 | 'group' => 'martin.forms::lang.components.shared.group_mail_resp',
111 | 'showExternalParam' => false
112 | ],
113 | 'mail_resp_field' => [
114 | 'title' => 'martin.forms::lang.components.shared.mail_resp_field.title',
115 | 'description' => 'martin.forms::lang.components.shared.mail_resp_field.description',
116 | 'type' => 'string',
117 | 'group' => 'martin.forms::lang.components.shared.group_mail_resp',
118 | 'showExternalParam' => false
119 | ],
120 | 'mail_resp_name' => [
121 | 'title' => 'martin.forms::lang.components.shared.mail_resp_name.title',
122 | 'description' => 'martin.forms::lang.components.shared.mail_resp_name.description',
123 | 'type' => 'string',
124 | 'group' => 'martin.forms::lang.components.shared.group_mail_resp',
125 | 'showExternalParam' => false
126 | ],
127 | 'mail_resp_from' => [
128 | 'title' => 'martin.forms::lang.components.shared.mail_resp_from.title',
129 | 'description' => 'martin.forms::lang.components.shared.mail_resp_from.description',
130 | 'type' => 'string',
131 | 'group' => 'martin.forms::lang.components.shared.group_mail_resp',
132 | 'showExternalParam' => false
133 | ],
134 | 'mail_resp_subject' => [
135 | 'title' => 'martin.forms::lang.components.shared.mail_resp_subject.title',
136 | 'description' => 'martin.forms::lang.components.shared.mail_resp_subject.description',
137 | 'type' => 'string',
138 | 'group' => 'martin.forms::lang.components.shared.group_mail_resp',
139 | 'showExternalParam' => false
140 | ],
141 | 'mail_resp_template' => [
142 | 'title' => 'martin.forms::lang.components.shared.mail_template.title',
143 | 'description' => 'martin.forms::lang.components.shared.mail_template.description',
144 | 'type' => 'string',
145 | 'group' => 'martin.forms::lang.components.shared.group_mail_resp',
146 | 'showExternalParam' => false
147 | ],
148 | 'reset_form' => [
149 | 'title' => 'martin.forms::lang.components.shared.reset_form.title',
150 | 'description' => 'martin.forms::lang.components.shared.reset_form.description',
151 | 'type' => 'checkbox',
152 | 'group' => 'martin.forms::lang.components.shared.group_settings',
153 | 'showExternalParam' => false
154 | ],
155 | 'redirect' => [
156 | 'title' => 'martin.forms::lang.components.shared.redirect.title',
157 | 'description' => 'martin.forms::lang.components.shared.redirect.description',
158 | 'type' => 'string',
159 | 'group' => 'martin.forms::lang.components.shared.group_settings',
160 | 'showExternalParam' => false
161 | ],
162 | 'inline_errors' => [
163 | 'title' => 'martin.forms::lang.components.shared.inline_errors.title',
164 | 'description' => 'martin.forms::lang.components.shared.inline_errors.description',
165 | 'type' => 'dropdown',
166 | 'options' => ['disabled' => 'martin.forms::lang.components.shared.inline_errors.disabled', 'display' => 'martin.forms::lang.components.shared.inline_errors.display', 'variable' => 'martin.forms::lang.components.shared.inline_errors.variable'],
167 | 'default' => 'disabled',
168 | 'group' => 'martin.forms::lang.components.shared.group_settings',
169 | 'showExternalParam' => false
170 | ],
171 | 'js_on_success' => [
172 | 'title' => 'martin.forms::lang.components.shared.js_on_success.title',
173 | 'description' => 'martin.forms::lang.components.shared.js_on_success.description',
174 | 'type' => 'text',
175 | 'group' => 'martin.forms::lang.components.shared.group_settings',
176 | 'showExternalParam' => false
177 | ],
178 | 'js_on_error' => [
179 | 'title' => 'martin.forms::lang.components.shared.js_on_error.title',
180 | 'description' => 'martin.forms::lang.components.shared.js_on_error.description',
181 | 'type' => 'text',
182 | 'group' => 'martin.forms::lang.components.shared.group_settings',
183 | 'showExternalParam' => false
184 | ],
185 | 'allowed_fields' => [
186 | 'title' => 'martin.forms::lang.components.shared.allowed_fields.title',
187 | 'description' => 'martin.forms::lang.components.shared.allowed_fields.description',
188 | 'type' => 'stringList',
189 | 'group' => 'martin.forms::lang.components.shared.group_security',
190 | 'showExternalParam' => false
191 | ],
192 | 'sanitize_data' => [
193 | 'title' => 'martin.forms::lang.components.shared.sanitize_data.title',
194 | 'description' => 'martin.forms::lang.components.shared.sanitize_data.description',
195 | 'type' => 'dropdown',
196 | 'options' => ['disabled' => 'martin.forms::lang.components.shared.sanitize_data.disabled', 'htmlspecialchars' => 'martin.forms::lang.components.shared.sanitize_data.htmlspecialchars'],
197 | 'default' => 'disabled',
198 | 'group' => 'martin.forms::lang.components.shared.group_security',
199 | 'showExternalParam' => false
200 | ],
201 | 'anonymize_ip' => [
202 | 'title' => 'martin.forms::lang.components.shared.anonymize_ip.title',
203 | 'description' => 'martin.forms::lang.components.shared.anonymize_ip.description',
204 | 'type' => 'dropdown',
205 | 'options' => ['disabled' => 'martin.forms::lang.components.shared.anonymize_ip.disabled', 'partial' => 'martin.forms::lang.components.shared.anonymize_ip.partial', 'full' => 'martin.forms::lang.components.shared.anonymize_ip.full'],
206 | 'default' => 'disabled',
207 | 'group' => 'martin.forms::lang.components.shared.group_security',
208 | 'showExternalParam' => false
209 | ],
210 | 'recaptcha_enabled' => [
211 | 'title' => 'martin.forms::lang.components.shared.recaptcha_enabled.title',
212 | 'description' => 'martin.forms::lang.components.shared.recaptcha_enabled.description',
213 | 'type' => 'checkbox',
214 | 'group' => 'martin.forms::lang.components.shared.group_recaptcha',
215 | 'showExternalParam' => false
216 | ],
217 | 'recaptcha_theme' => [
218 | 'title' => 'martin.forms::lang.components.shared.recaptcha_theme.title',
219 | 'description' => 'martin.forms::lang.components.shared.recaptcha_theme.description',
220 | 'type' => 'dropdown',
221 | 'options' => ['light' => 'martin.forms::lang.components.shared.recaptcha_theme.light', 'dark' => 'martin.forms::lang.components.shared.recaptcha_theme.dark'],
222 | 'default' => 'light',
223 | 'group' => 'martin.forms::lang.components.shared.group_recaptcha',
224 | 'showExternalParam' => false
225 | ],
226 | 'recaptcha_type' => [
227 | 'title' => 'martin.forms::lang.components.shared.recaptcha_type.title',
228 | 'description' => 'martin.forms::lang.components.shared.recaptcha_type.description',
229 | 'type' => 'dropdown',
230 | 'options' => ['image' => 'martin.forms::lang.components.shared.recaptcha_type.image', 'audio' => 'martin.forms::lang.components.shared.recaptcha_type.audio'],
231 | 'default' => 'image',
232 | 'group' => 'martin.forms::lang.components.shared.group_recaptcha',
233 | 'showExternalParam' => false
234 | ],
235 | 'recaptcha_size' => [
236 | 'title' => 'martin.forms::lang.components.shared.recaptcha_size.title',
237 | 'description' => 'martin.forms::lang.components.shared.recaptcha_size.description',
238 | 'type' => 'dropdown',
239 | 'options' => [
240 | 'normal' => 'martin.forms::lang.components.shared.recaptcha_size.normal',
241 | 'compact' => 'martin.forms::lang.components.shared.recaptcha_size.compact',
242 | 'invisible' => 'martin.forms::lang.components.shared.recaptcha_size.invisible',
243 | ],
244 | 'default' => 'normal',
245 | 'group' => 'martin.forms::lang.components.shared.group_recaptcha',
246 | 'showExternalParam' => false
247 | ],
248 | 'skip_database' => [
249 | 'title' => 'martin.forms::lang.components.shared.skip_database.title',
250 | 'description' => 'martin.forms::lang.components.shared.skip_database.description',
251 | 'type' => 'checkbox',
252 | 'group' => 'martin.forms::lang.components.shared.group_advanced',
253 | 'showExternalParam' => false
254 | ],
255 | 'emails_date_format' => [
256 | 'title' => 'martin.forms::lang.components.shared.emails_date_format.title',
257 | 'description' => 'martin.forms::lang.components.shared.emails_date_format.description',
258 | 'default' => 'Y-m-d',
259 | 'group' => 'martin.forms::lang.components.shared.group_advanced',
260 | 'showExternalParam' => false
261 | ],
262 | ];
263 | }
264 | }
265 |
--------------------------------------------------------------------------------
/classes/UnreadRecords.php:
--------------------------------------------------------------------------------
1 | count();
12 | return ($unread > 0) ? $unread : null;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/components/EmptyForm.php:
--------------------------------------------------------------------------------
1 | 'martin.forms::lang.components.empty_form.name',
12 | 'description' => 'martin.forms::lang.components.empty_form.description',
13 | ];
14 | }
15 |
16 | }
17 |
18 | ?>
--------------------------------------------------------------------------------
/components/FilePondForm.php:
--------------------------------------------------------------------------------
1 | 'martin.forms::lang.components.filepond_form.name',
13 | 'description' => 'martin.forms::lang.components.filepond_form.description',
14 | ];
15 | }
16 |
17 | public function defineProperties()
18 | {
19 | $local = [
20 | 'mail_uploads' => [
21 | 'title' => 'martin.forms::lang.components.shared.mail_uploads.title',
22 | 'description' => 'martin.forms::lang.components.shared.mail_uploads.description',
23 | 'type' => 'checkbox',
24 | 'default' => false,
25 | 'group' => 'martin.forms::lang.components.shared.group_mail',
26 | 'showExternalParam' => false
27 | ],
28 | 'uploader_enable' => [
29 | 'title' => 'martin.forms::lang.components.shared.uploader_enable.title',
30 | 'description' => 'martin.forms::lang.components.shared.uploader_enable.description',
31 | 'default' => false,
32 | 'type' => 'checkbox',
33 | 'group' => 'martin.forms::lang.components.shared.group_uploader',
34 | 'showExternalParam' => false,
35 | ],
36 | ];
37 |
38 | return array_merge(parent::defineProperties(), $local);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/components/GenericForm.php:
--------------------------------------------------------------------------------
1 | 'martin.forms::lang.components.generic_form.name',
12 | 'description' => 'martin.forms::lang.components.generic_form.description',
13 | ];
14 | }
15 |
16 | }
17 |
18 | ?>
--------------------------------------------------------------------------------
/components/emptyform/default.htm:
--------------------------------------------------------------------------------
1 | Here goes your custom form
2 | Override HTML by creating a new partial called default.htm (more info here)
3 | You can copy/paste this basic template:
4 |
5 | <form data-request="{{ __SELF__ }}::onFormSubmit">
6 | {{ form_token() }}
7 | <div id="{{ __SELF__ }}_forms_flash"></div>
8 | <!-- YOUR FORM FIELDS -->
9 | {% partial '@recaptcha' %}
10 | <!-- SUBMIT BUTTON -->
11 | </form>
12 |
13 |
--------------------------------------------------------------------------------
/components/filepondform/default.htm:
--------------------------------------------------------------------------------
1 | {% if __SELF__.property('uploader_enable') == 0 %}
2 |
3 |
Warning
4 | Uploads are disabled.
5 | You need to explicitly enable this option on the component (this is a security measure).
6 |
7 | {% endif %}
8 |
9 | {{ form_ajax(__SELF__ ~ '::onFormSubmit') }}
10 |
11 |
12 |
13 |
14 |
Apply for online job
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | {% if __SELF__.property('uploader_enable') == 1 %}
47 |
48 |
Upload your resume
49 |
50 |
51 |
52 |
53 | {% endif %}
54 |
55 |
56 | {% partial '@recaptcha' %}
57 |
58 |
59 | {{ form_submit() }}
60 |
61 | {{ form_close() }}
62 |
63 | {% partial '@filepond' %}
64 |
--------------------------------------------------------------------------------
/components/genericform/default.htm:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/components/partials/filepond.htm:
--------------------------------------------------------------------------------
1 | {% if __SELF__.property('uploader_enable') == 1 %}
2 |
3 |
4 |
5 |
6 |
43 |
44 |
49 | {% endif %}
50 |
--------------------------------------------------------------------------------
/components/partials/flash.htm:
--------------------------------------------------------------------------------
1 | {% spaceless %}
2 |
3 |
4 |
5 |
6 | {% if title %}
7 |
{{ title }}
8 | {% endif %}
9 |
10 | {% if content %}
11 |
{{ content }}
12 | {% endif %}
13 |
14 | {% if list %}
15 |
16 | {% for item in list %}
17 | - {{ item }}
18 | {% endfor %}
19 |
20 | {% endif %}
21 |
22 |
23 |
24 | {% if jscript %}
25 |
31 | {% endif %}
32 |
33 | {% endspaceless %}
34 |
--------------------------------------------------------------------------------
/components/partials/js/recaptcha.htm:
--------------------------------------------------------------------------------
1 | resetReCaptcha('{{ __SELF__ }}');
--------------------------------------------------------------------------------
/components/partials/js/reset-form.htm:
--------------------------------------------------------------------------------
1 | $('{{ id }}').parents('form')[0].reset();
--------------------------------------------------------------------------------
/components/partials/recaptcha.htm:
--------------------------------------------------------------------------------
1 | {% if (recaptcha_enabled) %}
2 |
3 | {% elseif (recaptcha_misconfigured) %}
4 | {{ recaptcha_warn }}
5 | {% endif %}
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "martin/wn-forms-plugin",
3 | "type": "winter-plugin",
4 | "description": "Create easy (and almost magic) AJAX forms",
5 | "keywords": ["winter", "cms", "plugin", "forms", "magic forms"],
6 | "homepage": "https://github.com/skydiver/wn-magic-forms/",
7 | "license": "MIT"
8 | }
9 |
--------------------------------------------------------------------------------
/controllers/Exports.php:
--------------------------------------------------------------------------------
1 | pageTitle = e(trans('martin.forms::lang.controllers.exports.title'));
31 | $this->create('frontend');
32 | }
33 |
34 | public function csv()
35 | {
36 |
37 | $records = Record::orderBy('created_at');
38 |
39 | // FILTER GROUPS
40 | if (!empty($groups = post('Record.filter_groups'))) {
41 | $records->whereIn('group', $groups);
42 | }
43 |
44 | // FILTER DATE
45 | if (!empty($date_after = post('Record.filter_date_after'))) {
46 | $records->whereDate('created_at', '>=', $date_after);
47 | }
48 |
49 | // FILTER DATE
50 | if (!empty($date_before = post('Record.filter_date_before'))) {
51 | $records->whereDate('created_at', '<=', $date_before);
52 | }
53 |
54 | // FILTER DELETED
55 | if (post('Record.options_deleted')) {
56 | $records->withTrashed();
57 | }
58 |
59 | // CREATE CSV
60 | $csv = CsvWriter::createFromFileObject(new SplTempFileObject());
61 |
62 | // CHANGE DELIMTER
63 | if (post('Record.options_delimiter')) {
64 | $csv->setDelimiter(';');
65 | }
66 |
67 | // SET UTF-8 Output
68 | if (post('Record.options_utf')) {
69 | $csv->setOutputBOM(AbstractCsv::BOM_UTF8);
70 | }
71 |
72 | // CSV HEADERS
73 | $headers = [];
74 |
75 | // METADATA HEADERS
76 | if (post('Record.options_metadata')) {
77 | $meta_headers = [
78 | e(trans('martin.forms::lang.controllers.records.columns.id')),
79 | e(trans('martin.forms::lang.controllers.records.columns.group')),
80 | e(trans('martin.forms::lang.controllers.records.columns.ip')),
81 | e(trans('martin.forms::lang.controllers.records.columns.created_at')),
82 | ];
83 | $headers = array_merge($meta_headers, $headers);
84 | }
85 |
86 | // ADD STORED FIELDS AS HEADER ROW IN CSV
87 | $filteredRecords = $records->get();
88 | $record = $filteredRecords->first();
89 | $headers = array_merge($headers, array_keys($record->form_data_arr));
90 |
91 | // ADD FILES HEADER
92 | if (post('Record.options_files')) {
93 | $headers[] = e(trans('martin.forms::lang.controllers.records.columns.files'));
94 | }
95 |
96 | // ADD HEADERS
97 | $csv->insertOne($headers);
98 |
99 | // WRITE CSV LINES
100 | foreach ($records->get() as $row) {
101 | $data = (array) json_decode($row['form_data']);
102 |
103 | // IF DATA IS ARRAY CONVERT TO JSON STRING
104 | foreach ($data as $field => $value) {
105 | if (is_array($value) || is_object($value)) {
106 | $data[$field] = json_encode($value);
107 | }
108 | }
109 |
110 | // ADD METADATA IF NEEDED
111 | if (post('Record.options_metadata')) {
112 | array_unshift($data, $row['id'], $row['group'], $row['ip'], $row['created_at']);
113 | }
114 |
115 | // ADD ATTACHED FILES
116 | if (post('Record.options_files') && $row->files->count() > 0) {
117 | $data[] = $row->filesList();
118 | }
119 |
120 | $csv->insertOne($data);
121 | }
122 |
123 | // RETURN CSV
124 | $csv->output('records.csv');
125 | exit();
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/controllers/Records.php:
--------------------------------------------------------------------------------
1 | unread = false;
42 | $record->save();
43 | $this->addCss('/plugins/martin/forms/assets/css/records.css');
44 | $this->pageTitle = e(trans('martin.forms::lang.controllers.records.view_title'));
45 | $this->vars['record'] = $record;
46 | }
47 |
48 | public function onDelete()
49 | {
50 | if (($checkedIds = post('checked')) && is_array($checkedIds) && count($checkedIds)) {
51 | Record::whereIn('id', $checkedIds)->delete();
52 | }
53 |
54 | $counter = UnreadRecords::getTotal();
55 |
56 | return [
57 | 'counter' => ($counter != null) ? $counter : 0,
58 | 'list' => $this->listRefresh()
59 | ];
60 | }
61 |
62 | public function onDeleteSingle()
63 | {
64 | $id = post('id');
65 | $record = Record::find($id);
66 |
67 | if ($record) {
68 | $record->delete();
69 | Flash::success(e(trans('martin.forms::lang.controllers.records.deleted')));
70 | } else {
71 | Flash::error(e(trans('martin.forms::lang.controllers.records.error')));
72 | }
73 |
74 | return Redirect::to(Backend::url('martin/forms/records'));
75 | }
76 |
77 | public function download($record_id, $file_id)
78 | {
79 | $record = Record::findOrFail($record_id);
80 | $file = $record->files->find($file_id);
81 |
82 | if (!$file) {
83 | App::abort(404, Lang::get('backend::lang.import_export.file_not_found_error'));
84 | }
85 |
86 | return response()->download($file->getLocalPath(), $file->getFilename());
87 | exit();
88 | }
89 |
90 | public function listInjectRowClass($record, $definition = null)
91 | {
92 | if ($record->unread) {
93 | return 'new';
94 | }
95 | }
96 |
97 | public function onReadState()
98 | {
99 | if (($checkedIds = post('checked')) && is_array($checkedIds) && count($checkedIds)) {
100 | $unread = (post('state') == 'read') ? 0 : 1;
101 | Record::whereIn('id', $checkedIds)->update(['unread' => $unread]);
102 | }
103 |
104 | $counter = UnreadRecords::getTotal();
105 |
106 | return [
107 | 'counter' => ($counter != null) ? $counter : 0,
108 | 'list' => $this->listRefresh()
109 | ];
110 | }
111 |
112 | public function onGDPRClean()
113 | {
114 | if ($this->user->hasPermission(['martin.forms.gdpr_cleanup'])) {
115 | GDPR::cleanRecords();
116 | Flash::success(e(trans('martin.forms::lang.controllers.records.alerts.gdpr_success')));
117 | } else {
118 | Flash::error(e(trans('martin.forms::lang.controllers.records.alerts.gdpr_perms')));
119 | }
120 |
121 | $counter = UnreadRecords::getTotal();
122 |
123 | return [
124 | 'counter' => ($counter != null) ? $counter : 0,
125 | 'list' => $this->listRefresh()
126 | ];
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/controllers/exports/config_form.yaml:
--------------------------------------------------------------------------------
1 | form : $/martin/forms/models/export/fields.yaml
2 | modelClass: Martin\Forms\Models\Record
--------------------------------------------------------------------------------
/controllers/exports/index.htm:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | Backend::url('martin/forms/exports/csv/')]) ?>
9 |
10 | formRender() ?>
11 |
12 |
13 | = Form::submit(e(trans('martin.forms::lang.controllers.exports.title')), ['class' => 'btn btn-primary']) ?>
14 |
15 |
16 |
--------------------------------------------------------------------------------
/controllers/records/config_filter.yaml:
--------------------------------------------------------------------------------
1 | scopes:
2 |
3 | group:
4 | label : martin.forms::lang.controllers.records.columns.group
5 | type : group
6 | modelClass: Martin\Forms\Models\Record
7 | options : filterGroups
8 | conditions: "`group` in (:filtered)"
9 |
10 | created_at:
11 | label : martin.forms::lang.controllers.records.columns.created_at
12 | type : daterange
13 | conditions: created_at >= ':after' AND created_at <= ':before'
--------------------------------------------------------------------------------
/controllers/records/config_list.yaml:
--------------------------------------------------------------------------------
1 | list : $/martin/forms/models/record/columns.yaml
2 | modelClass : Martin\Forms\Models\Record
3 | title : martin.forms::lang.controllers.records.title
4 | recordUrl : martin/forms/records/view/:id
5 | noRecordsMessage: backend::lang.list.no_records
6 | recordsPerPage : 20
7 | showSetup : true
8 | showSorting : true
9 | showCheckboxes : true
10 | filter : config_filter.yaml
11 |
12 | defaultSort:
13 | column : created_at
14 | direction: desc
15 |
16 | toolbar:
17 | buttons: partials/list_toolbar
18 | search :
19 | prompt: backend::lang.list.search_prompt
--------------------------------------------------------------------------------
/controllers/records/index.htm:
--------------------------------------------------------------------------------
1 | = $this->listRender() ?>
--------------------------------------------------------------------------------
/controllers/records/partials/_action_button.htm:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/controllers/records/partials/_list_toolbar.htm:
--------------------------------------------------------------------------------
1 |
2 |
3 | makePartial('$/martin/forms/controllers/records/partials/_action_button.htm', [
5 | 'type' => 'danger',
6 | 'icon' => 'oc-icon-trash',
7 | 'label' => e(trans('backend::lang.list.delete_selected')),
8 | 'onclick' => "$(this).data('request-data', { checked: $('.control-list').listWidget('getChecked') })",
9 | 'needs_selected' => true,
10 | 'request' => 'onDelete',
11 | 'request_confirm' => e(trans('backend::lang.list.delete_selected_confirm')),
12 | 'request_success' => "
13 | $('#records-toolbar').find('button').prop('disabled', true);
14 | $.oc.flashMsg({
15 | 'text': '" . e(trans('backend::lang.list.delete_selected_success')) . "',
16 | 'class': 'success',
17 | 'interval': 3
18 | });
19 | $.oc.sideNav.setCounter('forms/records', data.counter);
20 | $('#Lists').html(data.list['#Lists']);
21 | ",
22 | ]);
23 | ?>
24 |
25 |
26 | makePartial('$/martin/forms/controllers/records/partials/_action_button.htm', [
28 | 'type' => 'default',
29 | 'icon' => 'oc-icon-eye-slash',
30 | 'label' => e(trans('martin.forms::lang.controllers.records.buttons.unread')),
31 | 'onclick' => "$(this).data('request-data', { state: 'unread', checked: $('.control-list').listWidget('getChecked') })",
32 | 'needs_selected' => true,
33 | 'request' => 'onReadState',
34 | 'request_confirm' => '',
35 | 'request_success' => "
36 | $('#records-toolbar').find('button').prop('disabled', true);
37 | $.oc.sideNav.setCounter('forms/records', data.counter);
38 | $('#Lists').html(data.list['#Lists']);
39 | ",
40 | ]);
41 |
42 | echo $this->makePartial('$/martin/forms/controllers/records/partials/_action_button.htm', [
43 | 'type' => 'default',
44 | 'icon' => 'oc-icon-eye',
45 | 'label' => e(trans('martin.forms::lang.controllers.records.buttons.read')),
46 | 'onclick' => "$(this).data('request-data', { state: 'read', checked: $('.control-list').listWidget('getChecked') })",
47 | 'needs_selected' => true,
48 | 'request' => 'onReadState',
49 | 'request_confirm' => '',
50 | 'request_success' => "
51 | $('#records-toolbar').find('button').prop('disabled', true);
52 | $.oc.sideNav.setCounter('forms/records', data.counter);
53 | $('#Lists').html(data.list['#Lists']);
54 | ",
55 | ]);
56 | ?>
57 |
58 |
59 | makePartial('$/martin/forms/controllers/records/partials/_action_button.htm', [
62 | 'type' => 'danger',
63 | 'icon' => 'oc-icon-history',
64 | 'label' => e(trans('martin.forms::lang.controllers.records.buttons.gdpr_clean')),
65 | 'request' => 'onGDPRClean',
66 | 'request_confirm' => e(trans('martin.forms::lang.controllers.records.alerts.gdpr_confirm')),
67 | 'request_success' => "
68 | $('#records-toolbar').find('button').blur();
69 | $.oc.sideNav.setCounter('forms/records', data.counter);
70 | $('#Lists').html(data.list['#Lists']);
71 | ",
72 | ]);
73 | }
74 | ?>
75 |
76 |
--------------------------------------------------------------------------------
/controllers/records/partials/_view_toolbar.htm:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/controllers/records/view.htm:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - Record #id ?>
5 |
6 |
7 |
8 | = $this->makePartial('partials/view_toolbar') ?>
9 |
10 | Record #id ?>
11 |
12 |
13 | form_data_arr as $label => $value): ?>
14 |
15 | : |
16 |
17 |
18 |
21 |
22 |
23 |
24 | |
25 |
26 |
27 | files) > 0): ?>
28 |
29 | Attached Files: |
30 |
31 |
41 | |
42 |
43 |
44 |
45 |
46 |
47 |
: group ?>
48 |
: ip ?>
49 |
: created_at ?>
50 |
--------------------------------------------------------------------------------
/lang/de/lang.php:
--------------------------------------------------------------------------------
1 | [
6 | 'name' => 'Magic Forms',
7 | 'description' => 'einfaches Erstellen von AJAX Formularen'
8 | ],
9 |
10 | 'menu' => [
11 | 'label' => 'Magic Forms',
12 | 'records' => ['label' => 'Records'],
13 | 'exports' => ['label' => 'Export'],
14 | 'settings' => 'Konfigurieren der plugin parameter',
15 | ],
16 |
17 | 'controllers' => [
18 | 'records' => [
19 | 'title' => 'Einträge anzeigen',
20 | 'view_title' => 'Details zum Eintrag',
21 | 'error' => 'Eintrag nicht gefunden',
22 | 'deleted' => 'Eintrag erfolgreich gelöscht',
23 | 'columns' => [
24 | 'id' => 'Eintrag ID',
25 | 'group' => 'Gruppe',
26 | 'ip' => 'IP Adresse',
27 | 'form_data' => 'Gespeicherte Felder',
28 | 'files' => 'Anhänge',
29 | 'created_at' => 'Erstellt',
30 | ],
31 | 'buttons' => [
32 | 'read' => 'Als gelesen markieren',
33 | 'unread' => 'Als ungelesen markieren',
34 | 'gdpr_clean' => 'DSVGO Aufräumung',
35 | ],
36 | 'alerts' => [
37 | 'gdpr_confirm' => "Sind sie sicher dass Sie alte Einträge aufräumen wollen?\nDiese Aktion kann nicht wiederrufen werden!",
38 | 'gdpr_success' => 'DSVGO Aufräumung wurde erfolgreich ausgeführt',
39 | 'gdpr_perms' => 'Sie haben keine Berechtigung für diese Funktion.',
40 | ],
41 | ],
42 | 'exports' => [
43 | 'title' => 'Einträge exportieren',
44 | 'breadcrumb' => 'Exportieren',
45 | 'filter_section' => '1. Einträge filtern',
46 | 'filter_type' => 'Alle Einträge exportieren',
47 | 'filter_groups' => 'Gruppen',
48 | 'filter_date_after' => 'Nach dem Datum',
49 | 'filter_date_before' => 'Vor dem Datum',
50 | 'options_section' => '2. Extra Optionen',
51 | 'options_metadata' => 'Inklusive Metadaten',
52 | 'options_metadata_com' => 'Exportiere die Einträge mit Metadaten (Eintrag ID, Gruppe, IP, Erstellungsdatum)',
53 | 'options_deleted' => 'Inklusive gelöschter Einträge',
54 | 'options_delimiter' => 'Alternatives Trennzeichen benutzen',
55 | 'options_delimiter_com' => 'Semikolon als Trennzeichen',
56 | 'options_utf' => 'Enkodierung in UTF8',
57 | 'options_utf_com' => 'Enkodierung Ihrer CSV-Datei im UTF-8 Format für die Unterstützung von Umlauten und Sonderzeichen.',
58 | ],
59 | ],
60 |
61 | 'components' => [
62 | 'generic_form' => [
63 | 'name' => 'Generisches AJAX Formular',
64 | 'description' => 'Mit Standardeinstellungen rendered ein generisches Formular; Überschreib Komponenten HTML mit deinen eigenen Feldern.',
65 | ],
66 | 'upload_form' => [
67 | 'name' => 'Upload AJAX Formular [BETA]',
68 | 'description' => 'Zeigt wie man Dateiupload in deinem Formular implementiert.',
69 | ],
70 | 'empty_form' => [
71 | 'name' => 'Leeres AJAX Formular',
72 | 'description' => 'Erstellt eine leere Vorlage für dein individuelles Formular; Überschreib Komponenten HTML.',
73 | ],
74 | 'shared' => [
75 | 'csrf_error' => 'Formular Seitzung abgelaufen! Bitte die Seite neuladen.',
76 | 'recaptcha_warn' => 'Warnung: reCAPTCHA ist nicht ordentlich konfiguriert. Bitte, gehe zum Backend > Einstellungen > CMS > Magic Forms um reCAPTCHA zu kofigurieren.',
77 | 'group_validation' => 'Formular Validierung',
78 | 'group_messages' => 'Flash Benachrichtung',
79 | 'group_mail' => 'Benachrichtigungen Einstellungen',
80 | 'group_mail_resp' => 'Automatische Antwort Einstellungen',
81 | 'group_settings' => 'Weitere Einstellungen',
82 | 'group_security' => 'Sicherheit',
83 | 'group_recaptcha' => 'reCAPTCHA Einstellungen',
84 | 'group_advanced' => 'Fortgeschrittene Einstellungen',
85 | 'group_uploader' => 'Uploader Einstellungen',
86 | 'validation_req' => 'Die Eigenschaft wird benötigt',
87 | 'group' => ['title' => 'Gruppe' , 'description' => 'Organisiere deine Formulare durch eigene Gruppennamen. Diese Option ist für das Exportieren der Daten sehr praktischt.'],
88 | 'rules' => ['title' => 'Regeln' , 'description' => 'Erstelle eigene Regeln Mithilfe der Laravel Validierungsfunktion'],
89 | 'rules_messages' => ['title' => 'Regeln Benachrichtigungen' , 'description' => 'Erstelle eigene Benachrichtigungen Mithilfe der Laravel Validierungsfunktion'],
90 | 'custom_attributes' => ['title' => 'Eigene Attribute' , 'description' => 'Erstelle eigene Attribute Mithilfe der Laravel Validierungsfunktion'],
91 | 'messages_success' => ['title' => 'Erfolg' , 'description' => 'Nachricht bei erfolgreicher Übermittlung des Formulars', 'default' => 'Your form was successfully submitted' ],
92 | 'messages_errors' => ['title' => 'Fehler' , 'description' => 'Nachricht bei Fehlern während der Eingabe der Formulardaten' , 'default' => 'There were errors with your submission'],
93 | 'messages_partial' => ['title' => 'Benutze eigene Partial' , 'description' => 'Überschreibe Flash-Nachrichten mit deinem eigenem Partial in deinem Theme'],
94 | 'mail_enabled' => ['title' => 'Sende Benachrichtigungen' , 'description' => 'Sende eine Benachrichtigungsmail nach jeder erfolgreichen Übertragung.'],
95 | 'mail_subject' => ['title' => 'Betreff' , 'description' => 'Überschreibe die standard E-Mail Betreffszeile'],
96 | 'mail_recipients' => ['title' => 'Empfänger' , 'description' => 'E-Mail Empfänger eintragen (Füge eine E-Mail Adresse pro Zeile ein)'],
97 | 'mail_bcc' => ['title' => 'BCC' , 'description' => 'Sende blind carbon copy (BCC) zur folgenden Empfängern (Füge eine E-Mail Adresse per Zeile ein)'],
98 | 'mail_replyto' => ['title' => 'Empfänger E-Mail Feld', 'description' => 'Formular Feld das die E-Mail Adresse enthält. Diese Adresse wird als Empfänger Adresse benutzt.'],
99 | 'mail_template' => ['title' => 'E-Mail Vorlage' , 'description' => 'Benutze eigene E-Mail Vorlagen. Gebe einen Vorlagen Code ein wie z.B. "martin.forms::mail.notification" (Kann im Einstellungen der E-Mail Vorlagen im Backend gefunden werden). Leer lassen für Standardvorlage.'],
100 | 'mail_uploads' => ['title' => 'Sende Uploads' , 'description' => 'Sende Uploads als Anhang'],
101 | 'mail_resp_enabled' => ['title' => 'Sende Auto-Antwort' , 'description' => 'Sende eine automatische Antwort E-Mail zu der Person die dein Formular ausgfüllt.'],
102 | 'mail_resp_field' => ['title' => 'E-Mail Feld' , 'description' => 'Formular Feld das die E-Mail Adresse enthät für die automatische Antwort'],
103 | 'mail_resp_from' => ['title' => 'Absender Adresse' , 'description' => 'Absender E-Mail Adresse was benutzt werden soll (z.B. noreply@yourcompany.com)'],
104 | 'mail_resp_subject' => ['title' => 'Betreff' , 'description' => 'Überschreibe standard E-Mail Betreffszeile'],
105 | 'reset_form' => ['title' => 'Resete Formular' , 'description' => 'Resete das Formular nach erfolgreichen Übertragung'],
106 | 'redirect' => ['title' => 'Umleitung bei Erfolg', 'description' => 'URL-Umleitung nach erfoglreichen Übertragung'],
107 | 'inline_errors' => ['title' => 'Inline Fehler' , 'description' => 'Zeige Inline-Fehler an. Die Funktion braucht Extra-Code. Siehe in der Dokumentation für mehr Informationen.', 'disabled' => 'Deaktiviert', 'display' => 'Zeige Fehler', 'variable' => 'JS Variable'],
108 | 'js_on_success' => ['title' => 'JS bei Erfolg' , 'description' => 'Führe eigenen JavaScript-Code aus wenn das Formular erfolgreich übertragen wurde. Keine Script Tags benutzen!'],
109 | 'js_on_error' => ['title' => 'JS bei Fehlern' , 'description' => 'Führe eigenen JavaScript-Code aus wenn das Formular nicht validiert werden kann. Keine Script Tags benutzen!'],
110 | 'allowed_fields' => ['title' => 'Erlaubte Felder' , 'description' => 'Festlegen welche Felder gefiltert und gespeichert werden sollen. (Füge ein Feld per Zeile ein)'],
111 | 'anonymize_ip' => ['title' => 'IP-Anonymisierung' , 'description' => 'IP-Adresse nicht speichern.', 'full' => 'Voll', 'partial' => 'Teilanonymisierung', 'disabled' => 'Deaktiviert'],
112 | 'sanitize_data' => ['title' => 'Bereinigung der Formulardaten' , 'description' => 'Bereinige die Formulardaten und speichere das Ergebnis in der Datenbank', 'disabled' => 'deaktiviert', 'htmlspecialchars' => 'Benutzer htmlspecialchars'],
113 | 'recaptcha_enabled' => ['title' => 'Aktiviere reCAPTCHA' , 'description' => 'reCAPTCHA-Widget in deinem Formular einfügen'],
114 | 'recaptcha_theme' => ['title' => 'Theme' , 'description' => 'Farbschema des Widgets', 'light' => 'Hell' , 'dark' => 'Dunkel'],
115 | 'recaptcha_type' => ['title' => 'Typ' , 'description' => 'reCAPTCHA Typ festlegen' , 'image' => 'Bild' , 'audio' => 'Audio'],
116 | 'recaptcha_size' => [
117 | 'title' => 'Größe',
118 | 'description' => 'Die Größe des Widgets',
119 | 'normal' => 'Normal',
120 | 'compact' => 'Kompakt',
121 | 'invisible' => 'Unsichtbar',
122 | ],
123 | 'skip_database' => ['title' => 'Überspringe DB' , 'description' => 'Keine Speicherung der Formulardaten in der Datenbank. Nützlich wenn man Events mit einem eigenem Plugin nutzen möchte.'],
124 | 'emails_date_format' => ['title' => 'Datum Formatierung in E-Mails', 'description' => 'Ändere die Datumformatierung die dann in E-Mails verwendet wird.'],
125 | 'uploader_enable' => ['title' => 'Erlaube Uploads' , 'description' => 'Aktiviere Datei-Upload. Diese Option muss aufgrund der Sicherheitseinstellungen explizit aktiviert werden.'],
126 | 'uploader_multi' => ['title' => 'Merhfachdateien' , 'description' => 'Erlaube Mehrfachdateien-Uploads'],
127 | 'uploader_pholder' => ['title' => 'Platzhalter Text' , 'description' => 'Platzhalter Text der angezeigt wird so lange keine Datei hochgeladen wurde', 'default' => 'Click or drag files to upload'],
128 | 'uploader_maxsize' => ['title' => 'Dateigröße Limitierung' , 'description' => 'Die maximale Dateigröße die hochgeladen werden kann in Megabytes'],
129 | 'uploader_types' => ['title' => 'Erlaubte Datei-Typen' , 'description' => 'Erlaubte Dateiendungen oder ein Stern (*) für alle Typen (Füge eine Dateiendung per Zeile ein)'],
130 | 'uploader_remFile' => ['title' => 'Popup Text Datei entfernen' , 'description' => 'Text Platzhalter für die Abfrage, wenn eine Datei vor dem Upload entfernt wird', 'default' => 'Are you sure ?'],
131 | ]
132 | ],
133 |
134 | 'settings' => [
135 | 'tabs' => ['general' => 'Allgemein', 'recaptcha' => 'reCAPTCHA', 'gdpr' => 'DSVGO'],
136 | 'section_flash_messages' => 'Flash Nachrichten',
137 | 'global_messages_success' => ['label' => 'Globale Erfolgsnachricht', 'comment' => '(Diese Einstellung kann aus der Komponente heraus überschrieben werden)', 'default' => 'Your form was successfully submitted'],
138 | 'global_messages_errors' => ['label' => 'Globale Fehlernachricht' , 'comment' => '(Diese Einstellung kann aus der Komponente heraus überschrieben werden)', 'default' => 'There were errors with your submission'],
139 | 'plugin_help' => 'Die Plugindokumentation erreichst Du über die GitHub repo:',
140 | 'global_hide_button' => 'Navigationsobjekt vestecken',
141 | 'global_hide_button_desc' => 'Praktisch, wenn Du eigene Events mit einem eigenem Plugin nutzen möchtest.',
142 | 'section_recaptcha' => 'reCAPTCHA Einstellungen',
143 | 'recaptcha_site_key' => 'Site key',
144 | 'recaptcha_secret_key' => 'Secret key',
145 | 'gdpr_help_title' => 'Information',
146 | 'gdpr_help_comment' => 'Das neue EU-DSVGO Gesetz in Europa besagt dass die Einträge nicht mehr unendlich aufbewahrt werden dürfen. Diese müssen je nach nach Bedarf automatisiert gelöscht werden.',
147 | 'gdpr_enable' => 'Aktiviere EU-DSVGO konforme Aufräumung',
148 | 'gdpr_days' => 'Behalte die Einträge für die Anzahl an: X Tagen',
149 | ],
150 |
151 | 'permissions' => [
152 | 'tab' => 'Magic Forms',
153 | 'access_records' => 'Zugriff auf gespeicherte Formular-Einsendedaten',
154 | 'access_exports' => 'Zugriff für das Exportieren der gespeicherten Formular-Einsendedaten',
155 | 'access_settings' => 'Zugriff auf Modul-Konfiguration',
156 | 'gdpr_cleanup' => 'Führe EU-DSVGO Aufräumung der Datenbank durch.',
157 | ],
158 |
159 | 'mails' => [
160 | 'form_notification' => ['description' => 'Benachrichtung nach erfolgreicher Einsendung der Formulardaten.'],
161 | 'form_autoresponse' => ['description' => 'Automatische Antwort nach erfolgreicher Einsendung der Formulardaten.'],
162 | ],
163 |
164 | 'validation' => [
165 | 'recaptcha_error' => 'Kann das reCAPTCHA Feld nicht validieren.'
166 | ],
167 |
168 | 'classes' => [
169 | 'GDPR' => [
170 | 'alert_gdpr_disabled' => 'EU-DSVGO Einstellungen sind deaktiviert.',
171 | 'alert_invalid_gdpr' => 'Ungültige Einstellung des EU-DSVGO Aufräumvorgangs Intervals nach X Tagen.',
172 | ]
173 | ]
174 |
175 | ];
176 |
177 | ?>
178 |
--------------------------------------------------------------------------------
/lang/en/lang.php:
--------------------------------------------------------------------------------
1 | [
6 | 'name' => 'Magic Forms',
7 | 'description' => 'Create easy AJAX forms'
8 | ],
9 |
10 | 'menu' => [
11 | 'label' => 'Magic Forms',
12 | 'records' => ['label' => 'Records'],
13 | 'exports' => ['label' => 'Export'],
14 | 'settings' => 'Configure plugin parameters',
15 | ],
16 |
17 | 'controllers' => [
18 | 'records' => [
19 | 'title' => 'View Records',
20 | 'view_title' => 'Record Details',
21 | 'error' => 'Record not found',
22 | 'deleted' => 'Record deleted successfully',
23 | 'columns' => [
24 | 'id' => 'Record ID',
25 | 'group' => 'Group',
26 | 'ip' => 'IP Address',
27 | 'form_data' => 'Stored Fields',
28 | 'files' => 'Attached Files',
29 | 'created_at' => 'Created',
30 | ],
31 | 'buttons' => [
32 | 'read' => 'Mark as Read',
33 | 'unread' => 'Mark as Unread',
34 | 'gdpr_clean' => 'GDPR Clean',
35 | ],
36 | 'alerts' => [
37 | 'gdpr_confirm' => "Are you sure you want to clean old records?\nThis action cannot be undone!",
38 | 'gdpr_success' => 'GDPR cleanup was executed successfully',
39 | 'gdpr_perms' => 'You don\'t have permission to this feature',
40 | ],
41 | ],
42 | 'exports' => [
43 | 'title' => 'Export Records',
44 | 'breadcrumb' => 'Export',
45 | 'filter_section' => '1. Filter records',
46 | 'filter_type' => 'Export all records',
47 | 'filter_groups' => 'Groups',
48 | 'filter_date_after' => 'Date after',
49 | 'filter_date_before' => 'Date before',
50 | 'options_section' => '2. Extra options',
51 | 'options_metadata' => 'Include metadata',
52 | 'options_metadata_com' => 'Export records with metadata (Record ID, group, IP, created date)',
53 | 'options_deleted' => 'Include deleted records',
54 | 'options_files' => 'Include attached files',
55 | 'options_files_com' => 'Only download URLs will be exported',
56 | 'options_delimiter' => 'Use alternative delimiter',
57 | 'options_delimiter_com' => 'Use semicolon as delimiter',
58 | 'options_utf' => 'Encode in UTF8',
59 | 'options_utf_com' => 'Encode your csv in UTF-8 to support non standard characters',
60 | ],
61 | ],
62 |
63 | 'components' => [
64 | 'generic_form' => [
65 | 'name' => 'Generic AJAX Form',
66 | 'description' => 'By default renders a generic form; override component HTML with your custom fields.',
67 | ],
68 | 'empty_form' => [
69 | 'name' => 'Empty AJAX Form',
70 | 'description' => 'Create a empty template for your custom form; override component HTML.',
71 | ],
72 | 'filepond_form' => [
73 | 'name' => 'Upload AJAX Form',
74 | 'description' => 'Sample AJAX form with upload capabilities using FilePond.',
75 | ],
76 | 'shared' => [
77 | 'csrf_error' => 'Form session expired! Please refresh the page.',
78 | 'recaptcha_warn' => 'Warning: reCAPTCHA is not properly configured. Please, goto Backend > Settings > CMS > Magic Forms and configure.',
79 | 'group_validation' => 'Form Validation',
80 | 'group_messages' => 'Flash Messages',
81 | 'group_mail' => 'Notifications Settings',
82 | 'group_mail_resp' => 'Auto-Response Settings',
83 | 'group_settings' => 'More Settings',
84 | 'group_security' => 'Security',
85 | 'group_recaptcha' => 'reCAPTCHA Settings',
86 | 'group_advanced' => 'Advanced Settings',
87 | 'group_uploader' => 'Uploader Settings',
88 | 'validation_req' => 'The property is required',
89 | 'group' => ['title' => 'Group', 'description' => 'Organize your forms with a custom group name. This option is useful when exporting data.'],
90 | 'rules' => ['title' => 'Rules', 'description' => 'Set your own rules using Laravel validation'],
91 | 'rules_messages' => ['title' => 'Rules Messages', 'description' => 'Use your own rules messages using Laravel validation'],
92 | 'custom_attributes' => ['title' => 'Custom Attributes', 'description' => 'Use your own custom attributes using Laravel validation'],
93 | 'messages_success' => ['title' => 'Success', 'description' => 'Message when the form is successfully submitted', 'default' => 'Your form was successfully submitted'],
94 | 'messages_errors' => ['title' => 'Errors', 'description' => 'Message when the form contains errors', 'default' => 'There were errors with your submission'],
95 | 'messages_partial' => ['title' => 'Use Custom Partial', 'description' => 'Override flash messages with your custom partial inside your theme'],
96 | 'mail_enabled' => ['title' => 'Send Notifications', 'description' => 'Send mail notifications on every form submitted'],
97 | 'mail_subject' => ['title' => 'Subject', 'description' => 'Override default email subject'],
98 | 'mail_recipients' => ['title' => 'Recipients', 'description' => 'Specify email recipients (add one address per line)'],
99 | 'mail_bcc' => ['title' => 'BCC', 'description' => 'Send blind carbon copy to email recipients (add one address per line)'],
100 | 'mail_replyto' => ['title' => 'ReplyTo Email Field', 'description' => 'Form field containing the email address of sender to be used as "ReplyTo"'],
101 | 'mail_template' => ['title' => 'Mail Template', 'description' => 'Use custom mail template. Specify template code like "martin.forms::mail.notification" (found on Settings, Mail templates). Leave empty to use default.'],
102 | 'mail_uploads' => ['title' => 'Send Uploads', 'description' => 'Send uploads as attachments'],
103 | 'mail_resp_enabled' => ['title' => 'Send Auto-Response', 'description' => 'Send an auto-response email to the person submitting the form'],
104 | 'mail_resp_field' => ['title' => 'Email Field', 'description' => 'Form field containing the email address of the recipient of auto-response'],
105 | 'mail_resp_name' => ['title' => 'Sender Name', 'description' => 'Name of auto-response email sender (e.g. John Doe)'],
106 | 'mail_resp_from' => ['title' => 'Sender Address', 'description' => 'Email address of auto-response email sender (e.g. noreply@yourcompany.com)'],
107 | 'mail_resp_subject' => ['title' => 'Subject', 'description' => 'Override default email subject'],
108 | 'reset_form' => ['title' => 'Reset Form', 'description' => 'Reset form after successfully submit'],
109 | 'redirect' => ['title' => 'Redirect on Success', 'description' => 'Redirect to URL on successfully submit.'],
110 | 'inline_errors' => ['title' => 'Inline errors', 'description' => 'Display inline errors. This requires extra code, check documentation for more info.', 'disabled' => 'Disabled', 'display' => 'Display errors', 'variable' => 'JS variable'],
111 | 'js_on_success' => ['title' => 'JS on Success', 'description' => 'Execute custom JavaScript code when the form was successfully submitted. Don\'t use script tags.'],
112 | 'js_on_error' => ['title' => 'JS on Error', 'description' => 'Execute custom JavaScript code when the form doesn\'t validate. Don\'t use script tags.'],
113 | 'allowed_fields' => ['title' => 'Allowed Fields', 'description' => 'Specify which fields should be filtered and stored (add one field name per line)'],
114 | 'anonymize_ip' => ['title' => 'Anonymize IP', 'description' => 'Don\'t store IP address', 'full' => 'Full', 'partial' => 'Partial', 'disabled' => 'Disabled'],
115 | 'sanitize_data' => ['title' => 'Sanitize form data', 'description' => 'Sanitize form data and save result on database', 'disabled' => 'Disabled', 'htmlspecialchars' => 'Use htmlspecialchars'],
116 | 'recaptcha_enabled' => ['title' => 'Enable reCAPTCHA', 'description' => 'Insert the reCAPTCHA widget on your form'],
117 | 'recaptcha_theme' => ['title' => 'Theme', 'description' => 'The color theme of the widget', 'light' => 'Light', 'dark' => 'Dark'],
118 | 'recaptcha_type' => ['title' => 'Type', 'description' => 'The type of CAPTCHA to serve', 'image' => 'Image', 'audio' => 'Audio'],
119 | 'recaptcha_size' => [
120 | 'title' => 'Size',
121 | 'description' => 'The size of the widget',
122 | 'normal' => 'Normal',
123 | 'compact' => 'Compact',
124 | 'invisible' => 'Invisible',
125 | ],
126 | 'skip_database' => ['title' => 'Skip DB', 'description' => 'Don\'t store this form on database. Useful if you want to use events with your custom plugin.'],
127 | 'emails_date_format' => ['title' => 'Date format on emails', 'description' => 'Set custom format for dates used on emails subjects.'],
128 | 'uploader_enable' => ['title' => 'Allow uploads', 'description' => 'Enable files uploading. You need to explicitly enable this option as a security measure.'],
129 | 'uploader_filesize' => ['title' => 'File size limit', 'description' => 'The maximum file size that can be uploaded. Ex: 10MB, 750KB.'],
130 | ]
131 | ],
132 |
133 | 'settings' => [
134 | 'tabs' => ['general' => 'General', 'recaptcha' => 'reCAPTCHA', 'gdpr' => 'GDPR'],
135 | 'section_flash_messages' => 'Flash Messages',
136 | 'global_messages_success' => ['label' => 'Global Success Message', 'comment' => '(This setting can be overridden from the component)', 'default' => 'Your form was successfully submitted'],
137 | 'global_messages_errors' => ['label' => 'Global Errors Message', 'comment' => '(This setting can be overridden from the component)', 'default' => 'There were errors with your submission'],
138 | 'plugin_help' => 'You can access plugin documentation at GitHub repo:',
139 | 'global_hide_button' => 'Hide navigation item',
140 | 'global_hide_button_desc' => 'Useful if you want to use events with your custom plugin.',
141 | 'section_recaptcha' => 'reCAPTCHA Settings',
142 | 'recaptcha_site_key' => 'Site key',
143 | 'recaptcha_secret_key' => 'Secret key',
144 | 'gdpr_help_title' => 'Information',
145 | 'gdpr_help_comment' => 'New GDPR law in Europe, you can\'t keep records undefinitely, need to clear them after a certain period of time depending on your needs',
146 | 'gdpr_enable' => 'Enable GDPR',
147 | 'gdpr_days' => 'Keep records for a maximum of X days',
148 | ],
149 |
150 | 'permissions' => [
151 | 'tab' => 'Magic Forms',
152 | 'access_records' => 'Access stored forms data',
153 | 'access_exports' => 'Access to export stored data',
154 | 'access_settings' => 'Access module configuration',
155 | 'gdpr_cleanup' => 'Perform GDPR database cleanup',
156 | ],
157 |
158 | 'mails' => [
159 | 'form_notification' => ['description' => 'Notify when a form is submitted'],
160 | 'form_autoresponse' => ['description' => 'Auto-Response when a form is submitted'],
161 | ],
162 |
163 | 'validation' => [
164 | 'recaptcha_error' => 'Cannot validate reCAPTCHA field'
165 | ],
166 |
167 | 'classes' => [
168 | 'GDPR' => [
169 | 'alert_gdpr_disabled' => 'GDPR options are disabled',
170 | 'alert_invalid_gdpr' => 'Invalid GDPR days setting value',
171 | ],
172 | 'FilePond' => [
173 | 'error_filesize' => 'File size not allowed',
174 | 'error_filetype' => 'File type not allowed',
175 | 'error_savefile' => 'Could not save file',
176 | ]
177 | ]
178 |
179 | ];
180 |
--------------------------------------------------------------------------------
/lang/fr/lang.php:
--------------------------------------------------------------------------------
1 | [
4 | 'name' => 'Magic Forms',
5 | 'description' => 'Créer des formulaires AJAX facilement'
6 | ],
7 | 'menu' => [
8 | 'label' => 'Magic Forms',
9 | 'records' => ['label' => 'Enregistrements'],
10 | 'exports' => ['label' => 'Export'],
11 | 'settings' => 'Configurer kes parameters du plugin',
12 | ],
13 | 'controllers' => [
14 | 'records' => [
15 | 'title' => 'Voir les enregistrements',
16 | 'view_title' => 'Détails de l\'enregistrement',
17 | 'error' => 'Enregistrement non trouvé',
18 | 'deleted' => 'Enregistrement supprimé avec succès',
19 | 'columns' => [
20 | 'id' => 'Enregistrement N°',
21 | 'group' => 'Groupe',
22 | 'ip' => 'Adresse IP',
23 | 'form_data' => 'Champs stockés',
24 | 'files' => 'Fichiers attachés',
25 | 'created_at' => 'Créer le',
26 | ],
27 | 'buttons' => [
28 | 'read' => 'Marquer comme lu',
29 | 'unread' => 'Marquer comme non lu',
30 | 'gdpr_clean' => 'Nettoyage RGPD',
31 | ],
32 | 'alerts' => [
33 | 'gdpr_confirm' => "Êtes-vous sûr de vouloir nettoyer les anciens enregistrements?\nCette action ne peut pas être annulée!",
34 | 'gdpr_success' => 'Le nettoyage RGPD a été exécuté avec succès',
35 | 'gdpr_perms' => 'Vous n\'avez pas l\'autorisation pour utiliser cette fonctionnalité',
36 | ],
37 | ],
38 | 'exports' => [
39 | 'title' => 'Exporter les enregistrements',
40 | 'breadcrumb' => 'Exporter',
41 | 'filter_section' => '1. Filtrer les enregistrements',
42 | 'filter_type' => 'Exporter tous les enregistrements',
43 | 'filter_groups' => 'Groupes',
44 | 'filter_date_after' => 'Date de début',
45 | 'filter_date_before' => 'Date de fin',
46 | 'options_section' => '2. Options supplémentaires',
47 | 'options_metadata' => 'Inclure les metadonnées',
48 | 'options_metadata_com' => 'Exporter les enregistrements avec les metadonnées (n°, groupe, IP, date de création)',
49 | 'options_deleted' => 'Inclure les enregistrements supprimés',
50 | ],
51 | ],
52 | 'components' => [
53 | 'generic_form' => [
54 | 'name' => 'Formulaire générique AJAX',
55 | 'description' => 'Rendu d\'un formulaire générique; remplacer le composant HTML par vos propres champs personnalisés.',
56 | ],
57 | 'upload_form' => [
58 | 'name' => 'Téléchargements AJAX [BETA]',
59 | 'description' => 'Montre comment implémenter des téléchargements de fichiers sur votre formulaire.',
60 | ],
61 | 'empty_form' => [
62 | 'name' => 'Formulaire AJAX vide',
63 | 'description' => 'Créer un modèle vide pour votre formulaire personnalisé; remplacer le composant HTML.',
64 | ],
65 | 'shared' => [
66 | 'csrf_error' => 'La session du formulaire a expirée ! Veuillez actualiser la page.',
67 | 'recaptcha_warn' => 'Avertissement: reCAPTCHA n\'est pas correctement configuré. Àllez dans Backend > Paramètres > CMS > Magic Forms et configurez svp.',
68 | 'group_validation' => 'Validation du formulaire',
69 | 'group_messages' => 'Messages Flash ',
70 | 'group_mail' => 'Notifications',
71 | 'group_mail_resp' => 'Réponse automatique',
72 | 'group_settings' => 'Plus de réglages',
73 | 'group_security' => 'Securité',
74 | 'group_recaptcha' => 'reCAPTCHA',
75 | 'group_uploader' => 'Transfert de fichiers',
76 | 'validation_req' => 'La propriété est requise',
77 | 'group' => ['title' => 'Groupe' , 'description' => 'Organisez vos formulaires avec un nom de groupe personnalisé. Cette option est utile lorsque vous exportez des données.'],
78 | 'rules' => ['title' => 'Règles' , 'description' => 'Définissez vos propres règles en utilisant la validation de Laravel'],
79 | 'rules_messages' => ['title' => 'Messages de règles' , 'description' => 'Utilisez vos propres messages de règles en utilisant la validation de Laravel'],
80 | 'messages_success' => ['title' => 'Succès' , 'description' => 'Message lorsque le formulaire est soumis avec succès' , 'default' => 'Votre formulaire a été envoyé avec succès' ],
81 | 'messages_errors' => ['title' => 'Erreurs' , 'description' => 'Message lorsque le formulaire contient des erreurs' , 'default' => 'Il y a eu des erreurs dans votre formulaire'],
82 | 'messages_partial' => ['title' => 'Utiliser un modèle partiel personnalisé', 'description' => 'Modifier le message flash avec un modèle partiel personnalisé de votre thème'],
83 | 'mail_enabled' => ['title' => 'Envoyer des notifications' , 'description' => 'Envoyer des notifications par mail sur chaque formulaire envoyé'],
84 | 'mail_subject' => ['title' => 'Sujet' , 'description' => 'Remplacer le sujet par défaut du courrier électronique'],
85 | 'mail_recipients' => ['title' => 'Destinataires' , 'description' => 'Spécifier les destinataires des e-mails (ajouter une adresse par ligne)'],
86 | 'mail_bcc' => ['title' => 'CCI' , 'description' => 'Envoyer une copie carbone invisible aux destinataires des e-mails (ajouter une adresse par ligne)'],
87 | 'mail_replyto' => ['title' => 'Champ du email de réponse (ReplyTo)' , 'description' => 'Champ de formulaire contenant l\'adresse e-mail de l\'expéditeur à utiliser comme "ReplyTo"'],
88 | 'mail_uploads' => ['title' => 'Envoyer les téléchargements' , 'description' => 'Envoi des fichiers téléchargés en pièce jointe'],
89 | 'mail_template' => ['title' => 'Modèle e-mail' , 'description' => 'Utiliser un modèle e-mail personnalisé. Spécifiez un code de modèle tel que "martin.forms::mail.notification" (situé dans Paramètres, Modèles des e-mails). Laissez vide pour utiliser les paramètres par défaut.'],
90 | 'mail_uploads' => ['title' => 'Envoyer les téléchargements' , 'description' => 'Envoyer les téléchargements en pièces jointes'],
91 | 'mail_resp_enabled' => ['title' => 'Envoyer une réponse automatique' , 'description' => 'Envoyer un e-mail d\'auto-réponse à la personne qui soumet le formulaire'],
92 | 'mail_resp_field' => ['title' => 'Champ email' , 'description' => 'Champ de formulaire contenant l\'adresse e-mail du destinataire de réponse automatique '],
93 | 'mail_resp_from' => ['title' => 'Adresse de l\'expéditeur' , 'description' => 'Adresse e-mail de l\'expéditeur du courrier électronique de réponse automatique (par exemple nepasrepondre@votreentreprise.com)'],
94 | 'mail_resp_subject' => ['title' => 'Sujet' , 'description' => 'Remplacer le sujet par défaut du courrier électronique'],
95 | 'reset_form' => ['title' => 'Réinitialiser le formulaire' , 'description' => 'Réinitialiser le formulaire après l\'envoi réussi'],
96 | 'redirect' => ['title' => 'Redirection envoi réussi' , 'description' => 'Rediriger vers une URL spécifique lors de l\'envoi réussi. Remarque: doit être une URL valide commençant par http ou https ou la redirection sera ignorée.'],
97 | 'inline_errors' => ['title' => 'Erreurs sur la même ligne' , 'description' => 'Afficher les erreurs sur la même ligne. Cela nécéssite du code supplémentaire, consultez la documentation pour plus d\'informations. ', 'disabled' => 'Désactivé', 'display' => 'Afficher les erreurs', 'variable' => 'Variable Javascript'],
98 | 'js_on_success' => ['title' => 'JS au succès' , 'description' => 'Exécutez du code JavaScript personnalisé lorsque le formulaire a été soumis avec succès. Ne pas utiliser les balises script'],
99 | 'js_on_error' => ['title' => 'JS si erreur' , 'description' => 'Exécutez du code JavaScript personnalisé lorsque le formulaire ne se valide pas. Ne pas utiliser les balises script.'],
100 | 'allowed_fields' => ['title' => 'Allowed Fields' , 'description' => 'Spécifiez quels champs doivent être filtrés et stockés (ajoutez un nom de champ par ligne).'],
101 | 'anonymize_ip' => ['title' => 'Anonymiser IP' , 'description' => 'Ne pas enregistrer les adresses IP', 'full' => 'Complet', 'partial' => 'Partiel', 'disabled' => 'Désactivé'],
102 | 'sanitize_data' => ['title' => 'Désinfecter les données de formulaire' , 'description' => 'Désinfectez les données de formulaire et enregistrez le résultat dans la base de données', 'disabled' => 'Désactivé', 'htmlspecialchars' => 'Utilisez htmlspecialchars'],
103 | 'recaptcha_enabled' => ['title' => 'Activer reCAPTCHA' , 'description' => 'Insère le widget reCAPTCHA sur votre formulaire'],
104 | 'recaptcha_theme' => ['title' => 'Thème' , 'description' => 'Le thème de couleur du widget' , 'light' => 'Léger' , 'dark' => 'Sombre'],
105 | 'recaptcha_type' => ['title' => 'Type' , 'description' => 'Le type de CAPTCHA à servir' , 'image' => 'Image' , 'audio' => 'Audio'],
106 | 'recaptcha_size' => ['title' => 'Taille' , 'description' => 'La taille du widget' , 'normal' => 'Normale', 'compact' => 'Compacte'],
107 | 'skip_database' => ['title' => 'Ignore la BDD' , 'description' => 'Ne pas stocker ce formulaire dans la base de données. Utile si vous souhaitez utiliser des évènements avec votre plugin personnalisé.'],
108 | 'uploader_enable' => ['title' => 'Autoriser les téléchargements' , 'description' => 'Activer le téléchargement des fichiers. Vous devez activer cette option explicitement comme mesure de sécurité.'],
109 | 'uploader_multi' => ['title' => 'Fichiers multiples' , 'description' => 'Autoriser plusieurs téléchargements de fichiers'],
110 | 'uploader_pholder' => ['title' => 'Texte de remplacement' , 'description' => 'Texte à afficher quand aucun fichier n\'est téléchargé', 'default' => 'Cliquez pour choisir ou faites glisser les fichiers à télécharger'],
111 | 'uploader_maxsize' => ['title' => 'Limite de taille de fichier' , 'description' => 'Taille maximale du fichier pouvant être téléchargée en mégaoctets'],
112 | 'uploader_types' => ['title' => 'Types de fichiers autorisés' , 'description' => 'Extensions de fichiers autorisées ou étoile (*) pour tous les types (ajoutez une extension par ligne)'],
113 | 'uploader_remFile' => ['title' => 'Texte de Suppression' , 'description' => 'Texte à afficher dans la popup lors de la suppression d\'un fichier', 'default' => 'Êtes-vous sûr ?'],
114 | ]
115 | ],
116 | 'settings' => [
117 | 'tabs' => ['general' => 'Général', 'recaptcha' => 'reCAPTCHA', 'gdpr' => 'RGPD'],
118 | 'section_flash_messages' => 'Messages Flash',
119 | 'global_messages_success' => ['label' => 'Message de réussite', 'comment' => '(Ce paramètre peut être remplacé par le composant)', 'default' => 'Votre formulaire a été envoyé avec succès'],
120 | 'global_messages_errors' => ['label' => 'Global Errors Message' , 'comment' => '(Ce paramètre peut être remplacé par le composant)', 'default' => 'Il y a eu des erreurs dans votre formulaire'],
121 | 'plugin_help' => 'Vous pouvez accéder à la documentation du plugin sur GitHub :',
122 | 'global_hide_button' => 'Masquer l\'élément de navigation',
123 | 'global_hide_button_desc' => 'Utile si vous souhaitez utiliser des événements avec votre plugin personnalisé.',
124 | 'section_recaptcha' => 'Paramètres reCAPTCHA',
125 | 'recaptcha_site_key' => 'Clé du site',
126 | 'recaptcha_secret_key' => 'Clé secrète',
127 | 'gdpr_help_title' => 'Information',
128 | 'gdpr_help_comment' => 'Nouvelle loi RGPD/GDPR en Europe : vous ne pouvez pas conserver les enregistrements indéfiniment, vous devez les effacer au bout d\'un certain temps en fonction de vos besoins.',
129 | 'gdpr_enable' => 'Activer RGPD',
130 | 'gdpr_days' => 'Garder les enregistrements pour un maximum de X jours',
131 | ],
132 | 'permissions' => [
133 | 'tab' => 'Magic Forms',
134 | 'access_records' => 'Accéder aux données des formulaires stockés',
135 | 'access_exports' => 'Accès aux exports des formulaires stockés',
136 | 'access_settings' => 'Accès à la configuration du module',
137 | 'gdpr_cleanup' => 'Accès au nettoyage de la base de donnée RGPD',
138 | ],
139 | 'mails' => [
140 | 'form_notification' => ['description' => 'Notifier quand un formulaire est envoyé'],
141 | 'form_autoresponse' => ['description' => 'Auto-Réponse lorsqu\'un formulaire est envoyé'],
142 | ],
143 | 'validation' => [
144 | 'recaptcha_error' => 'Impossible de valider le champ reCAPTCHA'
145 | ],
146 | 'classes' => [
147 | 'GDPR' => [
148 | 'alert_gdpr_disabled' => 'LEs options RGPD sont désactivés',
149 | 'alert_invalid_gdpr' => 'Valeur de réglage des jours RGPD invalide',
150 | ]
151 | ]
152 | ];
153 | ?>
154 |
--------------------------------------------------------------------------------
/lang/nl/lang.php:
--------------------------------------------------------------------------------
1 | [
6 | 'name' => 'Magic Forms',
7 | 'description' => 'Eenvoudige AJAX-formulieren maken',
8 | ],
9 |
10 | 'menu' => [
11 | 'label' => 'Magic Forms',
12 | 'records' => ['label' => 'Records'],
13 | 'exports' => ['label' => 'Exporteren'],
14 | 'settings' => 'Configureer plugin parameters',
15 | ],
16 |
17 | 'controllers' => [
18 | 'records' => [
19 | 'title' => 'Bekijk Records',
20 | 'view_title' => 'Record Details',
21 | 'error' => 'Record niet gevonden',
22 | 'deleted' => 'Record succesvol verwijderd',
23 | 'columns' => [
24 | 'id' => 'Record ID',
25 | 'group' => 'Groep',
26 | 'ip' => 'IP adres',
27 | 'form_data' => 'Opgeslagen velden',
28 | 'files' => 'Bijlagen',
29 | 'created_at' => 'Gemaakt',
30 | ],
31 | 'buttons' => [
32 | 'read' => 'Als gelezen markeren',
33 | 'unread' => 'Als ongelezen markeren',
34 | 'gdpr_clean' => 'AVG opschoning uitvoeren',
35 | ],
36 | 'alerts' => [
37 | 'gdpr_confirm' => "Weet u zeker dat u oude gegevens wilt wissen?\nDeze actie kan niet ongedaan worden gemaakt!",
38 | 'gdpr_success' => 'AVG-opschoning is met succes uitgevoerd',
39 | 'gdpr_perms' => 'Je hebt geen toestemming voor deze functie.',
40 | ],
41 | ],
42 | 'exports' => [
43 | 'title' => 'Exporteer Records',
44 | 'breadcrumb' => 'Exporteren',
45 | 'filter_section' => '1. Filter records',
46 | 'filter_type' => 'Exporteer alle records',
47 | 'filter_groups' => 'Groepen',
48 | 'filter_date_after' => 'Vanaf datum',
49 | 'filter_date_before' => 'Tot datum',
50 | 'options_section' => '2. Extra opties',
51 | 'options_metadata' => 'Metagegevens meenemen',
52 | 'options_metadata_com' => 'Exporteer records met metadata (Record ID, groep, IP, aanmaakdatum)',
53 | 'options_deleted' => 'Verwijderde records meenemen',
54 | 'options_delimiter' => 'Use alternative delimiter',
55 | 'options_delimiter_com' => 'Gebruik alternatief scheidingsteken',
56 | 'options_utf' => 'Encoderen in UTF-8',
57 | 'options_utf_com' => 'Codeer uw csv in UTF-8 om niet-standaard tekens te ondersteunen',
58 | ],
59 | ],
60 |
61 | 'components' => [
62 | 'generic_form' => [
63 | 'name' => 'Algemeen AJAX Formulier',
64 | 'description' => 'Toont standaard een algemeen formulier; overschrijf component HTML met uw aangepaste velden.',
65 | ],
66 | 'upload_form' => [
67 | 'name' => 'Upload AJAX Formulier [BETA]',
68 | 'description' => 'Laat zien hoe je bestandsuploads kunt implementeren in je formulier.',
69 | ],
70 | 'empty_form' => [
71 | 'name' => 'Leeg AJAX Formulier',
72 | 'description' => 'Maak een leeg sjabloon voor uw aangepaste formulier; overschrijf component HTML.',
73 | ],
74 | 'shared' => [
75 | 'csrf_error' => 'Formulier sessie verlopen! Vernieuw de pagina.',
76 | 'recaptcha_warn' => 'Waarschuwing: reCAPTCHA is niet goed geconfigureerd. Ga aub naar Backend > Instellingen > CMS > Magic Forms en configureer.',
77 | 'group_validation' => 'Formulier Validatie',
78 | 'group_messages' => 'Flash Berichten',
79 | 'group_mail' => 'Notificatie Instellingen',
80 | 'group_mail_resp' => 'Auto-Response Instellingen',
81 | 'group_settings' => 'Meer Instellingen',
82 | 'group_security' => 'Beveiliging',
83 | 'group_recaptcha' => 'reCAPTCHA Instellingen',
84 | 'group_advanced' => 'Geavanceerde Instellingen',
85 | 'group_uploader' => 'Uploader Instellingen',
86 | 'validation_req' => 'De property is verplicht',
87 | 'group' => ['title' => 'Groep' , 'description' => 'Organiseer uw formulieren met een aangepaste groepsnaam. Deze optie is handig bij het exporteren van gegevens.'],
88 | 'rules' => ['title' => 'Regels' , 'description' => 'Stel je eigen regels in met Laravel validatie'],
89 | 'rules_messages' => ['title' => 'Regels Berichten' , 'description' => 'Gebruik uw eigen regels berichten met behulp van Laravel validatie'],
90 | 'custom_attributes' => ['title' => 'Aangepaste attributen' , 'description' => 'Gebruik je eigen aangepaste attributen met Laravel validatie'],
91 | 'messages_success' => ['title' => 'Succes' , 'description' => 'Bericht wanneer het formulier succesvol is verzonden', 'default' => 'Uw formulier is succesvol verzonden'],
92 | 'messages_errors' => ['title' => 'Fouten' , 'description' => 'Bericht wanneer het formulier fouten bevat' , 'default' => 'Er zijn fouten opgetreden bij uw inzending'],
93 | 'messages_partial' => ['title' => 'Gebruik Aangepaste Partial' , 'description' => 'Overschrijf flashberichten met uw eigen partials in uw thema'],
94 | 'mail_enabled' => ['title' => 'Stuur Notificaties' , 'description' => 'Stuur e-mailmeldingen over elk ingediend formulier'],
95 | 'mail_subject' => ['title' => 'Onderwerp' , 'description' => 'Standaard e-mail onderwerp overschrijven'],
96 | 'mail_recipients' => ['title' => 'Ontvangers' , 'description' => 'Geef de e-mailadressen op (één adres per regel)'],
97 | 'mail_bcc' => ['title' => 'BCC' , 'description' => 'Stuur een BBC naar de ontvangers (één adres per regel)'],
98 | 'mail_replyto' => ['title' => 'ReplyTo E-mail Veld' , 'description' => 'Formulierveld met het e-mailadres van de afzender dat moet worden gebruikt als "ReplyTo"'],
99 | 'mail_template' => ['title' => 'E-mail sjabloon' , 'description' => 'Gebruik een aangepast e-mail sjabloon. Specificeer sjabloon code zoals "martin.forms::mail.notification" (te vinden onder Instellingen, E-mail sjablonen). Laat leeg om de standaard te gebruiken.'],
100 | 'mail_uploads' => ['title' => 'Verstuur uploads' , 'description' => 'Verstuur uploads als bijlagen'],
101 | 'mail_resp_enabled' => ['title' => 'Stuur automatisch antwoord' , 'description' => 'Stuur een automatische e-mail naar de persoon die het formulier heeft ingediend'],
102 | 'mail_resp_field' => ['title' => 'E-mail Veld' , 'description' => 'Formulierveld met het e-mailadres van de ontvanger van het automatische antwoord'],
103 | 'mail_resp_from' => ['title' => 'Verzender adres' , 'description' => 'E-mailadres van de afzender van de auto-response e-mail (bv. noreply@yourcompany.com)'],
104 | 'mail_resp_subject' => ['title' => 'Onderwerp' , 'description' => 'Standaard e-mail onderwerp overschrijven'],
105 | 'reset_form' => ['title' => 'Reset Form' , 'description' => 'Formulier terugzetten na succesvol verzenden'],
106 | 'redirect' => ['title' => 'Redirect on Success' , 'description' => 'Doorverwijzen bij succes'],
107 | 'inline_errors' => ['title' => 'Inline fouten' , 'description' => 'Toon inline fouten. Dit heeft extra code nodig, zie de documentatie voor meer info info.', 'disabled' => 'Uitgeschakeld', 'display' => 'Toon fouten', 'variable' => 'JS variabele'],
108 | 'js_on_success' => ['title' => 'JS bij Succes' , 'description' => 'Voer aangepaste JavaScript code uit wanneer het formulier succesvol is verstuurd. Gebruik geen script tags.'],
109 | 'js_on_error' => ['title' => 'JS bij Fout' , 'description' => 'Voer aangepaste JavaScript code uit als het formulier niet valideert. Gebruik geen script tags.'],
110 | 'allowed_fields' => ['title' => 'Toegestane Velden' , 'description' => 'Geef aan welke velden moeten worden gefilterd en opgeslagen (voeg één veldnaam per regel toe)'],
111 | 'anonymize_ip' => ['title' => 'Anonimiseer IP' , 'description' => 'Sla het IP adres niet op', 'full' => 'Volledig', 'partial' => 'Gedeeltelijk', 'disabled' => 'Uitgeschakeld'],
112 | 'sanitize_data' => ['title' => 'Sanitize form data' , 'description' => 'Formuliergegevens zuiveren', 'disabled' => 'Uitgeschakeld', 'htmlspecialchars' => 'Gebruik htmlspecialchars'],
113 | 'recaptcha_enabled' => ['title' => 'Schakel reCAPTCHA in' , 'description' => 'Plaats de reCAPTCHA widget op uw formulierm'],
114 | 'recaptcha_theme' => ['title' => 'Thema' , 'description' => 'Het kleurenthema van de widget', 'light' => 'Licht' , 'dark' => 'Donker'],
115 | 'recaptcha_type' => ['title' => 'Type' , 'description' => 'Het type CAPTCHA dat moet worden gebruikt' , 'image' => 'Afbeelding' , 'audio' => 'Audio'],
116 | 'recaptcha_size' => [
117 | 'title' => 'Grootte',
118 | 'description' => 'De grootte van de widget',
119 | 'normal' => 'Normaal',
120 | 'compact' => 'Compact',
121 | 'invisible' => 'Onzichtbaar',
122 | ],
123 | 'skip_database' => ['title' => 'Sla database over' , 'description' => 'Sla dit formulier niet op in de database. Handig als je gebeurtenissen wilt gebruiken met je aangepaste plugin.'],
124 | 'emails_date_format' => ['title' => 'Datumnotatie op e-mails' , 'description' => 'Aangepaste notatie instellen voor datums in e-mailonderwerpen.'],
125 | 'uploader_enable' => ['title' => 'Sta uploads toe' , 'description' => 'Bestanden uploaden inschakelen. U moet deze optie expliciet inschakelen als veiligheidsmaatregel.'],
126 | 'uploader_multi' => ['title' => 'Meerdere bestanden' , 'description' => 'Sta uploaden van meerdere bestanden toe'],
127 | 'uploader_pholder' => ['title' => 'Placeholder tekst' , 'description' => 'Formulering die wordt weergegeven wanneer geen bestand is geüpload', 'default' => 'Klik of sleep bestanden om te uploaden'],
128 | 'uploader_maxsize' => ['title' => 'Limiet bestandsgrootte' , 'description' => 'De maximale bestandsgrootte die kan worden geüpload in megabytes'],
129 | 'uploader_types' => ['title' => 'Toegestane bestandstypen', 'description' => 'Toegestane bestandsextensies of sterretje (*) voor alle types (één extensie per regel)'],
130 | 'uploader_remFile' => ['title' => 'Popup tekst verwijderen' , 'description' => 'Tekst die wordt weergegeven in de popup wanneer u een bestand verwijdert', 'default' => 'Weet je het zeker?'],
131 | ],
132 | ],
133 |
134 | 'settings' => [
135 | 'tabs' => ['general' => 'Algemeen', 'recaptcha' => 'reCAPTCHA', 'gdpr' => 'AVG'],
136 | 'section_flash_messages' => 'Flash Berichten',
137 | 'global_messages_success' => ['label' => 'Globaal succesbericht', 'comment' => '((Deze instelling kan worden overschreven vanuit het component))', 'default' => 'Uw formulier is succesvol verzonden'],
138 | 'global_messages_errors' => ['label' => 'Globaal foutbericht' , 'comment' => '((Deze instelling kan worden overschreven vanuit het component))', 'default' => 'Er zijn fouten opgetreden bij uw inzending'],
139 | 'plugin_help' => 'U kunt de plugin documentatie vinden op de GitHub repo:',
140 | 'global_hide_button' => 'Verberg menu item',
141 | 'global_hide_button_desc' => 'Handig als je events wilt gebruiken met je aangepaste plugin.',
142 | 'section_recaptcha' => 'reCAPTCHA Instellingen',
143 | 'recaptcha_site_key' => 'Site key',
144 | 'recaptcha_secret_key' => 'Secret key',
145 | 'gdpr_help_title' => 'Informatie',
146 | 'gdpr_help_comment' => 'Voor AVG-wetgeving in Europa, u kunt records niet onbeperkt bewaren, u moet ze wissen na een bepaalde periode, afhankelijk van uw behoeften',
147 | 'gdpr_enable' => 'Schakel AVG in',
148 | 'gdpr_days' => 'Bewaar de gegevens maximaal X dagen',
149 | ],
150 |
151 | 'permissions' => [
152 | 'tab' => 'Magic Forms',
153 | 'access_records' => 'Toegang tot opgeslagen formuliergegevens',
154 | 'access_exports' => 'Toegang tot export van opgeslagen gegevens',
155 | 'access_settings' => 'Toegang tot instellingen',
156 | 'gdpr_cleanup' => 'AVG opschoning uitvoeren',
157 | ],
158 |
159 | 'mails' => [
160 | 'form_notification' => ['description' => 'Notificeer wanneer een formulier wordt verzonden'],
161 | 'form_autoresponse' => ['description' => 'Automatisch antwoord wanneer een formulier wordt verzonden'],
162 | ],
163 |
164 | 'validation' => [
165 | 'recaptcha_error' => 'Kan reCAPTCHA-veld niet valideren',
166 | ],
167 |
168 | 'classes' => [
169 | 'GDPR' => [
170 | 'alert_gdpr_disabled' => 'AVG opties zijn uitgeschakeld',
171 | 'alert_invalid_gdpr' => 'Ongeldige AVG instellingswaarde voor dagen',
172 | ],
173 | ],
174 |
175 | ];
176 |
--------------------------------------------------------------------------------
/lang/pt-br/lang.php:
--------------------------------------------------------------------------------
1 | [
5 | 'name' => 'Magic Forms',
6 | 'description' => 'Crie formulários fácilmente com AJAX'
7 | ],
8 | 'menu' => [
9 | 'label' => 'Formulários Mágico',
10 | 'records' => ['label' => 'Registros'],
11 | 'exports' => ['label' => 'Exportar'],
12 | 'settings' => 'Configurar parâmetros do plug-in',
13 | ],
14 | 'controllers' => [
15 | 'records' => [
16 | 'title' => 'Ver registos',
17 | 'view_title' => 'Detalhes do registro',
18 | 'error' => 'Registro não encontrado',
19 | 'deleted' => 'Registro excluído com sucesso',
20 | 'columns' => [
21 | 'id' => 'ID do registro',
22 | 'group' => 'Grupo',
23 | 'ip' => 'Endereço IP',
24 | 'form_data' => 'Campos armazenados',
25 | 'files' => 'Arquivos anexados',
26 | 'created_at' => 'Criado',
27 | ],
28 | ],
29 | 'exports' => [
30 | 'title' => 'Exportar registros',
31 | 'breadcrumb' => 'Exportar',
32 | 'filter_section' => '1. Filtrar registos',
33 | 'filter_type' => 'Exportar todos os registros',
34 | 'filter_groups' => 'Grupos',
35 | 'filter_date_after' => 'Data após',
36 | 'filter_date_before' => 'Data anterior',
37 | 'options_section' => '2. Opções extras',
38 | 'options_metadata' => 'Incluir metadados',
39 | 'options_metadata_com' => 'Exportar registros com metadados (ID de registro, grupo, IP, data criação)',
40 | 'options_deleted' => 'Incluir registros excluídos',
41 | ],
42 | ],
43 | 'components' => [
44 | 'generic_form' => [
45 | 'name' => 'Formulário AJAX Genérico',
46 | 'description' => 'Por padrão renderiza um formulário genérico; Substitua o componente HTML com seus campos personalizados.',
47 | ],
48 | 'upload_form' => [
49 | 'name' => 'formulário upload AJAX [BETA]',
50 | 'description' => 'Mostra como implementar uploads de arquivos no formulário.',
51 | ],
52 | 'empty_form' => [
53 | 'name' => 'Formulário AJAX vazio',
54 | 'description' => 'Crie um modelo vazio para seu formulário personalizado; Substituir o componente HTML.',
55 | ],
56 | 'shared' => [
57 | 'csrf_error' => 'A sessão do formulário expirou! Atualize a página.',
58 | 'recaptcha_warn' => 'Aviso: reCAPTCHA não está configurado corretamente. Por favor, vá para Back-end> Configurações> CMS> Magic Forms e configure.',
59 | 'group_validation' => 'Validação de formulários',
60 | 'group_messages' => 'Mensagens Flash',
61 | 'group_mail' => 'Configurações de Notificações',
62 | 'group_mail_resp' => 'Configurações de Resposta Automática',
63 | 'group_settings' => 'Mais configurações',
64 | 'group_security' => 'Segurança',
65 | 'group_recaptcha' => 'Configurações de reCAPTCHA',
66 | 'group_uploader' => 'Configurações do Uploader',
67 | 'validation_req' => 'A propriedade é obrigatória',
68 | 'group' => ['title' => 'Grupo' , 'description' => 'Organize seus formulários com um nome de grupo personalizado. Esta opção é útil ao exportar dados.'],
69 | 'rules' => ['title' => 'Regras' , 'description' => 'Defina suas próprias regras usando a validação Laravel'],
70 | 'rules_messages' => ['title' => 'Mensagens de Regras' , 'description' => 'Use suas próprias mensagens de regras usando a validação do Laravel'],
71 | 'messages_success' => ['title' => 'Sucesso' , 'description' => 'Mensagem quando o formulário é enviado com sucesso', 'default '=>' Seu formulário foi enviado com sucesso'],
72 | 'messages_errors' => ['title' => 'Erros' , 'description' => 'Mensagem quando o formulário contém erros', 'default' => 'Ocorreu um erro no envio'],
73 | 'mail_enabled' => ['title' => 'Enviar Notificações' , 'description' => 'Enviar notificações por email em todos os formulários enviados'],
74 | 'mail_subject' => ['title' => 'Assunto' , 'description' => 'Substitui o assunto de email padrão'],
75 | 'mail_recipients' => ['title' => 'Destinatários' , 'description' => 'Especifique destinatários de email (adicione um endereço por linha)'],
76 | 'mail_uploads' => ['title' => 'Enviar Uploads' , 'description' => 'Enviar uploads como anexos'],
77 | 'mail_resp_enabled' => ['title' => 'Enviar Resposta Automática' , 'description' => 'Enviar um email de resposta automática para a pessoa que envia o formulário'],
78 | 'mail_resp_field' => ['title' => 'Campo de Email' , 'description' => 'Campo de formulário que contém o endereço de email do destinatário da resposta automática'],
79 | 'mail_resp_from' => ['title' => 'Endereço do Remetente' , 'description' => 'Endereço de email do remetente de e-mail de resposta automática (por exemplo, noreply@yourcompany.com)'],
80 | 'mail_resp_subject' => ['title' => 'Assunto' , 'description' => 'Substitui assunto de e-mail padrão'],
81 | 'reset_form' => ['title' => 'Limpar Fomulário' , 'description' => 'Limpar o formulário após o envio com sucesso'],
82 | 'allowed_fields' => ['title' => 'Campos permitidos' , 'description' => 'Especifique quais campos devem ser filtrados e armazenados (adicione um nome de campo por linha)'],
83 | 'recaptcha_enabled' => ['title' => 'Habiltar reCAPTCHA' , 'description' => 'Inserir o widget reCAPTCHA no seu formulário'],
84 | 'recaptcha_theme' => ['title' => 'Tema' , 'description' => 'Cor do tema do widget', 'light' => 'Claro' , 'dark' => 'Escuro'],
85 | 'recaptcha_type' => ['title' => 'Tipo' , 'description' => 'O tipo de CAPTCHA para servir' , 'image' => 'Imagem' , 'audio' => 'Áudio'],
86 | 'recaptcha_size' => ['title' => 'Tamanho' , 'description' => 'O tamanho do widget' , 'normal' => 'Normal', 'compact' => 'Compacto'],
87 | 'uploader_enable' => ['title' => 'Permitir Uploads' , 'description' => 'Ativar o upload de arquivos. Você precisa ativar essa opção explicitamente como uma medida de segurança.'],
88 | 'uploader_multi' => ['title' => 'Multiplos Arquivos' , 'description' => 'Permitir multiplos uploads de arquivos'],
89 | 'uploader_pholder' => ['title' => 'texto do Placeholder' , 'description' => 'Texto a ser exibido quando nenhum arquivo é carregado', 'default' => 'Clique ou arraste os arquivos para fazer o upload'],
90 | 'uploader_maxsize' => ['title' => 'Limite do Tamanho do Arquivo' , 'description' => 'O tamanho máximo de arquivo que pode ser carregado em megabytes'],
91 | 'uploader_types' => ['title' => 'Tipos de Arquivos Permitidos' , 'description' => 'Extensões de arquivo permitido ou asterisco (*) para todos os tipos (adicionar uma extensão por linha)'],
92 | ]
93 | ],
94 | 'settings' => [
95 | 'section_flash_messages' => 'Mensagens Flash',
96 | 'global_messages_success' => ['label' => 'Mensagem de sucesso global' , 'comment' => '(Esta definição pode ser substituída a partir do componente)', 'default' => 'Seu formulário foi enviado com sucesso'],
97 | 'global_messages_errors' => ['label' => 'Mensagem de Erros Global' , 'comment' => '(Esta definição pode ser substituída a partir do componente)', 'default' => 'Ocorreu um erro o envio do formulário'],
98 | 'section_recaptcha' => 'Configurações de reCAPTCHA',
99 | 'recaptcha_site_key' => 'Chave do site',
100 | 'recaptcha_secret_key' => 'Chave secreta',
101 | ],
102 | 'permissions' => [
103 | 'tab' => 'Formulários Mágico',
104 | 'access_records' => 'Acessar dados de formulários armazenados',
105 | 'access_exports' => 'Acesso à exportação de dados armazenados',
106 | 'access_settings' => 'Acesso do módulo de configuração',
107 | ],
108 | 'mails' => [
109 | 'form_notification' => ['description' => 'Notificar quando um formulário é enviado'],
110 | 'form_autoresponse' => ['description' => 'Resposta automática quando um formulário é enviado'],
111 | ],
112 | 'validation' => [
113 | 'recaptcha_error' => 'Não é possível validar o campo reCAPTCHA'
114 | ],
115 | ];
116 | ?>
117 |
--------------------------------------------------------------------------------
/lang/ru/lang.php:
--------------------------------------------------------------------------------
1 | [
6 | 'name' => 'Magic Forms',
7 | 'description' => 'С легкостью создайте AJAX форму'
8 | ],
9 |
10 | 'menu' => [
11 | 'label' => 'Magic Forms',
12 | 'records' => ['label' => 'Записи'],
13 | 'exports' => ['label' => 'Экспорт'],
14 | 'settings' => 'Настройки плагина',
15 | ],
16 |
17 | 'controllers' => [
18 | 'records' => [
19 | 'title' => 'Все заявки',
20 | 'view_title' => 'Запись',
21 | 'error' => 'Запись не найдена',
22 | 'deleted' => 'Запись успешно удалена',
23 | 'columns' => [
24 | 'id' => 'ID записи',
25 | 'group' => 'Группа',
26 | 'ip' => 'IP адрес',
27 | 'form_data' => 'Поля формы',
28 | 'files' => 'Прикрепленные файлы',
29 | 'created_at' => 'Создано',
30 | ],
31 | 'buttons' => [
32 | 'read' => 'Отметить как прочитано',
33 | 'unread' => 'Отметить как непрочитанное',
34 | 'gdpr_clean' => 'Очистить GDPR',
35 | ],
36 | 'alerts' => [
37 | 'gdpr_confirm' => "Вы действительно хотите удалить эти старые записи?\nЭто действие не может быть отменено!",
38 | 'gdpr_success' => 'Очистка GDPR успешна',
39 | 'gdpr_perms' => 'У вас нет прав использования этой функции',
40 | ],
41 | ],
42 | 'exports' => [
43 | 'title' => 'Экспорт записей',
44 | 'breadcrumb' => 'Экспорт',
45 | 'filter_section' => '1. Фильтровать записи',
46 | 'filter_type' => 'Экспорт всех записей',
47 | 'filter_groups' => 'Группы',
48 | 'filter_date_after' => 'Дата от',
49 | 'filter_date_before' => 'дата до',
50 | 'options_section' => '2. Экстра опции',
51 | 'options_metadata' => 'Вложить метаданные',
52 | 'options_metadata_com' => 'Экспорт записей с метаданными(ID, группа, дата создания)',
53 | 'options_deleted' => 'Вложить удаленные записи',
54 | 'options_delimiter' => 'Использовать альтернативный разделитель полей',
55 | 'options_delimiter_com' => 'Точка с запятой вместо запятой',
56 | 'options_utf' => 'Кодировать в UTF8',
57 | 'options_utf_com' => 'Ваш CSV файл будет закодирван в UTF-8',
58 | ],
59 | ],
60 |
61 | 'components' => [
62 | 'generic_form' => [
63 | 'name' => 'Стандартная AJAX форма',
64 | 'description' => 'Выводит стандартную форму; Перезапишите HTML компонент со своей формой.',
65 | ],
66 | 'upload_form' => [
67 | 'name' => 'AJAX форма с файлами',
68 | 'description' => '[BETA] Форма с возможностью прикрепления файлов.',
69 | ],
70 | 'empty_form' => [
71 | 'name' => 'Пустая AJAX форма',
72 | 'description' => 'Создает пустой плейсхолдер для вашей формы, который вы должны перезаписать своим HTML кодом.',
73 | ],
74 | 'shared' => [
75 | 'csrf_error' => 'Сессия формы истекла! Перезагрузите страницу.',
76 | 'recaptcha_warn' => 'Внимание: reCAPTCHA настроена не правильно. Пожалуйста, перейдите в Настройки > CMS > Magic Forms и заполните все обязательные поля.',
77 | 'group_validation' => 'Валидация формы',
78 | 'group_messages' => 'Флэш-сообщения',
79 | 'group_mail' => 'Настройки уведомлений',
80 | 'group_mail_resp' => 'Настройки авто-ответа',
81 | 'group_settings' => 'Остальные настройки',
82 | 'group_security' => 'Безопасность',
83 | 'group_recaptcha' => 'Настройки reCAPTCHA',
84 | 'group_advanced' => 'Продвинутые настройки',
85 | 'group_uploader' => 'Настройки файлового загрузчика',
86 | 'validation_req' => 'Этот атрибут обязателен',
87 | 'group' => ['title' => 'Группа' , 'description' => 'Организуйте свои формы по группам. Эта опция полезна для экспорта записей.'],
88 | 'rules' => ['title' => 'Правила' , 'description' => 'Используйте валидацию Laravel для установки своих правил обработки полей.'],
89 | 'rules_messages' => ['title' => 'Сообщения от правил', 'description' => 'Используйте свои собственные сообщения от кастомных валидаций.'],
90 | 'custom_attributes' => ['title' => 'Кастомные атрибуты' , 'description' => 'Используйте кастомные атрибуты в своих правилах валидаций.'],
91 | 'messages_success' => ['title' => 'Успешно' , 'description' => 'Сообщение об успешной отправке формы', 'default' => 'Ваша форма была успешно отправлена!'],
92 | 'messages_errors' => ['title' => 'Ошибки' , 'description' => 'Сообщение с ошибками формы' , 'default' => 'В вашей заявке содержатся ошибки.'],
93 | 'messages_partial' => ['title' => 'Кастомный фрагмент' , 'description' => 'Переопределить стандартные уведомления кастомным фрагментом из вашей фронтенд темы.'],
94 | 'mail_enabled' => ['title' => 'Отправка уведомлений','description' => 'Отправлять увдеомления по email после каждого успешного заполенния формы.'],
95 | 'mail_subject' => ['title' => 'Тема письма' , 'description' => 'Переопределить тему письма по умолчанию'],
96 | 'mail_recipients' => ['title' => 'Получатели' , 'description' => 'Укажите получателей электронной почты (По одному адресу в строке)'],
97 | 'mail_bcc' => ['title' => 'BCC' , 'description' => 'Отправить копию получателям электронной почты (По одному адресу в строке)'],
98 | 'mail_replyto' => ['title' => 'ReplyTo поле email' , 'description' => 'Поле формы, содержащее адрес электронной почты отправителя, который будет использоваться как "ReplyTo"'],
99 | 'mail_template' => ['title' => 'Шаблон письма' , 'description' => 'Используйте собственный шаблон письма. Укажите код шаблона, например, "martin.forms::mail.notification" (находится в разделе Настройки -> Почтовые шаблоны). Оставьте пустым, чтобы использовать по умолчанию.'],
100 | 'mail_uploads' => ['title' => 'Прикреплять файлы' , 'description' => 'Прикреплять файлы к письму'],
101 | 'mail_resp_enabled' => ['title' => 'Отправить авто-ответ','description' => 'Отправить автоответчик на email отправителя.'],
102 | 'mail_resp_field' => ['title' => 'Email поле' , 'description' => 'Имя поля с email отправителя'],
103 | 'mail_resp_from' => ['title' => 'Email адрес отправителя', 'description' => 'Адрес электронной почты отправителя электронной почты с автоматическим ответом (например, noreply@yourcompany.com)'],
104 | 'mail_resp_subject' => ['title' => 'Тема пиьсма' , 'description' => 'Переопределить тему письма по умолчанию'],
105 | 'reset_form' => ['title' => 'Сбросить форму' , 'description' => 'Сбросить форму после успешной отправки'],
106 | 'redirect' => ['title' => 'Редирект' , 'description' => 'Редирект на URL после успешной отправки формы.'],
107 | 'inline_errors' => ['title' => 'Встроенные ошибки' , 'description' => 'Отображать встроенные ошибки. Это требует дополнительного кода, проверьте документацию для получения дополнительной информации.', 'disabled' => 'Отключено', 'display' => 'Отображать ошибки', 'variable' => 'JS переменная'],
108 | 'js_on_success' => ['title' => '"Успешный" JS' , 'description' => 'Выполнияет скрипт после успешной отправки формы. Не используйте