PluginProbe
Hash Form – Drag & Drop Form Builder / trunk
Hash Form – Drag & Drop Form Builder vtrunk
1.4.4 1.4.3 1.4.2 1.4.1 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.1.9 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.6.1 1.2.7 1.2.8 1.2.9 1.3.0 All 47 releases
hash-form / includes / HashFormSettings.php

HashFormSettings.php in Hash Form – Drag & Drop Form Builder trunk, at includes/HashFormSettings.php

383 lines 16.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 defined('ABSPATH') || die();
3
4 class HashFormSettings {
5
6 const DOCS_URL = 'https://hashthemes.com/documentation/hash-form-drag-and-drop-form-builder-documentation/';
7
8 public function __construct() {
9 add_action('admin_menu', array($this, 'menu'), 45);
10 add_action('in_admin_header', array($this, 'list_header'));
11
12 add_action('wp_ajax_hashform_test_email_template', array($this, 'send_test_email'), 10, 0);
13 }
14
15 public function menu() {
16 // With Pro active, every setting is managed from the per-module
17 // popups on the Modules screen, so the Settings page is not added.
18 // (Pro's OAuth callbacks still run on admin_init and exit before the
19 // page would render, so their registered redirect URIs keep working.)
20 if (!defined('HASH_FORM_PRO_VERSION')) {
21 add_submenu_page('hashform', 'Hash Form | ' . esc_html__('Settings', 'hash-form'), esc_html__('Settings', 'hash-form'), 'hashform_manage_settings', 'hashform-settings', array($this, 'route'));
22 }
23 add_submenu_page('hashform', esc_html__('Documentation', 'hash-form'), esc_html__('Documentation', 'hash-form'), 'hashform_view_forms', esc_url_raw(self::DOCS_URL));
24 }
25
26 /**
27 * The bar across the top of the Settings screen.
28 *
29 * The same component the Forms, Entries and style template lists use, so
30 * this screen is headed the way every other one is. It replaces the black
31 * uppercase title bar this page carried, which was the only one of its
32 * kind left in the plugin.
33 */
34 public function list_header() {
35 if (!self::is_settings_page()) {
36 return;
37 }
38
39 HashFormHelper::render_list_header(array(
40 'title' => esc_html__('Settings', 'hash-form'),
41 'docs' => self::DOCS_URL,
42 ));
43 }
44
45 private static function is_settings_page() {
46 return !defined('HASH_FORM_PRO_VERSION')
47 && 'hashform-settings' === HashFormHelper::get_var('page', 'sanitize_title');
48 }
49
50 public function route() {
51 $action = HashFormHelper::get_post('hashform_action', 'sanitize_title');
52 if ($action == 'process-form') {
53 self::process_form();
54 } else {
55 self::display_form();
56 }
57 }
58
59 public static function display_form() {
60 $settings = self::get_settings();
61 $sections = apply_filters('hash_form_settings_sections', array(
62 'captcha-settings' => array(
63 'name' => esc_html__('Captcha', 'hash-form'),
64 'icon' => 'mdi mdi-security',
65 'desc' => esc_html__('The reCAPTCHA keys every captcha field on the site checks against, and the language and score it runs at.', 'hash-form'),
66 ),
67 'email-settings' => array(
68 'name' => esc_html__('Email Settings', 'hash-form'),
69 'icon' => 'mdi mdi-email-multiple-outline',
70 'desc' => esc_html__('The header image and template used for every email this plugin sends.', 'hash-form'),
71 ),
72 'general-settings' => array(
73 'name' => esc_html__('General', 'hash-form'),
74 'icon' => 'mdi mdi-tune',
75 'desc' => esc_html__('Site-wide options that apply to every form.', 'hash-form'),
76 )
77 ));
78 $vars = apply_filters('hash_form_settings_vars', array(
79 'current' => 'captcha-settings'
80 ));
81 extract($vars);
82
83 // Deep-link support: honor ?t=<section> and never point at a section
84 // that does not exist (e.g. after the pro plugin swaps the list).
85 $requested_tab = HashFormHelper::get_var('t', 'sanitize_title');
86 if ($requested_tab && isset($sections[$requested_tab])) {
87 $current = $requested_tab;
88 } elseif (!isset($sections[$current])) {
89 $current = key($sections);
90 }
91 ?>
92
93 <?php // The header bar is printed on in_admin_header; see list_header(). ?>
94 <div class="hf-content hf-list-screen hf-settings-screen">
95 <div class="hf-list-wrap wrap">
96 <h1></h1>
97
98 <?php
99 /*
100 * esc_url, not esc_html. esc_html turned the '&amp;' into
101 * '&amp;amp;', which the browser submitted to a literal
102 * '&amp;t=' — so the save landed with $_GET['amp;t'] set and
103 * 't' absent, and every save from a tab that had not been
104 * clicked came back on the first one. esc_url emits '&#038;',
105 * which decodes to a plain '&'.
106 */
107 $action_url = '?page=hashform-settings' . ($current ? '&t=' . $current : '');
108 ?>
109 <form name="hashform_settings_form" method="post" action="<?php echo esc_url($action_url); ?>">
110 <input type="hidden" name="hashform_action" value="process-form" />
111 <input type="hidden" name="hashform_rendered_checkboxes" value="" />
112 <?php wp_nonce_field('hashform_process_form_action', 'hashform_process_form_nonce'); ?>
113
114 <div class="hf-settings-layout">
115 <div class="hf-settings-nav">
116 <ul class="hf-settings-tab">
117 <?php
118 foreach ($sections as $key => $section) {
119 ?>
120 <li class="<?php echo esc_attr($current === $key ? 'hf-active' : ''); ?>">
121 <a href="#hf-<?php echo esc_attr($key); ?>">
122 <i class="<?php echo esc_attr($section['icon']); ?>"></i>
123 <?php echo wp_kses_post($section['name']); ?>
124 </a>
125 </li>
126 <?php
127 }
128 ?>
129 </ul>
130 </div>
131
132 <div class="hf-settings-panel">
133 <?php HashFormHelper::print_message(); ?>
134
135 <?php
136 /*
137 * The sections are kept in a wrapper of their own:
138 * the tab script hides the clicked panel's
139 * siblings, and before this the nonce, the hidden
140 * inputs and the saved notice were all siblings
141 * too.
142 */
143 ?>
144 <div class="hf-settings-sections">
145 <?php
146 foreach ($sections as $key => $section) {
147 ?>
148 <div id="hf-<?php echo esc_attr($key); ?>" class="hf-settings-section <?php echo ($current === $key) ? '' : 'hf-hidden'; ?>">
149 <div class="hf-panel-head">
150 <h2><?php echo esc_html($section['name']); ?></h2>
151 <?php if (!empty($section['desc'])) { ?>
152 <p class="hf-panel-desc"><?php echo esc_html($section['desc']); ?></p>
153 <?php } ?>
154 </div>
155 <?php
156 $path = '';
157
158 if (file_exists(HASHFORM_PATH . 'admin/settings/' . $key . '.php')) {
159 $path = HASHFORM_PATH . 'admin/settings/' . $key . '.php';
160 } else {
161 $path = apply_filters('hash_form_settings_sections_path', $key);
162 }
163 include($path);
164 ?>
165 </div>
166 <?php
167 }
168 ?>
169 </div>
170
171 <div class="hf-footer">
172 <input class="button button-primary button-large" type="submit" value="<?php esc_attr_e('Save Changes', 'hash-form'); ?>" />
173 </div>
174 </div>
175 </div>
176 </form>
177 </div>
178 </div>
179 <script>
180 (function () {
181 // Keep the selected tab in the form action and the address bar so
182 // saving (or reloading) returns to the same section.
183 var form = document.forms['hashform_settings_form'];
184 if (!form) {
185 return;
186 }
187 // Tell the server which checkboxes this page rendered, so it only
188 // forces those off when they are unchecked (absent from the POST).
189 form.addEventListener('submit', function () {
190 var keys = [];
191 form.querySelectorAll('input[type="checkbox"]').forEach(function (box) {
192 var match = (box.name || '').match(/^hashform_settings\[([^\]]+)\]$/);
193 if (match) {
194 keys.push(match[1]);
195 }
196 });
197 var field = form.querySelector('input[name="hashform_rendered_checkboxes"]');
198 if (field) {
199 field.value = keys.join(',');
200 }
201 });
202 document.querySelectorAll('.hf-settings-tab a').forEach(function (link) {
203 link.addEventListener('click', function () {
204 var tab = (this.getAttribute('href') || '').replace('#hf-', '');
205 if (!tab) {
206 return;
207 }
208 var url = new URL(window.location.href);
209 url.searchParams.set('t', tab);
210 window.history.replaceState(null, '', url.toString());
211 form.setAttribute('action', '?page=hashform-settings&t=' + encodeURIComponent(tab));
212 });
213 });
214 })();
215 </script>
216 <?php
217 }
218
219 public static function process_form() {
220 $process_form = HashFormHelper::get_post('hashform_process_form_nonce');
221 if (!wp_verify_nonce($process_form, 'hashform_process_form_action')) {
222 wp_die(esc_html__('Permission Denied', 'hash-form'));
223 }
224
225 $posted = HashFormHelper::get_post('hashform_settings', 'esc_html');
226 $posted = is_array($posted) ? $posted : array();
227
228 // Unchecked checkboxes are absent from the POST. Only force off the
229 // ones this page actually rendered (listed by the form's JS), so
230 // settings managed elsewhere — e.g. the module popups — survive.
231 $rendered = HashFormHelper::get_post('hashform_rendered_checkboxes', 'sanitize_text_field');
232 $rendered = $rendered ? array_filter(array_map('sanitize_key', explode(',', $rendered))) : array_keys(self::checkbox_settings());
233 foreach ($rendered as $checkbox_key) {
234 if (!isset($posted[$checkbox_key])) {
235 $posted[$checkbox_key] = 'off';
236 }
237 }
238
239 $posted = HashFormHelper::sanitize_array($posted, self::sanitize_rules());
240
241 // Merge over the saved options instead of replacing them, so fields
242 // not present on this page are preserved.
243 $settings = array_merge(self::get_settings(), $posted);
244
245 update_option('hashform_options', $settings);
246 HashFormHelper::set_message(esc_html__('Settings Saved !', 'hash-form'));
247
248 self::display_form();
249 }
250
251 public static function get_settings() {
252 $settings = get_option('hashform_options');
253 if (!$settings) {
254 $settings = self::default_values();
255 } else {
256 $settings = wp_parse_args($settings, self::default_values());
257 }
258
259 return $settings;
260 }
261
262 public function send_test_email() {
263 HashFormCapabilities::require_cap_ajax('hashform_manage_settings');
264
265 check_ajax_referer('hashform_backend_ajax', 'backend_nonce');
266
267 $settings = self::get_settings();
268
269 $header_image = $settings['header_image'];
270
271 // Whitelist: the value feeds both a callable name and an include path.
272 $email_template = HashFormHelper::get_post('email_template');
273 if (!in_array($email_template, array('template1', 'template2', 'template3'), true)) {
274 $email_template = 'template1';
275 }
276
277 $test_email = HashFormHelper::get_post('test_email', 'sanitize_email');
278 $email_subject = esc_html__('Test Email', 'hash-form');
279 $count = 0;
280
281 $contents = array(
282 0 => array(
283 'title' => 'Name',
284 'value' => 'John Doe'
285 ),
286 1 => array(
287 'title' => 'Email',
288 'value' => 'noreply@gmail.com'
289 ),
290 2 => array(
291 'title' => 'Subject',
292 'value' => 'Exciting Updates and Important Information Inside!'
293 ),
294 3 => array(
295 'title' => 'Message',
296 'value' => '<p>I hope this email finds you well. We are thrilled to share some exciting updates and important information that we believe you will find valuable.</p><p>Your satisfaction is our priority, and we are committed to delivering the best possible experience.</p>'
297 )
298 );
299
300 $email_message = '<p style="margin-bottom:20px">';
301 $email_message .= esc_html__('Hello, this is a test email.', 'hash-form');
302 $email_message .= '</p>';
303 foreach ($contents as $content) {
304 $count++;
305 $email_message .= call_user_func('HashFormEmail::' . $email_template, $content['title'], $content['value'], $count);
306 }
307 ob_start();
308 include(HASHFORM_PATH . 'admin/settings/email-templates/' . $email_template . '.php');
309 $form_html = ob_get_clean();
310
311 $admin_email = get_option('admin_email');
312 $site_name = get_bloginfo('name');
313 $headers = array();
314 $headers[] = 'Content-Type: text/html; charset=UTF-8';
315 $headers[] = 'From: ' . esc_attr($site_name) . ' <' . esc_attr($admin_email) . '>';
316 $mail = wp_mail($test_email, $email_subject, $form_html, $headers);
317 if ($mail) {
318 die(wp_json_encode(
319 array(
320 'success' => true,
321 'message' => esc_html__('Email Sent Successfully', 'hash-form')
322 )
323 ));
324 }
325 die(wp_json_encode(
326 array(
327 'success' => false,
328 'message' => esc_html__('Failed to Send Email', 'hash-form')
329 )
330 ));
331 }
332
333 public static function checkbox_settings() {
334 return apply_filters('hash_form_settings_checkbox', array(
335 'load_google_fonts' => 'on',
336 ));
337 }
338
339 public static function default_values() {
340 return apply_filters('hash_form_settings_default', array(
341 're_type' => 'v2',
342 'pubkey_v2' => '',
343 'privkey_v2' => '',
344 'pubkey_v3' => '',
345 'privkey_v3' => '',
346 're_lang' => 'en',
347 're_threshold' => '0.5',
348 /*
349 * Read by every captcha field as the message shown when a
350 * challenge is not passed, and defined nowhere until now: each of
351 * them looked up an array key that did not exist, so a field
352 * carrying no message of its own told the visitor "null".
353 */
354 're_msg' => 'The captcha was not completed correctly. Please try again.',
355 'header_image' => '',
356 'email_template' => 'template1',
357 // Left on so an existing site's typography does not change under
358 // it. Sites that would rather not call out to Google can switch it
359 // off without touching their style templates.
360 'load_google_fonts' => 'on',
361 ));
362 }
363
364 public static function sanitize_rules() {
365 return apply_filters('hash_form_settings_sanitize', array(
366 're_type' => 'sanitize_text_field',
367 'pubkey_v2' => 'sanitize_text_field',
368 'privkey_v2' => 'sanitize_text_field',
369 'pubkey_v3' => 'sanitize_text_field',
370 'privkey_v3' => 'sanitize_text_field',
371 're_lang' => 'sanitize_text_field',
372 're_threshold' => 'sanitize_text_field',
373 're_msg' => 'sanitize_text_field',
374 'header_image' => 'sanitize_text_field',
375 'email_template' => 'sanitize_text_field',
376 'load_google_fonts' => 'hashform_sanitize_checkbox',
377 ));
378 }
379
380 }
381
382 new HashFormSettings();
383