|
1
|
|
|
<?php |
|
|
|
|
|
|
2
|
|
|
|
|
3
|
|
|
if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly |
|
4
|
|
|
} |
|
5
|
|
|
class TitanFrameworkOptionAjaxButton extends TitanFrameworkOption { |
|
6
|
|
|
|
|
7
|
|
|
private static $firstLoad = true; |
|
8
|
|
|
|
|
9
|
|
|
public $defaultSecondarySettings = array( |
|
10
|
|
|
'action' => 'custom_action', |
|
11
|
|
|
'label' => '', |
|
12
|
|
|
'class' => 'button-secondary', |
|
13
|
|
|
'wait_label' => '', |
|
14
|
|
|
'success_label' => '', |
|
15
|
|
|
'error_label' => '', |
|
16
|
|
|
'success_callback' => '', |
|
17
|
|
|
'error_callback' => '', |
|
18
|
|
|
); |
|
19
|
|
|
|
|
20
|
|
|
|
|
21
|
|
|
/** |
|
22
|
|
|
* This is first called when an ajax button is clicked. This checks whether the nonce |
|
23
|
|
|
* is valid and if we should continue; |
|
24
|
|
|
* |
|
25
|
|
|
* @return void |
|
26
|
|
|
*/ |
|
27
|
|
|
public function ajaxSecurityChecker() { |
|
28
|
|
|
if ( empty( $_POST['nonce'] ) ) { |
|
29
|
|
|
wp_send_json_error( __( 'Security check failed, please refresh the page and try again.', TF_I18NDOMAIN ) ); |
|
30
|
|
|
} |
|
31
|
|
|
if ( ! wp_verify_nonce( $_POST['nonce'], 'tf-ajax-button' ) ) { |
|
32
|
|
|
wp_send_json_error( __( 'Security check failed, please refresh the page and try again.', TF_I18NDOMAIN ) ); |
|
33
|
|
|
} |
|
34
|
|
|
} |
|
35
|
|
|
|
|
36
|
|
|
|
|
37
|
|
|
/** |
|
38
|
|
|
* This is last called when an ajax button is clicked. This just exist with a successful state, |
|
39
|
|
|
* since doing nothing reads as an error with wp.ajax |
|
40
|
|
|
* |
|
41
|
|
|
* @return void |
|
42
|
|
|
*/ |
|
43
|
|
|
public function ajaxLastSuccess() { |
|
44
|
|
|
wp_send_json_success(); |
|
45
|
|
|
} |
|
46
|
|
|
|
|
47
|
|
|
|
|
48
|
|
|
/** |
|
49
|
|
|
* Constructor, fixes the settings to allow for multiple ajax buttons in a single option |
|
50
|
|
|
* |
|
51
|
|
|
* @param $settings Array Option settings |
|
52
|
|
|
* @param $owner Object The container of the option |
|
53
|
|
|
* @return void |
|
|
|
|
|
|
54
|
|
|
*/ |
|
55
|
|
|
function __construct( $settings, $owner ) { |
|
|
|
|
|
|
56
|
|
|
parent::__construct( $settings, $owner ); |
|
57
|
|
|
|
|
58
|
|
|
add_action( 'admin_head', array( __CLASS__, 'createAjaxScript' ) ); |
|
59
|
|
|
|
|
60
|
|
|
// Adjust the settings |
|
61
|
|
|
foreach ( $this->defaultSecondarySettings as $key => $default ) { |
|
62
|
|
|
if ( ! is_array( $this->settings[ $key ] ) ) { |
|
63
|
|
|
$this->settings[ $key ] = array( $this->settings[ $key ] ); |
|
64
|
|
|
} |
|
65
|
|
|
} |
|
66
|
|
|
|
|
67
|
|
|
while ( count( $this->settings['label'] ) < count( $this->settings['action'] ) ) { |
|
68
|
|
|
$this->settings['label'][] = $this->settings['label'][ count( $this->settings['label'] ) - 1 ]; |
|
69
|
|
|
} |
|
70
|
|
|
while ( count( $this->settings['class'] ) < count( $this->settings['action'] ) ) { |
|
71
|
|
|
$this->settings['class'][] = 'button-secondary'; |
|
72
|
|
|
} |
|
73
|
|
|
while ( count( $this->settings['wait_label'] ) < count( $this->settings['action'] ) ) { |
|
74
|
|
|
$this->settings['wait_label'][] = $this->settings['wait_label'][ count( $this->settings['wait_label'] ) - 1 ]; |
|
75
|
|
|
} |
|
76
|
|
|
while ( count( $this->settings['error_label'] ) < count( $this->settings['action'] ) ) { |
|
77
|
|
|
$this->settings['error_label'][] = $this->settings['error_label'][ count( $this->settings['error_label'] ) - 1 ]; |
|
78
|
|
|
} |
|
79
|
|
|
while ( count( $this->settings['success_label'] ) < count( $this->settings['action'] ) ) { |
|
80
|
|
|
$this->settings['success_label'][] = $this->settings['success_label'][ count( $this->settings['success_label'] ) - 1 ]; |
|
81
|
|
|
} |
|
82
|
|
|
while ( count( $this->settings['success_callback'] ) < count( $this->settings['action'] ) ) { |
|
83
|
|
|
$this->settings['success_callback'][] = ''; |
|
84
|
|
|
} |
|
85
|
|
|
while ( count( $this->settings['error_callback'] ) < count( $this->settings['action'] ) ) { |
|
86
|
|
|
$this->settings['error_callback'][] = __( 'Something went wrong', TF_I18NDOMAIN ); |
|
87
|
|
|
} |
|
88
|
|
|
|
|
89
|
|
|
foreach ( $this->settings['label'] as $i => $label ) { |
|
90
|
|
|
if ( empty( $label ) ) { |
|
91
|
|
|
$this->settings['label'][ $i ] = __( 'Click me', TF_I18NDOMAIN ); |
|
92
|
|
|
} |
|
93
|
|
|
} |
|
94
|
|
|
foreach ( $this->settings['wait_label'] as $i => $label ) { |
|
95
|
|
|
if ( empty( $label ) ) { |
|
96
|
|
|
$this->settings['wait_label'][ $i ] = __( 'Please wait...', TF_I18NDOMAIN ); |
|
97
|
|
|
} |
|
98
|
|
|
} |
|
99
|
|
|
foreach ( $this->settings['error_label'] as $i => $label ) { |
|
100
|
|
|
if ( empty( $label ) ) { |
|
101
|
|
|
$this->settings['error_label'][ $i ] = $this->settings['label'][ $i ]; |
|
102
|
|
|
} |
|
103
|
|
|
} |
|
104
|
|
|
foreach ( $this->settings['success_label'] as $i => $label ) { |
|
105
|
|
|
if ( empty( $label ) ) { |
|
106
|
|
|
$this->settings['success_label'][ $i ] = $this->settings['label'][ $i ]; |
|
107
|
|
|
} |
|
108
|
|
|
} |
|
109
|
|
|
|
|
110
|
|
|
/** |
|
111
|
|
|
* Create ajax handlers for security and last resort success returns |
|
112
|
|
|
*/ |
|
113
|
|
|
foreach ( $this->settings['action'] as $i => $action ) { |
|
114
|
|
|
if ( ! empty( $action ) ) { |
|
115
|
|
|
add_action( 'wp_ajax_' . $action, array( $this, 'ajaxSecurityChecker' ), 1 ); |
|
116
|
|
|
add_action( 'wp_ajax_' . $action, array( $this, 'ajaxLastSuccess' ), 99999 ); |
|
117
|
|
|
} |
|
118
|
|
|
} |
|
119
|
|
|
|
|
120
|
|
|
} |
|
121
|
|
|
|
|
122
|
|
|
|
|
123
|
|
|
/** |
|
124
|
|
|
* Renders the option |
|
125
|
|
|
* |
|
126
|
|
|
* @return void |
|
127
|
|
|
*/ |
|
128
|
|
|
public function display() { |
|
129
|
|
|
$this->echoOptionHeader(); |
|
130
|
|
|
|
|
131
|
|
|
foreach ( $this->settings['action'] as $i => $action ) { |
|
|
|
|
|
|
132
|
|
|
printf( '<button class="button %s" data-action="%s" data-label="%s" data-wait-label="%s" data-error-label="%s" data-success-label="%s" data-nonce="%s" data-success-callback="%s" data-error-callback="%s">%s</button>', |
|
133
|
|
|
$this->settings['class'][ $i ], |
|
134
|
|
|
esc_attr( $action ), |
|
135
|
|
|
esc_attr( $this->settings['label'][ $i ] ), |
|
136
|
|
|
esc_attr( $this->settings['wait_label'][ $i ] ), |
|
137
|
|
|
esc_attr( $this->settings['error_label'][ $i ] ), |
|
138
|
|
|
esc_attr( $this->settings['success_label'][ $i ] ), |
|
139
|
|
|
esc_attr( wp_create_nonce( 'tf-ajax-button' ) ), |
|
140
|
|
|
esc_attr( $this->settings['success_callback'][ $i ] ), |
|
141
|
|
|
esc_attr( $this->settings['error_callback'][ $i ] ), |
|
142
|
|
|
esc_attr( $this->settings['label'][ $i ] ) |
|
143
|
|
|
); |
|
144
|
|
|
} |
|
145
|
|
|
|
|
146
|
|
|
$this->echoOptionFooter(); |
|
147
|
|
|
} |
|
148
|
|
|
|
|
149
|
|
|
|
|
150
|
|
|
/** |
|
151
|
|
|
* Prints the Javascript needed by ajax buttons. The script is only echoed once |
|
152
|
|
|
* |
|
153
|
|
|
* @return void |
|
154
|
|
|
*/ |
|
155
|
|
|
public static function createAjaxScript() { |
|
156
|
|
|
if ( ! self::$firstLoad ) { |
|
157
|
|
|
return; |
|
158
|
|
|
} |
|
159
|
|
|
self::$firstLoad = false; |
|
160
|
|
|
|
|
161
|
|
|
?> |
|
162
|
|
|
<script> |
|
163
|
|
|
jQuery(document).ready(function($) { |
|
164
|
|
|
"use strict"; |
|
165
|
|
|
|
|
166
|
|
|
$('.form-table, .customize-control').on( 'click', '.tf-ajax-button .button', function( e ) { |
|
167
|
|
|
|
|
168
|
|
|
// Only perform one ajax at a time |
|
169
|
|
|
if ( typeof this.doingAjax == 'undefined' ) { |
|
170
|
|
|
this.doingAjax = false; |
|
171
|
|
|
} |
|
172
|
|
|
e.preventDefault(); |
|
173
|
|
|
if ( this.doingAjax ) { |
|
174
|
|
|
return false; |
|
175
|
|
|
} |
|
176
|
|
|
this.doingAjax = true; |
|
177
|
|
|
|
|
178
|
|
|
// Form the data to send, we send the nonce and the post ID if possible |
|
179
|
|
|
var data = { nonce: $(this).attr('data-nonce') }; |
|
180
|
|
|
<?php |
|
181
|
|
|
global $post; |
|
|
|
|
|
|
182
|
|
|
if ( ! empty( $post ) ) { |
|
183
|
|
|
?>data['id'] = <?php echo esc_attr( $post->ID ) ?>;<?php |
|
184
|
|
|
} |
|
185
|
|
|
?> |
|
186
|
|
|
|
|
187
|
|
|
// Perform the ajax call |
|
188
|
|
|
wp.ajax.send( $(this).attr('data-action'), { |
|
189
|
|
|
|
|
190
|
|
|
// Success callback |
|
191
|
|
|
success: function( successMessage ) { |
|
192
|
|
|
|
|
193
|
|
|
this.labelTimer = setTimeout(function() { |
|
194
|
|
|
$(this).text( $(this).attr('data-label') ); |
|
195
|
|
|
this.labelTimer = undefined; |
|
196
|
|
|
}.bind(this), 3000 ); |
|
197
|
|
|
|
|
198
|
|
|
$(this).text( successMessage || $(this).attr('data-success-label') ); |
|
199
|
|
|
|
|
200
|
|
|
// Call the error callback |
|
201
|
|
|
if ( $(this).attr('data-success-callback') != '' ) { |
|
202
|
|
|
if ( typeof window[ $(this).attr('data-success-callback') ] != 'undefined' ) { |
|
203
|
|
|
window[ $(this).attr('data-success-callback') ](); |
|
204
|
|
|
} |
|
205
|
|
|
} |
|
206
|
|
|
this.doingAjax = false; |
|
207
|
|
|
|
|
208
|
|
|
}.bind(this), |
|
209
|
|
|
|
|
210
|
|
|
// Error callback |
|
211
|
|
|
error: function( errorMessage ) { |
|
212
|
|
|
this.labelTimer = setTimeout(function() { |
|
213
|
|
|
$(this).text( $(this).attr('data-label') ); |
|
214
|
|
|
this.labelTimer = undefined; |
|
215
|
|
|
}.bind(this), 3000 ); |
|
216
|
|
|
|
|
217
|
|
|
$(this).text( errorMessage || $(this).attr('data-error-label') ); |
|
218
|
|
|
|
|
219
|
|
|
// Call the error callback |
|
220
|
|
|
if ( $(this).attr('data-error-callback') != '' ) { |
|
221
|
|
|
if ( typeof window[ $(this).attr('data-error-callback') ] != 'undefined' ) { |
|
222
|
|
|
window[ $(this).attr('data-error-callback') ](); |
|
223
|
|
|
} |
|
224
|
|
|
} |
|
225
|
|
|
this.doingAjax = false; |
|
226
|
|
|
|
|
227
|
|
|
}.bind(this), |
|
228
|
|
|
|
|
229
|
|
|
// Pass the data |
|
230
|
|
|
data: data |
|
231
|
|
|
|
|
232
|
|
|
}); |
|
233
|
|
|
|
|
234
|
|
|
// Clear the label timer |
|
235
|
|
|
if ( typeof this.labelTimer != 'undefined' ) { |
|
236
|
|
|
clearTimeout( this.labelTimer ); |
|
237
|
|
|
this.labelTimer = undefined; |
|
238
|
|
|
} |
|
239
|
|
|
$(this).text( $(this).attr('data-wait-label') ); |
|
240
|
|
|
|
|
241
|
|
|
return false; |
|
242
|
|
|
} ); |
|
243
|
|
|
}); |
|
244
|
|
|
</script> |
|
245
|
|
|
<?php |
|
246
|
|
|
} |
|
247
|
|
|
|
|
248
|
|
|
/* |
|
249
|
|
|
* Display for theme customizer |
|
250
|
|
|
*/ |
|
251
|
|
|
public function registerCustomizerControl( $wp_customize, $section, $priority = 1 ) { |
|
252
|
|
|
// var_dump($section->getID()); |
|
|
|
|
|
|
253
|
|
|
$wp_customize->add_control( new TitanFrameworkOptionAjaxButtonControl( $wp_customize, '', array( |
|
254
|
|
|
'label' => $this->settings['name'], |
|
255
|
|
|
'section' => $section->getID(), |
|
256
|
|
|
'settings' => $this->getID(), |
|
257
|
|
|
'description' => $this->settings['desc'], |
|
258
|
|
|
'priority' => $priority, |
|
259
|
|
|
'options' => $this->settings, |
|
260
|
|
|
) ) ); |
|
261
|
|
|
} |
|
262
|
|
|
} |
|
263
|
|
|
|
|
264
|
|
|
/* |
|
265
|
|
|
* WP_Customize_Control with description |
|
266
|
|
|
*/ |
|
267
|
|
|
add_action( 'customize_register', 'registerTitanFrameworkOptionAjaxButtonControl', 1 ); |
|
268
|
|
|
function registerTitanFrameworkOptionAjaxButtonControl() { |
|
269
|
|
|
class TitanFrameworkOptionAjaxButtonControl extends WP_Customize_Control { |
|
270
|
|
|
public $description; |
|
271
|
|
|
public $options; |
|
272
|
|
|
|
|
273
|
|
|
public function render_content() { |
|
274
|
|
|
TitanFrameworkOptionAjaxButton::createAjaxScript(); |
|
275
|
|
|
|
|
276
|
|
|
?> |
|
277
|
|
|
<label class='tf-ajax-button'> |
|
278
|
|
|
<span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span><?php |
|
279
|
|
|
|
|
280
|
|
|
foreach ( $this->options['action'] as $i => $action ) { |
|
281
|
|
|
printf( '<button class="button %s" data-action="%s" data-label="%s" data-wait-label="%s" data-error-label="%s" data-success-label="%s" data-nonce="%s" data-success-callback="%s" data-error-callback="%s">%s</button>', |
|
282
|
|
|
$this->options['class'][ $i ], |
|
283
|
|
|
esc_attr( $action ), |
|
284
|
|
|
esc_attr( $this->options['label'][ $i ] ), |
|
285
|
|
|
esc_attr( $this->options['wait_label'][ $i ] ), |
|
286
|
|
|
esc_attr( $this->options['error_label'][ $i ] ), |
|
287
|
|
|
esc_attr( $this->options['success_label'][ $i ] ), |
|
288
|
|
|
esc_attr( wp_create_nonce( 'tf-ajax-button' ) ), |
|
289
|
|
|
esc_attr( $this->options['success_callback'][ $i ] ), |
|
290
|
|
|
esc_attr( $this->options['error_callback'][ $i ] ), |
|
291
|
|
|
esc_attr( $this->options['label'][ $i ] ) |
|
292
|
|
|
); |
|
293
|
|
|
} |
|
294
|
|
|
|
|
295
|
|
|
if ( ! empty( $this->description ) ) { |
|
296
|
|
|
echo "<p class='description'>" . $this->description . '</p>'; |
|
297
|
|
|
} |
|
298
|
|
|
|
|
299
|
|
|
?></label><?php |
|
300
|
|
|
} |
|
301
|
|
|
} |
|
302
|
|
|
} |
|
303
|
|
|
|
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.