| 1 |
<?php |
| 2 |
defined('ABSPATH') || die(); |
| 3 |
|
| 4 |
class HashFormBuilder { |
| 5 |
|
| 6 |
use HashFormListActions; |
| 7 |
|
| 8 |
public function __construct() { |
| 9 |
|
| 10 |
$this->includes(); |
| 11 |
|
| 12 |
add_action('admin_menu', array($this, 'add_menu'), 1); |
| 13 |
add_filter('set-screen-option', array($this, 'set_screen_option'), 10, 3); |
| 14 |
|
| 15 |
add_action('wp_ajax_hashform_update_form', array($this, 'update_form')); |
| 16 |
add_action('wp_ajax_hashform_create_form', array($this, 'create_form')); |
| 17 |
add_action('wp_ajax_hashform_save_form_settings', array($this, 'save_form_settings')); |
| 18 |
add_action('wp_ajax_hashform_save_form_style', array($this, 'save_form_style')); |
| 19 |
add_action('wp_ajax_hashform_form_preview', array($this, 'form_preview')); |
| 20 |
add_action('wp_ajax_hashform_add_more_condition_block', array($this, 'add_more_condition_block')); |
| 21 |
add_action('admin_footer', array($this, 'init_overlay_html')); |
| 22 |
|
| 23 |
// Printed above #wpbody so the bar sits flush under the admin bar and |
| 24 |
// clear of the Screen Options tab. |
| 25 |
add_action('in_admin_header', array($this, 'list_header')); |
| 26 |
|
| 27 |
// Notices are moved inside the screen wrapper; see buffer_notices(). |
| 28 |
add_action('admin_notices', array($this, 'buffer_notices'), -PHP_INT_MAX); |
| 29 |
add_action('all_admin_notices', array($this, 'capture_notices'), PHP_INT_MAX); |
| 30 |
|
| 31 |
add_filter('plugin_action_links_' . plugin_basename(HASHFORM_FILE), array($this, 'add_plugin_action_link'), 10, 1); |
| 32 |
|
| 33 |
add_action('wp_ajax_hashform_file_upload_action', array($this, 'file_upload_action')); |
| 34 |
add_action('wp_ajax_nopriv_hashform_file_upload_action', array($this, 'file_upload_action')); |
| 35 |
|
| 36 |
add_action('wp_ajax_hashform_file_delete_action', array($this, 'file_delete_action')); |
| 37 |
add_action('wp_ajax_nopriv_hashform_file_delete_action', array($this, 'file_delete_action')); |
| 38 |
|
| 39 |
add_action('wp_loaded', array($this, 'admin_notice'), 20); |
| 40 |
|
| 41 |
add_action('init', array($this, 'register_translation_strings')); |
| 42 |
add_filter('hashform_translate_string', array($this, 'translate_string'), 10, 3); |
| 43 |
} |
| 44 |
|
| 45 |
public function includes() { |
| 46 |
include HASHFORM_PATH . 'admin/forms/sanitization.php'; |
| 47 |
} |
| 48 |
|
| 49 |
public function add_menu() { |
| 50 |
global $hashform_listing_page; |
| 51 |
add_menu_page(esc_html__('Hash Form', 'hash-form'), esc_html__('Hash Form', 'hash-form'), 'hashform_view_forms', 'hashform', array($this, 'route'), 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMTcuNjYgMTUyLjI3IiBmaWxsPSIjYTdhYWFkIj48Zz48Zz48cGF0aCBkPSJNMCwzLjQ2QTMuNDYsMy40NiwwLDAsMSwzLjE0LDBoODBBMy41MywzLjUzLDAsMCwxLDg1LjYsMWwzMSwzMWEzLjQ3LDMuNDcsMCwwLDEsMSwyLjQzVjE0OC44MWEzLjQ2LDMuNDYsMCwwLDEtMy40NiwzLjQ2SDMxLjYzYTMuNDYsMy40NiwwLDEsMSwwLTYuOTJoNzkuMTFWMzguMDdIODMuMDVhMy40NiwzLjQ2LDAsMCwxLTMuNDYtMy40NlY2LjkySDYuOTJWMTQ1LjM1SDE0YTMuNDYsMy40NiwwLDEsMSwwLDYuOTJIMy40NkEzLjQ2LDMuNDYsMCwwLDEsMCwxNDguODFaTTEwNiwzMS4xNSw4Ni41MSwxMS42OFYzMS4xNVoiLz48cGF0aCBkPSJNNzguNjYsNTkuM0g5NS4wOXY2LjYxSDc4LjY2Vjg1Ljc1SDcyLjA1VjQyLjg3aDYuNjFabTAsMzkuNjd2MTYuNDJINzIuMDVWOTlINTIuMjJWOTIuMzZIOTUuMDlWOTlaTTM5LDk5SDIyLjU3VjkyLjM2SDM5VjcyLjUyaDYuNjFWMTE1LjRIMzlaTTM5LDU5LjNWNDIuODdoNi42MVY1OS4zSDY1LjQ0djYuNjFIMjIuNTdWNTkuM1oiLz48L2c+PC9nPjwvc3ZnPg==', 29); |
| 52 |
$hashform_listing_page = add_submenu_page('hashform', esc_html__('Forms', 'hash-form'), esc_html__('Forms', 'hash-form'), 'hashform_view_forms', 'hashform', array($this, 'route')); |
| 53 |
add_action("load-$hashform_listing_page", array($this, 'listing_page_screen_options')); |
| 54 |
} |
| 55 |
|
| 56 |
protected static function list_config() { |
| 57 |
return array( |
| 58 |
'page' => 'hashform', |
| 59 |
'table' => 'hashform_forms', |
| 60 |
'id_key' => 'form_id', |
| 61 |
'nonce_item' => 'form', |
| 62 |
'bulk_nonce' => 'bulk-forms', |
| 63 |
'caps' => array( |
| 64 |
'delete' => 'hashform_delete_forms', |
| 65 |
'edit' => 'hashform_edit_forms', |
| 66 |
'create' => 'hashform_create_forms', |
| 67 |
), |
| 68 |
'actions' => array('edit', 'trash', 'destroy', 'untrash', 'delete_all', 'duplicate', 'settings', 'style'), |
| 69 |
); |
| 70 |
} |
| 71 |
|
| 72 |
protected static function destroy_item($id) { |
| 73 |
return self::destroy_form($id); |
| 74 |
} |
| 75 |
|
| 76 |
protected static function render_list($message = '', $class = 'updated') { |
| 77 |
self::display_forms_list($message, $class); |
| 78 |
} |
| 79 |
|
| 80 |
protected static function message_trashed($count, $undo_open, $undo_close) { |
| 81 |
/* translators: 1: form count singular & plural, 2: link open, 3: link close */ |
| 82 |
return sprintf(_n('%1$s form moved to the Trash. %2$sUndo%3$s', '%1$s forms moved to the Trash. %2$sUndo%3$s', $count, 'hash-form'), $count, $undo_open, $undo_close); |
| 83 |
} |
| 84 |
|
| 85 |
protected static function message_untrashed($count) { |
| 86 |
/* translators: 1: form count singular & plural */ |
| 87 |
return sprintf(_n('%1$s form restored from the Trash.', '%1$s forms restored from the Trash.', $count, 'hash-form'), $count); |
| 88 |
} |
| 89 |
|
| 90 |
protected static function message_destroyed($count) { |
| 91 |
/* translators: 1: form count singular & plural */ |
| 92 |
return sprintf(_n('%1$s Form Permanently Deleted', '%1$s Forms Permanently Deleted', $count, 'hash-form'), $count); |
| 93 |
} |
| 94 |
|
| 95 |
protected static function message_deleted($count) { |
| 96 |
/* translators: 1: form count singular & plural */ |
| 97 |
return sprintf(_n('%1$s form permanently deleted.', '%1$s forms permanently deleted.', $count, 'hash-form'), $count); |
| 98 |
} |
| 99 |
|
| 100 |
protected static function message_none_specified() { |
| 101 |
return esc_html__('No forms were specified', 'hash-form'); |
| 102 |
} |
| 103 |
|
| 104 |
public static function display_forms_list($message = '', $class = 'updated') { |
| 105 |
?> |
| 106 |
<div class="hf-content hf-list-screen"> |
| 107 |
|
| 108 |
<?php // The header bar is printed on in_admin_header; see list_header(). ?> |
| 109 |
<div class="hf-list-wrap wrap"> |
| 110 |
<h1></h1> |
| 111 |
|
| 112 |
<?php |
| 113 |
self::print_notices(); |
| 114 |
self::display_message($message, $class); |
| 115 |
|
| 116 |
$form_table = new HashFormListing(); |
| 117 |
$form_status = HashFormHelper::get_var('status', 'sanitize_title', 'published'); |
| 118 |
|
| 119 |
// Prepared up front so the screen can tell an empty list from |
| 120 |
// a search that found nothing before deciding what to print. |
| 121 |
$form_table->prepare_items(); |
| 122 |
$is_searching = '' !== (string) HashFormHelper::get_var('s'); |
| 123 |
?> |
| 124 |
<form id="posts-filter" method="get"> |
| 125 |
<input type="hidden" name="page" value="<?php echo esc_attr(HashFormHelper::get_var('page', 'sanitize_title')); ?>" /> |
| 126 |
<input type="hidden" name="status" value="<?php echo esc_attr($form_status); ?>" /> |
| 127 |
|
| 128 |
<div class="hf-list-toolbar"> |
| 129 |
<?php |
| 130 |
$form_table->views(); |
| 131 |
|
| 132 |
// A search box over nothing is just noise on a first run. |
| 133 |
if ($form_table->has_items() || $is_searching) { |
| 134 |
$form_table->search_box(esc_html__('Search', 'hash-form'), 'search'); |
| 135 |
} |
| 136 |
?> |
| 137 |
</div> |
| 138 |
|
| 139 |
<?php $form_table->display(); ?> |
| 140 |
</form> |
| 141 |
</div> |
| 142 |
</div> |
| 143 |
<?php |
| 144 |
} |
| 145 |
|
| 146 |
/** |
| 147 |
* Totals for the header bar. Empty on a brand new install, where zeroes |
| 148 |
* say nothing. |
| 149 |
*/ |
| 150 |
private static function list_stats() { |
| 151 |
$stats = HashFormListing::get_stats(); |
| 152 |
|
| 153 |
if (!$stats['forms'] && !$stats['trash']) { |
| 154 |
return array(); |
| 155 |
} |
| 156 |
|
| 157 |
$chips = array( |
| 158 |
array( |
| 159 |
'value' => number_format_i18n($stats['forms']), |
| 160 |
'label' => _n('Form', 'Forms', $stats['forms'], 'hash-form'), |
| 161 |
), |
| 162 |
array( |
| 163 |
'value' => number_format_i18n($stats['entries']), |
| 164 |
'label' => _n('Entry', 'Entries', $stats['entries'], 'hash-form'), |
| 165 |
'url' => admin_url('admin.php?page=hashform-entries'), |
| 166 |
), |
| 167 |
); |
| 168 |
|
| 169 |
if ($stats['trash']) { |
| 170 |
$chips[] = array( |
| 171 |
'value' => number_format_i18n($stats['trash']), |
| 172 |
'label' => esc_html__('In Trash', 'hash-form'), |
| 173 |
'url' => admin_url('admin.php?page=hashform&status=trash'), |
| 174 |
); |
| 175 |
} |
| 176 |
|
| 177 |
return $chips; |
| 178 |
} |
| 179 |
|
| 180 |
/** |
| 181 |
* The bar across the top of the Forms list, through the shared renderer. |
| 182 |
*/ |
| 183 |
public function list_header() { |
| 184 |
if (!self::is_list_view()) { |
| 185 |
return; |
| 186 |
} |
| 187 |
|
| 188 |
HashFormHelper::render_list_header(array( |
| 189 |
'title' => esc_html__('Forms', 'hash-form'), |
| 190 |
'stats' => self::list_stats(), |
| 191 |
'action' => array( |
| 192 |
'label' => esc_html__('Add New', 'hash-form'), |
| 193 |
'url' => '#', |
| 194 |
'class' => 'hf-trigger-modal', |
| 195 |
), |
| 196 |
)); |
| 197 |
} |
| 198 |
|
| 199 |
public function create_form() { |
| 200 |
HashFormCapabilities::require_cap_ajax('hashform_create_forms'); |
| 201 |
|
| 202 |
check_ajax_referer('hashform_backend_ajax', 'backend_nonce'); |
| 203 |
|
| 204 |
$name = trim(HashFormHelper::get_post('name')); |
| 205 |
|
| 206 |
// The dialog checks this too, but a form with no name is only ever |
| 207 |
// reachable as "No Title" in the list, so do not create one. |
| 208 |
if ('' === $name) { |
| 209 |
echo wp_json_encode(array('error' => esc_html__('Please give the form a name.', 'hash-form'))); |
| 210 |
wp_die(); |
| 211 |
} |
| 212 |
|
| 213 |
$new_values = array( |
| 214 |
'name' => esc_html($name), |
| 215 |
'description' => '', |
| 216 |
'form_key' => sanitize_text_field($name), |
| 217 |
'options' => array( |
| 218 |
'submit_value' => esc_html__('Submit', 'hash-form'), |
| 219 |
'show_description' => 'on', |
| 220 |
'show_title' => 'on', |
| 221 |
), |
| 222 |
'settings' => HashFormHelper::get_form_settings_default($name) |
| 223 |
); |
| 224 |
$form_id = self::create($new_values); |
| 225 |
$response = array('redirect' => admin_url('admin.php?page=hashform&hashform_action=edit&id=' . absint($form_id))); |
| 226 |
echo wp_json_encode($response); |
| 227 |
wp_die(); |
| 228 |
} |
| 229 |
|
| 230 |
public static function create($values) { |
| 231 |
global $wpdb; |
| 232 |
$options = isset($values['options']) && is_array($values['options']) ? $values['options'] : array(); |
| 233 |
$options = HashFormHelper::recursive_parse_args($options, HashFormHelper::get_form_options_default()); |
| 234 |
$options = HashFormHelper::sanitize_array($options, HashFormHelper::get_form_options_sanitize_rules()); |
| 235 |
|
| 236 |
$settings = isset($values['settings']) && is_array($values['settings']) ? $values['settings'] : array(); |
| 237 |
$settings = HashFormHelper::recursive_parse_args($settings, HashFormHelper::get_form_settings_default()); |
| 238 |
$settings = HashFormHelper::sanitize_array($settings, HashFormHelper::get_form_settings_sanitize_rules()); |
| 239 |
|
| 240 |
$styles = isset($values['styles']) && is_array($values['styles']) ? $values['styles'] : array(); |
| 241 |
$styles = HashFormHelper::recursive_parse_args($styles, array('form_style' => 'default-style', 'form_style_template' => '')); |
| 242 |
$styles = HashFormHelper::sanitize_array($styles, HashFormHelper::get_form_styles_sanitize_rules()); |
| 243 |
|
| 244 |
$new_values = array( |
| 245 |
'form_key' => HashFormHelper::get_unique_key('hashform_forms', 'form_key'), |
| 246 |
'name' => esc_html($values['name']), |
| 247 |
'description' => esc_html($values['description']), |
| 248 |
'status' => isset($values['status']) ? sanitize_text_field($values['status']) : 'published', |
| 249 |
'created_at' => isset($values['created_at']) ? sanitize_text_field($values['created_at']) : current_time('mysql'), |
| 250 |
'options' => serialize($options), |
| 251 |
'settings' => serialize($settings), |
| 252 |
'styles' => serialize($styles), |
| 253 |
); |
| 254 |
$wpdb->insert($wpdb->prefix . 'hashform_forms', $new_values); |
| 255 |
$id = $wpdb->insert_id; |
| 256 |
return $id; |
| 257 |
} |
| 258 |
|
| 259 |
public function update_form() { |
| 260 |
HashFormCapabilities::require_cap_ajax('hashform_edit_forms'); |
| 261 |
|
| 262 |
check_ajax_referer('hashform_backend_ajax', 'backend_nonce'); |
| 263 |
|
| 264 |
$fields_array = $settings_array = array(); |
| 265 |
|
| 266 |
$fields = htmlspecialchars_decode(nl2br(str_replace('"', '"', HashFormHelper::get_post('hashform_fields', 'esc_html')))); |
| 267 |
|
| 268 |
if ($fields) { |
| 269 |
$fields_array = HashFormHelper::parse_json_array($fields); |
| 270 |
} |
| 271 |
|
| 272 |
$settings = htmlspecialchars_decode(nl2br(str_replace('"', '"', HashFormHelper::get_post('hashform_settings', 'esc_html')))); |
| 273 |
if ($settings) { |
| 274 |
$settings_array = HashFormHelper::parse_json_array($settings); |
| 275 |
} |
| 276 |
|
| 277 |
self::update($fields_array, $settings_array); |
| 278 |
} |
| 279 |
|
| 280 |
public static function update($fields_values, $settings_values) { |
| 281 |
$id = isset($fields_values['id']) ? absint($fields_values['id']) : ''; |
| 282 |
|
| 283 |
self::update_form_options($id, $settings_values); |
| 284 |
HashFormFields::update_form_fields($id, $fields_values); |
| 285 |
|
| 286 |
$message = '<span class="mdi mdi-check-circle"></span>' . esc_html__('Form was successfully updated.', 'hash-form'); |
| 287 |
|
| 288 |
if (defined('DOING_AJAX')) { |
| 289 |
/* |
| 290 |
* The span is allowed a class: without one, kses stripped it and |
| 291 |
* the tick that goes with the message never reached the page — |
| 292 |
* only the empty element it should have been drawn in. |
| 293 |
*/ |
| 294 |
wp_die(wp_kses($message, array( |
| 295 |
'a' => array('href' => array(), 'target' => array()), |
| 296 |
'span' => array('class' => array()), |
| 297 |
))); |
| 298 |
} |
| 299 |
} |
| 300 |
|
| 301 |
public static function update_form_options($id, $args) { |
| 302 |
global $wpdb; |
| 303 |
$options = HashFormHelper::recursive_parse_args($args, HashFormHelper::get_form_options_checkbox_settings()); |
| 304 |
$options = HashFormHelper::sanitize_array($options, HashFormHelper::get_form_options_sanitize_rules()); |
| 305 |
|
| 306 |
$query_results = $wpdb->update($wpdb->prefix . 'hashform_forms', array( |
| 307 |
'name' => esc_html(isset($args['title']) ? $args['title'] : ''), |
| 308 |
'description' => esc_html(isset($args['description']) ? $args['description'] : ''), |
| 309 |
'options' => maybe_serialize($options) |
| 310 |
), array('id' => $id)); |
| 311 |
return $query_results; |
| 312 |
} |
| 313 |
|
| 314 |
public static function edit() { |
| 315 |
require(HASHFORM_PATH . 'admin/forms/build/edit.php'); |
| 316 |
} |
| 317 |
|
| 318 |
public static function settings() { |
| 319 |
require HASHFORM_PATH . 'admin/forms/settings/settings.php'; |
| 320 |
} |
| 321 |
|
| 322 |
public static function style() { |
| 323 |
require HASHFORM_PATH . 'admin/forms/style/style.php'; |
| 324 |
} |
| 325 |
|
| 326 |
public function listing_page_screen_options() { |
| 327 |
|
| 328 |
global $hashform_listing_page; |
| 329 |
|
| 330 |
$screen = get_current_screen(); |
| 331 |
|
| 332 |
// get out of here if we are not on our settings page |
| 333 |
if (!is_object($screen) || $screen->id != $hashform_listing_page) { |
| 334 |
return; |
| 335 |
} |
| 336 |
|
| 337 |
$args = array( |
| 338 |
'label' => esc_html__('Forms per page', 'hash-form'), |
| 339 |
'default' => 10, |
| 340 |
'option' => 'forms_per_page' |
| 341 |
); |
| 342 |
add_screen_option('per_page', $args); |
| 343 |
|
| 344 |
new HashFormListing(); |
| 345 |
} |
| 346 |
|
| 347 |
public function set_screen_option($status, $option, $value) { |
| 348 |
return ('forms_per_page' === $option) ? $value : $status; |
| 349 |
} |
| 350 |
|
| 351 |
public static function destroy_form($id) { |
| 352 |
global $wpdb; |
| 353 |
$form = self::get_form_vars($id); |
| 354 |
if (!$form) { |
| 355 |
return false; |
| 356 |
} |
| 357 |
|
| 358 |
$id = $form->id; |
| 359 |
$entries = $wpdb->get_col($wpdb->prepare("SELECT id FROM {$wpdb->prefix}hashform_entries WHERE form_id=%d", $id)); |
| 360 |
|
| 361 |
foreach ($entries as $entry_id) { |
| 362 |
HashFormEntry::destroy_entry($entry_id); |
| 363 |
} |
| 364 |
|
| 365 |
$wpdb->query($wpdb->prepare('DELETE hfi FROM ' . $wpdb->prefix . 'hashform_fields AS hfi LEFT JOIN ' . $wpdb->prefix . 'hashform_forms hfm ON (hfi.form_id = hfm.id) WHERE hfi.form_id=%d', $id)); |
| 366 |
|
| 367 |
$results = $wpdb->query($wpdb->prepare('DELETE FROM ' . $wpdb->prefix . 'hashform_forms WHERE id=%d', $id)); |
| 368 |
return $results; |
| 369 |
} |
| 370 |
|
| 371 |
public static function duplicate() { |
| 372 |
global $wpdb; |
| 373 |
$message = ''; |
| 374 |
$nonce = HashFormHelper::get_var('_wpnonce'); |
| 375 |
$id = HashFormHelper::get_var('id', 'absint'); |
| 376 |
|
| 377 |
if (!wp_verify_nonce($nonce, 'duplicate_form_' . $id)) { |
| 378 |
wp_die(esc_html__('Error ! Refresh the page and try again.', 'hash-form')); |
| 379 |
} |
| 380 |
|
| 381 |
static::require_list_cap('create'); |
| 382 |
|
| 383 |
$values = self::get_form_vars($id); |
| 384 |
|
| 385 |
if (!$values) { |
| 386 |
return false; |
| 387 |
} |
| 388 |
|
| 389 |
$options = HashFormHelper::recursive_parse_args($values->options, HashFormHelper::get_form_options_default()); |
| 390 |
$options = HashFormHelper::sanitize_array($options, HashFormHelper::get_form_options_sanitize_rules()); |
| 391 |
|
| 392 |
$settings = HashFormHelper::recursive_parse_args($values->settings, HashFormHelper::get_form_settings_default()); |
| 393 |
$settings = HashFormHelper::sanitize_array($settings, HashFormHelper::get_form_settings_sanitize_rules()); |
| 394 |
|
| 395 |
$styles = HashFormHelper::recursive_parse_args($values->styles, array('form_style' => 'default-style', 'form_style_template' => '')); |
| 396 |
$styles = HashFormHelper::sanitize_array($styles, HashFormHelper::get_form_styles_sanitize_rules()); |
| 397 |
|
| 398 |
$new_values = array( |
| 399 |
'form_key' => HashFormHelper::get_unique_key('hashform_forms', 'form_key'), |
| 400 |
'name' => esc_html($values->name) . ' - ' . esc_html__('Copy', 'hash-form'), |
| 401 |
'description' => esc_html($values->description), |
| 402 |
'status' => $values->status ? sanitize_text_field($values->status) : 'published', |
| 403 |
'created_at' => sanitize_text_field(current_time('mysql')), |
| 404 |
'options' => serialize($options), |
| 405 |
'settings' => serialize($settings), |
| 406 |
'styles' => serialize($styles), |
| 407 |
); |
| 408 |
|
| 409 |
$query_results = $wpdb->insert($wpdb->prefix . 'hashform_forms', $new_values); |
| 410 |
|
| 411 |
$form_id = 0; |
| 412 |
if ($query_results) { |
| 413 |
$form_id = $wpdb->insert_id; |
| 414 |
$map = HashFormFields::duplicate_fields($id, $form_id); |
| 415 |
|
| 416 |
/* |
| 417 |
* The rules were written above with the source form's field ids, |
| 418 |
* which the copy does not have. Rewritten here, once the copy's own |
| 419 |
* fields exist and the two can be matched up. |
| 420 |
*/ |
| 421 |
self::remap_calculation_formulas($form_id, $map); |
| 422 |
|
| 423 |
if (!empty($settings['condition_action'])) { |
| 424 |
$settings = self::remap_conditions($settings, $map, $dropped); |
| 425 |
|
| 426 |
$wpdb->update( |
| 427 |
$wpdb->prefix . 'hashform_forms', |
| 428 |
array('settings' => serialize($settings)), |
| 429 |
array('id' => $form_id) |
| 430 |
); |
| 431 |
|
| 432 |
if ($dropped) { |
| 433 |
HashFormHelper::log(sprintf( |
| 434 |
'copying form %d: %d show/hide rule(s) named a field the form no longer has and were not copied', |
| 435 |
absint($id), |
| 436 |
$dropped |
| 437 |
)); |
| 438 |
} |
| 439 |
} |
| 440 |
} |
| 441 |
|
| 442 |
if ($form_id) { |
| 443 |
$message = esc_html__('Form was Successfully Copied', 'hash-form'); |
| 444 |
$class = 'updated'; |
| 445 |
} else { |
| 446 |
$message = esc_html__('Error! Form Can not be Copied', 'hash-form'); |
| 447 |
$class = 'error'; |
| 448 |
} |
| 449 |
|
| 450 |
self::display_forms_list($message, $class); |
| 451 |
} |
| 452 |
|
| 453 |
public static function get_admin_header($atts = array()) { |
| 454 |
$class = isset($atts['class']) ? $atts['class'] : ''; |
| 455 |
$form = $atts['form']; |
| 456 |
$form_title = $form->name; |
| 457 |
?> |
| 458 |
<?php |
| 459 |
/* |
| 460 |
* Two rows: what is being edited and what can be done to it on top, |
| 461 |
* where you are underneath. A single row had the form name, four tabs |
| 462 |
* and four actions competing for the same line, which left no room for |
| 463 |
* a long form title and no hierarchy between them. |
| 464 |
*/ |
| 465 |
$status = isset($form->status) ? $form->status : 'published'; |
| 466 |
$is_published = ('published' === $status); |
| 467 |
?> |
| 468 |
<div id="hf-header" class="<?php echo esc_attr($class); ?>"> |
| 469 |
<div class="hf-header-top"> |
| 470 |
<a class="hf-header-back" href="<?php echo esc_url(admin_url('admin.php?page=hashform')); ?>" aria-label="<?php esc_attr_e('Back to forms', 'hash-form'); ?>"> |
| 471 |
<span class="mdi mdi-arrow-left"></span> |
| 472 |
</a> |
| 473 |
|
| 474 |
<div class="hf-header-identity"> |
| 475 |
<span class="hf-header-mark" aria-hidden="true"> |
| 476 |
<span class="mdi mdi-file-document-outline"></span> |
| 477 |
</span> |
| 478 |
|
| 479 |
<h1 class="hf-header-title"><?php echo esc_html($form_title); ?></h1> |
| 480 |
<span class="hf-header-status<?php echo $is_published ? ' hf-is-published' : ''; ?>"> |
| 481 |
<?php echo $is_published ? esc_html__('Published', 'hash-form') : esc_html__('Draft', 'hash-form'); ?> |
| 482 |
</span> |
| 483 |
</div> |
| 484 |
|
| 485 |
<div class="hf-header-actions"> |
| 486 |
<a class="hf-preview-button" href="<?php echo esc_url(admin_url('admin-ajax.php?action=hashform_preview&form=' . absint($form->id))); ?>" target="_blank"> |
| 487 |
<span class="mdi mdi-eye-outline"></span><?php esc_html_e('Preview', 'hash-form'); ?> |
| 488 |
</a> |
| 489 |
|
| 490 |
<button class="hf-embed-button" type="button"> |
| 491 |
<span class="mdi mdi-code-brackets"></span><?php esc_html_e('Embed', 'hash-form'); ?> |
| 492 |
</button> |
| 493 |
|
| 494 |
<button class="hashform-ajax-udpate-button" type="button" id="hf-update-button"> |
| 495 |
<span class="mdi mdi-check-circle-outline"></span><?php esc_html_e('Save', 'hash-form'); ?> |
| 496 |
</button> |
| 497 |
</div> |
| 498 |
</div> |
| 499 |
|
| 500 |
<div class="hf-header-bottom"> |
| 501 |
<?php self::get_form_nav($form); ?> |
| 502 |
</div> |
| 503 |
</div> |
| 504 |
<?php |
| 505 |
} |
| 506 |
|
| 507 |
public static function get_form_nav($form) { |
| 508 |
if (!$form) { |
| 509 |
return; |
| 510 |
} |
| 511 |
$id = $form->id; |
| 512 |
$nav_items = self::get_form_nav_items($id); |
| 513 |
?> |
| 514 |
<ul class="hf-main-nav"> |
| 515 |
<?php foreach ($nav_items as $nav_item) { ?> |
| 516 |
<li> |
| 517 |
<a href="<?php echo esc_url($nav_item['link']); ?>" class="<?php echo self::is_current_page($nav_item['page'], $nav_item['current']) ? 'hf-active-nav' : ''; ?>"> |
| 518 |
<?php echo esc_html($nav_item['label']); ?> |
| 519 |
</a> |
| 520 |
</li> |
| 521 |
<?php } ?> |
| 522 |
</ul> |
| 523 |
<?php |
| 524 |
} |
| 525 |
|
| 526 |
public static function get_form_nav_items($id) { |
| 527 |
$nav_items = array( |
| 528 |
array( |
| 529 |
'link' => admin_url('admin.php?page=hashform&hashform_action=edit&id=' . absint($id)), |
| 530 |
'label' => esc_html__('Build', 'hash-form'), |
| 531 |
'current' => array('edit', 'new', 'duplicate'), |
| 532 |
'page' => 'hashform' |
| 533 |
), |
| 534 |
array( |
| 535 |
'link' => admin_url('admin.php?page=hashform&hashform_action=settings&id=' . absint($id)), |
| 536 |
'label' => esc_html__('Settings', 'hash-form'), |
| 537 |
'current' => array('settings'), |
| 538 |
'page' => 'hashform' |
| 539 |
), |
| 540 |
array( |
| 541 |
'link' => admin_url('admin.php?page=hashform&hashform_action=style&id=' . absint($id)), |
| 542 |
'label' => esc_html__('Style', 'hash-form'), |
| 543 |
'current' => array('style'), |
| 544 |
'page' => 'hashform' |
| 545 |
), |
| 546 |
array( |
| 547 |
'link' => admin_url('admin.php?page=hashform-entries&form_id=' . absint($id)), |
| 548 |
'label' => esc_html__('Entries', 'hash-form'), |
| 549 |
'current' => array(), |
| 550 |
'page' => 'hashform-entries' |
| 551 |
), |
| 552 |
); |
| 553 |
return $nav_items; |
| 554 |
} |
| 555 |
|
| 556 |
public static function is_current_page($page, $action = array()) { |
| 557 |
$current_page = HashFormHelper::get_var('page'); |
| 558 |
$hashform_action = HashFormHelper::get_var('hashform_action'); |
| 559 |
|
| 560 |
if (($page == $current_page) && (!empty($hashform_action) && in_array($hashform_action, $action))) { |
| 561 |
return true; |
| 562 |
} |
| 563 |
return false; |
| 564 |
} |
| 565 |
|
| 566 |
public static function get_all_forms() { |
| 567 |
global $wpdb; |
| 568 |
$results = $wpdb->get_results($wpdb->prepare("SELECT * FROM {$wpdb->prefix}hashform_forms WHERE id!=%d", 0)); |
| 569 |
return $results; |
| 570 |
} |
| 571 |
|
| 572 |
/** |
| 573 |
* The forms a visitor could actually be shown. |
| 574 |
* |
| 575 |
* get_all_forms() means all of them, trashed included, which is right for |
| 576 |
* anything looking back at what exists. It is wrong for a chooser: every |
| 577 |
* dropdown that offers a form to display was listing forms their owner had |
| 578 |
* thrown away, and picking one rendered it on the page. |
| 579 |
* |
| 580 |
* @return array |
| 581 |
*/ |
| 582 |
public static function get_published_forms() { |
| 583 |
global $wpdb; |
| 584 |
|
| 585 |
return $wpdb->get_results($wpdb->prepare( |
| 586 |
"SELECT * FROM {$wpdb->prefix}hashform_forms WHERE status = %s ORDER BY name", |
| 587 |
'published' |
| 588 |
)); |
| 589 |
} |
| 590 |
|
| 591 |
public static function get_form_vars($id) { |
| 592 |
global $wpdb; |
| 593 |
|
| 594 |
/** |
| 595 |
* Supply a form without one being stored. |
| 596 |
* |
| 597 |
* Return anything other than null and the query below is skipped. It is |
| 598 |
* what lets a form be rendered from a definition held in memory - the |
| 599 |
* template demos read their forms out of a json file rather than |
| 600 |
* writing one row per template into the database just to look at it. |
| 601 |
* |
| 602 |
* @param object|null $form null to load it as usual. |
| 603 |
* @param int $id |
| 604 |
*/ |
| 605 |
$pre = apply_filters('hashform_pre_get_form_vars', null, $id); |
| 606 |
|
| 607 |
if (null !== $pre) { |
| 608 |
return $pre; |
| 609 |
} |
| 610 |
|
| 611 |
$results = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$wpdb->prefix}hashform_forms WHERE id=%d", $id)); |
| 612 |
|
| 613 |
if (!$results) { |
| 614 |
return; |
| 615 |
} |
| 616 |
|
| 617 |
foreach ($results as $key => $value) { |
| 618 |
$results->$key = maybe_unserialize($value); |
| 619 |
} |
| 620 |
|
| 621 |
return $results; |
| 622 |
} |
| 623 |
|
| 624 |
public static function get_form_title($id) { |
| 625 |
global $wpdb; |
| 626 |
|
| 627 |
// Rendering calls this once or twice per field; cache per request. |
| 628 |
static $titles = array(); |
| 629 |
if (array_key_exists($id, $titles)) { |
| 630 |
return $titles[$id]; |
| 631 |
} |
| 632 |
|
| 633 |
$name = $wpdb->get_var($wpdb->prepare("SELECT name FROM {$wpdb->prefix}hashform_forms WHERE id=%d", $id)); |
| 634 |
$titles[$id] = (null === $name) ? null : maybe_unserialize($name); |
| 635 |
|
| 636 |
return $titles[$id]; |
| 637 |
} |
| 638 |
|
| 639 |
public function init_overlay_html() { |
| 640 |
$plugin_path = HASHFORM_PATH; |
| 641 |
$new_form_overlay = apply_filters('hashform_new_form_overlay_template', $plugin_path . 'admin/forms/new-form-overlay.php'); |
| 642 |
|
| 643 |
if (HashFormHelper::is_form_listing_page()) { |
| 644 |
include $new_form_overlay; |
| 645 |
} |
| 646 |
|
| 647 |
if (HashFormHelper::is_form_builder_page()) { |
| 648 |
include $plugin_path . 'admin/forms/shortcode-overlay.php'; |
| 649 |
} |
| 650 |
} |
| 651 |
|
| 652 |
public function save_form_settings() { |
| 653 |
HashFormCapabilities::require_cap_ajax('hashform_edit_forms'); |
| 654 |
|
| 655 |
$json_vars = htmlspecialchars_decode(nl2br(str_replace('"', '"', HashFormHelper::get_post('hashform_compact_fields')))); |
| 656 |
$vars = HashFormHelper::parse_json_array($json_vars); |
| 657 |
|
| 658 |
if (!isset($vars['hashform_process_form_nonce']) || !wp_verify_nonce($vars['hashform_process_form_nonce'], 'hashform_process_form_action')) { |
| 659 |
wp_die(esc_html__('Sorry, Nonce did not verify.', 'hash-form')); |
| 660 |
} |
| 661 |
|
| 662 |
$email_to_array = array(); |
| 663 |
$email_to_rows = isset($vars['email_to']) ? (array) $vars['email_to'] : array(); |
| 664 |
foreach ($email_to_rows as $row) { |
| 665 |
$email_to_val = trim($row); |
| 666 |
if ($email_to_val) { |
| 667 |
$email_to_array[] = $email_to_val; |
| 668 |
} |
| 669 |
} |
| 670 |
|
| 671 |
$vars['email_to'] = implode(',', $email_to_array); |
| 672 |
$vars = self::drop_incomplete_conditions($vars); |
| 673 |
$id = isset($vars['id']) ? absint($vars['id']) : HashFormHelper::get_var('id', 'absint'); |
| 674 |
unset($vars['id'], $vars['hashform_process_form_nonce'], $vars['_wp_http_referer']); |
| 675 |
|
| 676 |
self::update_settings($id, $vars); |
| 677 |
$message = '<span class="mdi mdi-check-circle"></span>' . esc_html__('Form was successfully updated.', 'hash-form'); |
| 678 |
wp_die(wp_kses_post($message)); |
| 679 |
|
| 680 |
} |
| 681 |
|
| 682 |
public function save_form_style() { |
| 683 |
HashFormCapabilities::require_cap_ajax('hashform_edit_forms'); |
| 684 |
|
| 685 |
check_ajax_referer('hashform_backend_ajax', 'backend_nonce'); |
| 686 |
|
| 687 |
$json_vars = htmlspecialchars_decode(nl2br(str_replace('"', '"', HashFormHelper::get_post('hashform_compact_fields')))); |
| 688 |
$vars = HashFormHelper::parse_json_array($json_vars); |
| 689 |
$id = isset($vars['id']) ? absint($vars['id']) : HashFormHelper::get_var('id', 'absint'); |
| 690 |
|
| 691 |
self::update_style($id, $vars); |
| 692 |
$message = '<span class="mdi mdi-check-circle"></span>' . esc_html__('Form was successfully updated.', 'hash-form'); |
| 693 |
wp_die(wp_kses_post($message)); |
| 694 |
} |
| 695 |
|
| 696 |
public function form_preview() { |
| 697 |
HashFormCapabilities::require_cap_ajax('hashform_edit_forms'); |
| 698 |
|
| 699 |
check_ajax_referer('hashform_backend_ajax', 'backend_nonce'); |
| 700 |
|
| 701 |
ob_start(); |
| 702 |
remove_action('wp_head', 'print_emoji_detection_script', 7); |
| 703 |
remove_action('wp_print_styles', 'print_emoji_styles'); |
| 704 |
wp_head(); |
| 705 |
|
| 706 |
$form_id = HashFormHelper::get_post('form_id', 'absint'); |
| 707 |
|
| 708 |
HashFormPreview::show_form($form_id); |
| 709 |
wp_footer(); |
| 710 |
wp_send_json_success(ob_get_clean()); |
| 711 |
|
| 712 |
} |
| 713 |
|
| 714 |
public static function update_settings($id, $values) { |
| 715 |
global $wpdb; |
| 716 |
$values = HashFormHelper::recursive_parse_args($values, HashFormHelper::get_form_settings_checkbox_settings()); |
| 717 |
$values = HashFormHelper::sanitize_array($values, HashFormHelper::get_form_settings_sanitize_rules()); |
| 718 |
|
| 719 |
$new_values = array( |
| 720 |
'settings' => serialize($values) |
| 721 |
); |
| 722 |
if (!empty($new_values)) { |
| 723 |
$query_results = $wpdb->update($wpdb->prefix . 'hashform_forms', $new_values, array('id' => $id)); |
| 724 |
} |
| 725 |
return $query_results; |
| 726 |
} |
| 727 |
|
| 728 |
public static function update_style($id, $value) { |
| 729 |
global $wpdb; |
| 730 |
$new_values = array( |
| 731 |
'styles' => serialize(HashFormHelper::sanitize_array($value)) |
| 732 |
); |
| 733 |
if (!empty($new_values)) { |
| 734 |
$query_results = $wpdb->update($wpdb->prefix . 'hashform_forms', $new_values, array('id' => $id)); |
| 735 |
} |
| 736 |
return $query_results; |
| 737 |
} |
| 738 |
|
| 739 |
/** |
| 740 |
* What a rule does when it matches. |
| 741 |
*/ |
| 742 |
public static function condition_actions() { |
| 743 |
return array( |
| 744 |
'show' => esc_html__('Show', 'hash-form'), |
| 745 |
'hide' => esc_html__('Hide', 'hash-form'), |
| 746 |
); |
| 747 |
} |
| 748 |
|
| 749 |
/** |
| 750 |
* How a rule compares the answer it watches. |
| 751 |
*/ |
| 752 |
public static function condition_operators() { |
| 753 |
return array( |
| 754 |
'equal' => esc_html__('Equals to', 'hash-form'), |
| 755 |
'not_equal' => esc_html__('Not Equals to', 'hash-form'), |
| 756 |
'greater_than' => esc_html__('Greater Than', 'hash-form'), |
| 757 |
'greater_than_or_equal' => esc_html__('Greater Than Or Equals to', 'hash-form'), |
| 758 |
'less_than' => esc_html__('Less Than', 'hash-form'), |
| 759 |
'less_than_or_equal' => esc_html__('Less Than Or Equals to', 'hash-form'), |
| 760 |
'is_like' => esc_html__('Is Like', 'hash-form'), |
| 761 |
'is_not_like' => esc_html__('Is Not Like', 'hash-form'), |
| 762 |
); |
| 763 |
} |
| 764 |
|
| 765 |
/** |
| 766 |
* Fields a rule can point at. |
| 767 |
* |
| 768 |
* Layout fields hold no answer, so they can neither be watched nor be |
| 769 |
* usefully shown and hidden. The trigger side excludes a little more: |
| 770 |
* name and address post several values under one id, which the comparison |
| 771 |
* has no way to pick between. |
| 772 |
* |
| 773 |
* This list used to be written out four times — twice in the panel and |
| 774 |
* twice in the AJAX handler that appends a row — and the copies had |
| 775 |
* already drifted. |
| 776 |
* |
| 777 |
* @param object[] $fields Form fields. |
| 778 |
* @param bool $trigger Whether this is the watched side of a rule. |
| 779 |
* @return object[] |
| 780 |
*/ |
| 781 |
public static function condition_fields($fields, $trigger = false) { |
| 782 |
$skip = array('heading', 'paragraph', 'separator', 'spacer', 'image', 'captcha'); |
| 783 |
|
| 784 |
if ($trigger) { |
| 785 |
// html and multi_step post nothing, so a rule watching one could |
| 786 |
// never match. They stay available on the other side, where a |
| 787 |
// rule can still show and hide them. |
| 788 |
$skip = array_merge($skip, array('name', 'address', 'html', 'multi_step')); |
| 789 |
} |
| 790 |
|
| 791 |
$usable = array(); |
| 792 |
|
| 793 |
foreach ($fields as $field) { |
| 794 |
if (!in_array($field->type, $skip, true)) { |
| 795 |
$usable[] = $field; |
| 796 |
} |
| 797 |
} |
| 798 |
|
| 799 |
return $usable; |
| 800 |
} |
| 801 |
|
| 802 |
/** |
| 803 |
* One rule, as shown in the Conditional Logic panel. |
| 804 |
* |
| 805 |
* The panel and the AJAX handler that appends a row both render through |
| 806 |
* here, so a newly added rule cannot look different from a saved one — |
| 807 |
* which it did: the saved rows carried untranslated English labels. |
| 808 |
* |
| 809 |
* @param object[] $fields Form fields. |
| 810 |
* @param array $row Saved rule, or empty for a new one. |
| 811 |
*/ |
| 812 |
public static function condition_row_html($fields, $row = array()) { |
| 813 |
$value = isset($row['compare_value']) ? $row['compare_value'] : ''; |
| 814 |
?> |
| 815 |
<div class="hf-condition-row"> |
| 816 |
<div class="hf-condition-head"> |
| 817 |
<span class="hf-condition-index" aria-hidden="true"></span> |
| 818 |
<button type="button" class="hf-condition-remove" title="<?php esc_attr_e('Delete this rule', 'hash-form'); ?>"> |
| 819 |
<span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> |
| 820 |
<span class="screen-reader-text"><?php esc_html_e('Delete this rule', 'hash-form'); ?></span> |
| 821 |
</button> |
| 822 |
</div> |
| 823 |
|
| 824 |
<div class="hf-condition-grid"> |
| 825 |
<label class="hf-condition-cell hf-condition-cell-action"> |
| 826 |
<span><?php esc_html_e('Action', 'hash-form'); ?></span> |
| 827 |
<select name="condition_action[]"> |
| 828 |
<?php foreach (self::condition_actions() as $key => $label) { ?> |
| 829 |
<option value="<?php echo esc_attr($key); ?>" <?php selected(isset($row['condition_action']) ? $row['condition_action'] : '', $key); ?>><?php echo esc_html($label); ?></option> |
| 830 |
<?php } ?> |
| 831 |
</select> |
| 832 |
</label> |
| 833 |
|
| 834 |
<label class="hf-condition-cell hf-condition-cell-target"> |
| 835 |
<span><?php esc_html_e('This field', 'hash-form'); ?></span> |
| 836 |
<select name="compare_from[]"> |
| 837 |
<option value=""><?php esc_html_e('Select a field', 'hash-form'); ?></option> |
| 838 |
<?php foreach (self::condition_fields($fields) as $field) { ?> |
| 839 |
<option value="<?php echo esc_attr($field->id); ?>" <?php selected(isset($row['compare_from']) ? $row['compare_from'] : '', $field->id); ?>><?php echo esc_html($field->name . ' (ID: ' . $field->id . ')'); ?></option> |
| 840 |
<?php } ?> |
| 841 |
</select> |
| 842 |
</label> |
| 843 |
|
| 844 |
<label class="hf-condition-cell hf-condition-cell-trigger"> |
| 845 |
<span><?php esc_html_e('When', 'hash-form'); ?></span> |
| 846 |
<select name="compare_to[]"> |
| 847 |
<option value=""><?php esc_html_e('Select a field', 'hash-form'); ?></option> |
| 848 |
<?php foreach (self::condition_fields($fields, true) as $field) { ?> |
| 849 |
<option value="<?php echo esc_attr($field->id); ?>" <?php selected(isset($row['compare_to']) ? $row['compare_to'] : '', $field->id); ?>><?php echo esc_html($field->name . ' (ID: ' . $field->id . ')'); ?></option> |
| 850 |
<?php } ?> |
| 851 |
</select> |
| 852 |
</label> |
| 853 |
|
| 854 |
<label class="hf-condition-cell hf-condition-cell-operator"> |
| 855 |
<span><?php esc_html_e('Is', 'hash-form'); ?></span> |
| 856 |
<select name="compare_condition[]"> |
| 857 |
<?php foreach (self::condition_operators() as $key => $label) { ?> |
| 858 |
<option value="<?php echo esc_attr($key); ?>" <?php selected(isset($row['compare_condition']) ? $row['compare_condition'] : '', $key); ?>><?php echo esc_html($label); ?></option> |
| 859 |
<?php } ?> |
| 860 |
</select> |
| 861 |
</label> |
| 862 |
|
| 863 |
<label class="hf-condition-cell hf-condition-cell-value"> |
| 864 |
<span><?php esc_html_e('Value', 'hash-form'); ?></span> |
| 865 |
<input type="text" name="compare_value[]" value="<?php echo esc_attr($value); ?>" placeholder="<?php esc_attr_e('Answer to compare against', 'hash-form'); ?>" /> |
| 866 |
</label> |
| 867 |
</div> |
| 868 |
</div> |
| 869 |
<?php |
| 870 |
} |
| 871 |
|
| 872 |
public function add_more_condition_block() { |
| 873 |
HashFormCapabilities::require_cap_ajax('hashform_edit_forms'); |
| 874 |
|
| 875 |
check_ajax_referer('hashform_backend_ajax', 'backend_nonce'); |
| 876 |
|
| 877 |
$form_id = HashFormHelper::get_post('form_id', 'absint', 0); |
| 878 |
self::condition_row_html(HashFormFields::get_form_fields($form_id)); |
| 879 |
die(); |
| 880 |
} |
| 881 |
|
| 882 |
/** |
| 883 |
* Discards conditional logic rules that name no fields. |
| 884 |
* |
| 885 |
* The panel is saved over AJAX from serializeArray(), so the browser's |
| 886 |
* own required checks never run — a rule left half-filled used to be |
| 887 |
* stored, then read back on every page load as a rule that can never |
| 888 |
* match. The five columns are parallel arrays, so they are rebuilt |
| 889 |
* together or they fall out of step. |
| 890 |
* |
| 891 |
* @param array $vars Posted settings. |
| 892 |
* @return array |
| 893 |
*/ |
| 894 |
private static function drop_incomplete_conditions($vars) { |
| 895 |
if (!isset($vars['condition_action']) || !is_array($vars['condition_action'])) { |
| 896 |
return $vars; |
| 897 |
} |
| 898 |
|
| 899 |
$columns = array('condition_action', 'compare_from', 'compare_to', 'compare_condition', 'compare_value'); |
| 900 |
$kept = array_fill_keys($columns, array()); |
| 901 |
|
| 902 |
foreach (array_keys($vars['condition_action']) as $key) { |
| 903 |
$from = isset($vars['compare_from'][$key]) ? trim($vars['compare_from'][$key]) : ''; |
| 904 |
$to = isset($vars['compare_to'][$key]) ? trim($vars['compare_to'][$key]) : ''; |
| 905 |
|
| 906 |
if ('' === $from || '' === $to) { |
| 907 |
continue; |
| 908 |
} |
| 909 |
|
| 910 |
foreach ($columns as $column) { |
| 911 |
$kept[$column][] = isset($vars[$column][$key]) ? $vars[$column][$key] : ''; |
| 912 |
} |
| 913 |
} |
| 914 |
|
| 915 |
return array_merge($vars, $kept); |
| 916 |
} |
| 917 |
|
| 918 |
public static function get_show_hide_conditions($id) { |
| 919 |
$form = HashFormBuilder::get_form_vars($id); |
| 920 |
$settings = isset($form->settings) ? $form->settings : array(); |
| 921 |
$conditions = array(); |
| 922 |
if (isset($settings['condition_action']) && $settings['condition_action']) { |
| 923 |
foreach ($settings['condition_action'] as $key => $row) { |
| 924 |
$condition = array( |
| 925 |
'condition_action' => $settings['condition_action'][$key], |
| 926 |
'compare_from' => $settings['compare_from'][$key], |
| 927 |
'compare_to' => $settings['compare_to'][$key], |
| 928 |
'compare_condition' => $settings['compare_condition'][$key], |
| 929 |
'compare_value' => $settings['compare_value'][$key], |
| 930 |
); |
| 931 |
$conditions[] = $condition; |
| 932 |
} |
| 933 |
} |
| 934 |
return $conditions; |
| 935 |
} |
| 936 |
|
| 937 |
/** |
| 938 |
* Point a copied form's rules at the copy's own fields. |
| 939 |
* |
| 940 |
* A rule stores field ids. Duplicating a form, importing one, or starting |
| 941 |
* from a template all build fresh fields with fresh ids, and the rules |
| 942 |
* were carried across untouched - so every one of them named a field that |
| 943 |
* did not exist on the new form. Nothing was shown or hidden, and the |
| 944 |
* Conditional Logic panel offered "Select a field" for both ends. |
| 945 |
* |
| 946 |
* A rule whose ends cannot both be mapped is dropped rather than kept |
| 947 |
* pointing at a stranger: the id it holds may since have been handed to an |
| 948 |
* unrelated field on another form, and a rule that silently governs the |
| 949 |
* wrong field is worse than no rule. |
| 950 |
* |
| 951 |
* @param array $settings The new form's settings, conditions included. |
| 952 |
* @param array $map old field id => new field id. |
| 953 |
* @param int $dropped Set to the number of rules that could not be kept. |
| 954 |
* @return array The settings, with the rules rewritten. |
| 955 |
*/ |
| 956 |
public static function remap_conditions($settings, $map, &$dropped = 0) { |
| 957 |
$dropped = 0; |
| 958 |
|
| 959 |
if (empty($settings['condition_action']) || !is_array($settings['condition_action'])) { |
| 960 |
return $settings; |
| 961 |
} |
| 962 |
|
| 963 |
$columns = array('condition_action', 'compare_from', 'compare_to', 'compare_condition', 'compare_value'); |
| 964 |
$kept = array_fill_keys($columns, array()); |
| 965 |
|
| 966 |
foreach (array_keys($settings['condition_action']) as $key) { |
| 967 |
$from = isset($settings['compare_from'][$key]) ? (int) $settings['compare_from'][$key] : 0; |
| 968 |
$to = isset($settings['compare_to'][$key]) ? (int) $settings['compare_to'][$key] : 0; |
| 969 |
|
| 970 |
if (!isset($map[$from], $map[$to])) { |
| 971 |
$dropped++; |
| 972 |
continue; |
| 973 |
} |
| 974 |
|
| 975 |
foreach ($columns as $column) { |
| 976 |
$value = isset($settings[$column][$key]) ? $settings[$column][$key] : ''; |
| 977 |
|
| 978 |
if ('compare_from' === $column) { |
| 979 |
$value = (string) $map[$from]; |
| 980 |
} else if ('compare_to' === $column) { |
| 981 |
$value = (string) $map[$to]; |
| 982 |
} |
| 983 |
|
| 984 |
$kept[$column][] = $value; |
| 985 |
} |
| 986 |
} |
| 987 |
|
| 988 |
foreach ($columns as $column) { |
| 989 |
$settings[$column] = $kept[$column]; |
| 990 |
} |
| 991 |
|
| 992 |
return $settings; |
| 993 |
} |
| 994 |
|
| 995 |
/** |
| 996 |
* Point a copied form's calculation formulas at the copy's own fields. |
| 997 |
* |
| 998 |
* A formula refers to its inputs as #field_id_12, and a repeater column as |
| 999 |
* #repeater_12_0 - field ids again, with the same problem the show and hide |
| 1000 |
* rules had: copy the form and every tag names a field that is not there, |
| 1001 |
* so the total silently stops adding up. |
| 1002 |
* |
| 1003 |
* Runs over the new form's own fields once they exist, so it is given the |
| 1004 |
* form to walk rather than the values to rewrite. |
| 1005 |
* |
| 1006 |
* @param int $form_id The form that has just been built. |
| 1007 |
* @param array $map old field id => new field id. |
| 1008 |
* @return int How many formulas were rewritten. |
| 1009 |
*/ |
| 1010 |
public static function remap_calculation_formulas($form_id, $map) { |
| 1011 |
global $wpdb; |
| 1012 |
|
| 1013 |
if (!$map) { |
| 1014 |
return 0; |
| 1015 |
} |
| 1016 |
|
| 1017 |
$rewritten = 0; |
| 1018 |
|
| 1019 |
foreach (HashFormFields::get_form_fields($form_id) as $field) { |
| 1020 |
$options = $field->field_options; |
| 1021 |
|
| 1022 |
if (!is_array($options) || empty($options['formula'])) { |
| 1023 |
continue; |
| 1024 |
} |
| 1025 |
|
| 1026 |
$formula = $options['formula']; |
| 1027 |
|
| 1028 |
// Both tag shapes in one pass, so a formula that mixes them cannot |
| 1029 |
// half-update. The id is looked up as it is met; anything the map |
| 1030 |
// does not know is left exactly as it was rather than guessed at. |
| 1031 |
$updated = preg_replace_callback( |
| 1032 |
'/#(field_id|repeater)_(\d+)(_\d+)?/', |
| 1033 |
function ($m) use ($map) { |
| 1034 |
$old = (int) $m[2]; |
| 1035 |
|
| 1036 |
if (!isset($map[$old])) { |
| 1037 |
return $m[0]; |
| 1038 |
} |
| 1039 |
|
| 1040 |
return '#' . $m[1] . '_' . $map[$old] . (isset($m[3]) ? $m[3] : ''); |
| 1041 |
}, |
| 1042 |
$formula |
| 1043 |
); |
| 1044 |
|
| 1045 |
if ($updated === $formula) { |
| 1046 |
continue; |
| 1047 |
} |
| 1048 |
|
| 1049 |
$options['formula'] = $updated; |
| 1050 |
$wpdb->update( |
| 1051 |
$wpdb->prefix . 'hashform_fields', |
| 1052 |
array('field_options' => serialize($options)), |
| 1053 |
array('id' => (int) $field->id) |
| 1054 |
); |
| 1055 |
$rewritten++; |
| 1056 |
} |
| 1057 |
|
| 1058 |
return $rewritten; |
| 1059 |
} |
| 1060 |
|
| 1061 |
/** |
| 1062 |
* Which fields a form's rules touch, and what each rule says. |
| 1063 |
* |
| 1064 |
* A rule names two fields: compare_from is the one shown or hidden, and |
| 1065 |
* compare_to is the one whose answer decides it. Both ends are worth |
| 1066 |
* marking on the canvas - a field that disappears for some visitors, and a |
| 1067 |
* field that is doing the deciding - so this returns an entry for each, |
| 1068 |
* keyed by field id. |
| 1069 |
* |
| 1070 |
* Read once per form and held for the request, because it is called from |
| 1071 |
* inside the loop that renders every field. |
| 1072 |
* |
| 1073 |
* @param int $form_id |
| 1074 |
* @return array field id => array( 'target' => string[], 'trigger' => string[] ) |
| 1075 |
*/ |
| 1076 |
public static function get_condition_hints($form_id) { |
| 1077 |
static $cache = array(); |
| 1078 |
|
| 1079 |
$form_id = absint($form_id); |
| 1080 |
|
| 1081 |
if (isset($cache[$form_id])) { |
| 1082 |
return $cache[$form_id]; |
| 1083 |
} |
| 1084 |
|
| 1085 |
$conditions = self::get_show_hide_conditions($form_id); |
| 1086 |
|
| 1087 |
if (!$conditions) { |
| 1088 |
$cache[$form_id] = array(); |
| 1089 |
return $cache[$form_id]; |
| 1090 |
} |
| 1091 |
|
| 1092 |
$names = array(); |
| 1093 |
|
| 1094 |
foreach (HashFormFields::get_form_fields($form_id) as $field) { |
| 1095 |
$names[(int) $field->id] = $field->name; |
| 1096 |
} |
| 1097 |
|
| 1098 |
$operators = self::condition_operators(); |
| 1099 |
$hints = array(); |
| 1100 |
|
| 1101 |
foreach ($conditions as $row) { |
| 1102 |
$target = isset($row['compare_from']) ? (int) $row['compare_from'] : 0; |
| 1103 |
$trigger = isset($row['compare_to']) ? (int) $row['compare_to'] : 0; |
| 1104 |
|
| 1105 |
// A half-filled rule does nothing on the front end, so it says |
| 1106 |
// nothing here either. |
| 1107 |
if (!$target || !$trigger) { |
| 1108 |
continue; |
| 1109 |
} |
| 1110 |
|
| 1111 |
/* |
| 1112 |
* Raw strings here, escaped where they are printed. Building them |
| 1113 |
* with esc_html__() turned the quotes into entities before the |
| 1114 |
* sentence was assembled, which then had to survive a second pass |
| 1115 |
* to come out right. |
| 1116 |
*/ |
| 1117 |
$is_show = !isset($row['condition_action']) || 'hide' !== $row['condition_action']; |
| 1118 |
$operator = isset($operators[$row['compare_condition']]) |
| 1119 |
? strtolower($operators[$row['compare_condition']]) |
| 1120 |
: $row['compare_condition']; |
| 1121 |
$unknown = __('a deleted field', 'hash-form'); |
| 1122 |
$trigger_name = isset($names[$trigger]) ? $names[$trigger] : $unknown; |
| 1123 |
$target_name = isset($names[$target]) ? $names[$target] : $unknown; |
| 1124 |
$value = isset($row['compare_value']) ? $row['compare_value'] : ''; |
| 1125 |
|
| 1126 |
// "Show"/"Hide" name the rule on the settings panel, where they are |
| 1127 |
// an instruction. Here they describe a field, so they need the |
| 1128 |
// participle: "Shown when ...", not "Show when ...". |
| 1129 |
$hints[$target]['target'][] = $is_show |
| 1130 |
/* translators: 1: the field being watched, 2: the comparison, 3: the value compared against. */ |
| 1131 |
? sprintf(__('Shown when "%1$s" %2$s "%3$s"', 'hash-form'), $trigger_name, $operator, $value) |
| 1132 |
/* translators: 1: the field being watched, 2: the comparison, 3: the value compared against. */ |
| 1133 |
: sprintf(__('Hidden when "%1$s" %2$s "%3$s"', 'hash-form'), $trigger_name, $operator, $value); |
| 1134 |
|
| 1135 |
$hints[$trigger]['trigger'][] = $is_show |
| 1136 |
/* translators: %s: the field this one decides. */ |
| 1137 |
? sprintf(__('Decides whether "%s" is shown', 'hash-form'), $target_name) |
| 1138 |
/* translators: %s: the field this one decides. */ |
| 1139 |
: sprintf(__('Decides whether "%s" is hidden', 'hash-form'), $target_name); |
| 1140 |
} |
| 1141 |
|
| 1142 |
$cache[$form_id] = $hints; |
| 1143 |
|
| 1144 |
return $cache[$form_id]; |
| 1145 |
} |
| 1146 |
|
| 1147 |
public function add_plugin_action_link($links) { |
| 1148 |
$custom['settings'] = sprintf( |
| 1149 |
'<a href="%s" aria-label="%s">%s</a>', esc_url(add_query_arg('page', 'hashform', admin_url('admin.php'))), esc_attr__('Hash Forms', 'hash-form'), esc_html__('Settings', 'hash-form') |
| 1150 |
); |
| 1151 |
|
| 1152 |
return array_merge($custom, (array) $links); |
| 1153 |
} |
| 1154 |
|
| 1155 |
public function file_upload_action() { |
| 1156 |
if (!wp_verify_nonce(HashFormHelper::get_var('file_uploader_nonce'), 'hashform-upload-ajax-nonce')) { |
| 1157 |
die(); |
| 1158 |
} |
| 1159 |
|
| 1160 |
$allowedExtensions = HashFormHelper::get_var('allowedExtensions'); |
| 1161 |
$sizeLimit = HashFormHelper::get_var('sizeLimit'); |
| 1162 |
$upload_dir = wp_upload_dir(); |
| 1163 |
|
| 1164 |
// One shared list, defined in admin/forms/sanitization.php, so this |
| 1165 |
// and the field's own sanitizer cannot disagree about a format. |
| 1166 |
$default_allowed_extenstions = hashform_allowed_file_extensions(); |
| 1167 |
|
| 1168 |
// get_allowed_mime_types() is applied again inside HashFormFileUploader, |
| 1169 |
// so anything this site has not actually enabled is still refused. |
| 1170 |
|
| 1171 |
// The request controls the shape of this value; a scalar would emit a |
| 1172 |
// warning into the middle of the JSON response below. |
| 1173 |
$allowedExtensions = is_array($allowedExtensions) ? $allowedExtensions : array(); |
| 1174 |
|
| 1175 |
$filtered_allowed_extenstions = array(); |
| 1176 |
foreach ($allowedExtensions as $ext) { |
| 1177 |
if (in_array($ext, $default_allowed_extenstions, true)) { |
| 1178 |
$filtered_allowed_extenstions[] = $ext; |
| 1179 |
} |
| 1180 |
} |
| 1181 |
|
| 1182 |
/* |
| 1183 |
* Tested after filtering rather than before. A request naming only |
| 1184 |
* extensions this plugin does not recognise used to satisfy a check on |
| 1185 |
* the raw list and then hand the uploader an empty one, which it read |
| 1186 |
* as "no restriction configured". |
| 1187 |
* |
| 1188 |
* Answered rather than dropped: an empty body left the uploader |
| 1189 |
* waiting on a response it could not parse, so the file appeared |
| 1190 |
* to hang instead of failing. |
| 1191 |
*/ |
| 1192 |
if (!$filtered_allowed_extenstions) { |
| 1193 |
wp_send_json(array('error' => esc_html__('This type of file is not allowed.', 'hash-form'))); |
| 1194 |
} |
| 1195 |
|
| 1196 |
// Never trust the request for the size limit beyond what the |
| 1197 |
// server would accept anyway. |
| 1198 |
$sizeLimit = min(absint($sizeLimit), wp_max_upload_size()); |
| 1199 |
|
| 1200 |
$uploader = new HashFormFileUploader($filtered_allowed_extenstions, $sizeLimit); |
| 1201 |
|
| 1202 |
/* |
| 1203 |
* Anything the upload machinery prints - a php notice from a host |
| 1204 |
* with a hardened open_basedir, a warning out of the mime sniffing - |
| 1205 |
* would land in front of the json and leave the browser unable to |
| 1206 |
* parse the reply. Whatever gets emitted is captured and dropped so |
| 1207 |
* the response is only ever the json below. |
| 1208 |
*/ |
| 1209 |
ob_start(); |
| 1210 |
$result = $uploader->handleUpload($upload_dir['basedir'] . HASHFORM_UPLOAD_DIR, false, $upload_dir['baseurl'] . HASHFORM_UPLOAD_DIR); |
| 1211 |
$stray_output = ob_get_clean(); |
| 1212 |
|
| 1213 |
if ($stray_output && defined('WP_DEBUG') && WP_DEBUG) { |
| 1214 |
HashFormHelper::log('Upload handler produced unexpected output: ' . $stray_output); |
| 1215 |
} |
| 1216 |
|
| 1217 |
wp_send_json($result); |
| 1218 |
} |
| 1219 |
|
| 1220 |
public function file_delete_action() { |
| 1221 |
if (wp_verify_nonce(HashFormHelper::get_post('_wpnonce'), 'hashform-upload-ajax-nonce')) { |
| 1222 |
$path = str_replace(' ', '+', HashFormHelper::get_post('path', 'wp_kses_post')); |
| 1223 |
$file = HashFormHelper::decrypt($path); |
| 1224 |
|
| 1225 |
// An empty name would resolve to the temp directory itself. |
| 1226 |
if ($file) { |
| 1227 |
$upload_dir = wp_upload_dir(); |
| 1228 |
$temp_dir = $upload_dir['basedir'] . HASHFORM_UPLOAD_DIR . '/temp'; |
| 1229 |
|
| 1230 |
// Unlike wp_delete_file(), this confirms the resolved path sits |
| 1231 |
// inside the temp directory and returns a usable bool on every |
| 1232 |
// supported WordPress version. |
| 1233 |
if (wp_delete_file_from_directory($temp_dir . '/' . $file, $temp_dir)) { |
| 1234 |
die('success'); |
| 1235 |
} |
| 1236 |
} |
| 1237 |
} |
| 1238 |
die('error'); |
| 1239 |
} |
| 1240 |
|
| 1241 |
public static function remove_old_temp_files() { |
| 1242 |
$max_file_age = apply_filters('hashform_temp_file_delete_time', 2 * 3600); |
| 1243 |
$upload_dir = wp_upload_dir(); |
| 1244 |
$temp_dir = $upload_dir['basedir'] . HASHFORM_UPLOAD_DIR . '/temp/'; |
| 1245 |
|
| 1246 |
// Remove old temp files |
| 1247 |
if (is_dir($temp_dir) and ($dir = opendir($temp_dir))) { |
| 1248 |
while (($file = readdir($dir)) !== false) { |
| 1249 |
$temp_file_path = $temp_dir . $file; |
| 1250 |
if (!is_file($temp_file_path)) { |
| 1251 |
continue; |
| 1252 |
} |
| 1253 |
if ((filemtime($temp_file_path) < time() - $max_file_age)) { |
| 1254 |
wp_delete_file($temp_file_path); |
| 1255 |
} |
| 1256 |
} |
| 1257 |
closedir($dir); |
| 1258 |
} |
| 1259 |
} |
| 1260 |
|
| 1261 |
public function admin_notice() { |
| 1262 |
add_action('admin_notices', array($this, 'admin_notice_content')); |
| 1263 |
} |
| 1264 |
|
| 1265 |
public function admin_notice_content() { |
| 1266 |
if (!$this->is_dismissed('review') && !empty(get_option('hashform_first_activation')) && time() > get_option('hashform_first_activation') + 15 * DAY_IN_SECONDS) { |
| 1267 |
$this->review_notice(); |
| 1268 |
} |
| 1269 |
} |
| 1270 |
|
| 1271 |
public static function is_dismissed($notice) { |
| 1272 |
$dismissed = get_option('hashform_dismissed_notices', array()); |
| 1273 |
|
| 1274 |
// Handle legacy user meta |
| 1275 |
$dismissed_meta = get_user_meta(get_current_user_id(), 'hashform_dismissed_notices', true); |
| 1276 |
if (is_array($dismissed_meta)) { |
| 1277 |
if (array_diff($dismissed_meta, $dismissed)) { |
| 1278 |
$dismissed = array_merge($dismissed, $dismissed_meta); |
| 1279 |
update_option('hashform_dismissed_notices', $dismissed); |
| 1280 |
} |
| 1281 |
if (!is_multisite()) { |
| 1282 |
// Don't delete on multisite to avoid the notices to appear in other sites. |
| 1283 |
delete_user_meta(get_current_user_id(), 'hashform_dismissed_notices'); |
| 1284 |
} |
| 1285 |
} |
| 1286 |
|
| 1287 |
return in_array($notice, $dismissed); |
| 1288 |
} |
| 1289 |
|
| 1290 |
public function review_notice() { |
| 1291 |
?> |
| 1292 |
<div class="hashform-notice notice notice-info"> |
| 1293 |
<?php $this->dismiss_button('review'); ?> |
| 1294 |
<div class="hashform-notice-logo"> |
| 1295 |
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 117.66 152.27"> |
| 1296 |
<g> |
| 1297 |
<g> |
| 1298 |
<path d="M0,3.46A3.46,3.46,0,0,1,3.14,0h80A3.53,3.53,0,0,1,85.6,1l31,31a3.47,3.47,0,0,1,1,2.43V148.81a3.46,3.46,0,0,1-3.46,3.46H31.63a3.46,3.46,0,1,1,0-6.92h79.11V38.07H83.05a3.46,3.46,0,0,1-3.46-3.46V6.92H6.92V145.35H14a3.46,3.46,0,1,1,0,6.92H3.46A3.46,3.46,0,0,1,0,148.81ZM106,31.15,86.51,11.68V31.15Z" /> |
| 1299 |
<path d="M78.66,59.3H95.09v6.61H78.66V85.75H72.05V42.87h6.61Zm0,39.67v16.42H72.05V99H52.22V92.36H95.09V99ZM39,99H22.57V92.36H39V72.52h6.61V115.4H39ZM39,59.3V42.87h6.61V59.3H65.44v6.61H22.57V59.3Z" /> |
| 1300 |
</g> |
| 1301 |
</g> |
| 1302 |
</svg> |
| 1303 |
</div> |
| 1304 |
|
| 1305 |
<div class="hashform-notice-content"> |
| 1306 |
<p> |
| 1307 |
<?php |
| 1308 |
printf( |
| 1309 |
/* translators: %1$s is link start tag, %2$s is link end tag. */ |
| 1310 |
esc_html__('Great to see that you have been using Hash Form for some time. We hope you love it, and we would really appreciate it if you would %1$sleave a review%2$s and spread your words to the world.', 'hash-form'), '<a target="_blank" href="https://wordpress.org/support/plugin/hash-form/reviews/">', '</a>' |
| 1311 |
); |
| 1312 |
?> |
| 1313 |
</p> |
| 1314 |
<a target="_blank" class="button button-primary button-large" href="https://wordpress.org/support/plugin/hash-form/reviews/"><span class="dashicons dashicons-thumbs-up"></span><?php echo esc_html__('Yes, of course', 'hash-form') ?></a> |
| 1315 |
<a class="button button-large" href="<?php echo esc_url(wp_nonce_url(add_query_arg('hashform-hide-notice', 'review'), 'review', 'hashform_notice_nonce')); ?>"><span class="dashicons dashicons-yes"></span><?php echo esc_html__('I have already rated', 'hash-form') ?></a> |
| 1316 |
</div> |
| 1317 |
</div> |
| 1318 |
<?php |
| 1319 |
} |
| 1320 |
|
| 1321 |
public function welcome_init() { |
| 1322 |
if (!get_option('hashform_first_activation')) { |
| 1323 |
update_option('hashform_first_activation', time()); |
| 1324 |
} |
| 1325 |
|
| 1326 |
if (isset($_GET['hashform-hide-notice'], $_GET['hashform_notice_nonce'])) { |
| 1327 |
$notice = sanitize_key($_GET['hashform-hide-notice']); |
| 1328 |
check_admin_referer($notice, 'hashform_notice_nonce'); |
| 1329 |
self::dismiss($notice); |
| 1330 |
wp_safe_redirect(remove_query_arg(array('hashform-hide-notice', 'hashform_notice_nonce'), wp_get_referer())); |
| 1331 |
exit; |
| 1332 |
} |
| 1333 |
} |
| 1334 |
|
| 1335 |
public function dismiss_button($name) { |
| 1336 |
printf('<a class="notice-dismiss" href="%s"><span class="screen-reader-text">%s</span></a>', esc_url(wp_nonce_url(add_query_arg('hashform-hide-notice', $name), $name, 'hashform_notice_nonce')), esc_html__('Dismiss this notice.', 'hash-form')); |
| 1337 |
} |
| 1338 |
|
| 1339 |
public static function dismiss($notice) { |
| 1340 |
$dismissed = get_option('hashform_dismissed_notices', array()); |
| 1341 |
|
| 1342 |
if (!in_array($notice, $dismissed)) { |
| 1343 |
$dismissed[] = $notice; |
| 1344 |
update_option('hashform_dismissed_notices', array_unique($dismissed)); |
| 1345 |
} |
| 1346 |
} |
| 1347 |
|
| 1348 |
|
| 1349 |
public function register_translation_strings() { |
| 1350 |
// Without WPML there is nothing to register; skip the full forms + |
| 1351 |
// fields scan that would otherwise run on every request. |
| 1352 |
if (!has_action('wpml_register_single_string')) { |
| 1353 |
return; |
| 1354 |
} |
| 1355 |
|
| 1356 |
$all_forms = HashFormListing::get_published_table_data(); |
| 1357 |
|
| 1358 |
foreach ($all_forms as $form) { |
| 1359 |
$form_title = $form['name']; |
| 1360 |
$options = HashFormHelper::unserialize_or_decode($form['options']); |
| 1361 |
$settings = HashFormHelper::unserialize_or_decode($form['settings']); |
| 1362 |
$string_array = array( |
| 1363 |
'Name' => $form_title, |
| 1364 |
'Description' => $form['description'], |
| 1365 |
'Email Auto Responder Subject' => isset($settings['email_subject_ar']) ? $settings['email_subject_ar'] : '', |
| 1366 |
'Email Auto Responder Message' => isset($settings['email_message_ar']) ? $settings['email_message_ar'] : '', |
| 1367 |
'Confirmation Message' => isset($settings['confirmation_message']) ? $settings['confirmation_message'] : '', |
| 1368 |
'Error Message' => isset($settings['error_message']) ? $settings['error_message'] : '', |
| 1369 |
'Submit Button Text' => isset($options['submit_value']) ? $options['submit_value'] : '' |
| 1370 |
); |
| 1371 |
|
| 1372 |
foreach ($string_array as $title => $strings) { |
| 1373 |
if (has_action('wpml_register_single_string')) { |
| 1374 |
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML's own hook, not one this plugin owns. |
| 1375 |
do_action('wpml_register_single_string', 'Hash Form', $form_title . ' - ' . $title, $strings); |
| 1376 |
} |
| 1377 |
} |
| 1378 |
|
| 1379 |
$form_fields = HashFormFields::get_form_fields($form['id']); |
| 1380 |
foreach ($form_fields as $field) { |
| 1381 |
$string_array = array( |
| 1382 |
'Field Label' => $field->name, |
| 1383 |
'Field Description' => $field->description, |
| 1384 |
'Field Validation Message' => isset($field->field_options['invalid']) ? $field->field_options['invalid'] : '', |
| 1385 |
); |
| 1386 |
|
| 1387 |
if ($field->type == 'paragraph' && isset($field->field_options['content'])) { |
| 1388 |
$string_array['Field Content'] = $field->field_options['content']; |
| 1389 |
} |
| 1390 |
|
| 1391 |
if ($field->type == 'name') { |
| 1392 |
$name_arrs = array('full', 'first', 'middle', 'last'); |
| 1393 |
foreach ($name_arrs as $name) { |
| 1394 |
$value = isset($field->default_value[$name]) ? $field->default_value[$name] : ''; |
| 1395 |
$placeholder = isset($field->placeholder[$name]) ? $field->placeholder[$name] : ''; |
| 1396 |
$label = isset($field->field_options['desc'][$name]) ? $field->field_options['desc'][$name] : ''; |
| 1397 |
$string_array[ucwords($name) . ' Label'] = $label; |
| 1398 |
$string_array[ucwords($name) . ' Value'] = $value; |
| 1399 |
$string_array[ucwords($name) . ' Placeholder'] = $placeholder; |
| 1400 |
} |
| 1401 |
} |
| 1402 |
|
| 1403 |
if ($field->type == 'address') { |
| 1404 |
$address_arrs = array('line1', 'line2', 'city', 'state', 'zip', 'country'); |
| 1405 |
foreach ($address_arrs as $address) { |
| 1406 |
$value = isset($field->default_value[$address]) ? $field->default_value[$address] : ''; |
| 1407 |
$placeholder = isset($field->placeholder[$address]) ? $field->placeholder[$address] : ''; |
| 1408 |
$label = isset($field->field_options['desc'][$address]) ? $field->field_options['desc'][$address] : ''; |
| 1409 |
$string_array[ucwords($address) . ' Label'] = $label; |
| 1410 |
$string_array[ucwords($address) . ' Value'] = $value; |
| 1411 |
$string_array[ucwords($address) . ' Placeholder'] = $placeholder; |
| 1412 |
} |
| 1413 |
} |
| 1414 |
|
| 1415 |
if (isset($field->default_value) && $field->type != 'name') { |
| 1416 |
if (is_array($field->default_value)) { |
| 1417 |
foreach ($field->default_value as $key => $defval) { |
| 1418 |
$string_array['Field Default ' . $key] = $defval; |
| 1419 |
} |
| 1420 |
} else { |
| 1421 |
$string_array['Field Default'] = $field->default_value; |
| 1422 |
} |
| 1423 |
} |
| 1424 |
|
| 1425 |
if (isset($field->field_options['placeholder'])) { |
| 1426 |
if (is_array($field->field_options['placeholder'])) { |
| 1427 |
foreach ($field->field_options['placeholder'] as $key => $defval) { |
| 1428 |
$string_array['Field Placeholder ' . $key] = $defval; |
| 1429 |
} |
| 1430 |
} else { |
| 1431 |
$string_array['Field Placeholder'] = $field->field_options['placeholder']; |
| 1432 |
} |
| 1433 |
} |
| 1434 |
|
| 1435 |
foreach ($string_array as $title => $strings) { |
| 1436 |
if (has_action('wpml_register_single_string')) { |
| 1437 |
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML's own hook, not one this plugin owns. |
| 1438 |
do_action('wpml_register_single_string', 'Hash Form', $form_title . ' - ' . $field->id . ' - ' . $title, $strings); |
| 1439 |
} |
| 1440 |
} |
| 1441 |
} |
| 1442 |
} |
| 1443 |
} |
| 1444 |
|
| 1445 |
public function translate_string($original_value, $domain, $name = '') { |
| 1446 |
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML's own hook, not one this plugin owns. |
| 1447 |
$wpml_translation = apply_filters('wpml_translate_single_string', $original_value, $domain, $name); |
| 1448 |
if ($wpml_translation === $original_value && function_exists('pll__')) { |
| 1449 |
return pll__($original_value); |
| 1450 |
} |
| 1451 |
return $wpml_translation; |
| 1452 |
} |
| 1453 |
|
| 1454 |
} |
| 1455 |
|
| 1456 |
new HashFormBuilder(); |