@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,874 @@
|
|
|
1
|
+
# WordPress Plugin Admin Interface
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This document covers creating admin interfaces for WordPress plugins, including settings pages, meta boxes, admin notices, list tables, and dashboard widgets.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Settings Pages
|
|
10
|
+
|
|
11
|
+
### Adding a Top-Level Menu
|
|
12
|
+
|
|
13
|
+
```php
|
|
14
|
+
<?php
|
|
15
|
+
add_action('admin_menu', 'my_plugin_add_admin_menu');
|
|
16
|
+
|
|
17
|
+
function my_plugin_add_admin_menu() {
|
|
18
|
+
add_menu_page(
|
|
19
|
+
'My Plugin Settings', // Page title
|
|
20
|
+
'My Plugin', // Menu title
|
|
21
|
+
'manage_options', // Capability
|
|
22
|
+
'my-plugin', // Menu slug
|
|
23
|
+
'my_plugin_settings_page', // Callback function
|
|
24
|
+
'dashicons-admin-generic', // Icon
|
|
25
|
+
20 // Position
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function my_plugin_settings_page() {
|
|
30
|
+
// Check user capabilities
|
|
31
|
+
if (!current_user_can('manage_options')) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
?>
|
|
36
|
+
<div class="wrap">
|
|
37
|
+
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
|
|
38
|
+
<form action="options.php" method="post">
|
|
39
|
+
<?php
|
|
40
|
+
settings_fields('my_plugin_options');
|
|
41
|
+
do_settings_sections('my-plugin');
|
|
42
|
+
submit_button('Save Settings');
|
|
43
|
+
?>
|
|
44
|
+
</form>
|
|
45
|
+
</div>
|
|
46
|
+
<?php
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Adding a Submenu Page
|
|
51
|
+
|
|
52
|
+
```php
|
|
53
|
+
<?php
|
|
54
|
+
add_action('admin_menu', 'my_plugin_add_submenu');
|
|
55
|
+
|
|
56
|
+
function my_plugin_add_submenu() {
|
|
57
|
+
// Add to Settings menu
|
|
58
|
+
add_options_page(
|
|
59
|
+
'My Plugin Settings', // Page title
|
|
60
|
+
'My Plugin', // Menu title
|
|
61
|
+
'manage_options', // Capability
|
|
62
|
+
'my-plugin', // Menu slug
|
|
63
|
+
'my_plugin_settings_page' // Callback
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// Or add to Tools menu
|
|
67
|
+
add_management_page(
|
|
68
|
+
'My Plugin Tools',
|
|
69
|
+
'My Plugin',
|
|
70
|
+
'manage_options',
|
|
71
|
+
'my-plugin-tools',
|
|
72
|
+
'my_plugin_tools_page'
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Settings API Implementation
|
|
78
|
+
|
|
79
|
+
```php
|
|
80
|
+
<?php
|
|
81
|
+
add_action('admin_init', 'my_plugin_settings_init');
|
|
82
|
+
|
|
83
|
+
function my_plugin_settings_init() {
|
|
84
|
+
// Register setting
|
|
85
|
+
register_setting(
|
|
86
|
+
'my_plugin_options', // Option group
|
|
87
|
+
'my_plugin_settings', // Option name
|
|
88
|
+
'my_plugin_sanitize_settings' // Sanitize callback
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// Add settings section
|
|
92
|
+
add_settings_section(
|
|
93
|
+
'my_plugin_section_general', // Section ID
|
|
94
|
+
'General Settings', // Title
|
|
95
|
+
'my_plugin_section_callback', // Callback
|
|
96
|
+
'my-plugin' // Page slug
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
// Add settings fields
|
|
100
|
+
add_settings_field(
|
|
101
|
+
'my_plugin_field_api_key', // Field ID
|
|
102
|
+
'API Key', // Title
|
|
103
|
+
'my_plugin_field_api_key_cb', // Callback
|
|
104
|
+
'my-plugin', // Page slug
|
|
105
|
+
'my_plugin_section_general', // Section ID
|
|
106
|
+
array(
|
|
107
|
+
'label_for' => 'api_key',
|
|
108
|
+
'class' => 'my_plugin_row'
|
|
109
|
+
)
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
add_settings_field(
|
|
113
|
+
'my_plugin_field_enabled',
|
|
114
|
+
'Enable Feature',
|
|
115
|
+
'my_plugin_field_enabled_cb',
|
|
116
|
+
'my-plugin',
|
|
117
|
+
'my_plugin_section_general',
|
|
118
|
+
array('label_for' => 'enabled')
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function my_plugin_section_callback($args) {
|
|
123
|
+
?>
|
|
124
|
+
<p>Configure your plugin settings below.</p>
|
|
125
|
+
<?php
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function my_plugin_field_api_key_cb($args) {
|
|
129
|
+
$options = get_option('my_plugin_settings');
|
|
130
|
+
$value = isset($options['api_key']) ? $options['api_key'] : '';
|
|
131
|
+
?>
|
|
132
|
+
<input
|
|
133
|
+
type="text"
|
|
134
|
+
id="<?php echo esc_attr($args['label_for']); ?>"
|
|
135
|
+
name="my_plugin_settings[<?php echo esc_attr($args['label_for']); ?>]"
|
|
136
|
+
value="<?php echo esc_attr($value); ?>"
|
|
137
|
+
class="regular-text"
|
|
138
|
+
/>
|
|
139
|
+
<p class="description">Enter your API key from the provider.</p>
|
|
140
|
+
<?php
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function my_plugin_field_enabled_cb($args) {
|
|
144
|
+
$options = get_option('my_plugin_settings');
|
|
145
|
+
$checked = isset($options['enabled']) ? $options['enabled'] : false;
|
|
146
|
+
?>
|
|
147
|
+
<input
|
|
148
|
+
type="checkbox"
|
|
149
|
+
id="<?php echo esc_attr($args['label_for']); ?>"
|
|
150
|
+
name="my_plugin_settings[<?php echo esc_attr($args['label_for']); ?>]"
|
|
151
|
+
value="1"
|
|
152
|
+
<?php checked($checked, 1); ?>
|
|
153
|
+
/>
|
|
154
|
+
<label for="<?php echo esc_attr($args['label_for']); ?>">
|
|
155
|
+
Enable this feature
|
|
156
|
+
</label>
|
|
157
|
+
<?php
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function my_plugin_sanitize_settings($input) {
|
|
161
|
+
$sanitized = array();
|
|
162
|
+
|
|
163
|
+
// Sanitize API key
|
|
164
|
+
if (isset($input['api_key'])) {
|
|
165
|
+
$sanitized['api_key'] = sanitize_text_field($input['api_key']);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Sanitize checkbox
|
|
169
|
+
$sanitized['enabled'] = isset($input['enabled']) ? 1 : 0;
|
|
170
|
+
|
|
171
|
+
return $sanitized;
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Object-Oriented Settings Page
|
|
176
|
+
|
|
177
|
+
```php
|
|
178
|
+
<?php
|
|
179
|
+
class My_Plugin_Admin {
|
|
180
|
+
private $plugin_name;
|
|
181
|
+
private $version;
|
|
182
|
+
|
|
183
|
+
public function __construct($plugin_name, $version) {
|
|
184
|
+
$this->plugin_name = $plugin_name;
|
|
185
|
+
$this->version = $version;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
public function add_admin_menu() {
|
|
189
|
+
add_menu_page(
|
|
190
|
+
'My Plugin',
|
|
191
|
+
'My Plugin',
|
|
192
|
+
'manage_options',
|
|
193
|
+
$this->plugin_name,
|
|
194
|
+
array($this, 'display_settings_page'),
|
|
195
|
+
'dashicons-admin-generic'
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
public function register_settings() {
|
|
200
|
+
register_setting(
|
|
201
|
+
$this->plugin_name . '_options',
|
|
202
|
+
$this->plugin_name . '_settings',
|
|
203
|
+
array($this, 'sanitize_settings')
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
add_settings_section(
|
|
207
|
+
$this->plugin_name . '_section',
|
|
208
|
+
'General Settings',
|
|
209
|
+
array($this, 'section_callback'),
|
|
210
|
+
$this->plugin_name
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
add_settings_field(
|
|
214
|
+
'api_key',
|
|
215
|
+
'API Key',
|
|
216
|
+
array($this, 'api_key_field'),
|
|
217
|
+
$this->plugin_name,
|
|
218
|
+
$this->plugin_name . '_section'
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
public function display_settings_page() {
|
|
223
|
+
if (!current_user_can('manage_options')) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
include_once plugin_dir_path(__FILE__) . 'partials/admin-display.php';
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
public function section_callback() {
|
|
231
|
+
echo '<p>Configure your plugin settings.</p>';
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
public function api_key_field() {
|
|
235
|
+
$options = get_option($this->plugin_name . '_settings');
|
|
236
|
+
$value = isset($options['api_key']) ? $options['api_key'] : '';
|
|
237
|
+
|
|
238
|
+
printf(
|
|
239
|
+
'<input type="text" name="%s[api_key]" value="%s" class="regular-text" />',
|
|
240
|
+
esc_attr($this->plugin_name . '_settings'),
|
|
241
|
+
esc_attr($value)
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
public function sanitize_settings($input) {
|
|
246
|
+
$sanitized = array();
|
|
247
|
+
|
|
248
|
+
if (isset($input['api_key'])) {
|
|
249
|
+
$sanitized['api_key'] = sanitize_text_field($input['api_key']);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return $sanitized;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Hook it up
|
|
257
|
+
$admin = new My_Plugin_Admin('my-plugin', '1.0.0');
|
|
258
|
+
add_action('admin_menu', array($admin, 'add_admin_menu'));
|
|
259
|
+
add_action('admin_init', array($admin, 'register_settings'));
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Meta Boxes
|
|
265
|
+
|
|
266
|
+
### Adding a Meta Box
|
|
267
|
+
|
|
268
|
+
```php
|
|
269
|
+
<?php
|
|
270
|
+
add_action('add_meta_boxes', 'my_plugin_add_meta_box');
|
|
271
|
+
|
|
272
|
+
function my_plugin_add_meta_box() {
|
|
273
|
+
add_meta_box(
|
|
274
|
+
'my_plugin_meta_box', // Meta box ID
|
|
275
|
+
'My Plugin Settings', // Title
|
|
276
|
+
'my_plugin_meta_box_callback', // Callback
|
|
277
|
+
'post', // Post type (or array of post types)
|
|
278
|
+
'side', // Context (normal, side, advanced)
|
|
279
|
+
'default' // Priority (high, default, low)
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function my_plugin_meta_box_callback($post) {
|
|
284
|
+
// Add nonce for security
|
|
285
|
+
wp_nonce_field('my_plugin_save_meta_box', 'my_plugin_meta_box_nonce');
|
|
286
|
+
|
|
287
|
+
// Get current value
|
|
288
|
+
$value = get_post_meta($post->ID, '_my_plugin_field', true);
|
|
289
|
+
|
|
290
|
+
?>
|
|
291
|
+
<label for="my_plugin_field">Custom Field:</label>
|
|
292
|
+
<input
|
|
293
|
+
type="text"
|
|
294
|
+
id="my_plugin_field"
|
|
295
|
+
name="my_plugin_field"
|
|
296
|
+
value="<?php echo esc_attr($value); ?>"
|
|
297
|
+
class="widefat"
|
|
298
|
+
/>
|
|
299
|
+
<?php
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Save meta box data
|
|
303
|
+
add_action('save_post', 'my_plugin_save_meta_box');
|
|
304
|
+
|
|
305
|
+
function my_plugin_save_meta_box($post_id) {
|
|
306
|
+
// Check nonce
|
|
307
|
+
if (!isset($_POST['my_plugin_meta_box_nonce'])) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (!wp_verify_nonce($_POST['my_plugin_meta_box_nonce'], 'my_plugin_save_meta_box')) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Check autosave
|
|
316
|
+
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Check permissions
|
|
321
|
+
if (!current_user_can('edit_post', $post_id)) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Sanitize and save
|
|
326
|
+
if (isset($_POST['my_plugin_field'])) {
|
|
327
|
+
$value = sanitize_text_field($_POST['my_plugin_field']);
|
|
328
|
+
update_post_meta($post_id, '_my_plugin_field', $value);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Object-Oriented Meta Box
|
|
334
|
+
|
|
335
|
+
```php
|
|
336
|
+
<?php
|
|
337
|
+
class My_Plugin_Meta_Box {
|
|
338
|
+
public function __construct() {
|
|
339
|
+
add_action('add_meta_boxes', array($this, 'add_meta_box'));
|
|
340
|
+
add_action('save_post', array($this, 'save_meta_box'));
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
public function add_meta_box() {
|
|
344
|
+
$post_types = array('post', 'page', 'custom_post_type');
|
|
345
|
+
|
|
346
|
+
foreach ($post_types as $post_type) {
|
|
347
|
+
add_meta_box(
|
|
348
|
+
'my_plugin_meta_box',
|
|
349
|
+
'My Plugin Settings',
|
|
350
|
+
array($this, 'render_meta_box'),
|
|
351
|
+
$post_type,
|
|
352
|
+
'normal',
|
|
353
|
+
'high'
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
public function render_meta_box($post) {
|
|
359
|
+
wp_nonce_field('my_plugin_meta_box', 'my_plugin_meta_box_nonce');
|
|
360
|
+
|
|
361
|
+
$project_url = get_post_meta($post->ID, '_project_url', true);
|
|
362
|
+
$project_date = get_post_meta($post->ID, '_project_date', true);
|
|
363
|
+
|
|
364
|
+
?>
|
|
365
|
+
<table class="form-table">
|
|
366
|
+
<tr>
|
|
367
|
+
<th><label for="project_url">Project URL</label></th>
|
|
368
|
+
<td>
|
|
369
|
+
<input
|
|
370
|
+
type="url"
|
|
371
|
+
id="project_url"
|
|
372
|
+
name="project_url"
|
|
373
|
+
value="<?php echo esc_url($project_url); ?>"
|
|
374
|
+
class="regular-text"
|
|
375
|
+
/>
|
|
376
|
+
</td>
|
|
377
|
+
</tr>
|
|
378
|
+
<tr>
|
|
379
|
+
<th><label for="project_date">Project Date</label></th>
|
|
380
|
+
<td>
|
|
381
|
+
<input
|
|
382
|
+
type="date"
|
|
383
|
+
id="project_date"
|
|
384
|
+
name="project_date"
|
|
385
|
+
value="<?php echo esc_attr($project_date); ?>"
|
|
386
|
+
/>
|
|
387
|
+
</td>
|
|
388
|
+
</tr>
|
|
389
|
+
</table>
|
|
390
|
+
<?php
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
public function save_meta_box($post_id) {
|
|
394
|
+
// Verify nonce
|
|
395
|
+
if (!isset($_POST['my_plugin_meta_box_nonce'])) {
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (!wp_verify_nonce($_POST['my_plugin_meta_box_nonce'], 'my_plugin_meta_box')) {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Check autosave
|
|
404
|
+
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Check permissions
|
|
409
|
+
if (!current_user_can('edit_post', $post_id)) {
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Save project URL
|
|
414
|
+
if (isset($_POST['project_url'])) {
|
|
415
|
+
update_post_meta(
|
|
416
|
+
$post_id,
|
|
417
|
+
'_project_url',
|
|
418
|
+
esc_url_raw($_POST['project_url'])
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Save project date
|
|
423
|
+
if (isset($_POST['project_date'])) {
|
|
424
|
+
update_post_meta(
|
|
425
|
+
$post_id,
|
|
426
|
+
'_project_date',
|
|
427
|
+
sanitize_text_field($_POST['project_date'])
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
new My_Plugin_Meta_Box();
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
## Admin Notices
|
|
439
|
+
|
|
440
|
+
### Basic Admin Notice
|
|
441
|
+
|
|
442
|
+
```php
|
|
443
|
+
<?php
|
|
444
|
+
add_action('admin_notices', 'my_plugin_admin_notice');
|
|
445
|
+
|
|
446
|
+
function my_plugin_admin_notice() {
|
|
447
|
+
?>
|
|
448
|
+
<div class="notice notice-success is-dismissible">
|
|
449
|
+
<p>Settings saved successfully!</p>
|
|
450
|
+
</div>
|
|
451
|
+
<?php
|
|
452
|
+
}
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### Notice Types
|
|
456
|
+
|
|
457
|
+
```php
|
|
458
|
+
<?php
|
|
459
|
+
function my_plugin_show_notices() {
|
|
460
|
+
// Success notice
|
|
461
|
+
?>
|
|
462
|
+
<div class="notice notice-success is-dismissible">
|
|
463
|
+
<p>Success message</p>
|
|
464
|
+
</div>
|
|
465
|
+
|
|
466
|
+
<!-- Error notice -->
|
|
467
|
+
<div class="notice notice-error">
|
|
468
|
+
<p>Error message</p>
|
|
469
|
+
</div>
|
|
470
|
+
|
|
471
|
+
<!-- Warning notice -->
|
|
472
|
+
<div class="notice notice-warning">
|
|
473
|
+
<p>Warning message</p>
|
|
474
|
+
</div>
|
|
475
|
+
|
|
476
|
+
<!-- Info notice -->
|
|
477
|
+
<div class="notice notice-info">
|
|
478
|
+
<p>Info message</p>
|
|
479
|
+
</div>
|
|
480
|
+
<?php
|
|
481
|
+
}
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
### Conditional Admin Notices
|
|
485
|
+
|
|
486
|
+
```php
|
|
487
|
+
<?php
|
|
488
|
+
add_action('admin_notices', 'my_plugin_conditional_notice');
|
|
489
|
+
|
|
490
|
+
function my_plugin_conditional_notice() {
|
|
491
|
+
// Only show on plugin settings page
|
|
492
|
+
$screen = get_current_screen();
|
|
493
|
+
if ($screen->id !== 'toplevel_page_my-plugin') {
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Check if settings are configured
|
|
498
|
+
$settings = get_option('my_plugin_settings');
|
|
499
|
+
if (empty($settings['api_key'])) {
|
|
500
|
+
?>
|
|
501
|
+
<div class="notice notice-warning">
|
|
502
|
+
<p>
|
|
503
|
+
<strong>My Plugin:</strong>
|
|
504
|
+
Please configure your API key in
|
|
505
|
+
<a href="<?php echo admin_url('admin.php?page=my-plugin'); ?>">settings</a>.
|
|
506
|
+
</p>
|
|
507
|
+
</div>
|
|
508
|
+
<?php
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
### Dismissible Notices with Transients
|
|
514
|
+
|
|
515
|
+
```php
|
|
516
|
+
<?php
|
|
517
|
+
add_action('admin_notices', 'my_plugin_dismissible_notice');
|
|
518
|
+
|
|
519
|
+
function my_plugin_dismissible_notice() {
|
|
520
|
+
// Check if notice was dismissed
|
|
521
|
+
if (get_transient('my_plugin_notice_dismissed')) {
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
?>
|
|
526
|
+
<div class="notice notice-info is-dismissible" data-notice="my-plugin-welcome">
|
|
527
|
+
<p>Welcome to My Plugin! <a href="#">Get started</a></p>
|
|
528
|
+
</div>
|
|
529
|
+
|
|
530
|
+
<script>
|
|
531
|
+
jQuery(document).ready(function($) {
|
|
532
|
+
$(document).on('click', '.notice[data-notice="my-plugin-welcome"] .notice-dismiss', function() {
|
|
533
|
+
$.post(ajaxurl, {
|
|
534
|
+
action: 'my_plugin_dismiss_notice',
|
|
535
|
+
nonce: '<?php echo wp_create_nonce('my_plugin_dismiss_notice'); ?>'
|
|
536
|
+
});
|
|
537
|
+
});
|
|
538
|
+
});
|
|
539
|
+
</script>
|
|
540
|
+
<?php
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// Handle AJAX dismiss
|
|
544
|
+
add_action('wp_ajax_my_plugin_dismiss_notice', 'my_plugin_handle_dismiss_notice');
|
|
545
|
+
|
|
546
|
+
function my_plugin_handle_dismiss_notice() {
|
|
547
|
+
check_ajax_referer('my_plugin_dismiss_notice', 'nonce');
|
|
548
|
+
|
|
549
|
+
set_transient('my_plugin_notice_dismissed', true, WEEK_IN_SECONDS);
|
|
550
|
+
|
|
551
|
+
wp_send_json_success();
|
|
552
|
+
}
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
---
|
|
556
|
+
|
|
557
|
+
## List Tables
|
|
558
|
+
|
|
559
|
+
### Custom List Table
|
|
560
|
+
|
|
561
|
+
```php
|
|
562
|
+
<?php
|
|
563
|
+
if (!class_exists('WP_List_Table')) {
|
|
564
|
+
require_once(ABSPATH . 'wp-admin/includes/class-wp-list-table.php');
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
class My_Plugin_List_Table extends WP_List_Table {
|
|
568
|
+
public function __construct() {
|
|
569
|
+
parent::__construct(array(
|
|
570
|
+
'singular' => 'item',
|
|
571
|
+
'plural' => 'items',
|
|
572
|
+
'ajax' => false
|
|
573
|
+
));
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
public function get_columns() {
|
|
577
|
+
return array(
|
|
578
|
+
'cb' => '<input type="checkbox" />',
|
|
579
|
+
'title' => 'Title',
|
|
580
|
+
'author' => 'Author',
|
|
581
|
+
'date' => 'Date',
|
|
582
|
+
'status' => 'Status'
|
|
583
|
+
);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
public function get_sortable_columns() {
|
|
587
|
+
return array(
|
|
588
|
+
'title' => array('title', false),
|
|
589
|
+
'date' => array('date', true)
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
public function column_default($item, $column_name) {
|
|
594
|
+
switch ($column_name) {
|
|
595
|
+
case 'title':
|
|
596
|
+
case 'author':
|
|
597
|
+
case 'date':
|
|
598
|
+
case 'status':
|
|
599
|
+
return $item[$column_name];
|
|
600
|
+
default:
|
|
601
|
+
return print_r($item, true);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
public function column_cb($item) {
|
|
606
|
+
return sprintf(
|
|
607
|
+
'<input type="checkbox" name="items[]" value="%s" />',
|
|
608
|
+
$item['id']
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
public function column_title($item) {
|
|
613
|
+
$actions = array(
|
|
614
|
+
'edit' => sprintf(
|
|
615
|
+
'<a href="?page=%s&action=edit&item=%s">Edit</a>',
|
|
616
|
+
$_REQUEST['page'],
|
|
617
|
+
$item['id']
|
|
618
|
+
),
|
|
619
|
+
'delete' => sprintf(
|
|
620
|
+
'<a href="?page=%s&action=delete&item=%s">Delete</a>',
|
|
621
|
+
$_REQUEST['page'],
|
|
622
|
+
$item['id']
|
|
623
|
+
)
|
|
624
|
+
);
|
|
625
|
+
|
|
626
|
+
return sprintf(
|
|
627
|
+
'%1$s %2$s',
|
|
628
|
+
$item['title'],
|
|
629
|
+
$this->row_actions($actions)
|
|
630
|
+
);
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
public function get_bulk_actions() {
|
|
634
|
+
return array(
|
|
635
|
+
'delete' => 'Delete',
|
|
636
|
+
'export' => 'Export'
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
public function prepare_items() {
|
|
641
|
+
$columns = $this->get_columns();
|
|
642
|
+
$hidden = array();
|
|
643
|
+
$sortable = $this->get_sortable_columns();
|
|
644
|
+
|
|
645
|
+
$this->_column_headers = array($columns, $hidden, $sortable);
|
|
646
|
+
|
|
647
|
+
// Get data
|
|
648
|
+
$data = $this->get_data();
|
|
649
|
+
|
|
650
|
+
// Pagination
|
|
651
|
+
$per_page = 20;
|
|
652
|
+
$current_page = $this->get_pagenum();
|
|
653
|
+
$total_items = count($data);
|
|
654
|
+
|
|
655
|
+
$this->set_pagination_args(array(
|
|
656
|
+
'total_items' => $total_items,
|
|
657
|
+
'per_page' => $per_page,
|
|
658
|
+
'total_pages' => ceil($total_items / $per_page)
|
|
659
|
+
));
|
|
660
|
+
|
|
661
|
+
$this->items = array_slice($data, (($current_page - 1) * $per_page), $per_page);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
private function get_data() {
|
|
665
|
+
// Get data from database
|
|
666
|
+
global $wpdb;
|
|
667
|
+
$table_name = $wpdb->prefix . 'my_plugin_data';
|
|
668
|
+
|
|
669
|
+
return $wpdb->get_results(
|
|
670
|
+
"SELECT * FROM $table_name ORDER BY date DESC",
|
|
671
|
+
ARRAY_A
|
|
672
|
+
);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// Display list table
|
|
677
|
+
function my_plugin_list_page() {
|
|
678
|
+
$list_table = new My_Plugin_List_Table();
|
|
679
|
+
$list_table->prepare_items();
|
|
680
|
+
|
|
681
|
+
?>
|
|
682
|
+
<div class="wrap">
|
|
683
|
+
<h1>My Plugin Items</h1>
|
|
684
|
+
<form method="post">
|
|
685
|
+
<?php $list_table->display(); ?>
|
|
686
|
+
</form>
|
|
687
|
+
</div>
|
|
688
|
+
<?php
|
|
689
|
+
}
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
---
|
|
693
|
+
|
|
694
|
+
## Dashboard Widgets
|
|
695
|
+
|
|
696
|
+
### Adding a Dashboard Widget
|
|
697
|
+
|
|
698
|
+
```php
|
|
699
|
+
<?php
|
|
700
|
+
add_action('wp_dashboard_setup', 'my_plugin_add_dashboard_widget');
|
|
701
|
+
|
|
702
|
+
function my_plugin_add_dashboard_widget() {
|
|
703
|
+
wp_add_dashboard_widget(
|
|
704
|
+
'my_plugin_dashboard_widget', // Widget ID
|
|
705
|
+
'My Plugin Stats', // Title
|
|
706
|
+
'my_plugin_dashboard_widget_render' // Callback
|
|
707
|
+
);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
function my_plugin_dashboard_widget_render() {
|
|
711
|
+
// Get stats
|
|
712
|
+
$total_items = wp_count_posts('my_custom_post_type')->publish;
|
|
713
|
+
$recent_items = get_posts(array(
|
|
714
|
+
'post_type' => 'my_custom_post_type',
|
|
715
|
+
'posts_per_page' => 5
|
|
716
|
+
));
|
|
717
|
+
|
|
718
|
+
?>
|
|
719
|
+
<div class="my-plugin-dashboard-widget">
|
|
720
|
+
<p><strong>Total Items:</strong> <?php echo esc_html($total_items); ?></p>
|
|
721
|
+
|
|
722
|
+
<h4>Recent Items</h4>
|
|
723
|
+
<ul>
|
|
724
|
+
<?php foreach ($recent_items as $item) : ?>
|
|
725
|
+
<li>
|
|
726
|
+
<a href="<?php echo get_edit_post_link($item->ID); ?>">
|
|
727
|
+
<?php echo esc_html($item->post_title); ?>
|
|
728
|
+
</a>
|
|
729
|
+
</li>
|
|
730
|
+
<?php endforeach; ?>
|
|
731
|
+
</ul>
|
|
732
|
+
</div>
|
|
733
|
+
<?php
|
|
734
|
+
}
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
---
|
|
738
|
+
|
|
739
|
+
## Admin Columns
|
|
740
|
+
|
|
741
|
+
### Adding Custom Columns to Post List
|
|
742
|
+
|
|
743
|
+
```php
|
|
744
|
+
<?php
|
|
745
|
+
// Add column
|
|
746
|
+
add_filter('manage_post_posts_columns', 'my_plugin_add_custom_column');
|
|
747
|
+
|
|
748
|
+
function my_plugin_add_custom_column($columns) {
|
|
749
|
+
$columns['my_custom_field'] = 'Custom Field';
|
|
750
|
+
return $columns;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// Populate column
|
|
754
|
+
add_action('manage_post_posts_custom_column', 'my_plugin_populate_custom_column', 10, 2);
|
|
755
|
+
|
|
756
|
+
function my_plugin_populate_custom_column($column, $post_id) {
|
|
757
|
+
if ($column === 'my_custom_field') {
|
|
758
|
+
$value = get_post_meta($post_id, '_my_custom_field', true);
|
|
759
|
+
echo esc_html($value);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// Make column sortable
|
|
764
|
+
add_filter('manage_edit-post_sortable_columns', 'my_plugin_sortable_column');
|
|
765
|
+
|
|
766
|
+
function my_plugin_sortable_column($columns) {
|
|
767
|
+
$columns['my_custom_field'] = 'my_custom_field';
|
|
768
|
+
return $columns;
|
|
769
|
+
}
|
|
770
|
+
```
|
|
771
|
+
|
|
772
|
+
---
|
|
773
|
+
|
|
774
|
+
## Best Practices
|
|
775
|
+
|
|
776
|
+
### Settings Pages
|
|
777
|
+
|
|
778
|
+
✅ Check user capabilities (`current_user_can()`)
|
|
779
|
+
✅ Use Settings API for options
|
|
780
|
+
✅ Sanitize all input
|
|
781
|
+
✅ Use nonces for form submissions
|
|
782
|
+
✅ Provide helpful descriptions
|
|
783
|
+
✅ Group related settings
|
|
784
|
+
✅ Use appropriate input types
|
|
785
|
+
|
|
786
|
+
### Meta Boxes
|
|
787
|
+
|
|
788
|
+
✅ Always use nonces
|
|
789
|
+
✅ Check autosave
|
|
790
|
+
✅ Verify user permissions
|
|
791
|
+
✅ Sanitize input based on type
|
|
792
|
+
✅ Use `update_post_meta()` not direct DB queries
|
|
793
|
+
✅ Prefix meta keys with underscore for private fields
|
|
794
|
+
|
|
795
|
+
### Admin Notices
|
|
796
|
+
|
|
797
|
+
✅ Use appropriate notice types
|
|
798
|
+
✅ Make notices dismissible when appropriate
|
|
799
|
+
✅ Show notices only on relevant pages
|
|
800
|
+
✅ Provide actionable information
|
|
801
|
+
✅ Don't overwhelm users with notices
|
|
802
|
+
|
|
803
|
+
### Security
|
|
804
|
+
|
|
805
|
+
✅ Always check capabilities
|
|
806
|
+
✅ Use nonces for all forms
|
|
807
|
+
✅ Sanitize input, escape output
|
|
808
|
+
✅ Use `wp_verify_nonce()` before processing
|
|
809
|
+
✅ Check `DOING_AUTOSAVE` in save hooks
|
|
810
|
+
✅ Validate data types
|
|
811
|
+
|
|
812
|
+
---
|
|
813
|
+
|
|
814
|
+
## Common Patterns
|
|
815
|
+
|
|
816
|
+
### Tabbed Settings Page
|
|
817
|
+
|
|
818
|
+
```php
|
|
819
|
+
<?php
|
|
820
|
+
function my_plugin_settings_page() {
|
|
821
|
+
$active_tab = isset($_GET['tab']) ? $_GET['tab'] : 'general';
|
|
822
|
+
|
|
823
|
+
?>
|
|
824
|
+
<div class="wrap">
|
|
825
|
+
<h1>My Plugin Settings</h1>
|
|
826
|
+
|
|
827
|
+
<h2 class="nav-tab-wrapper">
|
|
828
|
+
<a href="?page=my-plugin&tab=general" class="nav-tab <?php echo $active_tab == 'general' ? 'nav-tab-active' : ''; ?>">
|
|
829
|
+
General
|
|
830
|
+
</a>
|
|
831
|
+
<a href="?page=my-plugin&tab=advanced" class="nav-tab <?php echo $active_tab == 'advanced' ? 'nav-tab-active' : ''; ?>">
|
|
832
|
+
Advanced
|
|
833
|
+
</a>
|
|
834
|
+
<a href="?page=my-plugin&tab=help" class="nav-tab <?php echo $active_tab == 'help' ? 'nav-tab-active' : ''; ?>">
|
|
835
|
+
Help
|
|
836
|
+
</a>
|
|
837
|
+
</h2>
|
|
838
|
+
|
|
839
|
+
<form method="post" action="options.php">
|
|
840
|
+
<?php
|
|
841
|
+
if ($active_tab == 'general') {
|
|
842
|
+
settings_fields('my_plugin_general');
|
|
843
|
+
do_settings_sections('my-plugin-general');
|
|
844
|
+
} elseif ($active_tab == 'advanced') {
|
|
845
|
+
settings_fields('my_plugin_advanced');
|
|
846
|
+
do_settings_sections('my-plugin-advanced');
|
|
847
|
+
} else {
|
|
848
|
+
// Help tab content
|
|
849
|
+
echo '<p>Help content here</p>';
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
if ($active_tab != 'help') {
|
|
853
|
+
submit_button();
|
|
854
|
+
}
|
|
855
|
+
?>
|
|
856
|
+
</form>
|
|
857
|
+
</div>
|
|
858
|
+
<?php
|
|
859
|
+
}
|
|
860
|
+
```
|
|
861
|
+
|
|
862
|
+
---
|
|
863
|
+
|
|
864
|
+
## Summary
|
|
865
|
+
|
|
866
|
+
| Component | Purpose | Key Functions |
|
|
867
|
+
|-----------|---------|---------------|
|
|
868
|
+
| Settings Pages | Plugin configuration | `add_menu_page()`, `register_setting()` |
|
|
869
|
+
| Meta Boxes | Post/page custom fields | `add_meta_box()`, `update_post_meta()` |
|
|
870
|
+
| Admin Notices | User notifications | `admin_notices` hook |
|
|
871
|
+
| List Tables | Custom data tables | `WP_List_Table` class |
|
|
872
|
+
| Dashboard Widgets | Dashboard info | `wp_add_dashboard_widget()` |
|
|
873
|
+
| Admin Columns | Custom list columns | `manage_{post_type}_posts_columns` |
|
|
874
|
+
|