Skip to content

Commit 0898b57

Browse files
authored
Feature: dynamic values
I've been using this in production since I created this. Works as expected. * Initial feature commit * Fix regex * Allow changing submit url & add headers * Add REST route for submit * Add metabox * Add more data * Add labels to options * Show help * Fix missing callback * Add to README
1 parent 021e35c commit 0898b57

7 files changed

Lines changed: 217 additions & 4 deletions

File tree

README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Use standard HTML5 markup to create fully functional forms for WordPress
1414
- Full file upload support to Media Library with input type=file
1515
- Multilingual support with Polylang
1616
- Predefined static HTML forms via filter hooks
17+
- Dynamic values, like %USER_EMAIL% for pre-populating form data
1718

1819
## Why?
1920

@@ -175,6 +176,28 @@ Disabling additonal fields validation for all forms:
175176
add_filter( 'wplf_disable_validate_additional_fields' , __return_false );
176177
```
177178

179+
### Filter: wplf_dynamic_values
180+
181+
Add or customize dynamic values available in forms.
182+
183+
#### Example: new value
184+
185+
```php
186+
add_filter('wplf_dynamic_values', function($values) {
187+
$values['SOMETHING'] = [
188+
'callback' => function() { return 'something'; },
189+
'labels' => [
190+
'name' => 'Something',
191+
'description' => 'Something really useful.'
192+
],
193+
];
194+
195+
return $values;
196+
});
197+
198+
// <input type="text" placeholder="%SOMETHING%" name="something">
199+
```
200+
178201
## Javascript API
179202

180203
### Client side callbacks

assets/scripts/wplf-admin-form.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,26 @@ $(document).ready(function() {
8282
}
8383

8484
$('input[name="wplf_version_update_toggle"]').change(toggleVersionUpdate);
85+
86+
function showDynamicValueInfo(e) {
87+
var target = e.target;
88+
var value = target.value;
89+
var help = $('.wplf-dynamic-values-help');
90+
var desc = help.find('.description');
91+
var usage = help.find('.usage span');
92+
93+
if (value) {
94+
var option = target.querySelector('option[value="' + value + '"]');
95+
var labels = JSON.parse(option.getAttribute('data-labels'));
96+
desc.html(labels.description);
97+
usage.text('%' + value + '%');
98+
help.show();
99+
} else {
100+
help.hide();
101+
}
102+
}
103+
104+
$('select[name="wplf-dynamic-values"]').change(showDynamicValueInfo);
85105
});
86106

87107
})(jQuery);

assets/scripts/wplf-form.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,11 @@ window.wplf = {
2626
error.parentNode.removeChild(error);
2727
});
2828

29-
fetch(ajax_object.ajax_url + '?action=wplf_submit', {
29+
fetch(ajax_object.ajax_url, {
3030
method: "POST",
3131
credentials: ajax_object.ajax_credentials || 'same-origin',
32-
body: data
32+
body: data,
33+
headers: ajax_object.request_headers || {},
3334
}).then(function(response) {
3435
return response.text();
3536
}).then(function(response) {

assets/styles/wplf-admin-form.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,12 @@
99
color: #666;
1010
font-style: italic;
1111
}
12+
13+
.wplf-dynamic-values-help {
14+
display: none;
15+
margin-top: 1rem;
16+
}
17+
18+
.wplf-dynamic-values-help .description {
19+
margin-bottom: 2rem;
20+
}

classes/class-cpt-wplf-form.php

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,16 @@ public function add_meta_boxes_cpt() {
334334
'high'
335335
);
336336

337+
// Dynamic values
338+
add_meta_box(
339+
'wplf-shortcode',
340+
__( 'Dynamic values', 'wp-libre-form' ),
341+
array( $this, 'metabox_dynamic_values' ),
342+
'wplf-form',
343+
'normal',
344+
'high'
345+
);
346+
337347
// Messages meta box
338348
add_meta_box(
339349
'wplf-messages',
@@ -392,6 +402,35 @@ public function metabox_shortcode( $post ) {
392402
<?php
393403
}
394404

405+
/**
406+
* Meta box callback for dynamic values meta box
407+
*/
408+
public function metabox_dynamic_values( $post ) {
409+
unset( $post ); ?>
410+
<select name="wplf-dynamic-values">
411+
<option default value=""><?php esc_html_e( 'Choose a dynamic value', 'wp-libre-form' ); ?></option>
412+
413+
<?php foreach ( ( WPLF_Dynamic_Values::get_available() ) as $k => $v ) {
414+
$key = sanitize_text_field( $k );
415+
$labels = $v['labels'];
416+
$stringified = wp_json_encode( $labels );
417+
418+
// WPCS won't STFU. It's wrong. Again.
419+
echo "<option value='$key' data-labels='$stringified'>$labels[name]</option>"; // @codingStandardsIgnoreLine
420+
} ?>
421+
</select>
422+
423+
<!-- Shown with JS. -->
424+
<div class="wplf-dynamic-values-help">
425+
<div class="description"></div>
426+
<div class="usage">
427+
<strong><?php esc_html_e( 'Usage', 'wp-libre-form' ); ?>:&nbsp;</strong>
428+
<span></span>
429+
</div>
430+
</div>
431+
<?php
432+
}
433+
395434
/**
396435
* Meta box callback for fields meta box
397436
*/
@@ -849,7 +888,7 @@ public function wplf_form( $id, $content = '', $xclass = '', $attributes = [] )
849888
$content = apply_filters( "wplf_{$form->ID}_form", $content );
850889

851890
// run default filters after. The user probably wants to filter original content, not modified by WP
852-
$content = apply_filters( 'wplf_form', $content );
891+
$content = apply_filters( 'wplf_form', $content, $id, $xclass, $attributes );
853892

854893
ob_start();
855894
?>
@@ -924,10 +963,13 @@ public function maybe_enqueue_frontend_script() {
924963
true
925964
);
926965

966+
$admin_url = admin_url( 'admin-ajax.php' );
967+
927968
// add dynamic variables to the script's scope
928969
wp_localize_script('wplf-form-js', 'ajax_object', apply_filters( 'wplf_ajax_object', array(
929-
'ajax_url' => admin_url( 'admin-ajax.php' ),
970+
'ajax_url' => apply_filters( 'wplf_ajax_endpoint', "$admin_url?action=wplf_submit" ),
930971
'ajax_credentials' => apply_filters( 'wplf_ajax_fetch_credentials_mode', 'same-origin' ),
972+
'request_headers' => apply_filters( 'wplf_ajax_request_headers', '?action=wplf_submit' ),
931973
'wplf_assets_dir' => plugin_dir_url( realpath( __DIR__ . '/../wp-libre-form.php' ) ) . 'assets',
932974
) ) );
933975
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
if ( ! class_exists( 'WPLF_Polylang' ) ) {
3+
class WPLF_Dynamic_Values {
4+
5+
public static $instance;
6+
protected $regular_expression = "/%[^%%\n]+%/";
7+
8+
public static function init() {
9+
if ( is_null( self::$instance ) ) {
10+
self::$instance = new WPLF_Dynamic_Values();
11+
}
12+
return self::$instance;
13+
}
14+
15+
/**
16+
* Hook our actions, filters and such
17+
*/
18+
public function __construct() {
19+
add_filter( 'wplf_form', array( $this, 'render_form' ), 10, 4 );
20+
}
21+
22+
public function render_form( $content, $id, $xclass, $attributes ) {
23+
// Get all strings inside % placeholders
24+
preg_match_all( $this->regular_expression, $content, $matches );
25+
foreach ( $matches[0] as $match ) {
26+
// match contains the % chars, get rid of them.
27+
$string = trim( str_replace( array( '%' ), array( '' ), $match ) );
28+
$content = str_replace(
29+
$match,
30+
$this->populate_value(
31+
$string,
32+
compact( 'content', 'id', 'xclass', 'attributes' )
33+
),
34+
$content
35+
);
36+
}
37+
38+
return $content;
39+
}
40+
41+
public static function get_available() {
42+
$register = function( $value, $callback, $labels = array() ) use ( &$available ) {
43+
if ( ! is_callable( $callback ) ) {
44+
throw new Exception( '$callback is not callable' );
45+
}
46+
47+
$available[ $value ] = [
48+
'callback' => $callback,
49+
'labels' => array_merge([
50+
'name' => $value,
51+
'description' => esc_html__( 'No description provided', 'wp-libre-form' ),
52+
], $labels),
53+
];
54+
55+
return $available;
56+
};
57+
58+
return apply_filters( 'wplf_dynamic_values', array_merge(
59+
$register('USER_ID', 'get_current_user_id', [
60+
'name' => esc_html__( 'User ID', 'wp-libre-form' ),
61+
'description' => esc_html__( 'Get current user ID. Prints 0 if user isn\'t logged in.', 'wp-libre-form' ),
62+
]),
63+
$register('USER_EMAIL', function () {
64+
$user = wp_get_current_user();
65+
66+
if ( $user->ID === 0 ) {
67+
return false;
68+
}
69+
70+
return $user->user_email;
71+
}, [
72+
'name' => esc_html__( 'User email', 'wp-libre-form' ),
73+
'description' => esc_html__( 'Get user email, if it exists.', 'wp-libre-form' ),
74+
]),
75+
$register('USER_NAME', function () {
76+
$user = wp_get_current_user();
77+
78+
if ( $user->ID === 0 ) {
79+
return false;
80+
}
81+
82+
return "{$user->first_name} {$user->last_name}";
83+
}, [
84+
'name' => esc_html__( 'User name', 'wp-libre-form' ),
85+
'description' => esc_html__( 'Get user name, if it exists.', 'wp-libre-form' ),
86+
]),
87+
$register('TIMESTAMP', function () {
88+
return date( 'U' );
89+
}, [
90+
'name' => esc_html__( 'Timestamp', 'wp-libre-form' ),
91+
'description' => esc_html__(
92+
'Get UNIX epoch at the time of form render. Can be used to determine how long did it take for the user to fill the form.'
93+
),
94+
])
95+
) );
96+
}
97+
98+
public function populate_value( $string, $data = [] ) {
99+
$available = self::get_available();
100+
101+
if ( ! empty( $available[ $string ] ) && is_callable( $available[ $string ]['callback'] ) ) {
102+
return $available[ $string ]['callback']($data);
103+
}
104+
}
105+
}
106+
}

wp-libre-form.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public static function init() {
4747
private function __construct() {
4848
require_once 'classes/class-cpt-wplf-form.php';
4949
require_once 'classes/class-cpt-wplf-submission.php';
50+
require_once 'classes/class-wplf-dynamic-values.php';
5051
require_once 'inc/wplf-ajax.php';
5152

5253
// default functionality
@@ -56,11 +57,14 @@ private function __construct() {
5657
// init our plugin classes
5758
CPT_WPLF_Form::init();
5859
CPT_WPLF_Submission::init();
60+
WPLF_Dynamic_Values::init();
5961

6062
add_action( 'after_setup_theme', array( $this, 'init_polylang_support' ) );
6163

6264
add_action( 'plugins_loaded', array( $this, 'load_our_textdomain' ) );
6365

66+
add_action( 'rest_api_init', array( $this, 'register_rest_routes' ) );
67+
6468
// flush rewrites on activation since we have slugs for our cpts
6569
register_activation_hook( __FILE__, array( 'WP_Libre_Form', 'flush_rewrites' ) );
6670
register_deactivation_hook( __FILE__, 'flush_rewrite_rules' );
@@ -85,6 +89,14 @@ public static function load_our_textdomain() {
8589
}
8690
}
8791

92+
public function register_rest_routes() {
93+
register_rest_route( 'wplf/v1', 'submit', [
94+
'methods' => 'POST',
95+
'callback' => 'wplf_ajax_submit_handler', // admin-ajax handler, works but...
96+
// The REST API handbook discourages from using $_POST, and instead use $request->get_params()
97+
]);
98+
}
99+
88100
/**
89101
* Enable Polylang support
90102
*/

0 commit comments

Comments
 (0)