Initial commit
This commit is contained in:
406
templates/plugin-psr4/README.md
Normal file
406
templates/plugin-psr4/README.md
Normal file
@@ -0,0 +1,406 @@
|
||||
# PSR-4 WordPress Plugin Template
|
||||
|
||||
This is a modern PSR-4 autoloading pattern for WordPress plugins using Composer. Best for large, complex plugins that need professional architecture with namespaces and autoloading.
|
||||
|
||||
## Features
|
||||
|
||||
✅ PSR-4 autoloading with Composer
|
||||
✅ Namespace organization (MyPSR4Plugin\*)
|
||||
✅ Singleton pattern
|
||||
✅ Modular class structure by domain
|
||||
✅ Custom post type with meta boxes
|
||||
✅ Custom taxonomy
|
||||
✅ Admin settings using Settings API
|
||||
✅ REST API endpoints (GET, POST)
|
||||
✅ Asset management (admin + frontend)
|
||||
✅ Proper activation/deactivation hooks
|
||||
✅ Uninstall script for cleanup
|
||||
✅ Internationalization ready
|
||||
✅ WordPress Coding Standards ready (PHPCS)
|
||||
✅ Security best practices
|
||||
|
||||
## 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:
|
||||
- `MyPSR4Plugin` → YourPluginNamespace
|
||||
- `My PSR-4 Plugin` → Your plugin name
|
||||
- `my-psr4-plugin` → your-plugin-slug
|
||||
- `mypp_` → yourprefix_
|
||||
- `MYPP_` → YOURPREFIX_
|
||||
- `yourname/my-psr4-plugin` → your-composer/package-name
|
||||
- `https://example.com` → Your website
|
||||
- `Your Name` → Your name
|
||||
4. Run `composer install` in the plugin directory
|
||||
5. Activate in WordPress admin
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
my-psr4-plugin/
|
||||
├── my-psr4-plugin.php # Main plugin file (bootstrapper)
|
||||
├── composer.json # Composer dependencies and autoloading
|
||||
├── uninstall.php # Cleanup on uninstall
|
||||
├── README.md # This file
|
||||
├── src/ # Source code (PSR-4 autoloaded)
|
||||
│ ├── Plugin.php # Main plugin class
|
||||
│ ├── PostTypes/ # Custom post types
|
||||
│ │ └── Book.php
|
||||
│ ├── Taxonomies/ # Taxonomies
|
||||
│ │ └── Genre.php
|
||||
│ ├── Admin/ # Admin functionality
|
||||
│ │ └── Settings.php
|
||||
│ ├── Frontend/ # Frontend functionality
|
||||
│ │ └── Assets.php
|
||||
│ └── API/ # REST API endpoints
|
||||
│ └── BookEndpoints.php
|
||||
├── 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)
|
||||
└── vendor/ # Composer dependencies (auto-generated)
|
||||
```
|
||||
|
||||
## Composer Setup
|
||||
|
||||
### Install Dependencies
|
||||
|
||||
```bash
|
||||
cd wp-content/plugins/my-psr4-plugin
|
||||
composer install
|
||||
```
|
||||
|
||||
### Development Dependencies
|
||||
|
||||
The template includes WordPress Coding Standards (WPCS) for code quality:
|
||||
|
||||
```bash
|
||||
# Check code standards
|
||||
composer phpcs
|
||||
|
||||
# Fix code standards automatically
|
||||
composer phpcbf
|
||||
```
|
||||
|
||||
### Update Dependencies
|
||||
|
||||
```bash
|
||||
composer update
|
||||
```
|
||||
|
||||
## Namespaces
|
||||
|
||||
The plugin uses the following namespace structure:
|
||||
|
||||
- `MyPSR4Plugin\` - Root namespace
|
||||
- `MyPSR4Plugin\PostTypes\` - Custom post types
|
||||
- `MyPSR4Plugin\Taxonomies\` - Taxonomies
|
||||
- `MyPSR4Plugin\Admin\` - Admin functionality
|
||||
- `MyPSR4Plugin\Frontend\` - Frontend functionality
|
||||
- `MyPSR4Plugin\API\` - REST API endpoints
|
||||
|
||||
## Included Examples
|
||||
|
||||
### Custom Post Type (src/PostTypes/Book.php)
|
||||
- "Books" post type with Gutenberg support
|
||||
- Meta boxes for ISBN, Author, Publication Year
|
||||
- Nonce verification and sanitization
|
||||
|
||||
### Taxonomy (src/Taxonomies/Genre.php)
|
||||
- "Genres" hierarchical taxonomy
|
||||
- Linked to Books post type
|
||||
|
||||
### Admin Settings (src/Admin/Settings.php)
|
||||
- Located in Settings → PSR-4 Plugin
|
||||
- Uses WordPress Settings API
|
||||
- Multiple field types with validation
|
||||
|
||||
### REST API (src/API/BookEndpoints.php)
|
||||
- `GET /wp-json/mypp/v1/books` - List all books
|
||||
- `GET /wp-json/mypp/v1/books/{id}` - Get single book
|
||||
- `POST /wp-json/mypp/v1/books` - Create book
|
||||
- Permission callbacks and validation
|
||||
|
||||
### Assets (src/Frontend/Assets.php)
|
||||
- Conditional loading (admin + frontend)
|
||||
- Localized script data
|
||||
|
||||
## Adding New Classes
|
||||
|
||||
### 1. Create Class File
|
||||
|
||||
Create `src/YourNamespace/YourClass.php`:
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace MyPSR4Plugin\YourNamespace;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class YourClass {
|
||||
private static $instance = null;
|
||||
|
||||
public static function get_instance() {
|
||||
if ( null === self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
private function __construct() {
|
||||
// Initialize
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Initialize in Plugin.php
|
||||
|
||||
Add to `src/Plugin.php` in the `init()` method:
|
||||
|
||||
```php
|
||||
YourNamespace\YourClass::get_instance();
|
||||
```
|
||||
|
||||
### 3. Composer Will Auto-Load
|
||||
|
||||
No need to manually require files! Composer's PSR-4 autoloader handles it.
|
||||
|
||||
## Security Checklist
|
||||
|
||||
- [x] ABSPATH check in every file
|
||||
- [x] Namespaces prevent naming conflicts
|
||||
- [x] Private constructors (singletons)
|
||||
- [x] Nonces for all forms
|
||||
- [x] Capability checks (current_user_can)
|
||||
- [x] Input sanitization (sanitize_text_field, absint)
|
||||
- [x] Output escaping (esc_html, esc_attr)
|
||||
- [x] REST API permission callbacks
|
||||
- [x] REST API argument validation
|
||||
- [x] Conditional asset loading
|
||||
- [x] Composer autoloader check
|
||||
|
||||
## Advantages of PSR-4 Pattern
|
||||
|
||||
✅ **Professional** - Industry-standard autoloading
|
||||
✅ **No manual requires** - Composer handles class loading
|
||||
✅ **Organized** - Classes grouped by domain/functionality
|
||||
✅ **Scalable** - Easy to add new features
|
||||
✅ **Testable** - Each class is independently testable
|
||||
✅ **Standards** - Compatible with modern PHP tooling
|
||||
✅ **Namespaces** - Prevents naming conflicts
|
||||
✅ **Version control** - Clean git diffs (no huge files)
|
||||
|
||||
## When to Use PSR-4
|
||||
|
||||
**Use PSR-4 when**:
|
||||
- Plugin has 20+ classes
|
||||
- Multiple developers
|
||||
- Need professional architecture
|
||||
- Plan to add many features over time
|
||||
- Want to use Composer packages
|
||||
- Need unit testing
|
||||
- Building a commercial plugin
|
||||
|
||||
**Use OOP when**:
|
||||
- Plugin has 10-20 classes
|
||||
- Single developer
|
||||
- Don't need Composer dependencies
|
||||
|
||||
**Use Simple when**:
|
||||
- Plugin has <10 classes
|
||||
- Quick, focused functionality
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### 1. Create New Feature Class
|
||||
|
||||
```bash
|
||||
# Example: Create a new AJAX handler
|
||||
touch src/AJAX/BookActions.php
|
||||
```
|
||||
|
||||
### 2. Write Class with Namespace
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace MyPSR4Plugin\AJAX;
|
||||
|
||||
class BookActions {
|
||||
// Your code
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Initialize in Plugin.php
|
||||
|
||||
```php
|
||||
AJAX\BookActions::get_instance();
|
||||
```
|
||||
|
||||
### 4. Test
|
||||
|
||||
No need to run `composer dump-autoload` - PSR-4 discovers classes automatically!
|
||||
|
||||
## Code Quality
|
||||
|
||||
### Check Code Standards
|
||||
|
||||
```bash
|
||||
composer phpcs
|
||||
```
|
||||
|
||||
### Fix Code Standards
|
||||
|
||||
```bash
|
||||
composer phpcbf
|
||||
```
|
||||
|
||||
### Add Custom Rules
|
||||
|
||||
Edit `composer.json` scripts:
|
||||
|
||||
```json
|
||||
"scripts": {
|
||||
"phpcs": "phpcs --standard=WordPress --extensions=php src/",
|
||||
"phpcbf": "phpcbf --standard=WordPress --extensions=php src/"
|
||||
}
|
||||
```
|
||||
|
||||
## Distribution & Auto-Updates
|
||||
|
||||
### Enabling GitHub Auto-Updates
|
||||
|
||||
You can provide automatic updates from GitHub without submitting to WordPress.org:
|
||||
|
||||
**1. Install Plugin Update Checker via Composer:**
|
||||
|
||||
```bash
|
||||
composer require yahnis-elsts/plugin-update-checker
|
||||
```
|
||||
|
||||
Or as git submodule:
|
||||
|
||||
```bash
|
||||
git submodule add https://github.com/YahnisElsts/plugin-update-checker.git
|
||||
```
|
||||
|
||||
**2. Create Updater class:**
|
||||
|
||||
Create `src/Updater.php`:
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace MyPSR4Plugin;
|
||||
|
||||
use YahnisElsts\PluginUpdateChecker\v5\PucFactory;
|
||||
|
||||
class Updater {
|
||||
private static $instance = null;
|
||||
private $updateChecker;
|
||||
|
||||
public static function get_instance() {
|
||||
if ( null === self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
private function __construct() {
|
||||
$this->init_updater();
|
||||
}
|
||||
|
||||
private function init_updater() {
|
||||
$updater_path = plugin_dir_path( __DIR__ ) . 'vendor/yahnis-elsts/plugin-update-checker/plugin-update-checker.php';
|
||||
|
||||
if ( ! file_exists( $updater_path ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
require $updater_path;
|
||||
|
||||
$this->updateChecker = PucFactory::buildUpdateChecker(
|
||||
'https://github.com/yourusername/your-plugin/',
|
||||
plugin_dir_path( __DIR__ ) . 'my-psr4-plugin.php',
|
||||
'your-plugin-slug'
|
||||
);
|
||||
|
||||
$this->updateChecker->setBranch( 'main' );
|
||||
$this->updateChecker->getVcsApi()->enableReleaseAssets();
|
||||
|
||||
// Private repo authentication
|
||||
if ( defined( 'MYPP_GITHUB_TOKEN' ) ) {
|
||||
$this->updateChecker->setAuthentication( MYPP_GITHUB_TOKEN );
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**3. Initialize in Plugin.php:**
|
||||
|
||||
Add to `src/Plugin.php` `init()` method:
|
||||
|
||||
```php
|
||||
Updater::get_instance();
|
||||
```
|
||||
|
||||
**4. For private repos, add token to wp-config.php:**
|
||||
|
||||
```php
|
||||
define( 'MYPP_GITHUB_TOKEN', 'ghp_xxxxxxxxxxxxx' );
|
||||
```
|
||||
|
||||
**5. Create releases:**
|
||||
|
||||
```bash
|
||||
# Update version in plugin header
|
||||
git add my-psr4-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 with pre-built ZIP
|
||||
```
|
||||
|
||||
**6. Build release ZIP:**
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# build-release.sh
|
||||
|
||||
VERSION="1.0.1"
|
||||
PLUGIN_SLUG="my-psr4-plugin"
|
||||
|
||||
# Install dependencies (no dev packages)
|
||||
composer install --no-dev --optimize-autoloader
|
||||
|
||||
# Create ZIP
|
||||
zip -r "${PLUGIN_SLUG}-${VERSION}.zip" \
|
||||
"${PLUGIN_SLUG}/" \
|
||||
-x "*.git*" "*.github/*" "tests/*" "node_modules/*"
|
||||
|
||||
echo "Built: ${PLUGIN_SLUG}-${VERSION}.zip"
|
||||
```
|
||||
|
||||
### 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/)
|
||||
- [PSR-4 Autoloading](https://www.php-fig.org/psr/psr-4/)
|
||||
- [Composer Documentation](https://getcomposer.org/doc/)
|
||||
- [WordPress Coding Standards](https://developer.wordpress.org/coding-standards/)
|
||||
|
||||
## License
|
||||
|
||||
GPL v2 or later
|
||||
33
templates/plugin-psr4/composer.json
Normal file
33
templates/plugin-psr4/composer.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "yourname/my-psr4-plugin",
|
||||
"description": "A modern WordPress plugin using PSR-4 autoloading",
|
||||
"type": "wordpress-plugin",
|
||||
"license": "GPL-2.0-or-later",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Your Name",
|
||||
"email": "your@email.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"squizlabs/php_codesniffer": "^3.7",
|
||||
"wp-coding-standards/wpcs": "^3.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"MyPSR4Plugin\\": "src/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"phpcs": "phpcs --standard=WordPress src/",
|
||||
"phpcbf": "phpcbf --standard=WordPress src/"
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true
|
||||
}
|
||||
}
|
||||
}
|
||||
58
templates/plugin-psr4/my-psr4-plugin.php
Normal file
58
templates/plugin-psr4/my-psr4-plugin.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/**
|
||||
* Plugin Name: My PSR-4 Plugin
|
||||
* Plugin URI: https://example.com/my-psr4-plugin/
|
||||
* Description: A modern WordPress plugin using PSR-4 autoloading with Composer and namespaces.
|
||||
* 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-psr4-plugin
|
||||
* Domain Path: /languages
|
||||
*/
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Define plugin constants
|
||||
define( 'MYPP_VERSION', '1.0.0' );
|
||||
define( 'MYPP_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
|
||||
define( 'MYPP_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
|
||||
define( 'MYPP_PLUGIN_BASENAME', plugin_basename( __FILE__ ) );
|
||||
|
||||
// Require Composer autoloader
|
||||
if ( file_exists( MYPP_PLUGIN_DIR . 'vendor/autoload.php' ) ) {
|
||||
require_once MYPP_PLUGIN_DIR . 'vendor/autoload.php';
|
||||
} else {
|
||||
// Show admin notice if autoloader is missing
|
||||
add_action( 'admin_notices', function() {
|
||||
?>
|
||||
<div class="notice notice-error">
|
||||
<p>
|
||||
<?php
|
||||
printf(
|
||||
/* translators: %s: command to run */
|
||||
esc_html__( 'My PSR-4 Plugin requires Composer dependencies. Please run %s in the plugin directory.', 'my-psr4-plugin' ),
|
||||
'<code>composer install</code>'
|
||||
);
|
||||
?>
|
||||
</p>
|
||||
</div>
|
||||
<?php
|
||||
} );
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize plugin
|
||||
\MyPSR4Plugin\Plugin::get_instance();
|
||||
|
||||
// Activation hook
|
||||
register_activation_hook( __FILE__, array( \MyPSR4Plugin\Plugin::get_instance(), 'activate' ) );
|
||||
|
||||
// Deactivation hook
|
||||
register_deactivation_hook( __FILE__, array( \MyPSR4Plugin\Plugin::get_instance(), 'deactivate' ) );
|
||||
251
templates/plugin-psr4/src/API/BookEndpoints.php
Normal file
251
templates/plugin-psr4/src/API/BookEndpoints.php
Normal file
@@ -0,0 +1,251 @@
|
||||
<?php
|
||||
/**
|
||||
* Book REST API Endpoints
|
||||
*
|
||||
* @package MyPSR4Plugin\API
|
||||
*/
|
||||
|
||||
namespace MyPSR4Plugin\API;
|
||||
|
||||
use MyPSR4Plugin\PostTypes\Book;
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Book REST API endpoints class
|
||||
*/
|
||||
class BookEndpoints {
|
||||
|
||||
/**
|
||||
* Single instance
|
||||
*
|
||||
* @var BookEndpoints
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* Namespace
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const NAMESPACE = 'mypp/v1';
|
||||
|
||||
/**
|
||||
* Get instance
|
||||
*
|
||||
* @return BookEndpoints
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( null === self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
private function __construct() {
|
||||
add_action( 'rest_api_init', array( $this, 'register_routes' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register REST routes
|
||||
*/
|
||||
public function register_routes() {
|
||||
// Get all books
|
||||
register_rest_route(
|
||||
self::NAMESPACE,
|
||||
'/books',
|
||||
array(
|
||||
'methods' => 'GET',
|
||||
'callback' => array( $this, 'get_books' ),
|
||||
'permission_callback' => '__return_true',
|
||||
)
|
||||
);
|
||||
|
||||
// Get single book
|
||||
register_rest_route(
|
||||
self::NAMESPACE,
|
||||
'/books/(?P<id>\d+)',
|
||||
array(
|
||||
'methods' => 'GET',
|
||||
'callback' => array( $this, 'get_book' ),
|
||||
'permission_callback' => '__return_true',
|
||||
'args' => array(
|
||||
'id' => array(
|
||||
'required' => true,
|
||||
'validate_callback' => function( $param ) {
|
||||
return is_numeric( $param );
|
||||
},
|
||||
'sanitize_callback' => 'absint',
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
// Create book
|
||||
register_rest_route(
|
||||
self::NAMESPACE,
|
||||
'/books',
|
||||
array(
|
||||
'methods' => 'POST',
|
||||
'callback' => array( $this, 'create_book' ),
|
||||
'permission_callback' => array( $this, 'check_permission' ),
|
||||
'args' => $this->get_book_args(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get books
|
||||
*
|
||||
* @param \WP_REST_Request $request Request object.
|
||||
* @return \WP_REST_Response|\WP_Error
|
||||
*/
|
||||
public function get_books( $request ) {
|
||||
$args = array(
|
||||
'post_type' => Book::POST_TYPE,
|
||||
'posts_per_page' => 10,
|
||||
'post_status' => 'publish',
|
||||
);
|
||||
|
||||
$books = get_posts( $args );
|
||||
|
||||
$data = array();
|
||||
foreach ( $books as $book ) {
|
||||
$data[] = $this->prepare_book_data( $book );
|
||||
}
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get single book
|
||||
*
|
||||
* @param \WP_REST_Request $request Request object.
|
||||
* @return \WP_REST_Response|\WP_Error
|
||||
*/
|
||||
public function get_book( $request ) {
|
||||
$book_id = $request->get_param( 'id' );
|
||||
$book = get_post( $book_id );
|
||||
|
||||
if ( ! $book || Book::POST_TYPE !== $book->post_type ) {
|
||||
return new \WP_Error(
|
||||
'not_found',
|
||||
__( 'Book not found', 'my-psr4-plugin' ),
|
||||
array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
|
||||
$data = $this->prepare_book_data( $book );
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create book
|
||||
*
|
||||
* @param \WP_REST_Request $request Request object.
|
||||
* @return \WP_REST_Response|\WP_Error
|
||||
*/
|
||||
public function create_book( $request ) {
|
||||
$title = $request->get_param( 'title' );
|
||||
$content = $request->get_param( 'content' );
|
||||
$isbn = $request->get_param( 'isbn' );
|
||||
$author = $request->get_param( 'author' );
|
||||
$year = $request->get_param( 'year' );
|
||||
|
||||
$post_id = wp_insert_post( array(
|
||||
'post_title' => $title,
|
||||
'post_content' => $content,
|
||||
'post_type' => Book::POST_TYPE,
|
||||
'post_status' => 'draft',
|
||||
) );
|
||||
|
||||
if ( is_wp_error( $post_id ) ) {
|
||||
return $post_id;
|
||||
}
|
||||
|
||||
// Save meta data
|
||||
if ( $isbn ) {
|
||||
update_post_meta( $post_id, '_mypp_isbn', $isbn );
|
||||
}
|
||||
if ( $author ) {
|
||||
update_post_meta( $post_id, '_mypp_author', $author );
|
||||
}
|
||||
if ( $year ) {
|
||||
update_post_meta( $post_id, '_mypp_year', $year );
|
||||
}
|
||||
|
||||
$book = get_post( $post_id );
|
||||
$data = $this->prepare_book_data( $book );
|
||||
|
||||
return rest_ensure_response( $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare book data for response
|
||||
*
|
||||
* @param \WP_Post $book Post object.
|
||||
* @return array
|
||||
*/
|
||||
private function prepare_book_data( $book ) {
|
||||
return array(
|
||||
'id' => $book->ID,
|
||||
'title' => $book->post_title,
|
||||
'content' => $book->post_content,
|
||||
'excerpt' => $book->post_excerpt,
|
||||
'isbn' => get_post_meta( $book->ID, '_mypp_isbn', true ),
|
||||
'author' => get_post_meta( $book->ID, '_mypp_author', true ),
|
||||
'year' => (int) get_post_meta( $book->ID, '_mypp_year', true ),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check permission
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function check_permission() {
|
||||
return current_user_can( 'edit_posts' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get book arguments for validation
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function get_book_args() {
|
||||
return array(
|
||||
'title' => array(
|
||||
'required' => true,
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
),
|
||||
'content' => array(
|
||||
'required' => false,
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => 'wp_kses_post',
|
||||
),
|
||||
'isbn' => array(
|
||||
'required' => false,
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
),
|
||||
'author' => array(
|
||||
'required' => false,
|
||||
'type' => 'string',
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
),
|
||||
'year' => array(
|
||||
'required' => false,
|
||||
'type' => 'integer',
|
||||
'sanitize_callback' => 'absint',
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
248
templates/plugin-psr4/src/Admin/Settings.php
Normal file
248
templates/plugin-psr4/src/Admin/Settings.php
Normal file
@@ -0,0 +1,248 @@
|
||||
<?php
|
||||
/**
|
||||
* Admin Settings
|
||||
*
|
||||
* @package MyPSR4Plugin\Admin
|
||||
*/
|
||||
|
||||
namespace MyPSR4Plugin\Admin;
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings class
|
||||
*/
|
||||
class Settings {
|
||||
|
||||
/**
|
||||
* Single instance
|
||||
*
|
||||
* @var Settings
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* Option name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const OPTION_NAME = 'mypp_settings';
|
||||
|
||||
/**
|
||||
* Get instance
|
||||
*
|
||||
* @return Settings
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( null === self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
private function __construct() {
|
||||
add_action( 'admin_menu', array( $this, 'add_menu_page' ) );
|
||||
add_action( 'admin_init', array( $this, 'register_settings' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add menu page
|
||||
*/
|
||||
public function add_menu_page() {
|
||||
add_options_page(
|
||||
__( 'My PSR-4 Plugin Settings', 'my-psr4-plugin' ),
|
||||
__( 'PSR-4 Plugin', 'my-psr4-plugin' ),
|
||||
'manage_options',
|
||||
'my-psr4-plugin',
|
||||
array( $this, 'render_settings_page' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register settings
|
||||
*/
|
||||
public function register_settings() {
|
||||
register_setting(
|
||||
'mypp_settings_group',
|
||||
self::OPTION_NAME,
|
||||
array( $this, 'sanitize_settings' )
|
||||
);
|
||||
|
||||
add_settings_section(
|
||||
'mypp_general_section',
|
||||
__( 'General Settings', 'my-psr4-plugin' ),
|
||||
array( $this, 'render_section_description' ),
|
||||
'my-psr4-plugin'
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
'mypp_option1',
|
||||
__( 'Text Option', 'my-psr4-plugin' ),
|
||||
array( $this, 'render_text_field' ),
|
||||
'my-psr4-plugin',
|
||||
'mypp_general_section',
|
||||
array( 'field_id' => 'option1' )
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
'mypp_option2',
|
||||
__( 'Number Option', 'my-psr4-plugin' ),
|
||||
array( $this, 'render_number_field' ),
|
||||
'my-psr4-plugin',
|
||||
'mypp_general_section',
|
||||
array( 'field_id' => 'option2' )
|
||||
);
|
||||
|
||||
add_settings_field(
|
||||
'mypp_option3',
|
||||
__( 'Checkbox Option', 'my-psr4-plugin' ),
|
||||
array( $this, 'render_checkbox_field' ),
|
||||
'my-psr4-plugin',
|
||||
'mypp_general_section',
|
||||
array( 'field_id' => 'option3' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render settings page
|
||||
*/
|
||||
public function render_settings_page() {
|
||||
if ( ! current_user_can( 'manage_options' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
|
||||
<form method="post" action="options.php">
|
||||
<?php
|
||||
settings_fields( 'mypp_settings_group' );
|
||||
do_settings_sections( 'my-psr4-plugin' );
|
||||
submit_button( __( 'Save Settings', 'my-psr4-plugin' ) );
|
||||
?>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Render section description
|
||||
*/
|
||||
public function render_section_description() {
|
||||
echo '<p>' . esc_html__( 'Configure your plugin settings below.', 'my-psr4-plugin' ) . '</p>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Render text field
|
||||
*
|
||||
* @param array $args Field arguments.
|
||||
*/
|
||||
public function render_text_field( $args ) {
|
||||
$settings = $this->get_settings();
|
||||
$field_id = $args['field_id'];
|
||||
$value = isset( $settings[ $field_id ] ) ? $settings[ $field_id ] : '';
|
||||
|
||||
printf(
|
||||
'<input type="text" id="mypp_%1$s" name="%2$s[%1$s]" value="%3$s" class="regular-text" />',
|
||||
esc_attr( $field_id ),
|
||||
esc_attr( self::OPTION_NAME ),
|
||||
esc_attr( $value )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render number field
|
||||
*
|
||||
* @param array $args Field arguments.
|
||||
*/
|
||||
public function render_number_field( $args ) {
|
||||
$settings = $this->get_settings();
|
||||
$field_id = $args['field_id'];
|
||||
$value = isset( $settings[ $field_id ] ) ? $settings[ $field_id ] : 0;
|
||||
|
||||
printf(
|
||||
'<input type="number" id="mypp_%1$s" name="%2$s[%1$s]" value="%3$s" min="0" max="100" class="small-text" />',
|
||||
esc_attr( $field_id ),
|
||||
esc_attr( self::OPTION_NAME ),
|
||||
esc_attr( $value )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render checkbox field
|
||||
*
|
||||
* @param array $args Field arguments.
|
||||
*/
|
||||
public function render_checkbox_field( $args ) {
|
||||
$settings = $this->get_settings();
|
||||
$field_id = $args['field_id'];
|
||||
$value = isset( $settings[ $field_id ] ) ? $settings[ $field_id ] : false;
|
||||
|
||||
printf(
|
||||
'<input type="checkbox" id="mypp_%1$s" name="%2$s[%1$s]" value="1" %3$s />',
|
||||
esc_attr( $field_id ),
|
||||
esc_attr( self::OPTION_NAME ),
|
||||
checked( $value, true, false )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize settings
|
||||
*
|
||||
* @param array $input Input array.
|
||||
* @return array
|
||||
*/
|
||||
public function sanitize_settings( $input ) {
|
||||
$sanitized = array();
|
||||
|
||||
if ( isset( $input['option1'] ) ) {
|
||||
$sanitized['option1'] = sanitize_text_field( $input['option1'] );
|
||||
}
|
||||
|
||||
if ( isset( $input['option2'] ) ) {
|
||||
$sanitized['option2'] = absint( $input['option2'] );
|
||||
}
|
||||
|
||||
if ( isset( $input['option3'] ) ) {
|
||||
$sanitized['option3'] = (bool) $input['option3'];
|
||||
}
|
||||
|
||||
return $sanitized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get settings
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_settings() {
|
||||
$defaults = array(
|
||||
'option1' => '',
|
||||
'option2' => 0,
|
||||
'option3' => false,
|
||||
);
|
||||
|
||||
$settings = get_option( self::OPTION_NAME, $defaults );
|
||||
|
||||
return wp_parse_args( $settings, $defaults );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default settings on activation
|
||||
*/
|
||||
public function set_defaults() {
|
||||
if ( false === get_option( self::OPTION_NAME ) ) {
|
||||
add_option( self::OPTION_NAME, array(
|
||||
'option1' => '',
|
||||
'option2' => 0,
|
||||
'option3' => false,
|
||||
) );
|
||||
}
|
||||
}
|
||||
}
|
||||
111
templates/plugin-psr4/src/Frontend/Assets.php
Normal file
111
templates/plugin-psr4/src/Frontend/Assets.php
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
/**
|
||||
* Frontend Assets
|
||||
*
|
||||
* @package MyPSR4Plugin\Frontend
|
||||
*/
|
||||
|
||||
namespace MyPSR4Plugin\Frontend;
|
||||
|
||||
use MyPSR4Plugin\PostTypes\Book;
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assets class
|
||||
*/
|
||||
class Assets {
|
||||
|
||||
/**
|
||||
* Single instance
|
||||
*
|
||||
* @var Assets
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* Get instance
|
||||
*
|
||||
* @return Assets
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( null === self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
private function __construct() {
|
||||
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue frontend scripts and styles
|
||||
*/
|
||||
public function enqueue_scripts() {
|
||||
// Only load on single book pages
|
||||
if ( is_singular( Book::POST_TYPE ) ) {
|
||||
wp_enqueue_style(
|
||||
'mypp-style',
|
||||
MYPP_PLUGIN_URL . 'assets/css/style.css',
|
||||
array(),
|
||||
MYPP_VERSION
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'mypp-script',
|
||||
MYPP_PLUGIN_URL . 'assets/js/script.js',
|
||||
array( 'jquery' ),
|
||||
MYPP_VERSION,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue admin scripts and styles
|
||||
*
|
||||
* @param string $hook Current admin page hook.
|
||||
*/
|
||||
public function enqueue_admin_scripts( $hook ) {
|
||||
// Only load on book edit pages
|
||||
if ( 'post.php' !== $hook && 'post-new.php' !== $hook && 'edit.php' !== $hook ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$screen = get_current_screen();
|
||||
if ( $screen && Book::POST_TYPE === $screen->post_type ) {
|
||||
wp_enqueue_style(
|
||||
'mypp-admin-style',
|
||||
MYPP_PLUGIN_URL . 'assets/css/admin-style.css',
|
||||
array(),
|
||||
MYPP_VERSION
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'mypp-admin-script',
|
||||
MYPP_PLUGIN_URL . 'assets/js/admin-script.js',
|
||||
array( 'jquery' ),
|
||||
MYPP_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
// Localize script
|
||||
wp_localize_script(
|
||||
'mypp-admin-script',
|
||||
'myppData',
|
||||
array(
|
||||
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
||||
'nonce' => wp_create_nonce( 'mypp_ajax_nonce' ),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
130
templates/plugin-psr4/src/Plugin.php
Normal file
130
templates/plugin-psr4/src/Plugin.php
Normal file
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
/**
|
||||
* Main Plugin class
|
||||
*
|
||||
* @package MyPSR4Plugin
|
||||
*/
|
||||
|
||||
namespace MyPSR4Plugin;
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main Plugin class
|
||||
*/
|
||||
class Plugin {
|
||||
|
||||
/**
|
||||
* Single instance of the class
|
||||
*
|
||||
* @var Plugin
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* Get singleton instance
|
||||
*
|
||||
* @return Plugin
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( null === self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
private function __construct() {
|
||||
$this->init_hooks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent cloning
|
||||
*/
|
||||
private function __clone() {}
|
||||
|
||||
/**
|
||||
* Prevent unserializing
|
||||
*/
|
||||
public function __wakeup() {
|
||||
throw new \Exception( 'Cannot unserialize singleton' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize WordPress hooks
|
||||
*/
|
||||
private function init_hooks() {
|
||||
add_action( 'init', array( $this, 'init' ) );
|
||||
add_action( 'admin_menu', array( $this, 'register_admin_pages' ) );
|
||||
add_action( 'rest_api_init', array( $this, 'register_rest_routes' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize plugin
|
||||
*/
|
||||
public function init() {
|
||||
// Load text domain
|
||||
load_plugin_textdomain(
|
||||
'my-psr4-plugin',
|
||||
false,
|
||||
dirname( MYPP_PLUGIN_BASENAME ) . '/languages'
|
||||
);
|
||||
|
||||
// Initialize submodules
|
||||
PostTypes\Book::get_instance();
|
||||
Taxonomies\Genre::get_instance();
|
||||
Admin\Settings::get_instance();
|
||||
Frontend\Assets::get_instance();
|
||||
API\BookEndpoints::get_instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register admin pages
|
||||
*/
|
||||
public function register_admin_pages() {
|
||||
// Admin pages are handled by Admin\Settings class
|
||||
}
|
||||
|
||||
/**
|
||||
* Register REST routes
|
||||
*/
|
||||
public function register_rest_routes() {
|
||||
// REST routes are handled by API\BookEndpoints class
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin activation
|
||||
*/
|
||||
public function activate() {
|
||||
// Register post types and taxonomies
|
||||
PostTypes\Book::get_instance()->register();
|
||||
Taxonomies\Genre::get_instance()->register();
|
||||
|
||||
// Flush rewrite rules
|
||||
flush_rewrite_rules();
|
||||
|
||||
// Set default options
|
||||
Admin\Settings::get_instance()->set_defaults();
|
||||
|
||||
// Set activation timestamp
|
||||
if ( false === get_option( 'mypp_activated_time' ) ) {
|
||||
add_option( 'mypp_activated_time', current_time( 'timestamp' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin deactivation
|
||||
*/
|
||||
public function deactivate() {
|
||||
// Flush rewrite rules
|
||||
flush_rewrite_rules();
|
||||
|
||||
// Clear scheduled events
|
||||
wp_clear_scheduled_hook( 'mypp_cron_event' );
|
||||
}
|
||||
}
|
||||
215
templates/plugin-psr4/src/PostTypes/Book.php
Normal file
215
templates/plugin-psr4/src/PostTypes/Book.php
Normal file
@@ -0,0 +1,215 @@
|
||||
<?php
|
||||
/**
|
||||
* Book Custom Post Type
|
||||
*
|
||||
* @package MyPSR4Plugin\PostTypes
|
||||
*/
|
||||
|
||||
namespace MyPSR4Plugin\PostTypes;
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Book post type class
|
||||
*/
|
||||
class Book {
|
||||
|
||||
/**
|
||||
* Single instance
|
||||
*
|
||||
* @var Book
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* Post type slug
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const POST_TYPE = 'book';
|
||||
|
||||
/**
|
||||
* Get instance
|
||||
*
|
||||
* @return Book
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( null === self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
private function __construct() {
|
||||
add_action( 'init', array( $this, 'register' ) );
|
||||
add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) );
|
||||
add_action( 'save_post_' . self::POST_TYPE, array( $this, 'save_meta' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register post type
|
||||
*/
|
||||
public function register() {
|
||||
$labels = array(
|
||||
'name' => _x( 'Books', 'post type general name', 'my-psr4-plugin' ),
|
||||
'singular_name' => _x( 'Book', 'post type singular name', 'my-psr4-plugin' ),
|
||||
'menu_name' => _x( 'Books', 'admin menu', 'my-psr4-plugin' ),
|
||||
'add_new' => _x( 'Add New', 'book', 'my-psr4-plugin' ),
|
||||
'add_new_item' => __( 'Add New Book', 'my-psr4-plugin' ),
|
||||
'edit_item' => __( 'Edit Book', 'my-psr4-plugin' ),
|
||||
'new_item' => __( 'New Book', 'my-psr4-plugin' ),
|
||||
'view_item' => __( 'View Book', 'my-psr4-plugin' ),
|
||||
'search_items' => __( 'Search Books', 'my-psr4-plugin' ),
|
||||
'not_found' => __( 'No books found', 'my-psr4-plugin' ),
|
||||
'not_found_in_trash' => __( 'No books found in Trash', 'my-psr4-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,
|
||||
'supports' => array( 'title', 'editor', 'thumbnail', 'excerpt' ),
|
||||
);
|
||||
|
||||
register_post_type( self::POST_TYPE, $args );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add meta boxes
|
||||
*/
|
||||
public function add_meta_boxes() {
|
||||
add_meta_box(
|
||||
'mypp_book_details',
|
||||
__( 'Book Details', 'my-psr4-plugin' ),
|
||||
array( $this, 'render_meta_box' ),
|
||||
self::POST_TYPE,
|
||||
'normal',
|
||||
'high'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render meta box
|
||||
*
|
||||
* @param \WP_Post $post Post object.
|
||||
*/
|
||||
public function render_meta_box( $post ) {
|
||||
// Add nonce for security
|
||||
wp_nonce_field( 'mypp_save_book_meta', 'mypp_book_meta_nonce' );
|
||||
|
||||
// Get current values
|
||||
$isbn = get_post_meta( $post->ID, '_mypp_isbn', true );
|
||||
$author = get_post_meta( $post->ID, '_mypp_author', true );
|
||||
$year = get_post_meta( $post->ID, '_mypp_year', true );
|
||||
|
||||
?>
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th><label for="mypp_isbn"><?php esc_html_e( 'ISBN', 'my-psr4-plugin' ); ?></label></th>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
id="mypp_isbn"
|
||||
name="mypp_isbn"
|
||||
value="<?php echo esc_attr( $isbn ); ?>"
|
||||
class="regular-text"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><label for="mypp_author"><?php esc_html_e( 'Author', 'my-psr4-plugin' ); ?></label></th>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
id="mypp_author"
|
||||
name="mypp_author"
|
||||
value="<?php echo esc_attr( $author ); ?>"
|
||||
class="regular-text"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><label for="mypp_year"><?php esc_html_e( 'Publication Year', 'my-psr4-plugin' ); ?></label></th>
|
||||
<td>
|
||||
<input
|
||||
type="number"
|
||||
id="mypp_year"
|
||||
name="mypp_year"
|
||||
value="<?php echo esc_attr( $year ); ?>"
|
||||
min="1000"
|
||||
max="9999"
|
||||
class="small-text"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Save meta box data
|
||||
*
|
||||
* @param int $post_id Post ID.
|
||||
* @param \WP_Post $post Post object.
|
||||
*/
|
||||
public function save_meta( $post_id, $post ) {
|
||||
// Verify nonce
|
||||
if ( ! isset( $_POST['mypp_book_meta_nonce'] ) ||
|
||||
! wp_verify_nonce( $_POST['mypp_book_meta_nonce'], 'mypp_save_book_meta' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check autosave
|
||||
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check permissions
|
||||
if ( ! current_user_can( 'edit_post', $post_id ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save ISBN
|
||||
if ( isset( $_POST['mypp_isbn'] ) ) {
|
||||
update_post_meta(
|
||||
$post_id,
|
||||
'_mypp_isbn',
|
||||
sanitize_text_field( $_POST['mypp_isbn'] )
|
||||
);
|
||||
}
|
||||
|
||||
// Save Author
|
||||
if ( isset( $_POST['mypp_author'] ) ) {
|
||||
update_post_meta(
|
||||
$post_id,
|
||||
'_mypp_author',
|
||||
sanitize_text_field( $_POST['mypp_author'] )
|
||||
);
|
||||
}
|
||||
|
||||
// Save Year
|
||||
if ( isset( $_POST['mypp_year'] ) ) {
|
||||
update_post_meta(
|
||||
$post_id,
|
||||
'_mypp_year',
|
||||
absint( $_POST['mypp_year'] )
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
85
templates/plugin-psr4/src/Taxonomies/Genre.php
Normal file
85
templates/plugin-psr4/src/Taxonomies/Genre.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/**
|
||||
* Genre Taxonomy
|
||||
*
|
||||
* @package MyPSR4Plugin\Taxonomies
|
||||
*/
|
||||
|
||||
namespace MyPSR4Plugin\Taxonomies;
|
||||
|
||||
use MyPSR4Plugin\PostTypes\Book;
|
||||
|
||||
// Exit if accessed directly
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Genre taxonomy class
|
||||
*/
|
||||
class Genre {
|
||||
|
||||
/**
|
||||
* Single instance
|
||||
*
|
||||
* @var Genre
|
||||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* Taxonomy slug
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TAXONOMY = 'genre';
|
||||
|
||||
/**
|
||||
* Get instance
|
||||
*
|
||||
* @return Genre
|
||||
*/
|
||||
public static function get_instance() {
|
||||
if ( null === self::$instance ) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
private function __construct() {
|
||||
add_action( 'init', array( $this, 'register' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register taxonomy
|
||||
*/
|
||||
public function register() {
|
||||
$labels = array(
|
||||
'name' => _x( 'Genres', 'taxonomy general name', 'my-psr4-plugin' ),
|
||||
'singular_name' => _x( 'Genre', 'taxonomy singular name', 'my-psr4-plugin' ),
|
||||
'search_items' => __( 'Search Genres', 'my-psr4-plugin' ),
|
||||
'all_items' => __( 'All Genres', 'my-psr4-plugin' ),
|
||||
'parent_item' => __( 'Parent Genre', 'my-psr4-plugin' ),
|
||||
'parent_item_colon' => __( 'Parent Genre:', 'my-psr4-plugin' ),
|
||||
'edit_item' => __( 'Edit Genre', 'my-psr4-plugin' ),
|
||||
'update_item' => __( 'Update Genre', 'my-psr4-plugin' ),
|
||||
'add_new_item' => __( 'Add New Genre', 'my-psr4-plugin' ),
|
||||
'new_item_name' => __( 'New Genre Name', 'my-psr4-plugin' ),
|
||||
'menu_name' => __( 'Genres', 'my-psr4-plugin' ),
|
||||
);
|
||||
|
||||
$args = array(
|
||||
'hierarchical' => true,
|
||||
'labels' => $labels,
|
||||
'show_ui' => true,
|
||||
'show_admin_column' => true,
|
||||
'query_var' => true,
|
||||
'rewrite' => array( 'slug' => 'genre' ),
|
||||
'show_in_rest' => true,
|
||||
);
|
||||
|
||||
register_taxonomy( self::TAXONOMY, array( Book::POST_TYPE ), $args );
|
||||
}
|
||||
}
|
||||
47
templates/plugin-psr4/uninstall.php
Normal file
47
templates/plugin-psr4/uninstall.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/**
|
||||
* Uninstall script
|
||||
*
|
||||
* This file is called when the plugin is uninstalled via WordPress admin.
|
||||
*/
|
||||
|
||||
// Exit if not called by WordPress
|
||||
if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Delete plugin options
|
||||
delete_option( 'mypp_settings' );
|
||||
delete_option( 'mypp_activated_time' );
|
||||
|
||||
// Delete transients
|
||||
delete_transient( 'mypp_cache' );
|
||||
|
||||
// For multisite
|
||||
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( 'mypp_settings' );
|
||||
delete_option( 'mypp_activated_time' );
|
||||
delete_transient( 'mypp_cache' );
|
||||
|
||||
restore_current_blog();
|
||||
}
|
||||
}
|
||||
|
||||
// Delete custom post type data (optional)
|
||||
/*
|
||||
$books = get_posts( array(
|
||||
'post_type' => 'book',
|
||||
'posts_per_page' => -1,
|
||||
'post_status' => 'any',
|
||||
) );
|
||||
|
||||
foreach ( $books as $book ) {
|
||||
wp_delete_post( $book->ID, true );
|
||||
}
|
||||
*/
|
||||
Reference in New Issue
Block a user