PluginProbe
HTML Forms – Simple WordPress Forms Plugin / 1.7.0
HTML Forms – Simple WordPress Forms Plugin v1.7.0
1.7.0 trunk 1.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.2.0 1.3.0 1.3.1 1.3.10 1.3.11 1.3.12 1.3.13 1.3.14 1.3.15 1.3.16 All 67 releases
html-forms / src / functions.php

functions.php in HTML Forms – Simple WordPress Forms Plugin 1.7.0, at src/functions.php

548 lines 15.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 use HTML_Forms\Form;
4 use HTML_Forms\Submission;
5
6 /**
7 * @param array $args
8 * @return array
9 */
10 function hf_get_forms( array $args = array() ) {
11 $default_args = array(
12 'post_type' => 'html-form',
13 'post_status' => array( 'publish', 'draft', 'pending', 'future' ),
14 'posts_per_page' => -1,
15 'ignore_sticky_posts' => true,
16 'no_found_rows' => true,
17 );
18 $args = array_merge( $default_args, $args );
19 $query = new WP_Query;
20 $posts = $query->query( $args );
21 $forms = array_map( 'hf_get_form', $posts );
22 return $forms;
23 }
24
25 /**
26 * @param $form_id_or_slug int|string|WP_Post
27 * @return Form
28 * @throws Exception
29 */
30 function hf_get_form( $form_id_or_slug ) {
31
32 if ( is_numeric( $form_id_or_slug ) || $form_id_or_slug instanceof WP_Post ) {
33 $post = get_post( $form_id_or_slug );
34
35 if ( ! $post instanceof WP_Post || $post->post_type !== 'html-form' ) {
36 throw new Exception( 'Invalid form ID' );
37 }
38 } else {
39
40 $query = new WP_Query;
41 $posts = $query->query(
42 array(
43 'post_type' => 'html-form',
44 'name' => $form_id_or_slug,
45 'post_status' => 'publish',
46 'posts_per_page' => 1,
47 'ignore_sticky_posts' => true,
48 'no_found_rows' => true,
49 )
50 );
51 if ( empty( $posts ) ) {
52 throw new Exception( 'Invalid form slug' );
53 }
54 $post = $posts[0];
55 }
56
57 // get all post meta in a single call for performance
58 $post_meta = get_post_meta( $post->ID );
59
60 // grab & merge form settings
61 $default_settings = array(
62 'save_submissions' => 1,
63 'hide_after_success' => 0,
64 'redirect_url' => '',
65 'required_fields' => '',
66 'email_fields' => '',
67 );
68 $default_settings = apply_filters( 'hf_form_default_settings', $default_settings );
69 $settings = array();
70 if ( ! empty( $post_meta['_hf_settings'][0] ) ) {
71 $settings = (array) maybe_unserialize( $post_meta['_hf_settings'][0] );
72 }
73 $settings = array_merge( $default_settings, $settings );
74
75 // grab & merge form messages
76 $default_messages = array(
77 'success' => __( 'Thank you! We will be in touch soon.', 'html-forms' ),
78 'invalid_email' => __( 'Sorry, that email address looks invalid.', 'html-forms' ),
79 'required_field_missing' => __( 'Please fill in the required fields.', 'html-forms' ),
80 'error' => __( 'Oops. An error occurred.', 'html-forms' ),
81 'recaptcha_failed' => __( 'reCAPTCHA verification failed. Please try again.', 'html-forms' ),
82 'recaptcha_low_score' => __( 'Your submission appears to be spam. Please try again.', 'html-forms' ),
83 );
84 $default_messages = apply_filters( 'hf_form_default_messages', $default_messages );
85 $messages = array();
86 foreach ( $post_meta as $meta_key => $meta_values ) {
87 if ( strpos( $meta_key, 'hf_message_' ) === 0 ) {
88 $message_key = substr( $meta_key, strlen( 'hf_message_' ) );
89 $messages[ $message_key ] = (string) $meta_values[0];
90 }
91 }
92 $messages = array_merge( $default_messages, $messages );
93
94 // finally, create form instance
95 $form = new Form( $post->ID );
96 $form->title = $post->post_title;
97 $form->slug = $post->post_name;
98 $form->markup = $post->post_content;
99 $form->settings = $settings;
100 $form->messages = $messages;
101 return $form;
102 }
103
104 /**
105 * @param $form_id
106 * @return int
107 */
108 function hf_count_form_submissions( $form_id, $search = '' ) {
109 global $wpdb;
110 $table = $wpdb->prefix . 'hf_submissions';
111 if ( $search !== '' ) {
112 $result = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$table} s WHERE s.form_id = %d AND s.data LIKE %s;", $form_id, '%' . $wpdb->esc_like( $search ) . '%' ) );
113 } else {
114 $result = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$table} s WHERE s.form_id = %d;", $form_id ) );
115 }
116 return (int) $result;
117 }
118
119 /**
120 * @param $form_id
121 * @param array $args
122 * @return Submission[]
123 */
124 function hf_get_form_submissions( $form_id, array $args = array() ) {
125 $default_args = array(
126 'offset' => 0,
127 'limit' => 1000,
128 'orderby' => 'submitted_at',
129 'order' => 'DESC',
130 'search' => '',
131 );
132 $args = array_merge( $default_args, $args );
133
134 $allowed_orderby = array( 'submitted_at', 'id' );
135 $orderby = in_array( $args['orderby'], $allowed_orderby, true ) ? $args['orderby'] : 'submitted_at';
136 $order = strtoupper( $args['order'] ) === 'ASC' ? 'ASC' : 'DESC';
137
138 global $wpdb;
139 $table = $wpdb->prefix . 'hf_submissions';
140 if ( $args['search'] !== '' ) {
141 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $orderby and $order are whitelisted above
142 $results = $wpdb->get_results( $wpdb->prepare( "SELECT s.* FROM {$table} s WHERE s.form_id = %d AND s.data LIKE %s ORDER BY s.{$orderby} {$order} LIMIT %d, %d;", $form_id, '%' . $wpdb->esc_like( $args['search'] ) . '%', $args['offset'], $args['limit'] ), OBJECT_K );
143 } else {
144 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $orderby and $order are whitelisted above
145 $results = $wpdb->get_results( $wpdb->prepare( "SELECT s.* FROM {$table} s WHERE s.form_id = %d ORDER BY s.{$orderby} {$order} LIMIT %d, %d;", $form_id, $args['offset'], $args['limit'] ), OBJECT_K );
146 }
147 $submissions = array();
148 foreach ( $results as $key => $object ) {
149 $submission = Submission::from_object( $object );
150 $submissions[ $key ] = $submission;
151 }
152 return $submissions;
153 }
154
155 /**
156 * @param int $submission_id
157 * @return Submission
158 */
159 function hf_get_form_submission( $submission_id ) {
160 global $wpdb;
161 $table = $wpdb->prefix . 'hf_submissions';
162 $object = $wpdb->get_row( $wpdb->prepare( "SELECT s.* FROM {$table} s WHERE s.id = %d;", $submission_id ), OBJECT );
163 $submission = Submission::from_object( $object );
164 return $submission;
165 }
166 /**
167 * @return array
168 */
169 function hf_get_settings() {
170 $default_settings = array(
171 'enable_nonce' => 0,
172 'load_stylesheet' => 0,
173 'wrapper_tag' => 'p',
174 'google_recaptcha' => array(
175 'site_key' => '',
176 'secret_key' => '',
177 ),
178 );
179
180 $settings = get_option( 'hf_settings', null );
181
182 // prevent a SQL query when option does not yet exist
183 if ( $settings === null ) {
184 update_option( 'hf_settings', array(), true );
185 $settings = array();
186 }
187
188 // merge with default settings
189 $settings = array_merge( $default_settings, $settings );
190
191 // Ensure nested arrays are properly merged
192 if ( isset( $default_settings['google_recaptcha'] ) ) {
193 $settings['google_recaptcha'] = array_merge(
194 $default_settings['google_recaptcha'],
195 isset( $settings['google_recaptcha'] ) ? $settings['google_recaptcha'] : array()
196 );
197 }
198
199 /**
200 * Filters the global HTML Forms hf_settings
201 *
202 * @param array $settings
203 */
204 $settings = apply_filters( 'hf_settings', $settings );
205
206 return $settings;
207 }
208
209 /**
210 * Get element from array, allows for dot notation eg: "foo.bar"
211 *
212 * @param array $array
213 * @param string $key
214 * @param mixed $default
215 * @return mixed
216 */
217 function hf_array_get( $array, $key, $default = null ) {
218 if ( is_null( $key ) ) {
219 return $array;
220 }
221
222 if ( isset( $array[ $key ] ) ) {
223 return $array[ $key ];
224 }
225
226 foreach ( explode( '.', $key ) as $segment ) {
227 if ( ! is_array( $array ) || ! array_key_exists( $segment, $array ) ) {
228 return $default;
229 }
230
231 $array = $array[ $segment ];
232 }
233
234 return $array;
235 }
236
237 /**
238 * Processes template tags like {{user.user_email}}
239 *
240 * @param string $template
241 *
242 * @return string
243 */
244 function hf_template( $template ) {
245 $replacers = new HTML_Forms\TagReplacers();
246 $tags = array(
247 'user' => array( $replacers, 'user' ),
248 'post' => array( $replacers, 'post' ),
249 'url_params' => array( $replacers, 'url_params' ),
250 );
251
252 /**
253 * Filters the available tags in HTML Forms templates, like {{user.user_email}}.
254 *
255 * Can be used to add simple scalar replacements or more advanced replacement functions that accept a parameter.
256 *
257 * @param array $tags
258 */
259 $tags = apply_filters( 'hf_template_tags', $tags );
260
261 $template = preg_replace_callback(
262 '/\{\{ *(\w+)(?:\.([\w\.]+))? *(?:\|\| *(\w+))? *\}\}/',
263 function( $matches ) use ( $tags ) {
264 $tag = $matches[1];
265 $param = ! isset( $matches[2] ) ? '' : $matches[2];
266 $default = ! isset( $matches[3] ) ? '' : $matches[3];
267
268 // do not change anything if we have no replacer with that key, could be custom user logic or another plugin.
269 if ( ! isset( $tags[ $tag ] ) ) {
270 return $matches[0];
271 }
272
273 $replacement = $tags[ $tag ];
274 $value = is_callable( $replacement ) ? call_user_func_array( $replacement, array( $param ) ) : $replacement;
275 return ! empty( $value ) ? $value : $default;
276 },
277 $template
278 );
279
280 return $template;
281 }
282
283 /**
284 * @param string $string
285 * @param array $data
286 * @param Closure|string $escape_function
287 * @return string
288 */
289 function hf_replace_data_variables( $string, Submission $submission, $escape_function = null ) {
290 $data = ( !empty( $submission->data ) ? $submission->data : array() );
291 $submission_fields = array( 'HF_TIMESTAMP', 'HF_USER_AGENT', 'HF_IP_ADDRESS', 'HF_REFERRER_URL' );
292
293 return preg_replace_callback(
294 '/\[(.+?)\]/',
295 function( $matches ) use ( $submission, $submission_fields, $escape_function ) {
296 $key = $matches[1];
297
298 if ( in_array( $key, $submission_fields ) ) {
299 $replacement = '';
300
301 switch ( $key ) {
302 case 'HF_TIMESTAMP' :
303 $replacement = $submission->submitted_at;
304 break;
305 case 'HF_USER_AGENT' :
306 $replacement = $submission->user_agent;
307 break;
308 case 'HF_IP_ADDRESS' :
309 $replacement = $submission->ip_address;
310 break;
311 case 'HF_REFERRER_URL' :
312 $replacement = $submission->referer_url;
313 break;
314 default :
315 $replacement = '';
316 break;
317 }
318 } else {
319 // replace spaces in name with underscores to match PHP requirement for keys in $_POST superglobal
320 $key = str_replace( ' ', '_', $key );
321 $replacement = hf_array_get( $submission->data, $key, '' );
322 $replacement = hf_field_value( $replacement, 0, $escape_function );
323 }
324
325 return $replacement;
326 },
327 $string
328 );
329 }
330
331 /**
332 * Returns a formatted & HTML-escaped field value. Detects file-, array- and date-types.
333 *
334 * Caveat: if value is a file, an HTML string is returned (which means email action should use "Content-Type: html" when it includes a file field).
335 *
336 * @param string|array $value
337 * @param int $limit
338 * @param Closure|string $escape_function
339 * @return string
340 * @since 1.3.1
341 */
342 function hf_field_value( $value, $limit = 0, $escape_function = 'esc_html' ) {
343 if ( $value === '' ) {
344 return $value;
345 }
346
347 if ( hf_is_file( $value ) ) {
348 if ( ! is_array( $value )
349 || ! isset( $value['name'] )
350 || ! isset( $value['size'] )
351 || ! isset( $value['type'] ) ) {
352 return false;
353 }
354
355 // Verify attachment exists
356 if ( isset( $value['attachment_id'] ) && get_post( $value['attachment_id'] ) == null ) {
357 return __( 'File not found', 'html-forms' );
358 }
359
360 $file_url = isset( $value['url'] ) ? $value['url'] : '';
361 if ( isset( $value['attachment_id'] ) && apply_filters( 'hf_file_upload_use_direct_links', false ) === false ) {
362 $file_url = admin_url( sprintf( 'post.php?action=edit&post=%d', $value['attachment_id'] ) );
363 }
364
365 $short_name = substr( $value['name'], 0, 20 );
366 $suffix = strlen( $value['name'] ) > 20 ? '...' : '';
367 return sprintf( '<a href="%s">%s%s</a> (%s)', esc_url( $file_url ), esc_html( $short_name ), esc_html( $suffix ), hf_human_filesize( $value['size'] ) );
368 }
369
370 if ( hf_is_date( $value ) ) {
371 $date_format = get_option( 'date_format' );
372 return gmdate( $date_format, strtotime( str_replace( '/', '-', $value ) ) );
373 }
374
375 // join array-values with comma
376 if ( is_array( $value ) ) {
377 $value = join( ', ', $value );
378 }
379
380 // limit string to certain length
381 if ( $limit > 0 ) {
382 $limited = strlen( $value ) > $limit;
383 $value = substr( $value, 0, $limit );
384
385 if ( $limited ) {
386 $value .= '...';
387 }
388 }
389
390 // escape value
391 if ( $escape_function !== null && is_callable( $escape_function ) ) {
392 $value = $escape_function( $value );
393 }
394
395 // add line breaks, if not string limited to certain length
396 if ( $limit === 0 ) {
397 $value = nl2br( $value );
398 }
399
400 return $value;
401 }
402
403 /**
404 * Returns true if value is a "file"
405 *
406 * @param mixed $value
407 * @return bool
408 */
409 function hf_is_file( $value ) {
410 return is_array( $value )
411 && isset( $value['name'] )
412 && isset( $value['size'] )
413 && isset( $value['type'] );
414 }
415
416 /**
417 * Returns true if value looks like a date-string submitted from a <input type="date">
418 * @param mixed $value
419 * @return bool
420 * @since 1.3.1
421 */
422 function hf_is_date( $value ) {
423 if ( ! is_string( $value )
424 || strlen( $value ) !== 10
425 || (int) preg_match( '/\d{2,4}[-\/]\d{2}[-\/]\d{2,4}/', $value ) === 0 ) {
426 return false;
427 }
428
429 $timestamp = strtotime( $value );
430 return $timestamp != false;
431 }
432
433 /**
434 * @param int $size
435 * @param int $precision
436 * @return string
437 */
438 function hf_human_filesize( $size, $precision = 2 ) {
439 for ( $i = 0; ( $size / 1024 ) > 0.9; $i++, $size /= 1024 ) {
440 // nothing, loop logic contains everything
441 }
442 $steps = array( 'B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB' );
443 return round( $size, $precision ) . $steps[ $i ];
444 }
445
446 /**
447 * Gets all the form tabs to show in the admin.
448 * @param Form $form
449 * @return array
450 */
451 function hf_get_admin_tabs( Form $form ) {
452 $tabs = array(
453 'fields' => __( 'Fields', 'html-forms' ),
454 'messages' => __( 'Messages', 'html-forms' ),
455 'settings' => __( 'Settings', 'html-forms' ),
456 'actions' => __( 'Actions', 'html-forms' ),
457 );
458
459 if ( $form->settings['save_submissions'] ) {
460 $tabs['submissions'] = __( 'Submissions', 'html-forms' );
461 }
462 return apply_filters( 'hf_admin_tabs', $tabs, $form );
463 }
464
465 function _hf_on_plugin_activation() {
466 if ( is_multisite() ) {
467 _hf_on_plugin_activation_multisite();
468 return;
469 }
470
471 // install table for regular wp install
472 _hf_create_submissions_table();
473
474 // add "edit_forms" cap to user that activated the plugin
475 $user = wp_get_current_user();
476 $user->add_cap( 'edit_forms', true );
477 }
478
479 function _hf_on_plugin_activation_multisite() {
480 $added_caps = array();
481
482 foreach ( get_sites( array( 'number' => PHP_INT_MAX ) ) as $site ) {
483 switch_to_blog( (int) $site->blog_id );
484
485 // install table for current blog
486 _hf_create_submissions_table();
487
488 // iterate through current blog admins
489 foreach ( get_users(
490 array(
491 'blog_id' => (int) $site->blog_id,
492 'role' => 'administrator',
493 'fields' => 'ID',
494 )
495 ) as $admin_id ) {
496 if ( ! (int) $admin_id || in_array( $admin_id, $added_caps ) ) {
497 continue;
498 }
499
500 // add "edit_forms" cap to site admin
501 $user = new \WP_User( (int) $admin_id );
502 $user->add_cap( 'edit_forms', true );
503
504 $added_caps[] = $admin_id;
505 }
506
507 restore_current_blog();
508 }
509 }
510
511 // install table for main site on regular installs, or active site for multisite
512 function _hf_create_submissions_table() {
513 /** @var wpdb */
514 global $wpdb;
515
516 $charset_collate = $wpdb->get_charset_collate();
517
518 // create table for storing submissions
519 $table = $wpdb->prefix . 'hf_submissions';
520 $wpdb->query(
521 "CREATE TABLE IF NOT EXISTS {$table}(
522 `id` INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
523 `form_id` INT UNSIGNED NOT NULL,
524 `data` TEXT NOT NULL,
525 `user_agent` TEXT NULL,
526 `ip_address` VARCHAR(255) NULL,
527 `referer_url` TEXT NULL,
528 `submitted_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
529 ) {$charset_collate};"
530 );
531 }
532
533 function _hf_on_add_user_to_blog( $user_id, $role, $blog_id ) {
534 if ( 'administrator' !== $role ) {
535 return;
536 }
537
538 // add "edit_forms" cap to site admin
539 $user = new \WP_User( (int) $user_id );
540 $user->add_cap( 'edit_forms', true );
541 }
542
543 function _hf_on_wp_insert_site( \WP_Site $site ) {
544 switch_to_blog( (int) $site->blog_id );
545 _hf_create_submissions_table();
546 restore_current_blog();
547 }
548