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