@mytechtoday/augment-extensions 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/augment-extensions/domain-rules/wordpress/README.md +163 -0
- package/augment-extensions/domain-rules/wordpress/module.json +32 -0
- package/augment-extensions/domain-rules/wordpress/rules/coding-standards.md +617 -0
- package/augment-extensions/domain-rules/wordpress/rules/directory-structure.md +270 -0
- package/augment-extensions/domain-rules/wordpress/rules/file-patterns.md +423 -0
- package/augment-extensions/domain-rules/wordpress/rules/gutenberg-blocks.md +493 -0
- package/augment-extensions/domain-rules/wordpress/rules/performance.md +568 -0
- package/augment-extensions/domain-rules/wordpress/rules/plugin-development.md +510 -0
- package/augment-extensions/domain-rules/wordpress/rules/project-detection.md +251 -0
- package/augment-extensions/domain-rules/wordpress/rules/rest-api.md +501 -0
- package/augment-extensions/domain-rules/wordpress/rules/security.md +564 -0
- package/augment-extensions/domain-rules/wordpress/rules/theme-development.md +388 -0
- package/augment-extensions/domain-rules/wordpress/rules/woocommerce.md +441 -0
- package/augment-extensions/domain-rules/wordpress-plugin/README.md +139 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/ajax-plugin.md +1599 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/custom-post-type-plugin.md +1727 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/gutenberg-block-plugin.md +428 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/gutenberg-block.md +422 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/mvc-plugin.md +1623 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/object-oriented-plugin.md +1343 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/rest-endpoint.md +734 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/settings-page-plugin.md +1350 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/simple-procedural-plugin.md +503 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/singleton-plugin.md +971 -0
- package/augment-extensions/domain-rules/wordpress-plugin/module.json +53 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/activation-hooks.md +770 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/admin-interface.md +874 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/ajax-handlers.md +629 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/asset-management.md +559 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/context-providers.md +709 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/cron-jobs.md +736 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/database-management.md +1057 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/documentation-standards.md +463 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/frontend-functionality.md +478 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/gutenberg-blocks.md +818 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/internationalization.md +416 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/migration.md +667 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/performance-optimization.md +878 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/plugin-architecture.md +693 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/plugin-structure.md +352 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/rest-api.md +818 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/scaffolding-workflow.md +624 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/security-best-practices.md +866 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/testing-patterns.md +1165 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/testing.md +414 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/vscode-integration.md +751 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/woocommerce-integration.md +949 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/wordpress-org-submission.md +458 -0
- package/augment-extensions/examples/gutenberg-block-plugin/README.md +101 -0
- package/augment-extensions/examples/gutenberg-block-plugin/examples/testimonial-block.md +428 -0
- package/augment-extensions/examples/gutenberg-block-plugin/module.json +40 -0
- package/augment-extensions/examples/rest-api-plugin/README.md +98 -0
- package/augment-extensions/examples/rest-api-plugin/examples/task-manager-api.md +1299 -0
- package/augment-extensions/examples/rest-api-plugin/module.json +40 -0
- package/augment-extensions/examples/woocommerce-extension/README.md +98 -0
- package/augment-extensions/examples/woocommerce-extension/examples/product-customizer.md +763 -0
- package/augment-extensions/examples/woocommerce-extension/module.json +40 -0
- package/augment-extensions/workflows/wordpress-plugin/README.md +232 -0
- package/augment-extensions/workflows/wordpress-plugin/ai-prompts.md +839 -0
- package/augment-extensions/workflows/wordpress-plugin/bead-decomposition-patterns.md +854 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/complete-plugin-example.md +540 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/custom-post-type-example.md +1083 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/feature-addition-workflow.md +669 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/plugin-creation-workflow.md +597 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/secure-form-handler-example.md +925 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/security-audit-workflow.md +752 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/wordpress-org-submission-workflow.md +773 -0
- package/augment-extensions/workflows/wordpress-plugin/module.json +49 -0
- package/augment-extensions/workflows/wordpress-plugin/rules/best-practices.md +942 -0
- package/augment-extensions/workflows/wordpress-plugin/rules/development-workflow.md +702 -0
- package/augment-extensions/workflows/wordpress-plugin/rules/submission-workflow.md +728 -0
- package/augment-extensions/workflows/wordpress-plugin/rules/testing-workflow.md +775 -0
- package/cli/dist/cli.js +5 -1
- package/cli/dist/cli.js.map +1 -1
- package/cli/dist/commands/show.d.ts.map +1 -1
- package/cli/dist/commands/show.js +41 -0
- package/cli/dist/commands/show.js.map +1 -1
- package/modules.md +52 -0
- package/package.json +1 -1
|
@@ -0,0 +1,629 @@
|
|
|
1
|
+
# AJAX Handlers
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This guide covers WordPress AJAX implementation for plugins including script enqueuing, localization, nonce verification, capability checks, and JSON responses.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Basic AJAX Setup
|
|
10
|
+
|
|
11
|
+
### Enqueue Script with AJAX Data
|
|
12
|
+
|
|
13
|
+
```php
|
|
14
|
+
<?php
|
|
15
|
+
/**
|
|
16
|
+
* Enqueue AJAX script
|
|
17
|
+
*/
|
|
18
|
+
function my_plugin_enqueue_ajax_script() {
|
|
19
|
+
wp_enqueue_script(
|
|
20
|
+
'my-plugin-ajax',
|
|
21
|
+
plugins_url( 'js/ajax-handler.js', __FILE__ ),
|
|
22
|
+
array( 'jquery' ),
|
|
23
|
+
'1.0.0',
|
|
24
|
+
true
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
// Localize script with AJAX URL and nonce
|
|
28
|
+
wp_localize_script( 'my-plugin-ajax', 'myPluginAjax', array(
|
|
29
|
+
'ajaxurl' => admin_url( 'admin-ajax.php' ),
|
|
30
|
+
'nonce' => wp_create_nonce( 'my_plugin_ajax_nonce' ),
|
|
31
|
+
) );
|
|
32
|
+
}
|
|
33
|
+
add_action( 'wp_enqueue_scripts', 'my_plugin_enqueue_ajax_script' );
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### JavaScript AJAX Request
|
|
37
|
+
|
|
38
|
+
**js/ajax-handler.js:**
|
|
39
|
+
```javascript
|
|
40
|
+
jQuery(document).ready(function($) {
|
|
41
|
+
$('#my-button').on('click', function(e) {
|
|
42
|
+
e.preventDefault();
|
|
43
|
+
|
|
44
|
+
var data = {
|
|
45
|
+
action: 'my_plugin_ajax_action',
|
|
46
|
+
security: myPluginAjax.nonce,
|
|
47
|
+
item_id: $(this).data('item-id'),
|
|
48
|
+
value: $('#my-input').val()
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
$.ajax({
|
|
52
|
+
url: myPluginAjax.ajaxurl,
|
|
53
|
+
type: 'POST',
|
|
54
|
+
data: data,
|
|
55
|
+
beforeSend: function() {
|
|
56
|
+
$('#my-button').prop('disabled', true).text('Loading...');
|
|
57
|
+
},
|
|
58
|
+
success: function(response) {
|
|
59
|
+
if (response.success) {
|
|
60
|
+
console.log('Success:', response.data);
|
|
61
|
+
alert(response.data.message);
|
|
62
|
+
} else {
|
|
63
|
+
console.error('Error:', response.data);
|
|
64
|
+
alert('Error: ' + response.data.message);
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
error: function(xhr, status, error) {
|
|
68
|
+
console.error('AJAX Error:', error);
|
|
69
|
+
alert('An error occurred. Please try again.');
|
|
70
|
+
},
|
|
71
|
+
complete: function() {
|
|
72
|
+
$('#my-button').prop('disabled', false).text('Submit');
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### PHP AJAX Handler
|
|
80
|
+
|
|
81
|
+
```php
|
|
82
|
+
<?php
|
|
83
|
+
/**
|
|
84
|
+
* AJAX handler for logged-in users
|
|
85
|
+
*/
|
|
86
|
+
function my_plugin_ajax_handler() {
|
|
87
|
+
// Verify nonce
|
|
88
|
+
check_ajax_referer( 'my_plugin_ajax_nonce', 'security' );
|
|
89
|
+
|
|
90
|
+
// Check user capability
|
|
91
|
+
if ( ! current_user_can( 'edit_posts' ) ) {
|
|
92
|
+
wp_send_json_error( array(
|
|
93
|
+
'message' => __( 'You do not have permission to perform this action.', 'my-plugin' ),
|
|
94
|
+
) );
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Sanitize input
|
|
98
|
+
$item_id = isset( $_POST['item_id'] ) ? absint( $_POST['item_id'] ) : 0;
|
|
99
|
+
$value = isset( $_POST['value'] ) ? sanitize_text_field( $_POST['value'] ) : '';
|
|
100
|
+
|
|
101
|
+
// Validate input
|
|
102
|
+
if ( ! $item_id || empty( $value ) ) {
|
|
103
|
+
wp_send_json_error( array(
|
|
104
|
+
'message' => __( 'Invalid input data.', 'my-plugin' ),
|
|
105
|
+
) );
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Process request
|
|
109
|
+
$result = update_post_meta( $item_id, '_my_custom_field', $value );
|
|
110
|
+
|
|
111
|
+
if ( $result ) {
|
|
112
|
+
wp_send_json_success( array(
|
|
113
|
+
'message' => __( 'Data saved successfully.', 'my-plugin' ),
|
|
114
|
+
'item_id' => $item_id,
|
|
115
|
+
'value' => $value,
|
|
116
|
+
) );
|
|
117
|
+
} else {
|
|
118
|
+
wp_send_json_error( array(
|
|
119
|
+
'message' => __( 'Failed to save data.', 'my-plugin' ),
|
|
120
|
+
) );
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
add_action( 'wp_ajax_my_plugin_ajax_action', 'my_plugin_ajax_handler' );
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## AJAX for Logged-In and Non-Logged-In Users
|
|
129
|
+
|
|
130
|
+
### Handler for Both User Types
|
|
131
|
+
|
|
132
|
+
```php
|
|
133
|
+
<?php
|
|
134
|
+
/**
|
|
135
|
+
* AJAX handler for logged-in users
|
|
136
|
+
*/
|
|
137
|
+
function my_plugin_public_ajax_handler() {
|
|
138
|
+
// Verify nonce
|
|
139
|
+
check_ajax_referer( 'my_plugin_public_nonce', 'security' );
|
|
140
|
+
|
|
141
|
+
// Sanitize input
|
|
142
|
+
$search_term = isset( $_POST['search'] ) ? sanitize_text_field( $_POST['search'] ) : '';
|
|
143
|
+
|
|
144
|
+
// Query posts
|
|
145
|
+
$args = array(
|
|
146
|
+
'post_type' => 'post',
|
|
147
|
+
'posts_per_page' => 10,
|
|
148
|
+
's' => $search_term,
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
$query = new WP_Query( $args );
|
|
152
|
+
|
|
153
|
+
$results = array();
|
|
154
|
+
if ( $query->have_posts() ) {
|
|
155
|
+
while ( $query->have_posts() ) {
|
|
156
|
+
$query->the_post();
|
|
157
|
+
$results[] = array(
|
|
158
|
+
'id' => get_the_ID(),
|
|
159
|
+
'title' => get_the_title(),
|
|
160
|
+
'url' => get_permalink(),
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
wp_reset_postdata();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
wp_send_json_success( array(
|
|
167
|
+
'results' => $results,
|
|
168
|
+
'count' => count( $results ),
|
|
169
|
+
) );
|
|
170
|
+
}
|
|
171
|
+
// For logged-in users
|
|
172
|
+
add_action( 'wp_ajax_my_plugin_public_action', 'my_plugin_public_ajax_handler' );
|
|
173
|
+
// For non-logged-in users
|
|
174
|
+
add_action( 'wp_ajax_nopriv_my_plugin_public_action', 'my_plugin_public_ajax_handler' );
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Advanced AJAX Patterns
|
|
179
|
+
|
|
180
|
+
### File Upload via AJAX
|
|
181
|
+
|
|
182
|
+
**JavaScript:**
|
|
183
|
+
```javascript
|
|
184
|
+
jQuery(document).ready(function($) {
|
|
185
|
+
$('#upload-form').on('submit', function(e) {
|
|
186
|
+
e.preventDefault();
|
|
187
|
+
|
|
188
|
+
var formData = new FormData();
|
|
189
|
+
formData.append('action', 'my_plugin_upload_file');
|
|
190
|
+
formData.append('security', myPluginAjax.nonce);
|
|
191
|
+
formData.append('file', $('#file-input')[0].files[0]);
|
|
192
|
+
|
|
193
|
+
$.ajax({
|
|
194
|
+
url: myPluginAjax.ajaxurl,
|
|
195
|
+
type: 'POST',
|
|
196
|
+
data: formData,
|
|
197
|
+
processData: false,
|
|
198
|
+
contentType: false,
|
|
199
|
+
success: function(response) {
|
|
200
|
+
if (response.success) {
|
|
201
|
+
console.log('File uploaded:', response.data.file_url);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**PHP:**
|
|
210
|
+
```php
|
|
211
|
+
<?php
|
|
212
|
+
/**
|
|
213
|
+
* Handle file upload via AJAX
|
|
214
|
+
*/
|
|
215
|
+
function my_plugin_upload_file_handler() {
|
|
216
|
+
check_ajax_referer( 'my_plugin_ajax_nonce', 'security' );
|
|
217
|
+
|
|
218
|
+
if ( ! current_user_can( 'upload_files' ) ) {
|
|
219
|
+
wp_send_json_error( array(
|
|
220
|
+
'message' => __( 'You do not have permission to upload files.', 'my-plugin' ),
|
|
221
|
+
) );
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if ( empty( $_FILES['file'] ) ) {
|
|
225
|
+
wp_send_json_error( array(
|
|
226
|
+
'message' => __( 'No file uploaded.', 'my-plugin' ),
|
|
227
|
+
) );
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
require_once( ABSPATH . 'wp-admin/includes/file.php' );
|
|
231
|
+
|
|
232
|
+
$uploaded_file = wp_handle_upload( $_FILES['file'], array( 'test_form' => false ) );
|
|
233
|
+
|
|
234
|
+
if ( isset( $uploaded_file['error'] ) ) {
|
|
235
|
+
wp_send_json_error( array(
|
|
236
|
+
'message' => $uploaded_file['error'],
|
|
237
|
+
) );
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
wp_send_json_success( array(
|
|
241
|
+
'message' => __( 'File uploaded successfully.', 'my-plugin' ),
|
|
242
|
+
'file_url' => $uploaded_file['url'],
|
|
243
|
+
'file_path' => $uploaded_file['file'],
|
|
244
|
+
) );
|
|
245
|
+
}
|
|
246
|
+
add_action( 'wp_ajax_my_plugin_upload_file', 'my_plugin_upload_file_handler' );
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Long-Running Process with Progress Updates
|
|
250
|
+
|
|
251
|
+
**JavaScript:**
|
|
252
|
+
```javascript
|
|
253
|
+
function processLongRunningTask() {
|
|
254
|
+
var totalItems = 100;
|
|
255
|
+
var batchSize = 10;
|
|
256
|
+
var processed = 0;
|
|
257
|
+
|
|
258
|
+
function processBatch() {
|
|
259
|
+
$.ajax({
|
|
260
|
+
url: myPluginAjax.ajaxurl,
|
|
261
|
+
type: 'POST',
|
|
262
|
+
data: {
|
|
263
|
+
action: 'my_plugin_process_batch',
|
|
264
|
+
security: myPluginAjax.nonce,
|
|
265
|
+
offset: processed,
|
|
266
|
+
limit: batchSize
|
|
267
|
+
},
|
|
268
|
+
success: function(response) {
|
|
269
|
+
if (response.success) {
|
|
270
|
+
processed += response.data.processed;
|
|
271
|
+
var progress = Math.round((processed / totalItems) * 100);
|
|
272
|
+
|
|
273
|
+
$('#progress-bar').css('width', progress + '%');
|
|
274
|
+
$('#progress-text').text(progress + '%');
|
|
275
|
+
|
|
276
|
+
if (processed < totalItems) {
|
|
277
|
+
processBatch(); // Process next batch
|
|
278
|
+
} else {
|
|
279
|
+
alert('Processing complete!');
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
processBatch();
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
**PHP:**
|
|
291
|
+
```php
|
|
292
|
+
<?php
|
|
293
|
+
/**
|
|
294
|
+
* Process batch of items
|
|
295
|
+
*/
|
|
296
|
+
function my_plugin_process_batch_handler() {
|
|
297
|
+
check_ajax_referer( 'my_plugin_ajax_nonce', 'security' );
|
|
298
|
+
|
|
299
|
+
if ( ! current_user_can( 'manage_options' ) ) {
|
|
300
|
+
wp_send_json_error( array(
|
|
301
|
+
'message' => __( 'Insufficient permissions.', 'my-plugin' ),
|
|
302
|
+
) );
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
$offset = isset( $_POST['offset'] ) ? absint( $_POST['offset'] ) : 0;
|
|
306
|
+
$limit = isset( $_POST['limit'] ) ? absint( $_POST['limit'] ) : 10;
|
|
307
|
+
|
|
308
|
+
// Get items to process
|
|
309
|
+
$items = get_posts( array(
|
|
310
|
+
'post_type' => 'my_custom_post_type',
|
|
311
|
+
'posts_per_page' => $limit,
|
|
312
|
+
'offset' => $offset,
|
|
313
|
+
) );
|
|
314
|
+
|
|
315
|
+
$processed = 0;
|
|
316
|
+
foreach ( $items as $item ) {
|
|
317
|
+
// Process each item
|
|
318
|
+
update_post_meta( $item->ID, '_processed', true );
|
|
319
|
+
$processed++;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
wp_send_json_success( array(
|
|
323
|
+
'processed' => $processed,
|
|
324
|
+
'offset' => $offset + $processed,
|
|
325
|
+
) );
|
|
326
|
+
}
|
|
327
|
+
add_action( 'wp_ajax_my_plugin_process_batch', 'my_plugin_process_batch_handler' );
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Heartbeat API Integration
|
|
331
|
+
|
|
332
|
+
```php
|
|
333
|
+
<?php
|
|
334
|
+
/**
|
|
335
|
+
* Modify Heartbeat API data
|
|
336
|
+
*/
|
|
337
|
+
function my_plugin_heartbeat_received( $response, $data ) {
|
|
338
|
+
if ( isset( $data['my_plugin_check'] ) ) {
|
|
339
|
+
// Get real-time data
|
|
340
|
+
$response['my_plugin_data'] = array(
|
|
341
|
+
'notifications' => get_user_meta( get_current_user_id(), 'unread_notifications', true ),
|
|
342
|
+
'timestamp' => current_time( 'timestamp' ),
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return $response;
|
|
347
|
+
}
|
|
348
|
+
add_filter( 'heartbeat_received', 'my_plugin_heartbeat_received', 10, 2 );
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
**JavaScript:**
|
|
352
|
+
```javascript
|
|
353
|
+
jQuery(document).on('heartbeat-send', function(event, data) {
|
|
354
|
+
data.my_plugin_check = true;
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
jQuery(document).on('heartbeat-tick', function(event, data) {
|
|
358
|
+
if (data.my_plugin_data) {
|
|
359
|
+
console.log('Notifications:', data.my_plugin_data.notifications);
|
|
360
|
+
$('#notification-count').text(data.my_plugin_data.notifications);
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
## Error Handling
|
|
368
|
+
|
|
369
|
+
### Comprehensive Error Handling
|
|
370
|
+
|
|
371
|
+
**JavaScript:**
|
|
372
|
+
```javascript
|
|
373
|
+
function handleAjaxRequest(data) {
|
|
374
|
+
$.ajax({
|
|
375
|
+
url: myPluginAjax.ajaxurl,
|
|
376
|
+
type: 'POST',
|
|
377
|
+
data: data,
|
|
378
|
+
timeout: 30000, // 30 seconds
|
|
379
|
+
success: function(response) {
|
|
380
|
+
if (response.success) {
|
|
381
|
+
// Handle success
|
|
382
|
+
console.log('Success:', response.data);
|
|
383
|
+
} else {
|
|
384
|
+
// Handle error from server
|
|
385
|
+
console.error('Server error:', response.data);
|
|
386
|
+
showErrorMessage(response.data.message || 'An error occurred');
|
|
387
|
+
}
|
|
388
|
+
},
|
|
389
|
+
error: function(xhr, status, error) {
|
|
390
|
+
// Handle AJAX error
|
|
391
|
+
if (status === 'timeout') {
|
|
392
|
+
showErrorMessage('Request timed out. Please try again.');
|
|
393
|
+
} else if (status === 'abort') {
|
|
394
|
+
showErrorMessage('Request was aborted.');
|
|
395
|
+
} else {
|
|
396
|
+
showErrorMessage('Network error: ' + error);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function showErrorMessage(message) {
|
|
403
|
+
$('#error-container').html('<div class="error">' + message + '</div>').show();
|
|
404
|
+
}
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
**PHP:**
|
|
408
|
+
```php
|
|
409
|
+
<?php
|
|
410
|
+
/**
|
|
411
|
+
* AJAX handler with comprehensive error handling
|
|
412
|
+
*/
|
|
413
|
+
function my_plugin_safe_ajax_handler() {
|
|
414
|
+
try {
|
|
415
|
+
// Verify nonce
|
|
416
|
+
if ( ! check_ajax_referer( 'my_plugin_ajax_nonce', 'security', false ) ) {
|
|
417
|
+
throw new Exception( __( 'Security check failed.', 'my-plugin' ) );
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Check capability
|
|
421
|
+
if ( ! current_user_can( 'edit_posts' ) ) {
|
|
422
|
+
throw new Exception( __( 'Insufficient permissions.', 'my-plugin' ) );
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Validate input
|
|
426
|
+
$item_id = isset( $_POST['item_id'] ) ? absint( $_POST['item_id'] ) : 0;
|
|
427
|
+
if ( ! $item_id ) {
|
|
428
|
+
throw new Exception( __( 'Invalid item ID.', 'my-plugin' ) );
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Process request
|
|
432
|
+
$result = my_plugin_process_item( $item_id );
|
|
433
|
+
|
|
434
|
+
if ( is_wp_error( $result ) ) {
|
|
435
|
+
throw new Exception( $result->get_error_message() );
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
wp_send_json_success( array(
|
|
439
|
+
'message' => __( 'Item processed successfully.', 'my-plugin' ),
|
|
440
|
+
'data' => $result,
|
|
441
|
+
) );
|
|
442
|
+
|
|
443
|
+
} catch ( Exception $e ) {
|
|
444
|
+
wp_send_json_error( array(
|
|
445
|
+
'message' => $e->getMessage(),
|
|
446
|
+
'code' => $e->getCode(),
|
|
447
|
+
) );
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
add_action( 'wp_ajax_my_plugin_safe_action', 'my_plugin_safe_ajax_handler' );
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
---
|
|
454
|
+
|
|
455
|
+
## Best Practices
|
|
456
|
+
|
|
457
|
+
### Security
|
|
458
|
+
|
|
459
|
+
1. **Always verify nonces**: Use `check_ajax_referer()` in every AJAX handler
|
|
460
|
+
2. **Check capabilities**: Verify user has permission to perform the action
|
|
461
|
+
3. **Sanitize input**: Use appropriate sanitization functions for all input
|
|
462
|
+
4. **Escape output**: Use `esc_html()`, `esc_url()`, etc. when outputting data
|
|
463
|
+
5. **Validate data**: Check that required data is present and valid
|
|
464
|
+
|
|
465
|
+
### Performance
|
|
466
|
+
|
|
467
|
+
1. **Limit query results**: Don't return large datasets in AJAX responses
|
|
468
|
+
2. **Use caching**: Cache expensive queries with transients
|
|
469
|
+
3. **Batch processing**: Break long-running tasks into smaller batches
|
|
470
|
+
4. **Debounce requests**: Prevent rapid-fire AJAX requests
|
|
471
|
+
5. **Set timeouts**: Configure appropriate timeout values
|
|
472
|
+
|
|
473
|
+
### User Experience
|
|
474
|
+
|
|
475
|
+
1. **Show loading states**: Disable buttons and show loading indicators
|
|
476
|
+
2. **Provide feedback**: Show success/error messages to users
|
|
477
|
+
3. **Handle errors gracefully**: Display user-friendly error messages
|
|
478
|
+
4. **Prevent duplicate requests**: Disable submit buttons during processing
|
|
479
|
+
5. **Use progress indicators**: For long-running operations
|
|
480
|
+
|
|
481
|
+
### Code Organization
|
|
482
|
+
|
|
483
|
+
1. **Separate concerns**: Keep JavaScript and PHP handlers focused
|
|
484
|
+
2. **Use action names consistently**: Follow naming convention `{plugin}_{action}`
|
|
485
|
+
3. **Group related handlers**: Organize AJAX handlers in dedicated files
|
|
486
|
+
4. **Document handlers**: Add PHPDoc blocks explaining what each handler does
|
|
487
|
+
5. **Use classes**: For complex plugins, use OOP to organize AJAX handlers
|
|
488
|
+
|
|
489
|
+
---
|
|
490
|
+
|
|
491
|
+
## Common Pitfalls
|
|
492
|
+
|
|
493
|
+
### ❌ DON'T
|
|
494
|
+
|
|
495
|
+
```php
|
|
496
|
+
// Don't skip nonce verification
|
|
497
|
+
function bad_ajax_handler() {
|
|
498
|
+
// No nonce check - BAD!
|
|
499
|
+
$data = $_POST['data'];
|
|
500
|
+
update_option( 'my_option', $data );
|
|
501
|
+
wp_send_json_success();
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// Don't skip capability checks
|
|
505
|
+
function bad_ajax_handler2() {
|
|
506
|
+
check_ajax_referer( 'my_nonce', 'security' );
|
|
507
|
+
// No capability check - BAD!
|
|
508
|
+
wp_delete_post( $_POST['post_id'], true );
|
|
509
|
+
wp_send_json_success();
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// Don't forget to sanitize
|
|
513
|
+
function bad_ajax_handler3() {
|
|
514
|
+
check_ajax_referer( 'my_nonce', 'security' );
|
|
515
|
+
$value = $_POST['value']; // Not sanitized - BAD!
|
|
516
|
+
update_post_meta( $post_id, 'key', $value );
|
|
517
|
+
wp_send_json_success();
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Don't echo and then use wp_send_json
|
|
521
|
+
function bad_ajax_handler4() {
|
|
522
|
+
echo 'Processing...'; // BAD!
|
|
523
|
+
wp_send_json_success( array( 'message' => 'Done' ) );
|
|
524
|
+
}
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
### ✅ DO
|
|
528
|
+
|
|
529
|
+
```php
|
|
530
|
+
// Always verify nonce
|
|
531
|
+
function good_ajax_handler() {
|
|
532
|
+
check_ajax_referer( 'my_plugin_ajax_nonce', 'security' );
|
|
533
|
+
|
|
534
|
+
// Check capability
|
|
535
|
+
if ( ! current_user_can( 'edit_posts' ) ) {
|
|
536
|
+
wp_send_json_error( array( 'message' => 'Insufficient permissions' ) );
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// Sanitize input
|
|
540
|
+
$data = sanitize_text_field( $_POST['data'] );
|
|
541
|
+
|
|
542
|
+
// Process and respond
|
|
543
|
+
update_option( 'my_option', $data );
|
|
544
|
+
wp_send_json_success( array( 'message' => 'Saved successfully' ) );
|
|
545
|
+
}
|
|
546
|
+
add_action( 'wp_ajax_my_plugin_action', 'good_ajax_handler' );
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
---
|
|
550
|
+
|
|
551
|
+
## Complete Example
|
|
552
|
+
|
|
553
|
+
### Plugin File Structure
|
|
554
|
+
|
|
555
|
+
```
|
|
556
|
+
my-plugin/
|
|
557
|
+
├── my-plugin.php
|
|
558
|
+
├── js/
|
|
559
|
+
│ └── ajax-handler.js
|
|
560
|
+
└── includes/
|
|
561
|
+
└── class-ajax-handler.php
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
### Class-Based AJAX Handler
|
|
565
|
+
|
|
566
|
+
**includes/class-ajax-handler.php:**
|
|
567
|
+
```php
|
|
568
|
+
<?php
|
|
569
|
+
class My_Plugin_Ajax_Handler {
|
|
570
|
+
|
|
571
|
+
public function __construct() {
|
|
572
|
+
add_action( 'wp_ajax_my_plugin_save_data', array( $this, 'save_data' ) );
|
|
573
|
+
add_action( 'wp_ajax_my_plugin_load_data', array( $this, 'load_data' ) );
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
public function save_data() {
|
|
577
|
+
check_ajax_referer( 'my_plugin_ajax_nonce', 'security' );
|
|
578
|
+
|
|
579
|
+
if ( ! current_user_can( 'edit_posts' ) ) {
|
|
580
|
+
wp_send_json_error( array(
|
|
581
|
+
'message' => __( 'Insufficient permissions.', 'my-plugin' ),
|
|
582
|
+
) );
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
$item_id = isset( $_POST['item_id'] ) ? absint( $_POST['item_id'] ) : 0;
|
|
586
|
+
$value = isset( $_POST['value'] ) ? sanitize_text_field( $_POST['value'] ) : '';
|
|
587
|
+
|
|
588
|
+
if ( ! $item_id || empty( $value ) ) {
|
|
589
|
+
wp_send_json_error( array(
|
|
590
|
+
'message' => __( 'Invalid data.', 'my-plugin' ),
|
|
591
|
+
) );
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
$result = update_post_meta( $item_id, '_my_custom_field', $value );
|
|
595
|
+
|
|
596
|
+
if ( $result ) {
|
|
597
|
+
wp_send_json_success( array(
|
|
598
|
+
'message' => __( 'Data saved.', 'my-plugin' ),
|
|
599
|
+
'item_id' => $item_id,
|
|
600
|
+
) );
|
|
601
|
+
} else {
|
|
602
|
+
wp_send_json_error( array(
|
|
603
|
+
'message' => __( 'Failed to save.', 'my-plugin' ),
|
|
604
|
+
) );
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
public function load_data() {
|
|
609
|
+
check_ajax_referer( 'my_plugin_ajax_nonce', 'security' );
|
|
610
|
+
|
|
611
|
+
$item_id = isset( $_POST['item_id'] ) ? absint( $_POST['item_id'] ) : 0;
|
|
612
|
+
|
|
613
|
+
if ( ! $item_id ) {
|
|
614
|
+
wp_send_json_error( array(
|
|
615
|
+
'message' => __( 'Invalid item ID.', 'my-plugin' ),
|
|
616
|
+
) );
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
$value = get_post_meta( $item_id, '_my_custom_field', true );
|
|
620
|
+
|
|
621
|
+
wp_send_json_success( array(
|
|
622
|
+
'value' => $value,
|
|
623
|
+
) );
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
new My_Plugin_Ajax_Handler();
|
|
628
|
+
```
|
|
629
|
+
|