Initial commit
This commit is contained in:
181
templates/plugin-simple/README.md
Normal file
181
templates/plugin-simple/README.md
Normal file
@@ -0,0 +1,181 @@
|
||||
# Simple WordPress Plugin Template
|
||||
|
||||
This is a functional programming pattern for WordPress plugins. Best for small to medium plugins that don't require complex object-oriented architecture.
|
||||
|
||||
## Features
|
||||
|
||||
✅ Complete plugin header with all fields
|
||||
✅ ABSPATH security check
|
||||
✅ Unique function prefix (mysp_)
|
||||
✅ Custom post type registration (Books)
|
||||
✅ Admin settings page with nonce verification
|
||||
✅ AJAX handler with security checks
|
||||
✅ Proper activation/deactivation hooks
|
||||
✅ Uninstall script for cleanup
|
||||
✅ Internationalization ready
|
||||
✅ Conditional asset loading
|
||||
✅ Security best practices (sanitization, escaping, capability checks)
|
||||
|
||||
## Installation
|
||||
|
||||
1. Copy this folder to `wp-content/plugins/`
|
||||
2. Rename folder and files to match your plugin name
|
||||
3. Find and replace the following:
|
||||
- `My Simple Plugin` → Your plugin name
|
||||
- `my-simple-plugin` → your-plugin-slug
|
||||
- `mysp_` → yourprefix_
|
||||
- `MYSP_` → YOURPREFIX_
|
||||
- `https://example.com` → Your website
|
||||
- `Your Name` → Your name
|
||||
4. Activate in WordPress admin
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
my-simple-plugin/
|
||||
├── my-simple-plugin.php # Main plugin file
|
||||
├── uninstall.php # Cleanup on uninstall
|
||||
├── README.md # This file
|
||||
├── assets/ # CSS/JS files (create as needed)
|
||||
│ ├── css/
|
||||
│ │ ├── admin-style.css
|
||||
│ │ └── style.css
|
||||
│ └── js/
|
||||
│ ├── admin-script.js
|
||||
│ └── script.js
|
||||
└── languages/ # Translation files (create as needed)
|
||||
```
|
||||
|
||||
## Included Examples
|
||||
|
||||
### Custom Post Type
|
||||
- Registers "Books" post type
|
||||
- Gutenberg-enabled
|
||||
- Archive page support
|
||||
- Custom rewrite slug
|
||||
|
||||
### Settings Page
|
||||
- Located in Settings → Simple Plugin
|
||||
- Nonce verification
|
||||
- Sanitization and validation
|
||||
- Settings error handling
|
||||
|
||||
### AJAX Handler
|
||||
- Action: `wp_ajax_mysp_action`
|
||||
- Nonce verification
|
||||
- Capability checking
|
||||
- JSON response
|
||||
|
||||
### Activation/Deactivation
|
||||
- Flushes rewrite rules
|
||||
- Sets default options
|
||||
- Cleans up scheduled events
|
||||
|
||||
### Uninstall
|
||||
- Deletes all plugin options
|
||||
- Clears transients
|
||||
- Multisite support
|
||||
- Optional: Delete custom post type data
|
||||
|
||||
## Security Checklist
|
||||
|
||||
- [x] ABSPATH check at top of file
|
||||
- [x] Unique function prefix (mysp_)
|
||||
- [x] Nonces for all forms
|
||||
- [x] Capability checks (current_user_can)
|
||||
- [x] Input sanitization (sanitize_text_field)
|
||||
- [x] Output escaping (esc_html, esc_attr)
|
||||
- [x] AJAX nonce verification (check_ajax_referer)
|
||||
- [x] Conditional asset loading (don't load everywhere)
|
||||
- [x] Proper uninstall cleanup
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Create the `assets/` directory structure
|
||||
2. Add your CSS and JavaScript files
|
||||
3. Extend with additional features:
|
||||
- Meta boxes
|
||||
- Shortcodes
|
||||
- Widgets
|
||||
- REST API endpoints
|
||||
- Custom taxonomies
|
||||
- WP-CLI commands
|
||||
|
||||
## Distribution & Auto-Updates
|
||||
|
||||
### Enabling GitHub Auto-Updates
|
||||
|
||||
You can provide automatic updates from GitHub without submitting to WordPress.org:
|
||||
|
||||
**1. Install Plugin Update Checker library:**
|
||||
|
||||
```bash
|
||||
cd your-plugin/
|
||||
git submodule add https://github.com/YahnisElsts/plugin-update-checker.git
|
||||
```
|
||||
|
||||
**2. Add to your main plugin file:**
|
||||
|
||||
```php
|
||||
// Include Plugin Update Checker
|
||||
require plugin_dir_path( __FILE__ ) . 'plugin-update-checker/plugin-update-checker.php';
|
||||
use YahnisElsts\PluginUpdateChecker\v5\PucFactory;
|
||||
|
||||
// Initialize update checker
|
||||
$updateChecker = PucFactory::buildUpdateChecker(
|
||||
'https://github.com/yourusername/your-plugin/',
|
||||
__FILE__,
|
||||
'your-plugin-slug'
|
||||
);
|
||||
|
||||
// Set branch (default: main)
|
||||
$updateChecker->setBranch( 'main' );
|
||||
|
||||
// Use GitHub Releases (recommended)
|
||||
$updateChecker->getVcsApi()->enableReleaseAssets();
|
||||
```
|
||||
|
||||
**3. For private repos, add token to wp-config.php:**
|
||||
|
||||
```php
|
||||
define( 'YOUR_PLUGIN_GITHUB_TOKEN', 'ghp_xxxxxxxxxxxxx' );
|
||||
```
|
||||
|
||||
Then in your plugin:
|
||||
|
||||
```php
|
||||
if ( defined( 'YOUR_PLUGIN_GITHUB_TOKEN' ) ) {
|
||||
$updateChecker->setAuthentication( YOUR_PLUGIN_GITHUB_TOKEN );
|
||||
}
|
||||
```
|
||||
|
||||
**4. Create releases:**
|
||||
|
||||
```bash
|
||||
# Update version in plugin header
|
||||
git add my-simple-plugin.php
|
||||
git commit -m "Bump version to 1.0.1"
|
||||
git tag 1.0.1
|
||||
git push origin main
|
||||
git push origin 1.0.1
|
||||
|
||||
# Create GitHub Release (optional but recommended)
|
||||
# - Upload pre-built ZIP file
|
||||
# - Add release notes
|
||||
```
|
||||
|
||||
### Resources
|
||||
|
||||
- **Complete Guide**: See `references/github-auto-updates.md`
|
||||
- **Implementation Examples**: See `examples/github-updater.php`
|
||||
- **Plugin Update Checker**: https://github.com/YahnisElsts/plugin-update-checker
|
||||
|
||||
## Resources
|
||||
|
||||
- [WordPress Plugin Handbook](https://developer.wordpress.org/plugins/)
|
||||
- [WordPress Coding Standards](https://developer.wordpress.org/coding-standards/)
|
||||
- [Plugin Security](https://developer.wordpress.org/apis/security/)
|
||||
|
||||
## License
|
||||
|
||||
GPL v2 or later
|
||||
272
templates/plugin-simple/my-simple-plugin.php
Normal file
272
templates/plugin-simple/my-simple-plugin.php
Normal file
@@ -0,0 +1,272 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin Name: My Simple Plugin
|
||||
* Plugin URI: https://example.com/my-simple-plugin/
|
||||
* Description: A simple WordPress plugin demonstrating functional programming pattern with security best practices.
|
||||
* Version: 1.0.0
|
||||
* Requires at least: 5.9
|
||||
* Requires PHP: 7.4
|
||||
* Author: Your Name
|
||||
* Author URI: https://example.com/
|
||||
* License: GPL v2 or later
|
||||
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
|
||||
* Text Domain: my-simple-plugin
|
||||
* Domain Path: /languages
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Define plugin constants
|
||||
define( 'MYSP_VERSION', '1.0.0' );
|
||||
define( 'MYSP_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
|
||||
define( 'MYSP_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
|
||||
define( 'MYSP_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );
|
||||
|
||||
/**
|
||||
* Initialize the plugin
|
||||
*/
|
||||
function mysp_init() {
|
||||
// Load text domain for translations
|
||||
load_plugin_textdomain( 'my-simple-plugin', false, dirname( MYSP_PLUGIN_BASENAME ) . '/languages' );
|
||||
|
||||
// Register custom post type (example)
|
||||
mysp_register_book_post_type();
|
||||
}
|
||||
add_action( 'init', 'mysp_init' );
|
||||
|
||||
/**
|
||||
* Register a custom post type (example)
|
||||
*/
|
||||
function mysp_register_book_post_type() {
|
||||
$labels = array(
|
||||
'name' => _x( 'Books', 'post type general name', 'my-simple-plugin' ),
|
||||
'singular_name' => _x( 'Book', 'post type singular name', 'my-simple-plugin' ),
|
||||
'menu_name' => _x( 'Books', 'admin menu', 'my-simple-plugin' ),
|
||||
'add_new' => _x( 'Add New', 'book', 'my-simple-plugin' ),
|
||||
'add_new_item' => __( 'Add New Book', 'my-simple-plugin' ),
|
||||
'edit_item' => __( 'Edit Book', 'my-simple-plugin' ),
|
||||
'new_item' => __( 'New Book', 'my-simple-plugin' ),
|
||||
'view_item' => __( 'View Book', 'my-simple-plugin' ),
|
||||
'search_items' => __( 'Search Books', 'my-simple-plugin' ),
|
||||
'not_found' => __( 'No books found', 'my-simple-plugin' ),
|
||||
'not_found_in_trash' => __( 'No books found in Trash', 'my-simple-plugin' ),
|
||||
);
|
||||
|
||||
$args = array(
|
||||
'labels' => $labels,
|
||||
'public' => true,
|
||||
'publicly_queryable' => true,
|
||||
'show_ui' => true,
|
||||
'show_in_menu' => true,
|
||||
'query_var' => true,
|
||||
'rewrite' => array( 'slug' => 'books' ),
|
||||
'capability_type' => 'post',
|
||||
'has_archive' => true,
|
||||
'hierarchical' => false,
|
||||
'menu_position' => 5,
|
||||
'menu_icon' => 'dashicons-book',
|
||||
'show_in_rest' => true, // Enable Gutenberg editor
|
||||
'supports' => array( 'title', 'editor', 'thumbnail', 'excerpt' ),
|
||||
);
|
||||
|
||||
register_post_type( 'book', $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue admin scripts and styles
|
||||
*/
|
||||
function mysp_admin_enqueue_scripts( $hook ) {
|
||||
// Only load on specific admin pages
|
||||
if ( 'edit.php' !== $hook && 'post.php' !== $hook && 'post-new.php' !== $hook ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$screen = get_current_screen();
|
||||
if ( $screen && 'book' === $screen->post_type ) {
|
||||
wp_enqueue_style(
|
||||
'mysp-admin-style',
|
||||
MYSP_PLUGIN_URL . 'assets/css/admin-style.css',
|
||||
array(),
|
||||
MYSP_VERSION
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'mysp-admin-script',
|
||||
MYSP_PLUGIN_URL . 'assets/js/admin-script.js',
|
||||
array( 'jquery' ),
|
||||
MYSP_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
// Localize script with nonce and AJAX URL
|
||||
wp_localize_script(
|
||||
'mysp-admin-script',
|
||||
'myspData',
|
||||
array(
|
||||
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
||||
'nonce' => wp_create_nonce( 'mysp_ajax_nonce' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
add_action( 'admin_enqueue_scripts', 'mysp_admin_enqueue_scripts' );
|
||||
|
||||
/**
|
||||
* Enqueue frontend scripts and styles
|
||||
*/
|
||||
function mysp_enqueue_scripts() {
|
||||
// Only load on single book pages
|
||||
if ( is_singular( 'book' ) ) {
|
||||
wp_enqueue_style(
|
||||
'mysp-style',
|
||||
MYSP_PLUGIN_URL . 'assets/css/style.css',
|
||||
array(),
|
||||
MYSP_VERSION
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'mysp-script',
|
||||
MYSP_PLUGIN_URL . 'assets/js/script.js',
|
||||
array( 'jquery' ),
|
||||
MYSP_VERSION,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
add_action( 'wp_enqueue_scripts', 'mysp_enqueue_scripts' );
|
||||
|
||||
/**
|
||||
* Add settings page to admin menu
|
||||
*/
|
||||
function mysp_add_settings_page() {
|
||||
add_options_page(
|
||||
__( 'My Simple Plugin Settings', 'my-simple-plugin' ),
|
||||
__( 'Simple Plugin', 'my-simple-plugin' ),
|
||||
'manage_options',
|
||||
'my-simple-plugin',
|
||||
'mysp_render_settings_page'
|
||||
);
|
||||
}
|
||||
add_action( 'admin_menu', 'mysp_add_settings_page' );
|
||||
|
||||
/**
|
||||
* Render settings page
|
||||
*/
|
||||
function mysp_render_settings_page() {
|
||||
// Check user capabilities
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle form submission
|
||||
if ( isset( $_POST['mysp_settings_submit'] ) ) {
|
||||
// Verify nonce
|
||||
if ( ! isset( $_POST['mysp_settings_nonce'] ) || ! wp_verify_nonce( $_POST['mysp_settings_nonce'], 'mysp_settings_action' ) ) {
|
||||
wp_die( __( 'Security check failed', 'my-simple-plugin' ) );
|
||||
}
|
||||
|
||||
// Sanitize and save option
|
||||
$option_value = isset( $_POST['mysp_option'] ) ? sanitize_text_field( $_POST['mysp_option'] ) : '';
|
||||
update_option( 'mysp_option', $option_value );
|
||||
|
||||
// Show success message
|
||||
add_settings_error(
|
||||
'mysp_messages',
|
||||
'mysp_message',
|
||||
__( 'Settings Saved', 'my-simple-plugin' ),
|
||||
'updated'
|
||||
);
|
||||
}
|
||||
|
||||
// Get current option value
|
||||
$option_value = get_option( 'mysp_option', '' );
|
||||
|
||||
// Display settings page
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
|
||||
<?php settings_errors( 'mysp_messages' ); ?>
|
||||
<form method="post" action="">
|
||||
<?php wp_nonce_field( 'mysp_settings_action', 'mysp_settings_nonce' ); ?>
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="mysp_option"><?php esc_html_e( 'Example Option', 'my-simple-plugin' ); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
id="mysp_option"
|
||||
name="mysp_option"
|
||||
value="<?php echo esc_attr( $option_value ); ?>"
|
||||
class="regular-text"
|
||||
/>
|
||||
<p class="description"><?php esc_html_e( 'Enter some text here', 'my-simple-plugin' ); ?></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<?php submit_button( __( 'Save Settings', 'my-simple-plugin' ), 'primary', 'mysp_settings_submit' ); ?>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Example AJAX handler
|
||||
*/
|
||||
function mysp_ajax_handler() {
|
||||
// Verify nonce
|
||||
check_ajax_referer( 'mysp_ajax_nonce', 'nonce' );
|
||||
|
||||
// Check user capability
|
||||
if ( ! current_user_can( 'edit_posts' ) ) {
|
||||
wp_send_json_error( array( 'message' => __( 'Permission denied', 'my-simple-plugin' ) ) );
|
||||
}
|
||||
|
||||
// Get and sanitize input
|
||||
$input = isset( $_POST['data'] ) ? sanitize_text_field( $_POST['data'] ) : '';
|
||||
|
||||
// Process and return response
|
||||
wp_send_json_success( array(
|
||||
'message' => __( 'Success!', 'my-simple-plugin' ),
|
||||
'data' => $input,
|
||||
) );
|
||||
}
|
||||
add_action( 'wp_ajax_mysp_action', 'mysp_ajax_handler' );
|
||||
|
||||
/**
|
||||
* Activation hook
|
||||
*/
|
||||
function mysp_activate() {
|
||||
// Register post type before flushing rewrite rules
|
||||
mysp_register_book_post_type();
|
||||
|
||||
// Flush rewrite rules
|
||||
flush_rewrite_rules();
|
||||
|
||||
// Set default options
|
||||
if ( false === get_option( 'mysp_option' ) ) {
|
||||
add_option( 'mysp_option', '' );
|
||||
}
|
||||
|
||||
// Set activation timestamp
|
||||
if ( false === get_option( 'mysp_activated_time' ) ) {
|
||||
add_option( 'mysp_activated_time', current_time( 'timestamp' ) );
|
||||
}
|
||||
}
|
||||
register_activation_hook( __FILE__, 'mysp_activate' );
|
||||
|
||||
/**
|
||||
* Deactivation hook
|
||||
*/
|
||||
function mysp_deactivate() {
|
||||
// Flush rewrite rules
|
||||
flush_rewrite_rules();
|
||||
|
||||
// Clear any scheduled events
|
||||
wp_clear_scheduled_hook( 'mysp_cron_event' );
|
||||
}
|
||||
register_deactivation_hook( __FILE__, 'mysp_deactivate' );
|
||||
56
templates/plugin-simple/uninstall.php
Normal file
56
templates/plugin-simple/uninstall.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
/**
|
||||
* Uninstall script
|
||||
*
|
||||
* This file is called when the plugin is uninstalled via WordPress admin.
|
||||
* It should clean up all plugin data.
|
||||
*/
|
||||
|
||||
// Exit if not called by WordPress
|
||||
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Delete plugin options
|
||||
delete_option( 'mysp_option' );
|
||||
delete_option( 'mysp_activated_time' );
|
||||
|
||||
// Delete transients
|
||||
delete_transient( 'mysp_cache' );
|
||||
|
||||
// For multisite, delete from all sites
|
||||
if ( is_multisite() ) {
|
||||
global $wpdb;
|
||||
$blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
|
||||
|
||||
foreach ( $blog_ids as $blog_id ) {
|
||||
switch_to_blog( $blog_id );
|
||||
|
||||
delete_option( 'mysp_option' );
|
||||
delete_option( 'mysp_activated_time' );
|
||||
delete_transient( 'mysp_cache' );
|
||||
|
||||
restore_current_blog();
|
||||
}
|
||||
}
|
||||
|
||||
// Delete custom post type data (optional - consider if users want to keep content)
|
||||
// Uncomment the following if you want to delete all custom post type posts on uninstall
|
||||
/*
|
||||
$books = get_posts( array(
|
||||
'post_type' => 'book',
|
||||
'posts_per_page' => -1,
|
||||
'post_status' => 'any',
|
||||
) );
|
||||
|
||||
foreach ( $books as $book ) {
|
||||
wp_delete_post( $book->ID, true ); // true = force delete (skip trash)
|
||||
}
|
||||
*/
|
||||
|
||||
// If you created custom database tables, drop them here
|
||||
/*
|
||||
global $wpdb;
|
||||
$table_name = $wpdb->prefix . 'mysp_custom_table';
|
||||
$wpdb->query( "DROP TABLE IF EXISTS $table_name" );
|
||||
*/
|
||||
Reference in New Issue
Block a user