@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.
Files changed (79) hide show
  1. package/augment-extensions/domain-rules/wordpress/README.md +163 -0
  2. package/augment-extensions/domain-rules/wordpress/module.json +32 -0
  3. package/augment-extensions/domain-rules/wordpress/rules/coding-standards.md +617 -0
  4. package/augment-extensions/domain-rules/wordpress/rules/directory-structure.md +270 -0
  5. package/augment-extensions/domain-rules/wordpress/rules/file-patterns.md +423 -0
  6. package/augment-extensions/domain-rules/wordpress/rules/gutenberg-blocks.md +493 -0
  7. package/augment-extensions/domain-rules/wordpress/rules/performance.md +568 -0
  8. package/augment-extensions/domain-rules/wordpress/rules/plugin-development.md +510 -0
  9. package/augment-extensions/domain-rules/wordpress/rules/project-detection.md +251 -0
  10. package/augment-extensions/domain-rules/wordpress/rules/rest-api.md +501 -0
  11. package/augment-extensions/domain-rules/wordpress/rules/security.md +564 -0
  12. package/augment-extensions/domain-rules/wordpress/rules/theme-development.md +388 -0
  13. package/augment-extensions/domain-rules/wordpress/rules/woocommerce.md +441 -0
  14. package/augment-extensions/domain-rules/wordpress-plugin/README.md +139 -0
  15. package/augment-extensions/domain-rules/wordpress-plugin/examples/ajax-plugin.md +1599 -0
  16. package/augment-extensions/domain-rules/wordpress-plugin/examples/custom-post-type-plugin.md +1727 -0
  17. package/augment-extensions/domain-rules/wordpress-plugin/examples/gutenberg-block-plugin.md +428 -0
  18. package/augment-extensions/domain-rules/wordpress-plugin/examples/gutenberg-block.md +422 -0
  19. package/augment-extensions/domain-rules/wordpress-plugin/examples/mvc-plugin.md +1623 -0
  20. package/augment-extensions/domain-rules/wordpress-plugin/examples/object-oriented-plugin.md +1343 -0
  21. package/augment-extensions/domain-rules/wordpress-plugin/examples/rest-endpoint.md +734 -0
  22. package/augment-extensions/domain-rules/wordpress-plugin/examples/settings-page-plugin.md +1350 -0
  23. package/augment-extensions/domain-rules/wordpress-plugin/examples/simple-procedural-plugin.md +503 -0
  24. package/augment-extensions/domain-rules/wordpress-plugin/examples/singleton-plugin.md +971 -0
  25. package/augment-extensions/domain-rules/wordpress-plugin/module.json +53 -0
  26. package/augment-extensions/domain-rules/wordpress-plugin/rules/activation-hooks.md +770 -0
  27. package/augment-extensions/domain-rules/wordpress-plugin/rules/admin-interface.md +874 -0
  28. package/augment-extensions/domain-rules/wordpress-plugin/rules/ajax-handlers.md +629 -0
  29. package/augment-extensions/domain-rules/wordpress-plugin/rules/asset-management.md +559 -0
  30. package/augment-extensions/domain-rules/wordpress-plugin/rules/context-providers.md +709 -0
  31. package/augment-extensions/domain-rules/wordpress-plugin/rules/cron-jobs.md +736 -0
  32. package/augment-extensions/domain-rules/wordpress-plugin/rules/database-management.md +1057 -0
  33. package/augment-extensions/domain-rules/wordpress-plugin/rules/documentation-standards.md +463 -0
  34. package/augment-extensions/domain-rules/wordpress-plugin/rules/frontend-functionality.md +478 -0
  35. package/augment-extensions/domain-rules/wordpress-plugin/rules/gutenberg-blocks.md +818 -0
  36. package/augment-extensions/domain-rules/wordpress-plugin/rules/internationalization.md +416 -0
  37. package/augment-extensions/domain-rules/wordpress-plugin/rules/migration.md +667 -0
  38. package/augment-extensions/domain-rules/wordpress-plugin/rules/performance-optimization.md +878 -0
  39. package/augment-extensions/domain-rules/wordpress-plugin/rules/plugin-architecture.md +693 -0
  40. package/augment-extensions/domain-rules/wordpress-plugin/rules/plugin-structure.md +352 -0
  41. package/augment-extensions/domain-rules/wordpress-plugin/rules/rest-api.md +818 -0
  42. package/augment-extensions/domain-rules/wordpress-plugin/rules/scaffolding-workflow.md +624 -0
  43. package/augment-extensions/domain-rules/wordpress-plugin/rules/security-best-practices.md +866 -0
  44. package/augment-extensions/domain-rules/wordpress-plugin/rules/testing-patterns.md +1165 -0
  45. package/augment-extensions/domain-rules/wordpress-plugin/rules/testing.md +414 -0
  46. package/augment-extensions/domain-rules/wordpress-plugin/rules/vscode-integration.md +751 -0
  47. package/augment-extensions/domain-rules/wordpress-plugin/rules/woocommerce-integration.md +949 -0
  48. package/augment-extensions/domain-rules/wordpress-plugin/rules/wordpress-org-submission.md +458 -0
  49. package/augment-extensions/examples/gutenberg-block-plugin/README.md +101 -0
  50. package/augment-extensions/examples/gutenberg-block-plugin/examples/testimonial-block.md +428 -0
  51. package/augment-extensions/examples/gutenberg-block-plugin/module.json +40 -0
  52. package/augment-extensions/examples/rest-api-plugin/README.md +98 -0
  53. package/augment-extensions/examples/rest-api-plugin/examples/task-manager-api.md +1299 -0
  54. package/augment-extensions/examples/rest-api-plugin/module.json +40 -0
  55. package/augment-extensions/examples/woocommerce-extension/README.md +98 -0
  56. package/augment-extensions/examples/woocommerce-extension/examples/product-customizer.md +763 -0
  57. package/augment-extensions/examples/woocommerce-extension/module.json +40 -0
  58. package/augment-extensions/workflows/wordpress-plugin/README.md +232 -0
  59. package/augment-extensions/workflows/wordpress-plugin/ai-prompts.md +839 -0
  60. package/augment-extensions/workflows/wordpress-plugin/bead-decomposition-patterns.md +854 -0
  61. package/augment-extensions/workflows/wordpress-plugin/examples/complete-plugin-example.md +540 -0
  62. package/augment-extensions/workflows/wordpress-plugin/examples/custom-post-type-example.md +1083 -0
  63. package/augment-extensions/workflows/wordpress-plugin/examples/feature-addition-workflow.md +669 -0
  64. package/augment-extensions/workflows/wordpress-plugin/examples/plugin-creation-workflow.md +597 -0
  65. package/augment-extensions/workflows/wordpress-plugin/examples/secure-form-handler-example.md +925 -0
  66. package/augment-extensions/workflows/wordpress-plugin/examples/security-audit-workflow.md +752 -0
  67. package/augment-extensions/workflows/wordpress-plugin/examples/wordpress-org-submission-workflow.md +773 -0
  68. package/augment-extensions/workflows/wordpress-plugin/module.json +49 -0
  69. package/augment-extensions/workflows/wordpress-plugin/rules/best-practices.md +942 -0
  70. package/augment-extensions/workflows/wordpress-plugin/rules/development-workflow.md +702 -0
  71. package/augment-extensions/workflows/wordpress-plugin/rules/submission-workflow.md +728 -0
  72. package/augment-extensions/workflows/wordpress-plugin/rules/testing-workflow.md +775 -0
  73. package/cli/dist/cli.js +5 -1
  74. package/cli/dist/cli.js.map +1 -1
  75. package/cli/dist/commands/show.d.ts.map +1 -1
  76. package/cli/dist/commands/show.js +41 -0
  77. package/cli/dist/commands/show.js.map +1 -1
  78. package/modules.md +52 -0
  79. package/package.json +1 -1
