@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,770 @@
|
|
|
1
|
+
# WordPress Plugin Activation, Deactivation, and Uninstall Hooks
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This document covers the proper implementation of plugin lifecycle hooks: activation, deactivation, and uninstall. These hooks are critical for setting up plugin requirements, cleaning up data, and ensuring a smooth user experience.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Activation Hook
|
|
10
|
+
|
|
11
|
+
### Purpose
|
|
12
|
+
|
|
13
|
+
The activation hook runs **once** when the plugin is activated. Use it to:
|
|
14
|
+
|
|
15
|
+
- Create custom database tables
|
|
16
|
+
- Add default options
|
|
17
|
+
- Set up capabilities/roles
|
|
18
|
+
- Schedule cron events
|
|
19
|
+
- Flush rewrite rules
|
|
20
|
+
- Check system requirements
|
|
21
|
+
|
|
22
|
+
### Basic Implementation
|
|
23
|
+
|
|
24
|
+
```php
|
|
25
|
+
<?php
|
|
26
|
+
/**
|
|
27
|
+
* Plugin Name: My Plugin
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
// Register activation hook
|
|
31
|
+
register_activation_hook(__FILE__, 'my_plugin_activate');
|
|
32
|
+
|
|
33
|
+
function my_plugin_activate() {
|
|
34
|
+
// Activation code here
|
|
35
|
+
|
|
36
|
+
// Example: Add default options
|
|
37
|
+
add_option('my_plugin_version', '1.0.0');
|
|
38
|
+
add_option('my_plugin_settings', array(
|
|
39
|
+
'enabled' => true,
|
|
40
|
+
'api_key' => ''
|
|
41
|
+
));
|
|
42
|
+
|
|
43
|
+
// Flush rewrite rules
|
|
44
|
+
flush_rewrite_rules();
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Object-Oriented Implementation
|
|
49
|
+
|
|
50
|
+
```php
|
|
51
|
+
<?php
|
|
52
|
+
class My_Plugin_Activator {
|
|
53
|
+
public static function activate() {
|
|
54
|
+
self::create_tables();
|
|
55
|
+
self::add_default_options();
|
|
56
|
+
self::add_capabilities();
|
|
57
|
+
self::schedule_cron();
|
|
58
|
+
|
|
59
|
+
// Store activation timestamp
|
|
60
|
+
update_option('my_plugin_activated_time', time());
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private static function create_tables() {
|
|
64
|
+
global $wpdb;
|
|
65
|
+
$table_name = $wpdb->prefix . 'my_plugin_data';
|
|
66
|
+
$charset_collate = $wpdb->get_charset_collate();
|
|
67
|
+
|
|
68
|
+
$sql = "CREATE TABLE $table_name (
|
|
69
|
+
id mediumint(9) NOT NULL AUTO_INCREMENT,
|
|
70
|
+
name varchar(100) NOT NULL,
|
|
71
|
+
data text NOT NULL,
|
|
72
|
+
created_at datetime DEFAULT CURRENT_TIMESTAMP,
|
|
73
|
+
PRIMARY KEY (id)
|
|
74
|
+
) $charset_collate;";
|
|
75
|
+
|
|
76
|
+
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
|
77
|
+
dbDelta($sql);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private static function add_default_options() {
|
|
81
|
+
$defaults = array(
|
|
82
|
+
'my_plugin_version' => '1.0.0',
|
|
83
|
+
'my_plugin_settings' => array(
|
|
84
|
+
'enabled' => true,
|
|
85
|
+
'items_per_page' => 10
|
|
86
|
+
)
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
foreach ($defaults as $key => $value) {
|
|
90
|
+
add_option($key, $value);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private static function add_capabilities() {
|
|
95
|
+
$role = get_role('administrator');
|
|
96
|
+
if ($role) {
|
|
97
|
+
$role->add_cap('manage_my_plugin');
|
|
98
|
+
$role->add_cap('edit_my_plugin_items');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private static function schedule_cron() {
|
|
103
|
+
if (!wp_next_scheduled('my_plugin_daily_task')) {
|
|
104
|
+
wp_schedule_event(time(), 'daily', 'my_plugin_daily_task');
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Register hook
|
|
110
|
+
register_activation_hook(__FILE__, array('My_Plugin_Activator', 'activate'));
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### System Requirements Check
|
|
114
|
+
|
|
115
|
+
```php
|
|
116
|
+
<?php
|
|
117
|
+
function my_plugin_activate() {
|
|
118
|
+
// Check PHP version
|
|
119
|
+
if (version_compare(PHP_VERSION, '7.4', '<')) {
|
|
120
|
+
deactivate_plugins(plugin_basename(__FILE__));
|
|
121
|
+
wp_die('This plugin requires PHP 7.4 or higher.');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Check WordPress version
|
|
125
|
+
global $wp_version;
|
|
126
|
+
if (version_compare($wp_version, '5.8', '<')) {
|
|
127
|
+
deactivate_plugins(plugin_basename(__FILE__));
|
|
128
|
+
wp_die('This plugin requires WordPress 5.8 or higher.');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Check for required plugins
|
|
132
|
+
if (!is_plugin_active('woocommerce/woocommerce.php')) {
|
|
133
|
+
deactivate_plugins(plugin_basename(__FILE__));
|
|
134
|
+
wp_die('This plugin requires WooCommerce to be installed and activated.');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Check for required PHP extensions
|
|
138
|
+
if (!extension_loaded('curl')) {
|
|
139
|
+
deactivate_plugins(plugin_basename(__FILE__));
|
|
140
|
+
wp_die('This plugin requires the PHP cURL extension.');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// All checks passed - proceed with activation
|
|
144
|
+
my_plugin_setup();
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Multisite Activation
|
|
149
|
+
|
|
150
|
+
```php
|
|
151
|
+
<?php
|
|
152
|
+
function my_plugin_activate($network_wide) {
|
|
153
|
+
if (is_multisite() && $network_wide) {
|
|
154
|
+
// Network activation
|
|
155
|
+
global $wpdb;
|
|
156
|
+
|
|
157
|
+
// Get all blog IDs
|
|
158
|
+
$blog_ids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs");
|
|
159
|
+
|
|
160
|
+
foreach ($blog_ids as $blog_id) {
|
|
161
|
+
switch_to_blog($blog_id);
|
|
162
|
+
my_plugin_activate_single_site();
|
|
163
|
+
restore_current_blog();
|
|
164
|
+
}
|
|
165
|
+
} else {
|
|
166
|
+
// Single site activation
|
|
167
|
+
my_plugin_activate_single_site();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function my_plugin_activate_single_site() {
|
|
172
|
+
// Activation code for single site
|
|
173
|
+
add_option('my_plugin_version', '1.0.0');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
register_activation_hook(__FILE__, 'my_plugin_activate');
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Deactivation Hook
|
|
182
|
+
|
|
183
|
+
### Purpose
|
|
184
|
+
|
|
185
|
+
The deactivation hook runs when the plugin is deactivated. Use it to:
|
|
186
|
+
|
|
187
|
+
- Clear scheduled cron events
|
|
188
|
+
- Flush rewrite rules
|
|
189
|
+
- Clear transients/caches
|
|
190
|
+
- **DO NOT** delete user data or options
|
|
191
|
+
|
|
192
|
+
### Basic Implementation
|
|
193
|
+
|
|
194
|
+
```php
|
|
195
|
+
<?php
|
|
196
|
+
register_deactivation_hook(__FILE__, 'my_plugin_deactivate');
|
|
197
|
+
|
|
198
|
+
function my_plugin_deactivate() {
|
|
199
|
+
// Clear scheduled events
|
|
200
|
+
$timestamp = wp_next_scheduled('my_plugin_daily_task');
|
|
201
|
+
if ($timestamp) {
|
|
202
|
+
wp_unschedule_event($timestamp, 'my_plugin_daily_task');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Flush rewrite rules
|
|
206
|
+
flush_rewrite_rules();
|
|
207
|
+
|
|
208
|
+
// Clear transients
|
|
209
|
+
delete_transient('my_plugin_cache');
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Object-Oriented Implementation
|
|
214
|
+
|
|
215
|
+
```php
|
|
216
|
+
<?php
|
|
217
|
+
class My_Plugin_Deactivator {
|
|
218
|
+
public static function deactivate() {
|
|
219
|
+
self::clear_cron_jobs();
|
|
220
|
+
self::flush_cache();
|
|
221
|
+
self::flush_rewrite_rules();
|
|
222
|
+
|
|
223
|
+
// Log deactivation
|
|
224
|
+
update_option('my_plugin_deactivated_time', time());
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
private static function clear_cron_jobs() {
|
|
228
|
+
$cron_hooks = array(
|
|
229
|
+
'my_plugin_daily_task',
|
|
230
|
+
'my_plugin_hourly_task',
|
|
231
|
+
'my_plugin_cleanup_task'
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
foreach ($cron_hooks as $hook) {
|
|
235
|
+
$timestamp = wp_next_scheduled($hook);
|
|
236
|
+
if ($timestamp) {
|
|
237
|
+
wp_unschedule_event($timestamp, $hook);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
private static function flush_cache() {
|
|
243
|
+
// Delete transients
|
|
244
|
+
delete_transient('my_plugin_cache');
|
|
245
|
+
delete_transient('my_plugin_api_response');
|
|
246
|
+
|
|
247
|
+
// Clear object cache
|
|
248
|
+
wp_cache_flush();
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
private static function flush_rewrite_rules() {
|
|
252
|
+
flush_rewrite_rules();
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
register_deactivation_hook(__FILE__, array('My_Plugin_Deactivator', 'deactivate'));
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### What NOT to Do on Deactivation
|
|
260
|
+
|
|
261
|
+
❌ **DO NOT** delete user data
|
|
262
|
+
❌ **DO NOT** delete options/settings
|
|
263
|
+
❌ **DO NOT** drop database tables
|
|
264
|
+
❌ **DO NOT** remove user capabilities
|
|
265
|
+
|
|
266
|
+
**Reason**: Users may reactivate the plugin and expect their data to be intact.
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## Uninstall Hook
|
|
271
|
+
|
|
272
|
+
### Purpose
|
|
273
|
+
|
|
274
|
+
The uninstall hook runs when the plugin is **deleted** (not just deactivated). Use it to:
|
|
275
|
+
|
|
276
|
+
- Delete all plugin options
|
|
277
|
+
- Drop custom database tables
|
|
278
|
+
- Remove custom capabilities
|
|
279
|
+
- Delete uploaded files
|
|
280
|
+
- Clean up everything
|
|
281
|
+
|
|
282
|
+
### Method 1: uninstall.php (Recommended)
|
|
283
|
+
|
|
284
|
+
Create a file named `uninstall.php` in the plugin root:
|
|
285
|
+
|
|
286
|
+
```php
|
|
287
|
+
<?php
|
|
288
|
+
/**
|
|
289
|
+
* Uninstall script
|
|
290
|
+
*
|
|
291
|
+
* Runs when plugin is deleted via WordPress admin
|
|
292
|
+
*/
|
|
293
|
+
|
|
294
|
+
// Exit if accessed directly
|
|
295
|
+
if (!defined('WP_UNINSTALL_PLUGIN')) {
|
|
296
|
+
exit;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Delete options
|
|
300
|
+
delete_option('my_plugin_version');
|
|
301
|
+
delete_option('my_plugin_settings');
|
|
302
|
+
delete_option('my_plugin_activated_time');
|
|
303
|
+
delete_option('my_plugin_deactivated_time');
|
|
304
|
+
|
|
305
|
+
// Delete transients
|
|
306
|
+
delete_transient('my_plugin_cache');
|
|
307
|
+
|
|
308
|
+
// Drop custom tables
|
|
309
|
+
global $wpdb;
|
|
310
|
+
$table_name = $wpdb->prefix . 'my_plugin_data';
|
|
311
|
+
$wpdb->query("DROP TABLE IF EXISTS $table_name");
|
|
312
|
+
|
|
313
|
+
// Remove capabilities
|
|
314
|
+
$role = get_role('administrator');
|
|
315
|
+
if ($role) {
|
|
316
|
+
$role->remove_cap('manage_my_plugin');
|
|
317
|
+
$role->remove_cap('edit_my_plugin_items');
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Delete uploaded files
|
|
321
|
+
$upload_dir = wp_upload_dir();
|
|
322
|
+
$plugin_upload_dir = $upload_dir['basedir'] . '/my-plugin';
|
|
323
|
+
if (is_dir($plugin_upload_dir)) {
|
|
324
|
+
// Recursively delete directory
|
|
325
|
+
array_map('unlink', glob("$plugin_upload_dir/*.*"));
|
|
326
|
+
rmdir($plugin_upload_dir);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Clear scheduled events (if any remain)
|
|
330
|
+
wp_clear_scheduled_hook('my_plugin_daily_task');
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Method 2: register_uninstall_hook()
|
|
334
|
+
|
|
335
|
+
```php
|
|
336
|
+
<?php
|
|
337
|
+
/**
|
|
338
|
+
* Plugin Name: My Plugin
|
|
339
|
+
*/
|
|
340
|
+
|
|
341
|
+
// Register uninstall hook
|
|
342
|
+
register_uninstall_hook(__FILE__, 'my_plugin_uninstall');
|
|
343
|
+
|
|
344
|
+
function my_plugin_uninstall() {
|
|
345
|
+
// Uninstall code here
|
|
346
|
+
delete_option('my_plugin_version');
|
|
347
|
+
delete_option('my_plugin_settings');
|
|
348
|
+
|
|
349
|
+
global $wpdb;
|
|
350
|
+
$table_name = $wpdb->prefix . 'my_plugin_data';
|
|
351
|
+
$wpdb->query("DROP TABLE IF EXISTS $table_name");
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### Object-Oriented Uninstall (uninstall.php)
|
|
356
|
+
|
|
357
|
+
```php
|
|
358
|
+
<?php
|
|
359
|
+
if (!defined('WP_UNINSTALL_PLUGIN')) {
|
|
360
|
+
exit;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Load plugin classes
|
|
364
|
+
require_once plugin_dir_path(__FILE__) . 'includes/class-uninstaller.php';
|
|
365
|
+
|
|
366
|
+
// Run uninstall
|
|
367
|
+
My_Plugin_Uninstaller::uninstall();
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
**Uninstaller Class** (`includes/class-uninstaller.php`):
|
|
371
|
+
|
|
372
|
+
```php
|
|
373
|
+
<?php
|
|
374
|
+
class My_Plugin_Uninstaller {
|
|
375
|
+
public static function uninstall() {
|
|
376
|
+
self::delete_options();
|
|
377
|
+
self::delete_transients();
|
|
378
|
+
self::drop_tables();
|
|
379
|
+
self::remove_capabilities();
|
|
380
|
+
self::delete_files();
|
|
381
|
+
self::clear_cron();
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
private static function delete_options() {
|
|
385
|
+
$options = array(
|
|
386
|
+
'my_plugin_version',
|
|
387
|
+
'my_plugin_settings',
|
|
388
|
+
'my_plugin_activated_time',
|
|
389
|
+
'my_plugin_deactivated_time'
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
foreach ($options as $option) {
|
|
393
|
+
delete_option($option);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
private static function delete_transients() {
|
|
398
|
+
global $wpdb;
|
|
399
|
+
|
|
400
|
+
// Delete all transients with plugin prefix
|
|
401
|
+
$wpdb->query(
|
|
402
|
+
"DELETE FROM $wpdb->options
|
|
403
|
+
WHERE option_name LIKE '_transient_my_plugin_%'
|
|
404
|
+
OR option_name LIKE '_transient_timeout_my_plugin_%'"
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
private static function drop_tables() {
|
|
409
|
+
global $wpdb;
|
|
410
|
+
|
|
411
|
+
$tables = array(
|
|
412
|
+
$wpdb->prefix . 'my_plugin_data',
|
|
413
|
+
$wpdb->prefix . 'my_plugin_logs'
|
|
414
|
+
);
|
|
415
|
+
|
|
416
|
+
foreach ($tables as $table) {
|
|
417
|
+
$wpdb->query("DROP TABLE IF EXISTS $table");
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
private static function remove_capabilities() {
|
|
422
|
+
$capabilities = array(
|
|
423
|
+
'manage_my_plugin',
|
|
424
|
+
'edit_my_plugin_items',
|
|
425
|
+
'delete_my_plugin_items'
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
$roles = array('administrator', 'editor');
|
|
429
|
+
|
|
430
|
+
foreach ($roles as $role_name) {
|
|
431
|
+
$role = get_role($role_name);
|
|
432
|
+
if ($role) {
|
|
433
|
+
foreach ($capabilities as $cap) {
|
|
434
|
+
$role->remove_cap($cap);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
private static function delete_files() {
|
|
441
|
+
$upload_dir = wp_upload_dir();
|
|
442
|
+
$plugin_dir = $upload_dir['basedir'] . '/my-plugin';
|
|
443
|
+
|
|
444
|
+
if (is_dir($plugin_dir)) {
|
|
445
|
+
self::delete_directory($plugin_dir);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
private static function delete_directory($dir) {
|
|
450
|
+
if (!is_dir($dir)) {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
$files = array_diff(scandir($dir), array('.', '..'));
|
|
455
|
+
|
|
456
|
+
foreach ($files as $file) {
|
|
457
|
+
$path = $dir . '/' . $file;
|
|
458
|
+
is_dir($path) ? self::delete_directory($path) : unlink($path);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
rmdir($dir);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
private static function clear_cron() {
|
|
465
|
+
wp_clear_scheduled_hook('my_plugin_daily_task');
|
|
466
|
+
wp_clear_scheduled_hook('my_plugin_hourly_task');
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
### Multisite Uninstall
|
|
472
|
+
|
|
473
|
+
```php
|
|
474
|
+
<?php
|
|
475
|
+
if (!defined('WP_UNINSTALL_PLUGIN')) {
|
|
476
|
+
exit;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (is_multisite()) {
|
|
480
|
+
global $wpdb;
|
|
481
|
+
|
|
482
|
+
// Get all blog IDs
|
|
483
|
+
$blog_ids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs");
|
|
484
|
+
|
|
485
|
+
foreach ($blog_ids as $blog_id) {
|
|
486
|
+
switch_to_blog($blog_id);
|
|
487
|
+
my_plugin_uninstall_single_site();
|
|
488
|
+
restore_current_blog();
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// Delete network-wide options
|
|
492
|
+
delete_site_option('my_plugin_network_settings');
|
|
493
|
+
} else {
|
|
494
|
+
my_plugin_uninstall_single_site();
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
function my_plugin_uninstall_single_site() {
|
|
498
|
+
// Delete options
|
|
499
|
+
delete_option('my_plugin_version');
|
|
500
|
+
delete_option('my_plugin_settings');
|
|
501
|
+
|
|
502
|
+
// Drop tables
|
|
503
|
+
global $wpdb;
|
|
504
|
+
$table_name = $wpdb->prefix . 'my_plugin_data';
|
|
505
|
+
$wpdb->query("DROP TABLE IF EXISTS $table_name");
|
|
506
|
+
}
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
---
|
|
510
|
+
|
|
511
|
+
## Database Table Management
|
|
512
|
+
|
|
513
|
+
### Creating Tables (Activation)
|
|
514
|
+
|
|
515
|
+
```php
|
|
516
|
+
<?php
|
|
517
|
+
function my_plugin_create_tables() {
|
|
518
|
+
global $wpdb;
|
|
519
|
+
|
|
520
|
+
$table_name = $wpdb->prefix . 'my_plugin_data';
|
|
521
|
+
$charset_collate = $wpdb->get_charset_collate();
|
|
522
|
+
|
|
523
|
+
$sql = "CREATE TABLE $table_name (
|
|
524
|
+
id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
|
525
|
+
user_id bigint(20) unsigned NOT NULL,
|
|
526
|
+
title varchar(255) NOT NULL,
|
|
527
|
+
content longtext NOT NULL,
|
|
528
|
+
status varchar(20) DEFAULT 'draft',
|
|
529
|
+
created_at datetime DEFAULT CURRENT_TIMESTAMP,
|
|
530
|
+
updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
531
|
+
PRIMARY KEY (id),
|
|
532
|
+
KEY user_id (user_id),
|
|
533
|
+
KEY status (status)
|
|
534
|
+
) $charset_collate;";
|
|
535
|
+
|
|
536
|
+
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
|
537
|
+
dbDelta($sql);
|
|
538
|
+
|
|
539
|
+
// Store database version
|
|
540
|
+
add_option('my_plugin_db_version', '1.0');
|
|
541
|
+
}
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
### Updating Tables (Version Check)
|
|
545
|
+
|
|
546
|
+
```php
|
|
547
|
+
<?php
|
|
548
|
+
function my_plugin_update_db_check() {
|
|
549
|
+
$current_version = get_option('my_plugin_db_version', '0');
|
|
550
|
+
$new_version = '1.1';
|
|
551
|
+
|
|
552
|
+
if (version_compare($current_version, $new_version, '<')) {
|
|
553
|
+
my_plugin_update_tables();
|
|
554
|
+
update_option('my_plugin_db_version', $new_version);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
add_action('plugins_loaded', 'my_plugin_update_db_check');
|
|
558
|
+
|
|
559
|
+
function my_plugin_update_tables() {
|
|
560
|
+
global $wpdb;
|
|
561
|
+
|
|
562
|
+
$table_name = $wpdb->prefix . 'my_plugin_data';
|
|
563
|
+
|
|
564
|
+
// Add new column
|
|
565
|
+
$wpdb->query("ALTER TABLE $table_name ADD COLUMN new_field varchar(100) DEFAULT NULL");
|
|
566
|
+
}
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
---
|
|
570
|
+
|
|
571
|
+
## Best Practices
|
|
572
|
+
|
|
573
|
+
### Activation
|
|
574
|
+
|
|
575
|
+
✅ Check system requirements (PHP, WordPress, extensions)
|
|
576
|
+
✅ Create database tables using `dbDelta()`
|
|
577
|
+
✅ Add default options with `add_option()` (not `update_option()`)
|
|
578
|
+
✅ Flush rewrite rules if registering custom post types/taxonomies
|
|
579
|
+
✅ Schedule cron events
|
|
580
|
+
✅ Store activation timestamp
|
|
581
|
+
✅ Check for multisite and handle accordingly
|
|
582
|
+
|
|
583
|
+
### Deactivation
|
|
584
|
+
|
|
585
|
+
✅ Clear scheduled cron events
|
|
586
|
+
✅ Flush rewrite rules
|
|
587
|
+
✅ Clear transients and caches
|
|
588
|
+
❌ **DO NOT** delete user data
|
|
589
|
+
❌ **DO NOT** delete options
|
|
590
|
+
❌ **DO NOT** drop tables
|
|
591
|
+
|
|
592
|
+
### Uninstall
|
|
593
|
+
|
|
594
|
+
✅ Use `uninstall.php` (preferred) or `register_uninstall_hook()`
|
|
595
|
+
✅ Delete ALL plugin options
|
|
596
|
+
✅ Drop ALL custom tables
|
|
597
|
+
✅ Remove ALL capabilities
|
|
598
|
+
✅ Delete ALL uploaded files
|
|
599
|
+
✅ Clear ALL scheduled events
|
|
600
|
+
✅ Handle multisite properly
|
|
601
|
+
✅ Be thorough - leave no trace
|
|
602
|
+
|
|
603
|
+
---
|
|
604
|
+
|
|
605
|
+
## Common Patterns
|
|
606
|
+
|
|
607
|
+
### Version-Based Activation
|
|
608
|
+
|
|
609
|
+
```php
|
|
610
|
+
<?php
|
|
611
|
+
function my_plugin_activate() {
|
|
612
|
+
$current_version = get_option('my_plugin_version', '0');
|
|
613
|
+
$new_version = '2.0.0';
|
|
614
|
+
|
|
615
|
+
if (version_compare($current_version, $new_version, '<')) {
|
|
616
|
+
// Upgrade from older version
|
|
617
|
+
my_plugin_upgrade($current_version, $new_version);
|
|
618
|
+
} else {
|
|
619
|
+
// Fresh install
|
|
620
|
+
my_plugin_fresh_install();
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
update_option('my_plugin_version', $new_version);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
function my_plugin_upgrade($from, $to) {
|
|
627
|
+
// Migration logic
|
|
628
|
+
if (version_compare($from, '1.5.0', '<')) {
|
|
629
|
+
// Migrate from < 1.5.0
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
if (version_compare($from, '2.0.0', '<')) {
|
|
633
|
+
// Migrate from < 2.0.0
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
function my_plugin_fresh_install() {
|
|
638
|
+
// Fresh installation logic
|
|
639
|
+
my_plugin_create_tables();
|
|
640
|
+
my_plugin_add_default_options();
|
|
641
|
+
}
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
### Conditional Uninstall
|
|
645
|
+
|
|
646
|
+
```php
|
|
647
|
+
<?php
|
|
648
|
+
// uninstall.php
|
|
649
|
+
|
|
650
|
+
if (!defined('WP_UNINSTALL_PLUGIN')) {
|
|
651
|
+
exit;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// Check if user wants to keep data
|
|
655
|
+
$keep_data = get_option('my_plugin_keep_data_on_uninstall', false);
|
|
656
|
+
|
|
657
|
+
if (!$keep_data) {
|
|
658
|
+
// Delete everything
|
|
659
|
+
delete_option('my_plugin_version');
|
|
660
|
+
delete_option('my_plugin_settings');
|
|
661
|
+
|
|
662
|
+
global $wpdb;
|
|
663
|
+
$wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}my_plugin_data");
|
|
664
|
+
} else {
|
|
665
|
+
// Keep data, just mark as uninstalled
|
|
666
|
+
update_option('my_plugin_uninstalled', true);
|
|
667
|
+
}
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
---
|
|
671
|
+
|
|
672
|
+
## Security Considerations
|
|
673
|
+
|
|
674
|
+
### Activation
|
|
675
|
+
|
|
676
|
+
✅ Verify user capabilities before activation
|
|
677
|
+
✅ Validate system requirements
|
|
678
|
+
✅ Use nonces for AJAX activation
|
|
679
|
+
✅ Sanitize any user input during activation
|
|
680
|
+
|
|
681
|
+
### Uninstall
|
|
682
|
+
|
|
683
|
+
✅ Check `WP_UNINSTALL_PLUGIN` constant
|
|
684
|
+
✅ Verify user has permission to delete plugins
|
|
685
|
+
✅ Use `$wpdb->prepare()` for database queries
|
|
686
|
+
✅ Validate file paths before deletion
|
|
687
|
+
|
|
688
|
+
---
|
|
689
|
+
|
|
690
|
+
## Testing
|
|
691
|
+
|
|
692
|
+
### Test Activation
|
|
693
|
+
|
|
694
|
+
```php
|
|
695
|
+
<?php
|
|
696
|
+
// Test activation in different scenarios
|
|
697
|
+
function test_my_plugin_activation() {
|
|
698
|
+
// Test fresh install
|
|
699
|
+
delete_option('my_plugin_version');
|
|
700
|
+
my_plugin_activate();
|
|
701
|
+
assert(get_option('my_plugin_version') === '1.0.0');
|
|
702
|
+
|
|
703
|
+
// Test upgrade
|
|
704
|
+
update_option('my_plugin_version', '0.9.0');
|
|
705
|
+
my_plugin_activate();
|
|
706
|
+
assert(get_option('my_plugin_version') === '1.0.0');
|
|
707
|
+
}
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
### Test Uninstall
|
|
711
|
+
|
|
712
|
+
```php
|
|
713
|
+
<?php
|
|
714
|
+
// Test uninstall cleanup
|
|
715
|
+
function test_my_plugin_uninstall() {
|
|
716
|
+
// Activate plugin
|
|
717
|
+
my_plugin_activate();
|
|
718
|
+
|
|
719
|
+
// Verify data exists
|
|
720
|
+
assert(get_option('my_plugin_version') !== false);
|
|
721
|
+
|
|
722
|
+
// Uninstall
|
|
723
|
+
require_once 'uninstall.php';
|
|
724
|
+
|
|
725
|
+
// Verify cleanup
|
|
726
|
+
assert(get_option('my_plugin_version') === false);
|
|
727
|
+
|
|
728
|
+
global $wpdb;
|
|
729
|
+
$table_exists = $wpdb->get_var(
|
|
730
|
+
"SHOW TABLES LIKE '{$wpdb->prefix}my_plugin_data'"
|
|
731
|
+
);
|
|
732
|
+
assert($table_exists === null);
|
|
733
|
+
}
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
---
|
|
737
|
+
|
|
738
|
+
## Troubleshooting
|
|
739
|
+
|
|
740
|
+
### Activation Errors
|
|
741
|
+
|
|
742
|
+
**Problem**: Plugin activates but doesn't create tables
|
|
743
|
+
**Solution**: Check `dbDelta()` syntax, ensure `upgrade.php` is included
|
|
744
|
+
|
|
745
|
+
**Problem**: Activation fails silently
|
|
746
|
+
**Solution**: Enable `WP_DEBUG` and check error logs
|
|
747
|
+
|
|
748
|
+
**Problem**: Multisite activation doesn't work
|
|
749
|
+
**Solution**: Check `$network_wide` parameter and use `switch_to_blog()`
|
|
750
|
+
|
|
751
|
+
### Uninstall Issues
|
|
752
|
+
|
|
753
|
+
**Problem**: `uninstall.php` doesn't run
|
|
754
|
+
**Solution**: Ensure file is in plugin root, check `WP_UNINSTALL_PLUGIN` constant
|
|
755
|
+
|
|
756
|
+
**Problem**: Data not fully deleted
|
|
757
|
+
**Solution**: Check for site-specific options in multisite, verify table names
|
|
758
|
+
|
|
759
|
+
---
|
|
760
|
+
|
|
761
|
+
## Summary
|
|
762
|
+
|
|
763
|
+
| Hook | When | Purpose | Delete Data? |
|
|
764
|
+
|------|------|---------|--------------|
|
|
765
|
+
| Activation | Plugin activated | Setup, create tables, add options | No |
|
|
766
|
+
| Deactivation | Plugin deactivated | Clear caches, unschedule cron | **NO** |
|
|
767
|
+
| Uninstall | Plugin deleted | Complete cleanup | **YES** |
|
|
768
|
+
|
|
769
|
+
**Golden Rule**: Only delete user data during uninstall, never during deactivation.
|
|
770
|
+
|