@@ -0,0 +1,1599 @@
1
+ # AJAX Plugin Example
2
+
3
+ ## Overview
4
+
5
+ This example demonstrates a complete **AJAX Plugin** with JavaScript AJAX calls, PHP handlers, nonce verification, JSON responses, error handling, and both frontend and admin AJAX functionality.
6
+
7
+ **Complexity**: Medium
8
+ **File Count**: 5-10 files
9
+ **Team Size**: 1-2 developers
10
+ **Use Case**: Dynamic content loading, form submissions, real-time updates, user interactions
11
+
12
+ ---
13
+
14
+ ## Complete Plugin: "AJAX Content Manager"
15
+
16
+ A comprehensive AJAX plugin demonstrating WordPress AJAX API, nonce security, JSON responses, error handling, and best practices for both frontend and admin AJAX requests.
17
+
18
+ ---
19
+
20
+ ## Directory Structure
21
+
22
+ ```
23
+ ajax-content-manager/
24
+ ├── ajax-content-manager.php # Main plugin file
25
+ ├── uninstall.php # Uninstall cleanup
26
+ ├── readme.txt # WordPress.org readme
27
+ ├── includes/
28
+ │ ├── class-ajax-handler.php # AJAX handler class
29
+ │ ├── class-content-manager.php # Content management
30
+ │ └── class-ajax-validator.php # Validation logic
31
+ ├── admin/
32
+ │ ├── css/
33
+ │ │ └── admin.css # Admin styles
34
+ │ ├── js/
35
+ │ │ └── admin-ajax.js # Admin AJAX scripts
36
+ │ └── views/
37
+ │ └── admin-page.php # Admin page view
38
+ └── public/
39
+ ├── css/
40
+ │ └── public.css # Public styles
41
+ └── js/
42
+ └── public-ajax.js # Public AJAX scripts
43
+ ```
44
+
45
+ ---
46
+
47
+ ## Main Plugin File
48
+
49
+ ### File: `ajax-content-manager.php`
50
+
51
+ ```php
52
+ <?php
53
+ /**
54
+ * Plugin Name: AJAX Content Manager
55
+ * Plugin URI: https://example.com/ajax-content-manager
56
+ * Description: Complete AJAX functionality with nonce security, JSON responses, and error handling
57
+ * Version: 1.0.0
58
+ * Author: Your Name
59
+ * Author URI: https://example.com
60
+ * License: GPL-2.0+
61
+ * License URI: http://www.gnu.org/licenses/gpl-2.0.txt
62
+ * Text Domain: ajax-content-manager
63
+ * Domain Path: /languages
64
+ *
65
+ * @package AJAX_Content_Manager
66
+ */
67
+
68
+ // Exit if accessed directly
69
+ if (!defined('ABSPATH')) {
70
+ exit;
71
+ }
72
+
73
+ // Define plugin constants
74
+ define('ACM_VERSION', '1.0.0');
75
+ define('ACM_PLUGIN_DIR', plugin_dir_path(__FILE__));
76
+ define('ACM_PLUGIN_URL', plugin_dir_url(__FILE__));
77
+ define('ACM_PLUGIN_FILE', __FILE__);
78
+
79
+ /**
80
+ * Autoloader
81
+ */
82
+ spl_autoload_register(function ($class) {
83
+ $prefix = 'ACM_';
84
+
85
+ if (strpos($class, $prefix) !== 0) {
86
+ return;
87
+ }
88
+
89
+ $class_name = str_replace($prefix, '', $class);
90
+ $class_file = 'class-' . str_replace('_', '-', strtolower($class_name)) . '.php';
91
+
92
+ $file = ACM_PLUGIN_DIR . 'includes/' . $class_file;
93
+ if (file_exists($file)) {
94
+ require $file;
95
+ }
96
+ });
97
+
98
+ /**
99
+ * Activation hook
100
+ */
101
+ register_activation_hook(__FILE__, function() {
102
+ // Create custom table for demo
103
+ global $wpdb;
104
+ $table_name = $wpdb->prefix . 'acm_items';
105
+
106
+ $charset_collate = $wpdb->get_charset_collate();
107
+
108
+ $sql = "CREATE TABLE $table_name (
109
+ id mediumint(9) NOT NULL AUTO_INCREMENT,
110
+ title varchar(255) NOT NULL,
111
+ content text NOT NULL,
112
+ status varchar(20) DEFAULT 'active',
113
+ created_at datetime DEFAULT CURRENT_TIMESTAMP,
114
+ PRIMARY KEY (id)
115
+ ) $charset_collate;";
116
+
117
+ require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
118
+ dbDelta($sql);
119
+ });
120
+
121
+ /**
122
+ * Deactivation hook
123
+ */
124
+ register_deactivation_hook(__FILE__, function() {
125
+ // Cleanup if needed
126
+ });
127
+
128
+ /**
129
+ * Initialize the plugin
130
+ */
131
+ function run_ajax_content_manager() {
132
+ // Initialize AJAX handlers
133
+ ACM_Ajax_Handler::init();
134
+
135
+ // Load text domain
136
+ add_action('plugins_loaded', 'acm_load_textdomain');
137
+
138
+ // Enqueue scripts
139
+ add_action('wp_enqueue_scripts', 'acm_enqueue_public_assets');
140
+ add_action('admin_enqueue_scripts', 'acm_enqueue_admin_assets');
141
+
142
+ // Add admin menu
143
+ add_action('admin_menu', 'acm_add_admin_menu');
144
+ }
145
+ add_action('plugins_loaded', 'run_ajax_content_manager');
146
+
147
+ /**
148
+ * Load plugin text domain
149
+ */
150
+ function acm_load_textdomain() {
151
+ load_plugin_textdomain(
152
+ 'ajax-content-manager',
153
+ false,
154
+ dirname(plugin_basename(ACM_PLUGIN_FILE)) . '/languages/'
155
+ );
156
+ }
157
+
158
+ /**
159
+ * Enqueue public assets
160
+ */
161
+ function acm_enqueue_public_assets() {
162
+ wp_enqueue_style(
163
+ 'acm-public',
164
+ ACM_PLUGIN_URL . 'public/css/public.css',
165
+ array(),
166
+ ACM_VERSION
167
+ );
168
+
169
+ wp_enqueue_script(
170
+ 'acm-public',
171
+ ACM_PLUGIN_URL . 'public/js/public-ajax.js',
172
+ array('jquery'),
173
+ ACM_VERSION,
174
+ true
175
+ );
176
+
177
+ // Localize script with AJAX URL and nonce
178
+ wp_localize_script('acm-public', 'acmAjax', array(
179
+ 'ajaxUrl' => admin_url('admin-ajax.php'),
180
+ 'nonce' => wp_create_nonce('acm_public_nonce'),
181
+ ));
182
+ }
183
+
184
+ /**
185
+ * Enqueue admin assets
186
+ */
187
+ function acm_enqueue_admin_assets($hook) {
188
+ // Only load on our admin page
189
+ if ('toplevel_page_ajax-content-manager' !== $hook) {
190
+ return;
191
+ }
192
+
193
+ wp_enqueue_style(
194
+ 'acm-admin',
195
+ ACM_PLUGIN_URL . 'admin/css/admin.css',
196
+ array(),
197
+ ACM_VERSION
198
+ );
199
+
200
+ wp_enqueue_script(
201
+ 'acm-admin',
202
+ ACM_PLUGIN_URL . 'admin/js/admin-ajax.js',
203
+ array('jquery'),
204
+ ACM_VERSION,
205
+ true
206
+ );
207
+
208
+ // Localize script with AJAX URL and nonce
209
+ wp_localize_script('acm-admin', 'acmAdminAjax', array(
210
+ 'ajaxUrl' => admin_url('admin-ajax.php'),
211
+ 'nonce' => wp_create_nonce('acm_admin_nonce'),
212
+ ));
213
+ }
214
+
215
+ /**
216
+ * Add admin menu
217
+ */
218
+ function acm_add_admin_menu() {
219
+ add_menu_page(
220
+ __('AJAX Content Manager', 'ajax-content-manager'),
221
+ __('AJAX Manager', 'ajax-content-manager'),
222
+ 'manage_options',
223
+ 'ajax-content-manager',
224
+ 'acm_render_admin_page',
225
+ 'dashicons-update',
226
+ 30
227
+ );
228
+ }
229
+
230
+ /**
231
+ * Render admin page
232
+ */
233
+ function acm_render_admin_page() {
234
+ if (!current_user_can('manage_options')) {
235
+ wp_die(__('You do not have sufficient permissions to access this page.', 'ajax-content-manager'));
236
+ }
237
+
238
+ include ACM_PLUGIN_DIR . 'admin/views/admin-page.php';
239
+ }
240
+ ```
241
+
242
+ ---
243
+
244
+ ## AJAX Handler Class
245
+
246
+ ### File: `includes/class-ajax-handler.php`
247
+
248
+ ```php
249
+ <?php
250
+ /**
251
+ * AJAX handler class
252
+ *
253
+ * @package AJAX_Content_Manager
254
+ */
255
+
256
+ class ACM_Ajax_Handler {
257
+ /**
258
+ * Initialize AJAX handlers
259
+ */
260
+ public static function init() {
261
+ // Public AJAX actions (for logged-in and non-logged-in users)
262
+ add_action('wp_ajax_acm_load_content', array(__CLASS__, 'load_content'));
263
+ add_action('wp_ajax_nopriv_acm_load_content', array(__CLASS__, 'load_content'));
264
+
265
+ add_action('wp_ajax_acm_submit_form', array(__CLASS__, 'submit_form'));
266
+ add_action('wp_ajax_nopriv_acm_submit_form', array(__CLASS__, 'submit_form'));
267
+
268
+ // Admin AJAX actions (for logged-in users only)
269
+ add_action('wp_ajax_acm_create_item', array(__CLASS__, 'create_item'));
270
+ add_action('wp_ajax_acm_update_item', array(__CLASS__, 'update_item'));
271
+ add_action('wp_ajax_acm_delete_item', array(__CLASS__, 'delete_item'));
272
+ add_action('wp_ajax_acm_get_items', array(__CLASS__, 'get_items'));
273
+ }
274
+
275
+ /**
276
+ * Load content (public AJAX)
277
+ */
278
+ public static function load_content() {
279
+ // Verify nonce
280
+ if (!check_ajax_referer('acm_public_nonce', 'nonce', false)) {
281
+ wp_send_json_error(array(
282
+ 'message' => __('Security check failed.', 'ajax-content-manager'),
283
+ ), 403);
284
+ }
285
+
286
+ // Get and sanitize input
287
+ $post_id = isset($_POST['post_id']) ? intval($_POST['post_id']) : 0;
288
+
289
+ if (!$post_id) {
290
+ wp_send_json_error(array(
291
+ 'message' => __('Invalid post ID.', 'ajax-content-manager'),
292
+ ), 400);
293
+ }
294
+
295
+ // Get post
296
+ $post = get_post($post_id);
297
+
298
+ if (!$post) {
299
+ wp_send_json_error(array(
300
+ 'message' => __('Post not found.', 'ajax-content-manager'),
301
+ ), 404);
302
+ }
303
+
304
+ // Prepare response
305
+ $response = array(
306
+ 'id' => $post->ID,
307
+ 'title' => get_the_title($post),
308
+ 'content' => apply_filters('the_content', $post->post_content),
309
+ 'excerpt' => get_the_excerpt($post),
310
+ 'date' => get_the_date('', $post),
311
+ 'author' => get_the_author_meta('display_name', $post->post_author),
312
+ );
313
+
314
+ wp_send_json_success($response);
315
+ }
316
+
317
+ /**
318
+ * Submit form (public AJAX)
319
+ */
320
+ public static function submit_form() {
321
+ // Verify nonce
322
+ if (!check_ajax_referer('acm_public_nonce', 'nonce', false)) {
323
+ wp_send_json_error(array(
324
+ 'message' => __('Security check failed.', 'ajax-content-manager'),
325
+ ), 403);
326
+ }
327
+
328
+ // Get and sanitize input
329
+ $name = isset($_POST['name']) ? sanitize_text_field($_POST['name']) : '';
330
+ $email = isset($_POST['email']) ? sanitize_email($_POST['email']) : '';
331
+ $message = isset($_POST['message']) ? sanitize_textarea_field($_POST['message']) : '';
332
+
333
+ // Validate input
334
+ $errors = array();
335
+
336
+ if (empty($name)) {
337
+ $errors[] = __('Name is required.', 'ajax-content-manager');
338
+ }
339
+
340
+ if (empty($email) || !is_email($email)) {
341
+ $errors[] = __('Valid email is required.', 'ajax-content-manager');
342
+ }
343
+
344
+ if (empty($message)) {
345
+ $errors[] = __('Message is required.', 'ajax-content-manager');
346
+ }
347
+
348
+ if (!empty($errors)) {
349
+ wp_send_json_error(array(
350
+ 'message' => implode(' ', $errors),
351
+ 'errors' => $errors,
352
+ ), 400);
353
+ }
354
+
355
+ // Process form (example: send email)
356
+ $to = get_option('admin_email');
357
+ $subject = sprintf(__('New message from %s', 'ajax-content-manager'), $name);
358
+ $body = sprintf(
359
+ "Name: %s\nEmail: %s\n\nMessage:\n%s",
360
+ $name,
361
+ $email,
362
+ $message
363
+ );
364
+
365
+ $sent = wp_mail($to, $subject, $body);
366
+
367
+ if ($sent) {
368
+ wp_send_json_success(array(
369
+ 'message' => __('Form submitted successfully!', 'ajax-content-manager'),
370
+ ));
371
+ } else {
372
+ wp_send_json_error(array(
373
+ 'message' => __('Failed to send email.', 'ajax-content-manager'),
374
+ ), 500);
375
+ }
376
+ }
377
+
378
+ /**
379
+ * Create item (admin AJAX)
380
+ */
381
+ public static function create_item() {
382
+ // Verify nonce
383
+ if (!check_ajax_referer('acm_admin_nonce', 'nonce', false)) {
384
+ wp_send_json_error(array(
385
+ 'message' => __('Security check failed.', 'ajax-content-manager'),
386
+ ), 403);
387
+ }
388
+
389
+ // Check capabilities
390
+ if (!current_user_can('manage_options')) {
391
+ wp_send_json_error(array(
392
+ 'message' => __('Permission denied.', 'ajax-content-manager'),
393
+ ), 403);
394
+ }
395
+
396
+ // Get and sanitize input
397
+ $title = isset($_POST['title']) ? sanitize_text_field($_POST['title']) : '';
398
+ $content = isset($_POST['content']) ? sanitize_textarea_field($_POST['content']) : '';
399
+
400
+ // Validate input
401
+ if (empty($title)) {
402
+ wp_send_json_error(array(
403
+ 'message' => __('Title is required.', 'ajax-content-manager'),
404
+ ), 400);
405
+ }
406
+
407
+ // Insert into database
408
+ global $wpdb;
409
+ $table_name = $wpdb->prefix . 'acm_items';
410
+
411
+ $result = $wpdb->insert(
412
+ $table_name,
413
+ array(
414
+ 'title' => $title,
415
+ 'content' => $content,
416
+ 'status' => 'active',
417
+ ),
418
+ array('%s', '%s', '%s')
419
+ );
420
+
421
+ if ($result === false) {
422
+ wp_send_json_error(array(
423
+ 'message' => __('Failed to create item.', 'ajax-content-manager'),
424
+ ), 500);
425
+ }
426
+
427
+ $item_id = $wpdb->insert_id;
428
+
429
+ wp_send_json_success(array(
430
+ 'message' => __('Item created successfully!', 'ajax-content-manager'),
431
+ 'item_id' => $item_id,
432
+ ));
433
+ }
434
+
435
+ /**
436
+ * Update item (admin AJAX)
437
+ */
438
+ public static function update_item() {
439
+ // Verify nonce
440
+ if (!check_ajax_referer('acm_admin_nonce', 'nonce', false)) {
441
+ wp_send_json_error(array(
442
+ 'message' => __('Security check failed.', 'ajax-content-manager'),
443
+ ), 403);
444
+ }
445
+
446
+ // Check capabilities
447
+ if (!current_user_can('manage_options')) {
448
+ wp_send_json_error(array(
449
+ 'message' => __('Permission denied.', 'ajax-content-manager'),
450
+ ), 403);
451
+ }
452
+
453
+ // Get and sanitize input
454
+ $item_id = isset($_POST['item_id']) ? intval($_POST['item_id']) : 0;
455
+ $title = isset($_POST['title']) ? sanitize_text_field($_POST['title']) : '';
456
+ $content = isset($_POST['content']) ? sanitize_textarea_field($_POST['content']) : '';
457
+ $status = isset($_POST['status']) ? sanitize_text_field($_POST['status']) : 'active';
458
+
459
+ // Validate input
460
+ if (!$item_id) {
461
+ wp_send_json_error(array(
462
+ 'message' => __('Invalid item ID.', 'ajax-content-manager'),
463
+ ), 400);
464
+ }
465
+
466
+ if (empty($title)) {
467
+ wp_send_json_error(array(
468
+ 'message' => __('Title is required.', 'ajax-content-manager'),
469
+ ), 400);
470
+ }
471
+
472
+ // Update in database
473
+ global $wpdb;
474
+ $table_name = $wpdb->prefix . 'acm_items';
475
+
476
+ $result = $wpdb->update(
477
+ $table_name,
478
+ array(
479
+ 'title' => $title,
480
+ 'content' => $content,
481
+ 'status' => $status,
482
+ ),
483
+ array('id' => $item_id),
484
+ array('%s', '%s', '%s'),
485
+ array('%d')
486
+ );
487
+
488
+ if ($result === false) {
489
+ wp_send_json_error(array(
490
+ 'message' => __('Failed to update item.', 'ajax-content-manager'),
491
+ ), 500);
492
+ }
493
+
494
+ wp_send_json_success(array(
495
+ 'message' => __('Item updated successfully!', 'ajax-content-manager'),
496
+ ));
497
+ }
498
+
499
+ /**
500
+ * Delete item (admin AJAX)
501
+ */
502
+ public static function delete_item() {
503
+ // Verify nonce
504
+ if (!check_ajax_referer('acm_admin_nonce', 'nonce', false)) {
505
+ wp_send_json_error(array(
506
+ 'message' => __('Security check failed.', 'ajax-content-manager'),
507
+ ), 403);
508
+ }
509
+
510
+ // Check capabilities
511
+ if (!current_user_can('manage_options')) {
512
+ wp_send_json_error(array(
513
+ 'message' => __('Permission denied.', 'ajax-content-manager'),
514
+ ), 403);
515
+ }
516
+
517
+ // Get and sanitize input
518
+ $item_id = isset($_POST['item_id']) ? intval($_POST['item_id']) : 0;
519
+
520
+ // Validate input
521
+ if (!$item_id) {
522
+ wp_send_json_error(array(
523
+ 'message' => __('Invalid item ID.', 'ajax-content-manager'),
524
+ ), 400);
525
+ }
526
+
527
+ // Delete from database
528
+ global $wpdb;
529
+ $table_name = $wpdb->prefix . 'acm_items';
530
+
531
+ $result = $wpdb->delete(
532
+ $table_name,
533
+ array('id' => $item_id),
534
+ array('%d')
535
+ );
536
+
537
+ if ($result === false) {
538
+ wp_send_json_error(array(
539
+ 'message' => __('Failed to delete item.', 'ajax-content-manager'),
540
+ ), 500);
541
+ }
542
+
543
+ wp_send_json_success(array(
544
+ 'message' => __('Item deleted successfully!', 'ajax-content-manager'),
545
+ ));
546
+ }
547
+
548
+ /**
549
+ * Get items (admin AJAX)
550
+ */
551
+ public static function get_items() {
552
+ // Verify nonce
553
+ if (!check_ajax_referer('acm_admin_nonce', 'nonce', false)) {
554
+ wp_send_json_error(array(
555
+ 'message' => __('Security check failed.', 'ajax-content-manager'),
556
+ ), 403);
557
+ }
558
+
559
+ // Check capabilities
560
+ if (!current_user_can('manage_options')) {
561
+ wp_send_json_error(array(
562
+ 'message' => __('Permission denied.', 'ajax-content-manager'),
563
+ ), 403);
564
+ }
565
+
566
+ // Get pagination parameters
567
+ $page = isset($_POST['page']) ? intval($_POST['page']) : 1;
568
+ $per_page = isset($_POST['per_page']) ? intval($_POST['per_page']) : 10;
569
+ $offset = ($page - 1) * $per_page;
570
+
571
+ // Get items from database
572
+ global $wpdb;
573
+ $table_name = $wpdb->prefix . 'acm_items';
574
+
575
+ $items = $wpdb->get_results(
576
+ $wpdb->prepare(
577
+ "SELECT * FROM $table_name ORDER BY created_at DESC LIMIT %d OFFSET %d",
578
+ $per_page,
579
+ $offset
580
+ ),
581
+ ARRAY_A
582
+ );
583
+
584
+ // Get total count
585
+ $total = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
586
+
587
+ wp_send_json_success(array(
588
+ 'items' => $items,
589
+ 'total' => intval($total),
590
+ 'page' => $page,
591
+ 'per_page' => $per_page,
592
+ 'total_pages' => ceil($total / $per_page),
593
+ ));
594
+ }
595
+ }
596
+ ```
597
+
598
+ ---
599
+
600
+ ## JavaScript Files
601
+
602
+ ### File: `public/js/public-ajax.js`
603
+
604
+ ```javascript
605
+ (function($) {
606
+ 'use strict';
607
+
608
+ $(document).ready(function() {
609
+
610
+ /**
611
+ * Load content via AJAX
612
+ */
613
+ $('.acm-load-content').on('click', function(e) {
614
+ e.preventDefault();
615
+
616
+ var $button = $(this);
617
+ var postId = $button.data('post-id');
618
+ var $container = $('#acm-content-container');
619
+
620
+ // Disable button
621
+ $button.prop('disabled', true).text('Loading...');
622
+
623
+ // Show loading state
624
+ $container.html('<p>Loading content...</p>');
625
+
626
+ $.ajax({
627
+ url: acmAjax.ajaxUrl,
628
+ type: 'POST',
629
+ data: {
630
+ action: 'acm_load_content',
631
+ nonce: acmAjax.nonce,
632
+ post_id: postId
633
+ },
634
+ success: function(response) {
635
+ if (response.success) {
636
+ var data = response.data;
637
+ var html = '<article class="acm-post">';
638
+ html += '<h2>' + data.title + '</h2>';
639
+ html += '<div class="acm-meta">';
640
+ html += '<span class="acm-date">' + data.date + '</span>';
641
+ html += '<span class="acm-author">By ' + data.author + '</span>';
642
+ html += '</div>';
643
+ html += '<div class="acm-content">' + data.content + '</div>';
644
+ html += '</article>';
645
+
646
+ $container.html(html);
647
+ } else {
648
+ $container.html('<p class="error">' + response.data.message + '</p>');
649
+ }
650
+ },
651
+ error: function(xhr, status, error) {
652
+ $container.html('<p class="error">An error occurred: ' + error + '</p>');
653
+ },
654
+ complete: function() {
655
+ $button.prop('disabled', false).text('Load Content');
656
+ }
657
+ });
658
+ });
659
+
660
+ /**
661
+ * Submit form via AJAX
662
+ */
663
+ $('#acm-contact-form').on('submit', function(e) {
664
+ e.preventDefault();
665
+
666
+ var $form = $(this);
667
+ var $submit = $form.find('button[type=\"submit\"]');
668
+ var $message = $('#acm-form-message');
669
+
670
+ // Get form data
671
+ var formData = {
672
+ action: 'acm_submit_form',
673
+ nonce: acmAjax.nonce,
674
+ name: $form.find('#acm-name').val(),
675
+ email: $form.find('#acm-email').val(),
676
+ message: $form.find('#acm-message').val()
677
+ };
678
+
679
+ // Disable submit button
680
+ $submit.prop('disabled', true).text('Submitting...');
681
+ $message.html('');
682
+
683
+ $.ajax({
684
+ url: acmAjax.ajaxUrl,
685
+ type: 'POST',
686
+ data: formData,
687
+ success: function(response) {
688
+ if (response.success) {
689
+ $message.html('<p class=\"success\">' + response.data.message + '</p>');
690
+ $form[0].reset();
691
+ } else {
692
+ $message.html('<p class=\"error\">' + response.data.message + '</p>');
693
+ }
694
+ },
695
+ error: function(xhr, status, error) {
696
+ $message.html('<p class=\"error\">An error occurred: ' + error + '</p>');
697
+ },
698
+ complete: function() {
699
+ $submit.prop('disabled', false).text('Submit');
700
+ }
701
+ });
702
+ });
703
+
704
+ /**
705
+ * Real-time search example
706
+ */
707
+ var searchTimeout;
708
+ $('#acm-search').on('keyup', function() {
709
+ var $input = $(this);
710
+ var query = $input.val();
711
+ var $results = $('#acm-search-results');
712
+
713
+ // Clear previous timeout
714
+ clearTimeout(searchTimeout);
715
+
716
+ // Don't search if query is too short
717
+ if (query.length < 3) {
718
+ $results.html('');
719
+ return;
720
+ }
721
+
722
+ // Show loading
723
+ $results.html('<p>Searching...</p>');
724
+
725
+ // Debounce search
726
+ searchTimeout = setTimeout(function() {
727
+ $.ajax({
728
+ url: acmAjax.ajaxUrl,
729
+ type: 'POST',
730
+ data: {
731
+ action: 'acm_search',
732
+ nonce: acmAjax.nonce,
733
+ query: query
734
+ },
735
+ success: function(response) {
736
+ if (response.success && response.data.results.length > 0) {
737
+ var html = '<ul>';
738
+ $.each(response.data.results, function(index, item) {
739
+ html += '<li><a href=\"' + item.url + '\">' + item.title + '</a></li>';
740
+ });
741
+ html += '</ul>';
742
+ $results.html(html);
743
+ } else {
744
+ $results.html('<p>No results found.</p>');
745
+ }
746
+ },
747
+ error: function() {
748
+ $results.html('<p class=\"error\">Search failed.</p>');
749
+ }
750
+ });
751
+ }, 500); // 500ms debounce
752
+ });
753
+
754
+ });
755
+
756
+ })(jQuery);
757
+ ```
758
+
759
+ ### File: `admin/js/admin-ajax.js`
760
+
761
+ ```javascript
762
+ (function($) {
763
+ 'use strict';
764
+
765
+ $(document).ready(function() {
766
+
767
+ /**
768
+ * Load items on page load
769
+ */
770
+ loadItems();
771
+
772
+ /**
773
+ * Create item
774
+ */
775
+ $('#acm-create-form').on('submit', function(e) {
776
+ e.preventDefault();
777
+
778
+ var $form = $(this);
779
+ var $submit = $form.find('button[type=\"submit\"]');
780
+
781
+ var formData = {
782
+ action: 'acm_create_item',
783
+ nonce: acmAdminAjax.nonce,
784
+ title: $form.find('#acm-title').val(),
785
+ content: $form.find('#acm-content').val()
786
+ };
787
+
788
+ $submit.prop('disabled', true).text('Creating...');
789
+
790
+ $.ajax({
791
+ url: acmAdminAjax.ajaxUrl,
792
+ type: 'POST',
793
+ data: formData,
794
+ success: function(response) {
795
+ if (response.success) {
796
+ alert(response.data.message);
797
+ $form[0].reset();
798
+ loadItems(); // Reload items list
799
+ } else {
800
+ alert('Error: ' + response.data.message);
801
+ }
802
+ },
803
+ error: function(xhr, status, error) {
804
+ alert('An error occurred: ' + error);
805
+ },
806
+ complete: function() {
807
+ $submit.prop('disabled', false).text('Create Item');
808
+ }
809
+ });
810
+ });
811
+
812
+ /**
813
+ * Delete item
814
+ */
815
+ $(document).on('click', '.acm-delete-item', function(e) {
816
+ e.preventDefault();
817
+
818
+ if (!confirm('Are you sure you want to delete this item?')) {
819
+ return;
820
+ }
821
+
822
+ var $button = $(this);
823
+ var itemId = $button.data('item-id');
824
+
825
+ $.ajax({
826
+ url: acmAdminAjax.ajaxUrl,
827
+ type: 'POST',
828
+ data: {
829
+ action: 'acm_delete_item',
830
+ nonce: acmAdminAjax.nonce,
831
+ item_id: itemId
832
+ },
833
+ success: function(response) {
834
+ if (response.success) {
835
+ alert(response.data.message);
836
+ loadItems(); // Reload items list
837
+ } else {
838
+ alert('Error: ' + response.data.message);
839
+ }
840
+ },
841
+ error: function(xhr, status, error) {
842
+ alert('An error occurred: ' + error);
843
+ }
844
+ });
845
+ });
846
+
847
+ /**
848
+ * Update item (inline edit example)
849
+ */
850
+ $(document).on('click', '.acm-update-item', function(e) {
851
+ e.preventDefault();
852
+
853
+ var $button = $(this);
854
+ var $row = $button.closest('tr');
855
+ var itemId = $button.data('item-id');
856
+
857
+ var newTitle = prompt('Enter new title:', $row.find('.acm-item-title').text());
858
+ if (!newTitle) {
859
+ return;
860
+ }
861
+
862
+ $.ajax({
863
+ url: acmAdminAjax.ajaxUrl,
864
+ type: 'POST',
865
+ data: {
866
+ action: 'acm_update_item',
867
+ nonce: acmAdminAjax.nonce,
868
+ item_id: itemId,
869
+ title: newTitle,
870
+ content: '', // Could get from a modal or inline editor
871
+ status: 'active'
872
+ },
873
+ success: function(response) {
874
+ if (response.success) {
875
+ alert(response.data.message);
876
+ loadItems(); // Reload items list
877
+ } else {
878
+ alert('Error: ' + response.data.message);
879
+ }
880
+ },
881
+ error: function(xhr, status, error) {
882
+ alert('An error occurred: ' + error);
883
+ }
884
+ });
885
+ });
886
+
887
+ /**
888
+ * Load items with pagination
889
+ */
890
+ function loadItems(page) {
891
+ page = page || 1;
892
+
893
+ var $container = $('#acm-items-list');
894
+ $container.html('<p>Loading items...</p>');
895
+
896
+ $.ajax({
897
+ url: acmAdminAjax.ajaxUrl,
898
+ type: 'POST',
899
+ data: {
900
+ action: 'acm_get_items',
901
+ nonce: acmAdminAjax.nonce,
902
+ page: page,
903
+ per_page: 10
904
+ },
905
+ success: function(response) {
906
+ if (response.success) {
907
+ renderItems(response.data);
908
+ } else {
909
+ $container.html('<p class=\"error\">' + response.data.message + '</p>');
910
+ }
911
+ },
912
+ error: function(xhr, status, error) {
913
+ $container.html('<p class=\"error\">Failed to load items: ' + error + '</p>');
914
+ }
915
+ });
916
+ }
917
+
918
+ /**
919
+ * Render items table
920
+ */
921
+ function renderItems(data) {
922
+ var $container = $('#acm-items-list');
923
+
924
+ if (data.items.length === 0) {
925
+ $container.html('<p>No items found.</p>');
926
+ return;
927
+ }
928
+
929
+ var html = '<table class=\"wp-list-table widefat fixed striped\">';
930
+ html += '<thead><tr>';
931
+ html += '<th>ID</th>';
932
+ html += '<th>Title</th>';
933
+ html += '<th>Status</th>';
934
+ html += '<th>Created</th>';
935
+ html += '<th>Actions</th>';
936
+ html += '</tr></thead>';
937
+ html += '<tbody>';
938
+
939
+ $.each(data.items, function(index, item) {
940
+ html += '<tr>';
941
+ html += '<td>' + item.id + '</td>';
942
+ html += '<td class=\"acm-item-title\">' + item.title + '</td>';
943
+ html += '<td>' + item.status + '</td>';
944
+ html += '<td>' + item.created_at + '</td>';
945
+ html += '<td>';
946
+ html += '<button class=\"button acm-update-item\" data-item-id=\"' + item.id + '\">Edit</button> ';
947
+ html += '<button class=\"button acm-delete-item\" data-item-id=\"' + item.id + '\">Delete</button>';
948
+ html += '</td>';
949
+ html += '</tr>';
950
+ });
951
+
952
+ html += '</tbody></table>';
953
+
954
+ // Add pagination
955
+ if (data.total_pages > 1) {
956
+ html += '<div class=\"acm-pagination\">';
957
+ for (var i = 1; i <= data.total_pages; i++) {
958
+ var activeClass = (i === data.page) ? ' button-primary' : '';
959
+ html += '<button class=\"button acm-page-btn' + activeClass + '\" data-page=\"' + i + '\">' + i + '</button> ';
960
+ }
961
+ html += '</div>';
962
+ }
963
+
964
+ $container.html(html);
965
+ }
966
+
967
+ /**
968
+ * Pagination click
969
+ */
970
+ $(document).on('click', '.acm-page-btn', function() {
971
+ var page = $(this).data('page');
972
+ loadItems(page);
973
+ });
974
+
975
+ });
976
+
977
+ })(jQuery);
978
+ ```
979
+
980
+ ---
981
+
982
+ ## Admin View
983
+
984
+ ### File: `admin/views/admin-page.php`
985
+
986
+ ```php
987
+ <?php
988
+ /**
989
+ * Admin page view
990
+ *
991
+ * @package AJAX_Content_Manager
992
+ */
993
+
994
+ // Exit if accessed directly
995
+ if (!defined('ABSPATH')) {
996
+ exit;
997
+ }
998
+ ?>
999
+
1000
+ <div class="wrap acm-admin-wrap">
1001
+ <h1><?php echo esc_html(get_admin_page_title()); ?></h1>
1002
+
1003
+ <div class="acm-admin-container">
1004
+ <div class="acm-admin-main">
1005
+ <div class="acm-section">
1006
+ <h2><?php _e('Create New Item', 'ajax-content-manager'); ?></h2>
1007
+ <form id="acm-create-form">
1008
+ <table class="form-table">
1009
+ <tr>
1010
+ <th><label for="acm-title"><?php _e('Title', 'ajax-content-manager'); ?></label></th>
1011
+ <td>
1012
+ <input type="text" id="acm-title" class="regular-text" required>
1013
+ </td>
1014
+ </tr>
1015
+ <tr>
1016
+ <th><label for="acm-content"><?php _e('Content', 'ajax-content-manager'); ?></label></th>
1017
+ <td>
1018
+ <textarea id="acm-content" rows="5" class="large-text"></textarea>
1019
+ </td>
1020
+ </tr>
1021
+ </table>
1022
+ <p class="submit">
1023
+ <button type="submit" class="button button-primary"><?php _e('Create Item', 'ajax-content-manager'); ?></button>
1024
+ </p>
1025
+ </form>
1026
+ </div>
1027
+
1028
+ <div class="acm-section">
1029
+ <h2><?php _e('Items List', 'ajax-content-manager'); ?></h2>
1030
+ <div id="acm-items-list">
1031
+ <p><?php _e('Loading items...', 'ajax-content-manager'); ?></p>
1032
+ </div>
1033
+ </div>
1034
+ </div>
1035
+
1036
+ <div class="acm-admin-sidebar">
1037
+ <div class="acm-sidebar-box">
1038
+ <h3><?php _e('About AJAX', 'ajax-content-manager'); ?></h3>
1039
+ <p><?php _e('This plugin demonstrates WordPress AJAX functionality with proper security and error handling.', 'ajax-content-manager'); ?></p>
1040
+ </div>
1041
+
1042
+ <div class="acm-sidebar-box">
1043
+ <h3><?php _e('Features', 'ajax-content-manager'); ?></h3>
1044
+ <ul>
1045
+ <li><?php _e('Nonce verification', 'ajax-content-manager'); ?></li>
1046
+ <li><?php _e('JSON responses', 'ajax-content-manager'); ?></li>
1047
+ <li><?php _e('Error handling', 'ajax-content-manager'); ?></li>
1048
+ <li><?php _e('Capability checks', 'ajax-content-manager'); ?></li>
1049
+ <li><?php _e('Input sanitization', 'ajax-content-manager'); ?></li>
1050
+ </ul>
1051
+ </div>
1052
+ </div>
1053
+ </div>
1054
+ </div>
1055
+ ```
1056
+
1057
+ ---
1058
+
1059
+ ## CSS Files
1060
+
1061
+ ### File: `public/css/public.css`
1062
+
1063
+ ```css
1064
+ /* AJAX Content Container */
1065
+ #acm-content-container {
1066
+ padding: 20px;
1067
+ background: #f9f9f9;
1068
+ border: 1px solid #ddd;
1069
+ border-radius: 4px;
1070
+ margin: 20px 0;
1071
+ }
1072
+
1073
+ .acm-post {
1074
+ background: #fff;
1075
+ padding: 20px;
1076
+ border-radius: 4px;
1077
+ }
1078
+
1079
+ .acm-post h2 {
1080
+ margin-top: 0;
1081
+ color: #333;
1082
+ }
1083
+
1084
+ .acm-meta {
1085
+ color: #666;
1086
+ font-size: 14px;
1087
+ margin-bottom: 15px;
1088
+ padding-bottom: 10px;
1089
+ border-bottom: 1px solid #eee;
1090
+ }
1091
+
1092
+ .acm-meta span {
1093
+ margin-right: 15px;
1094
+ }
1095
+
1096
+ .acm-content {
1097
+ line-height: 1.6;
1098
+ }
1099
+
1100
+ /* Contact Form */
1101
+ #acm-contact-form {
1102
+ max-width: 600px;
1103
+ margin: 20px 0;
1104
+ }
1105
+
1106
+ #acm-contact-form label {
1107
+ display: block;
1108
+ margin-bottom: 5px;
1109
+ font-weight: 600;
1110
+ }
1111
+
1112
+ #acm-contact-form input[type="text"],
1113
+ #acm-contact-form input[type="email"],
1114
+ #acm-contact-form textarea {
1115
+ width: 100%;
1116
+ padding: 10px;
1117
+ border: 1px solid #ddd;
1118
+ border-radius: 4px;
1119
+ margin-bottom: 15px;
1120
+ }
1121
+
1122
+ #acm-contact-form button {
1123
+ background: #0073aa;
1124
+ color: #fff;
1125
+ padding: 10px 20px;
1126
+ border: none;
1127
+ border-radius: 4px;
1128
+ cursor: pointer;
1129
+ }
1130
+
1131
+ #acm-contact-form button:hover {
1132
+ background: #005177;
1133
+ }
1134
+
1135
+ #acm-contact-form button:disabled {
1136
+ background: #ccc;
1137
+ cursor: not-allowed;
1138
+ }
1139
+
1140
+ /* Messages */
1141
+ #acm-form-message .success {
1142
+ color: #46b450;
1143
+ background: #ecf7ed;
1144
+ padding: 10px;
1145
+ border-left: 4px solid #46b450;
1146
+ margin: 15px 0;
1147
+ }
1148
+
1149
+ #acm-form-message .error,
1150
+ .error {
1151
+ color: #dc3232;
1152
+ background: #fbeaea;
1153
+ padding: 10px;
1154
+ border-left: 4px solid #dc3232;
1155
+ margin: 15px 0;
1156
+ }
1157
+
1158
+ /* Search */
1159
+ #acm-search {
1160
+ width: 100%;
1161
+ max-width: 400px;
1162
+ padding: 10px;
1163
+ border: 1px solid #ddd;
1164
+ border-radius: 4px;
1165
+ }
1166
+
1167
+ #acm-search-results {
1168
+ margin-top: 15px;
1169
+ }
1170
+
1171
+ #acm-search-results ul {
1172
+ list-style: none;
1173
+ padding: 0;
1174
+ }
1175
+
1176
+ #acm-search-results li {
1177
+ padding: 10px;
1178
+ border-bottom: 1px solid #eee;
1179
+ }
1180
+
1181
+ #acm-search-results li:last-child {
1182
+ border-bottom: none;
1183
+ }
1184
+
1185
+ #acm-search-results a {
1186
+ text-decoration: none;
1187
+ color: #0073aa;
1188
+ }
1189
+
1190
+ #acm-search-results a:hover {
1191
+ text-decoration: underline;
1192
+ }
1193
+ ```
1194
+
1195
+ ### File: `admin/css/admin.css`
1196
+
1197
+ ```css
1198
+ /* Admin Layout */
1199
+ .acm-admin-wrap {
1200
+ margin: 20px 20px 0 0;
1201
+ }
1202
+
1203
+ .acm-admin-container {
1204
+ display: flex;
1205
+ gap: 30px;
1206
+ margin-top: 20px;
1207
+ }
1208
+
1209
+ .acm-admin-main {
1210
+ flex: 1;
1211
+ }
1212
+
1213
+ .acm-admin-sidebar {
1214
+ width: 300px;
1215
+ flex-shrink: 0;
1216
+ }
1217
+
1218
+ @media (max-width: 1024px) {
1219
+ .acm-admin-container {
1220
+ flex-direction: column;
1221
+ }
1222
+
1223
+ .acm-admin-sidebar {
1224
+ width: 100%;
1225
+ }
1226
+ }
1227
+
1228
+ /* Sections */
1229
+ .acm-section {
1230
+ background: #fff;
1231
+ padding: 20px;
1232
+ margin-bottom: 20px;
1233
+ border: 1px solid #ccd0d4;
1234
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
1235
+ }
1236
+
1237
+ .acm-section h2 {
1238
+ margin-top: 0;
1239
+ padding-bottom: 10px;
1240
+ border-bottom: 1px solid #e0e0e0;
1241
+ }
1242
+
1243
+ /* Items List */
1244
+ #acm-items-list {
1245
+ margin-top: 15px;
1246
+ }
1247
+
1248
+ #acm-items-list table {
1249
+ margin-top: 0;
1250
+ }
1251
+
1252
+ /* Pagination */
1253
+ .acm-pagination {
1254
+ margin-top: 15px;
1255
+ text-align: center;
1256
+ }
1257
+
1258
+ .acm-pagination .button {
1259
+ margin: 0 2px;
1260
+ }
1261
+
1262
+ /* Sidebar */
1263
+ .acm-sidebar-box {
1264
+ background: #fff;
1265
+ padding: 20px;
1266
+ margin-bottom: 20px;
1267
+ border: 1px solid #ccd0d4;
1268
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
1269
+ }
1270
+
1271
+ .acm-sidebar-box h3 {
1272
+ margin-top: 0;
1273
+ margin-bottom: 15px;
1274
+ font-size: 14px;
1275
+ font-weight: 600;
1276
+ }
1277
+
1278
+ .acm-sidebar-box ul {
1279
+ margin: 0;
1280
+ padding-left: 20px;
1281
+ }
1282
+
1283
+ .acm-sidebar-box li {
1284
+ margin-bottom: 8px;
1285
+ font-size: 13px;
1286
+ }
1287
+ ```
1288
+
1289
+ ---
1290
+
1291
+ ## Uninstall
1292
+
1293
+ ### File: `uninstall.php`
1294
+
1295
+ ```php
1296
+ <?php
1297
+ /**
1298
+ * Uninstall script
1299
+ *
1300
+ * @package AJAX_Content_Manager
1301
+ */
1302
+
1303
+ // Exit if accessed directly or not uninstalling
1304
+ if (!defined('WP_UNINSTALL_PLUGIN')) {
1305
+ exit;
1306
+ }
1307
+
1308
+ // Delete custom table
1309
+ global $wpdb;
1310
+ $table_name = $wpdb->prefix . 'acm_items';
1311
+ $wpdb->query("DROP TABLE IF EXISTS $table_name");
1312
+
1313
+ // Delete options (if any)
1314
+ delete_option('acm_settings');
1315
+
1316
+ // Clear any cached data
1317
+ wp_cache_flush();
1318
+ ```
1319
+
1320
+ ---
1321
+
1322
+ ## Usage Examples
1323
+
1324
+ ### Frontend AJAX Example
1325
+
1326
+ **HTML:**
1327
+ ```html
1328
+ <button class="acm-load-content" data-post-id="123">Load Post</button>
1329
+ <div id="acm-content-container"></div>
1330
+
1331
+ <form id="acm-contact-form">
1332
+ <label for="acm-name">Name</label>
1333
+ <input type="text" id="acm-name" required>
1334
+
1335
+ <label for="acm-email">Email</label>
1336
+ <input type="email" id="acm-email" required>
1337
+
1338
+ <label for="acm-message">Message</label>
1339
+ <textarea id="acm-message" required></textarea>
1340
+
1341
+ <button type="submit">Submit</button>
1342
+ </form>
1343
+ <div id="acm-form-message"></div>
1344
+ ```
1345
+
1346
+ ### Admin AJAX Example
1347
+
1348
+ The admin page automatically loads items and provides create/update/delete functionality through AJAX.
1349
+
1350
+ ### Custom AJAX Handler
1351
+
1352
+ ```php
1353
+ <?php
1354
+ // Add to includes/class-ajax-handler.php
1355
+
1356
+ public static function custom_action() {
1357
+ // Verify nonce
1358
+ if (!check_ajax_referer('acm_public_nonce', 'nonce', false)) {
1359
+ wp_send_json_error(array(
1360
+ 'message' => __('Security check failed.', 'ajax-content-manager'),
1361
+ ), 403);
1362
+ }
1363
+
1364
+ // Get and sanitize input
1365
+ $data = isset($_POST['data']) ? sanitize_text_field($_POST['data']) : '';
1366
+
1367
+ // Process data
1368
+ $result = do_something_with_data($data);
1369
+
1370
+ // Send response
1371
+ if ($result) {
1372
+ wp_send_json_success(array(
1373
+ 'message' => __('Success!', 'ajax-content-manager'),
1374
+ 'data' => $result,
1375
+ ));
1376
+ } else {
1377
+ wp_send_json_error(array(
1378
+ 'message' => __('Failed to process data.', 'ajax-content-manager'),
1379
+ ), 500);
1380
+ }
1381
+ }
1382
+
1383
+ // Register the action
1384
+ add_action('wp_ajax_acm_custom_action', array(__CLASS__, 'custom_action'));
1385
+ add_action('wp_ajax_nopriv_acm_custom_action', array(__CLASS__, 'custom_action'));
1386
+ ?>
1387
+ ```
1388
+
1389
+ ### JavaScript AJAX Call
1390
+
1391
+ ```javascript
1392
+ $.ajax({
1393
+ url: acmAjax.ajaxUrl,
1394
+ type: 'POST',
1395
+ data: {
1396
+ action: 'acm_custom_action',
1397
+ nonce: acmAjax.nonce,
1398
+ data: 'your data here'
1399
+ },
1400
+ success: function(response) {
1401
+ if (response.success) {
1402
+ console.log('Success:', response.data);
1403
+ } else {
1404
+ console.error('Error:', response.data.message);
1405
+ }
1406
+ },
1407
+ error: function(xhr, status, error) {
1408
+ console.error('AJAX error:', error);
1409
+ }
1410
+ });
1411
+ ```
1412
+
1413
+ ---
1414
+
1415
+ ## Key Features
1416
+
1417
+ ### 1. Security
1418
+ - **Nonce verification**: All AJAX requests verified with `check_ajax_referer()`
1419
+ - **Capability checks**: Admin actions require `manage_options` capability
1420
+ - **Input sanitization**: All inputs sanitized with appropriate functions
1421
+ - **Output escaping**: All outputs escaped for security
1422
+
1423
+ ### 2. AJAX Actions
1424
+ - **Public actions**: Available to all users (logged-in and non-logged-in)
1425
+ - **Admin actions**: Available only to logged-in users with proper capabilities
1426
+ - **Proper registration**: Using `wp_ajax_` and `wp_ajax_nopriv_` hooks
1427
+
1428
+ ### 3. JSON Responses
1429
+ - **Success responses**: `wp_send_json_success($data)`
1430
+ - **Error responses**: `wp_send_json_error($data, $status_code)`
1431
+ - **Consistent format**: All responses follow WordPress standards
1432
+
1433
+ ### 4. Error Handling
1434
+ - **Validation errors**: Clear error messages for invalid input
1435
+ - **Database errors**: Proper error handling for database operations
1436
+ - **AJAX errors**: JavaScript error handling with user feedback
1437
+
1438
+ ### 5. User Experience
1439
+ - **Loading states**: Buttons disabled during AJAX requests
1440
+ - **Progress indicators**: "Loading..." messages
1441
+ - **Success/error messages**: Clear feedback to users
1442
+ - **Debouncing**: Search input debounced to reduce server load
1443
+
1444
+ ---
1445
+
1446
+ ## Best Practices Demonstrated
1447
+
1448
+ ### Security
1449
+ ✅ Nonce verification (`check_ajax_referer`)
1450
+ ✅ Capability checks (`current_user_can`)
1451
+ ✅ Input sanitization (`sanitize_text_field`, `sanitize_email`)
1452
+ ✅ Output escaping (`esc_html`, `esc_attr`, `esc_url`)
1453
+ ✅ Prepared statements (`$wpdb->prepare`)
1454
+
1455
+ ### WordPress Standards
1456
+ ✅ Proper AJAX action registration
1457
+ ✅ `wp_send_json_success` and `wp_send_json_error`
1458
+ ✅ `wp_localize_script` for passing data to JavaScript
1459
+ ✅ Internationalization (`__()`, `_e()`)
1460
+ ✅ WordPress coding standards
1461
+
1462
+ ### JavaScript
1463
+ ✅ jQuery document ready
1464
+ ✅ Event delegation for dynamic elements
1465
+ ✅ Debouncing for search inputs
1466
+ ✅ Loading states and user feedback
1467
+ ✅ Error handling
1468
+
1469
+ ### Code Quality
1470
+ ✅ Separation of concerns (handler, validator, manager)
1471
+ ✅ Reusable AJAX handler methods
1472
+ ✅ Consistent naming conventions
1473
+ ✅ Proper documentation
1474
+ ✅ Clean uninstall process
1475
+
1476
+ ---
1477
+
1478
+ ## Common AJAX Patterns
1479
+
1480
+ ### 1. Load More Posts
1481
+
1482
+ ```javascript
1483
+ $('.load-more').on('click', function() {
1484
+ var page = $(this).data('page');
1485
+
1486
+ $.ajax({
1487
+ url: acmAjax.ajaxUrl,
1488
+ type: 'POST',
1489
+ data: {
1490
+ action: 'acm_load_more_posts',
1491
+ nonce: acmAjax.nonce,
1492
+ page: page
1493
+ },
1494
+ success: function(response) {
1495
+ if (response.success) {
1496
+ $('.posts-container').append(response.data.html);
1497
+ $('.load-more').data('page', page + 1);
1498
+ }
1499
+ }
1500
+ });
1501
+ });
1502
+ ```
1503
+
1504
+ ### 2. Like/Unlike Button
1505
+
1506
+ ```javascript
1507
+ $('.like-button').on('click', function() {
1508
+ var postId = $(this).data('post-id');
1509
+ var $button = $(this);
1510
+
1511
+ $.ajax({
1512
+ url: acmAjax.ajaxUrl,
1513
+ type: 'POST',
1514
+ data: {
1515
+ action: 'acm_toggle_like',
1516
+ nonce: acmAjax.nonce,
1517
+ post_id: postId
1518
+ },
1519
+ success: function(response) {
1520
+ if (response.success) {
1521
+ $button.text(response.data.liked ? 'Unlike' : 'Like');
1522
+ $button.siblings('.like-count').text(response.data.count);
1523
+ }
1524
+ }
1525
+ });
1526
+ });
1527
+ ```
1528
+
1529
+ ### 3. Auto-save Draft
1530
+
1531
+ ```javascript
1532
+ var autoSaveTimeout;
1533
+ $('#content').on('keyup', function() {
1534
+ clearTimeout(autoSaveTimeout);
1535
+
1536
+ autoSaveTimeout = setTimeout(function() {
1537
+ $.ajax({
1538
+ url: acmAjax.ajaxUrl,
1539
+ type: 'POST',
1540
+ data: {
1541
+ action: 'acm_auto_save',
1542
+ nonce: acmAjax.nonce,
1543
+ content: $('#content').val()
1544
+ },
1545
+ success: function(response) {
1546
+ if (response.success) {
1547
+ $('.save-status').text('Saved at ' + new Date().toLocaleTimeString());
1548
+ }
1549
+ }
1550
+ });
1551
+ }, 2000); // Auto-save after 2 seconds of inactivity
1552
+ });
1553
+ ```
1554
+
1555
+ ---
1556
+
1557
+ ## Testing Checklist
1558
+
1559
+ - [ ] Test nonce verification (try with invalid nonce)
1560
+ - [ ] Test capability checks (try as non-admin user)
1561
+ - [ ] Test input validation (submit invalid data)
1562
+ - [ ] Test success responses
1563
+ - [ ] Test error responses
1564
+ - [ ] Test loading states
1565
+ - [ ] Test pagination
1566
+ - [ ] Test search debouncing
1567
+ - [ ] Test form submission
1568
+ - [ ] Test CRUD operations
1569
+ - [ ] Check for JavaScript errors in console
1570
+ - [ ] Test on different browsers
1571
+ - [ ] Test with slow network (throttling)
1572
+
1573
+ ---
1574
+
1575
+ ## Comparison with Other Patterns
1576
+
1577
+ | Feature | This Plugin | Basic AJAX | Advanced AJAX | REST API |\n|---------|-------------|------------|---------------|----------|\n| **Complexity** | Medium | Low | High | High |\n| **Security** | ✅ Full | ⚠️ Basic | ✅ Full | ✅ Full |\n| **Nonce Verification** | ✅ | ❌ | ✅ | ✅ |\n| **JSON Responses** | ✅ | ⚠️ | ✅ | ✅ |\n| **Error Handling** | ✅ Advanced | ❌ | ✅ Advanced | ✅ Advanced |\n| **Capability Checks** | ✅ | ❌ | ✅ | ✅ |\n| **Loading States** | ✅ | ❌ | ✅ | ✅ |\n| **Best For** | Most plugins | Simple tasks | Complex apps | APIs |\n\n---
1578
+
1579
+ ## Summary
1580
+
1581
+ This example demonstrates a **complete AJAX plugin** with:
1582
+
1583
+ - ✅ WordPress AJAX API integration
1584
+ - ✅ Nonce verification for security
1585
+ - ✅ JSON responses with `wp_send_json_success` and `wp_send_json_error`
1586
+ - ✅ Both frontend and admin AJAX functionality
1587
+ - ✅ Proper error handling and validation
1588
+ - ✅ Loading states and user feedback
1589
+ - ✅ Capability checks for admin actions
1590
+ - ✅ Input sanitization and output escaping
1591
+ - ✅ Debouncing for search inputs
1592
+ - ✅ Pagination support
1593
+ - ✅ CRUD operations via AJAX
1594
+ - ✅ Clean uninstall process
1595
+
1596
+ **Perfect for**: Dynamic content loading, form submissions, real-time updates, user interactions, admin interfaces, or any plugin requiring asynchronous communication with the server.
1597
+
1598
+ **Character Count**: ~28,000 characters
1599
+