@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,1083 @@
|
|
|
1
|
+
# Custom Post Type Example
|
|
2
|
+
|
|
3
|
+
This example demonstrates creating a custom post type with taxonomy, meta boxes, and frontend display in a WordPress plugin.
|
|
4
|
+
|
|
5
|
+
## Scenario
|
|
6
|
+
|
|
7
|
+
Creating a "Book Review" custom post type for a WordPress plugin that allows users to manage and display book reviews.
|
|
8
|
+
|
|
9
|
+
## Plugin Context
|
|
10
|
+
|
|
11
|
+
**Plugin**: Book Reviews
|
|
12
|
+
**Feature**: Custom post type for book reviews with rating, author, and genre
|
|
13
|
+
**Custom Post Type**: `book_review`
|
|
14
|
+
**Custom Taxonomy**: `book_genre`
|
|
15
|
+
|
|
16
|
+
## Complete Implementation
|
|
17
|
+
|
|
18
|
+
### Main Plugin File
|
|
19
|
+
|
|
20
|
+
**File**: `book-reviews.php`
|
|
21
|
+
|
|
22
|
+
```php
|
|
23
|
+
<?php
|
|
24
|
+
/**
|
|
25
|
+
* Plugin Name: Book Reviews
|
|
26
|
+
* Plugin URI: https://example.com/book-reviews
|
|
27
|
+
* Description: Manage and display book reviews with ratings and genres
|
|
28
|
+
* Version: 1.0.0
|
|
29
|
+
* Author: Your Name
|
|
30
|
+
* Author URI: https://example.com
|
|
31
|
+
* License: GPL-2.0+
|
|
32
|
+
* License URI: http://www.gnu.org/licenses/gpl-2.0.txt
|
|
33
|
+
* Text Domain: book-reviews
|
|
34
|
+
* Domain Path: /languages
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
// If this file is called directly, abort.
|
|
38
|
+
if (!defined('WPINC')) {
|
|
39
|
+
die;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
define('BOOK_REVIEWS_VERSION', '1.0.0');
|
|
43
|
+
define('BOOK_REVIEWS_PLUGIN_DIR', plugin_dir_path(__FILE__));
|
|
44
|
+
define('BOOK_REVIEWS_PLUGIN_URL', plugin_dir_url(__FILE__));
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The code that runs during plugin activation.
|
|
48
|
+
*/
|
|
49
|
+
function activate_book_reviews() {
|
|
50
|
+
require_once BOOK_REVIEWS_PLUGIN_DIR . 'includes/class-book-reviews-activator.php';
|
|
51
|
+
Book_Reviews_Activator::activate();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The code that runs during plugin deactivation.
|
|
56
|
+
*/
|
|
57
|
+
function deactivate_book_reviews() {
|
|
58
|
+
require_once BOOK_REVIEWS_PLUGIN_DIR . 'includes/class-book-reviews-deactivator.php';
|
|
59
|
+
Book_Reviews_Deactivator::deactivate();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
register_activation_hook(__FILE__, 'activate_book_reviews');
|
|
63
|
+
register_deactivation_hook(__FILE__, 'deactivate_book_reviews');
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The core plugin class.
|
|
67
|
+
*/
|
|
68
|
+
require BOOK_REVIEWS_PLUGIN_DIR . 'includes/class-book-reviews.php';
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Begins execution of the plugin.
|
|
72
|
+
*/
|
|
73
|
+
function run_book_reviews() {
|
|
74
|
+
$plugin = new Book_Reviews();
|
|
75
|
+
$plugin->run();
|
|
76
|
+
}
|
|
77
|
+
run_book_reviews();
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Activator Class
|
|
81
|
+
|
|
82
|
+
**File**: `includes/class-book-reviews-activator.php`
|
|
83
|
+
|
|
84
|
+
```php
|
|
85
|
+
<?php
|
|
86
|
+
/**
|
|
87
|
+
* Fired during plugin activation.
|
|
88
|
+
*/
|
|
89
|
+
class Book_Reviews_Activator {
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Activate the plugin.
|
|
93
|
+
*/
|
|
94
|
+
public static function activate() {
|
|
95
|
+
// Register post type and taxonomy
|
|
96
|
+
self::register_post_type();
|
|
97
|
+
self::register_taxonomy();
|
|
98
|
+
|
|
99
|
+
// Flush rewrite rules
|
|
100
|
+
flush_rewrite_rules();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Register the custom post type.
|
|
105
|
+
*/
|
|
106
|
+
private static function register_post_type() {
|
|
107
|
+
require_once BOOK_REVIEWS_PLUGIN_DIR . 'includes/class-book-review-post-type.php';
|
|
108
|
+
$post_type = new Book_Review_Post_Type();
|
|
109
|
+
$post_type->register();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Register the custom taxonomy.
|
|
114
|
+
*/
|
|
115
|
+
private static function register_taxonomy() {
|
|
116
|
+
require_once BOOK_REVIEWS_PLUGIN_DIR . 'includes/class-book-genre-taxonomy.php';
|
|
117
|
+
$taxonomy = new Book_Genre_Taxonomy();
|
|
118
|
+
$taxonomy->register();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Deactivator Class
|
|
124
|
+
|
|
125
|
+
**File**: `includes/class-book-reviews-deactivator.php`
|
|
126
|
+
|
|
127
|
+
```php
|
|
128
|
+
<?php
|
|
129
|
+
/**
|
|
130
|
+
* Fired during plugin deactivation.
|
|
131
|
+
*/
|
|
132
|
+
class Book_Reviews_Deactivator {
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Deactivate the plugin.
|
|
136
|
+
*/
|
|
137
|
+
public static function deactivate() {
|
|
138
|
+
// Flush rewrite rules
|
|
139
|
+
flush_rewrite_rules();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Core Plugin Class
|
|
145
|
+
|
|
146
|
+
**File**: `includes/class-book-reviews.php`
|
|
147
|
+
|
|
148
|
+
```php
|
|
149
|
+
<?php
|
|
150
|
+
/**
|
|
151
|
+
* The core plugin class.
|
|
152
|
+
*/
|
|
153
|
+
class Book_Reviews {
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* The loader that's responsible for maintaining and registering all hooks.
|
|
157
|
+
*/
|
|
158
|
+
protected $loader;
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* The unique identifier of this plugin.
|
|
162
|
+
*/
|
|
163
|
+
protected $plugin_name;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* The current version of the plugin.
|
|
167
|
+
*/
|
|
168
|
+
protected $version;
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Define the core functionality of the plugin.
|
|
172
|
+
*/
|
|
173
|
+
public function __construct() {
|
|
174
|
+
$this->version = BOOK_REVIEWS_VERSION;
|
|
175
|
+
$this->plugin_name = 'book-reviews';
|
|
176
|
+
|
|
177
|
+
$this->load_dependencies();
|
|
178
|
+
$this->define_admin_hooks();
|
|
179
|
+
$this->define_public_hooks();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Load the required dependencies for this plugin.
|
|
184
|
+
*/
|
|
185
|
+
private function load_dependencies() {
|
|
186
|
+
require_once BOOK_REVIEWS_PLUGIN_DIR . 'includes/class-book-reviews-loader.php';
|
|
187
|
+
require_once BOOK_REVIEWS_PLUGIN_DIR . 'includes/class-book-review-post-type.php';
|
|
188
|
+
require_once BOOK_REVIEWS_PLUGIN_DIR . 'includes/class-book-genre-taxonomy.php';
|
|
189
|
+
require_once BOOK_REVIEWS_PLUGIN_DIR . 'admin/class-book-reviews-admin.php';
|
|
190
|
+
require_once BOOK_REVIEWS_PLUGIN_DIR . 'public/class-book-reviews-public.php';
|
|
191
|
+
|
|
192
|
+
$this->loader = new Book_Reviews_Loader();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Register all of the hooks related to the admin area functionality.
|
|
197
|
+
*/
|
|
198
|
+
private function define_admin_hooks() {
|
|
199
|
+
$admin = new Book_Reviews_Admin($this->get_plugin_name(), $this->get_version());
|
|
200
|
+
|
|
201
|
+
$this->loader->add_action('admin_enqueue_scripts', $admin, 'enqueue_styles');
|
|
202
|
+
$this->loader->add_action('admin_enqueue_scripts', $admin, 'enqueue_scripts');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Register all of the hooks related to the public-facing functionality.
|
|
207
|
+
*/
|
|
208
|
+
private function define_public_hooks() {
|
|
209
|
+
$public = new Book_Reviews_Public($this->get_plugin_name(), $this->get_version());
|
|
210
|
+
|
|
211
|
+
$this->loader->add_action('wp_enqueue_scripts', $public, 'enqueue_styles');
|
|
212
|
+
$this->loader->add_action('wp_enqueue_scripts', $public, 'enqueue_scripts');
|
|
213
|
+
|
|
214
|
+
// Register post type and taxonomy
|
|
215
|
+
$post_type = new Book_Review_Post_Type();
|
|
216
|
+
$this->loader->add_action('init', $post_type, 'register');
|
|
217
|
+
|
|
218
|
+
$taxonomy = new Book_Genre_Taxonomy();
|
|
219
|
+
$this->loader->add_action('init', $taxonomy, 'register');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Run the loader to execute all of the hooks with WordPress.
|
|
224
|
+
*/
|
|
225
|
+
public function run() {
|
|
226
|
+
$this->loader->run();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* The name of the plugin used to uniquely identify it.
|
|
231
|
+
*/
|
|
232
|
+
public function get_plugin_name() {
|
|
233
|
+
return $this->plugin_name;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Retrieve the version number of the plugin.
|
|
238
|
+
*/
|
|
239
|
+
public function get_version() {
|
|
240
|
+
return $this->version;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Custom Post Type Class
|
|
246
|
+
|
|
247
|
+
**File**: `includes/class-book-review-post-type.php`
|
|
248
|
+
|
|
249
|
+
```php
|
|
250
|
+
<?php
|
|
251
|
+
/**
|
|
252
|
+
* Register the Book Review custom post type.
|
|
253
|
+
*/
|
|
254
|
+
class Book_Review_Post_Type {
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Register the custom post type.
|
|
258
|
+
*/
|
|
259
|
+
public function register() {
|
|
260
|
+
$labels = array(
|
|
261
|
+
'name' => _x('Book Reviews', 'Post Type General Name', 'book-reviews'),
|
|
262
|
+
'singular_name' => _x('Book Review', 'Post Type Singular Name', 'book-reviews'),
|
|
263
|
+
'menu_name' => __('Book Reviews', 'book-reviews'),
|
|
264
|
+
'name_admin_bar' => __('Book Review', 'book-reviews'),
|
|
265
|
+
'archives' => __('Book Review Archives', 'book-reviews'),
|
|
266
|
+
'attributes' => __('Book Review Attributes', 'book-reviews'),
|
|
267
|
+
'parent_item_colon' => __('Parent Book Review:', 'book-reviews'),
|
|
268
|
+
'all_items' => __('All Book Reviews', 'book-reviews'),
|
|
269
|
+
'add_new_item' => __('Add New Book Review', 'book-reviews'),
|
|
270
|
+
'add_new' => __('Add New', 'book-reviews'),
|
|
271
|
+
'new_item' => __('New Book Review', 'book-reviews'),
|
|
272
|
+
'edit_item' => __('Edit Book Review', 'book-reviews'),
|
|
273
|
+
'update_item' => __('Update Book Review', 'book-reviews'),
|
|
274
|
+
'view_item' => __('View Book Review', 'book-reviews'),
|
|
275
|
+
'view_items' => __('View Book Reviews', 'book-reviews'),
|
|
276
|
+
'search_items' => __('Search Book Review', 'book-reviews'),
|
|
277
|
+
'not_found' => __('Not found', 'book-reviews'),
|
|
278
|
+
'not_found_in_trash' => __('Not found in Trash', 'book-reviews'),
|
|
279
|
+
'featured_image' => __('Book Cover Image', 'book-reviews'),
|
|
280
|
+
'set_featured_image' => __('Set book cover image', 'book-reviews'),
|
|
281
|
+
'remove_featured_image' => __('Remove book cover image', 'book-reviews'),
|
|
282
|
+
'use_featured_image' => __('Use as book cover image', 'book-reviews'),
|
|
283
|
+
'insert_into_item' => __('Insert into book review', 'book-reviews'),
|
|
284
|
+
'uploaded_to_this_item' => __('Uploaded to this book review', 'book-reviews'),
|
|
285
|
+
'items_list' => __('Book Reviews list', 'book-reviews'),
|
|
286
|
+
'items_list_navigation' => __('Book Reviews list navigation', 'book-reviews'),
|
|
287
|
+
'filter_items_list' => __('Filter book reviews list', 'book-reviews'),
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
$args = array(
|
|
291
|
+
'label' => __('Book Review', 'book-reviews'),
|
|
292
|
+
'description' => __('Book reviews with ratings and metadata', 'book-reviews'),
|
|
293
|
+
'labels' => $labels,
|
|
294
|
+
'supports' => array('title', 'editor', 'thumbnail', 'excerpt', 'comments'),
|
|
295
|
+
'taxonomies' => array('book_genre'),
|
|
296
|
+
'hierarchical' => false,
|
|
297
|
+
'public' => true,
|
|
298
|
+
'show_ui' => true,
|
|
299
|
+
'show_in_menu' => true,
|
|
300
|
+
'menu_position' => 5,
|
|
301
|
+
'menu_icon' => 'dashicons-book',
|
|
302
|
+
'show_in_admin_bar' => true,
|
|
303
|
+
'show_in_nav_menus' => true,
|
|
304
|
+
'can_export' => true,
|
|
305
|
+
'has_archive' => true,
|
|
306
|
+
'exclude_from_search' => false,
|
|
307
|
+
'publicly_queryable' => true,
|
|
308
|
+
'capability_type' => 'post',
|
|
309
|
+
'show_in_rest' => true,
|
|
310
|
+
'rewrite' => array('slug' => 'book-reviews'),
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
register_post_type('book_review', $args);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Custom Taxonomy Class
|
|
319
|
+
|
|
320
|
+
**File**: `includes/class-book-genre-taxonomy.php`
|
|
321
|
+
|
|
322
|
+
```php
|
|
323
|
+
<?php
|
|
324
|
+
/**
|
|
325
|
+
* Register the Book Genre custom taxonomy.
|
|
326
|
+
*/
|
|
327
|
+
class Book_Genre_Taxonomy {
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Register the custom taxonomy.
|
|
331
|
+
*/
|
|
332
|
+
public function register() {
|
|
333
|
+
$labels = array(
|
|
334
|
+
'name' => _x('Book Genres', 'Taxonomy General Name', 'book-reviews'),
|
|
335
|
+
'singular_name' => _x('Book Genre', 'Taxonomy Singular Name', 'book-reviews'),
|
|
336
|
+
'menu_name' => __('Book Genres', 'book-reviews'),
|
|
337
|
+
'all_items' => __('All Book Genres', 'book-reviews'),
|
|
338
|
+
'parent_item' => __('Parent Book Genre', 'book-reviews'),
|
|
339
|
+
'parent_item_colon' => __('Parent Book Genre:', 'book-reviews'),
|
|
340
|
+
'new_item_name' => __('New Book Genre Name', 'book-reviews'),
|
|
341
|
+
'add_new_item' => __('Add New Book Genre', 'book-reviews'),
|
|
342
|
+
'edit_item' => __('Edit Book Genre', 'book-reviews'),
|
|
343
|
+
'update_item' => __('Update Book Genre', 'book-reviews'),
|
|
344
|
+
'view_item' => __('View Book Genre', 'book-reviews'),
|
|
345
|
+
'separate_items_with_commas' => __('Separate genres with commas', 'book-reviews'),
|
|
346
|
+
'add_or_remove_items' => __('Add or remove genres', 'book-reviews'),
|
|
347
|
+
'choose_from_most_used' => __('Choose from the most used', 'book-reviews'),
|
|
348
|
+
'popular_items' => __('Popular Genres', 'book-reviews'),
|
|
349
|
+
'search_items' => __('Search Genres', 'book-reviews'),
|
|
350
|
+
'not_found' => __('Not Found', 'book-reviews'),
|
|
351
|
+
'no_terms' => __('No genres', 'book-reviews'),
|
|
352
|
+
'items_list' => __('Genres list', 'book-reviews'),
|
|
353
|
+
'items_list_navigation' => __('Genres list navigation', 'book-reviews'),
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
$args = array(
|
|
357
|
+
'labels' => $labels,
|
|
358
|
+
'hierarchical' => true,
|
|
359
|
+
'public' => true,
|
|
360
|
+
'show_ui' => true,
|
|
361
|
+
'show_admin_column' => true,
|
|
362
|
+
'show_in_nav_menus' => true,
|
|
363
|
+
'show_tagcloud' => true,
|
|
364
|
+
'show_in_rest' => true,
|
|
365
|
+
'rewrite' => array('slug' => 'book-genre'),
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
register_taxonomy('book_genre', array('book_review'), $args);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Admin Class with Meta Boxes
|
|
374
|
+
|
|
375
|
+
**File**: `admin/class-book-reviews-admin.php`
|
|
376
|
+
|
|
377
|
+
```php
|
|
378
|
+
<?php
|
|
379
|
+
/**
|
|
380
|
+
* The admin-specific functionality of the plugin.
|
|
381
|
+
*/
|
|
382
|
+
class Book_Reviews_Admin {
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* The ID of this plugin.
|
|
386
|
+
*/
|
|
387
|
+
private $plugin_name;
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* The version of this plugin.
|
|
391
|
+
*/
|
|
392
|
+
private $version;
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Initialize the class and set its properties.
|
|
396
|
+
*/
|
|
397
|
+
public function __construct($plugin_name, $version) {
|
|
398
|
+
$this->plugin_name = $plugin_name;
|
|
399
|
+
$this->version = $version;
|
|
400
|
+
|
|
401
|
+
add_action('add_meta_boxes', array($this, 'add_meta_boxes'));
|
|
402
|
+
add_action('save_post_book_review', array($this, 'save_meta_boxes'), 10, 2);
|
|
403
|
+
add_filter('manage_book_review_posts_columns', array($this, 'add_custom_columns'));
|
|
404
|
+
add_action('manage_book_review_posts_custom_column', array($this, 'render_custom_columns'), 10, 2);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Register the stylesheets for the admin area.
|
|
409
|
+
*/
|
|
410
|
+
public function enqueue_styles() {
|
|
411
|
+
wp_enqueue_style(
|
|
412
|
+
$this->plugin_name,
|
|
413
|
+
BOOK_REVIEWS_PLUGIN_URL . 'admin/css/book-reviews-admin.css',
|
|
414
|
+
array(),
|
|
415
|
+
$this->version,
|
|
416
|
+
'all'
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Register the JavaScript for the admin area.
|
|
422
|
+
*/
|
|
423
|
+
public function enqueue_scripts() {
|
|
424
|
+
wp_enqueue_script(
|
|
425
|
+
$this->plugin_name,
|
|
426
|
+
BOOK_REVIEWS_PLUGIN_URL . 'admin/js/book-reviews-admin.js',
|
|
427
|
+
array('jquery'),
|
|
428
|
+
$this->version,
|
|
429
|
+
false
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Add meta boxes for book review details.
|
|
435
|
+
*/
|
|
436
|
+
public function add_meta_boxes() {
|
|
437
|
+
add_meta_box(
|
|
438
|
+
'book_review_details',
|
|
439
|
+
__('Book Details', 'book-reviews'),
|
|
440
|
+
array($this, 'render_book_details_meta_box'),
|
|
441
|
+
'book_review',
|
|
442
|
+
'normal',
|
|
443
|
+
'high'
|
|
444
|
+
);
|
|
445
|
+
|
|
446
|
+
add_meta_box(
|
|
447
|
+
'book_review_rating',
|
|
448
|
+
__('Book Rating', 'book-reviews'),
|
|
449
|
+
array($this, 'render_book_rating_meta_box'),
|
|
450
|
+
'book_review',
|
|
451
|
+
'side',
|
|
452
|
+
'default'
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Render the book details meta box.
|
|
458
|
+
*/
|
|
459
|
+
public function render_book_details_meta_box($post) {
|
|
460
|
+
// Add nonce for security
|
|
461
|
+
wp_nonce_field('book_review_details_nonce', 'book_review_details_nonce_field');
|
|
462
|
+
|
|
463
|
+
// Get current values
|
|
464
|
+
$author = get_post_meta($post->ID, '_book_author', true);
|
|
465
|
+
$isbn = get_post_meta($post->ID, '_book_isbn', true);
|
|
466
|
+
$publisher = get_post_meta($post->ID, '_book_publisher', true);
|
|
467
|
+
$publication_date = get_post_meta($post->ID, '_book_publication_date', true);
|
|
468
|
+
|
|
469
|
+
?>
|
|
470
|
+
<table class="form-table">
|
|
471
|
+
<tr>
|
|
472
|
+
<th><label for="book_author"><?php _e('Author', 'book-reviews'); ?></label></th>
|
|
473
|
+
<td>
|
|
474
|
+
<input type="text"
|
|
475
|
+
id="book_author"
|
|
476
|
+
name="book_author"
|
|
477
|
+
value="<?php echo esc_attr($author); ?>"
|
|
478
|
+
class="regular-text" />
|
|
479
|
+
</td>
|
|
480
|
+
</tr>
|
|
481
|
+
<tr>
|
|
482
|
+
<th><label for="book_isbn"><?php _e('ISBN', 'book-reviews'); ?></label></th>
|
|
483
|
+
<td>
|
|
484
|
+
<input type="text"
|
|
485
|
+
id="book_isbn"
|
|
486
|
+
name="book_isbn"
|
|
487
|
+
value="<?php echo esc_attr($isbn); ?>"
|
|
488
|
+
class="regular-text" />
|
|
489
|
+
</td>
|
|
490
|
+
</tr>
|
|
491
|
+
<tr>
|
|
492
|
+
<th><label for="book_publisher"><?php _e('Publisher', 'book-reviews'); ?></label></th>
|
|
493
|
+
<td>
|
|
494
|
+
<input type="text"
|
|
495
|
+
id="book_publisher"
|
|
496
|
+
name="book_publisher"
|
|
497
|
+
value="<?php echo esc_attr($publisher); ?>"
|
|
498
|
+
class="regular-text" />
|
|
499
|
+
</td>
|
|
500
|
+
</tr>
|
|
501
|
+
<tr>
|
|
502
|
+
<th><label for="book_publication_date"><?php _e('Publication Date', 'book-reviews'); ?></label></th>
|
|
503
|
+
<td>
|
|
504
|
+
<input type="date"
|
|
505
|
+
id="book_publication_date"
|
|
506
|
+
name="book_publication_date"
|
|
507
|
+
value="<?php echo esc_attr($publication_date); ?>" />
|
|
508
|
+
</td>
|
|
509
|
+
</tr>
|
|
510
|
+
</table>
|
|
511
|
+
<?php
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Render the book rating meta box.
|
|
516
|
+
*/
|
|
517
|
+
public function render_book_rating_meta_box($post) {
|
|
518
|
+
// Add nonce for security
|
|
519
|
+
wp_nonce_field('book_review_rating_nonce', 'book_review_rating_nonce_field');
|
|
520
|
+
|
|
521
|
+
// Get current value
|
|
522
|
+
$rating = get_post_meta($post->ID, '_book_rating', true);
|
|
523
|
+
|
|
524
|
+
?>
|
|
525
|
+
<p>
|
|
526
|
+
<label for="book_rating"><?php _e('Rating (1-5 stars)', 'book-reviews'); ?></label>
|
|
527
|
+
<select id="book_rating" name="book_rating">
|
|
528
|
+
<option value=""><?php _e('Select rating', 'book-reviews'); ?></option>
|
|
529
|
+
<?php for ($i = 1; $i <= 5; $i++) : ?>
|
|
530
|
+
<option value="<?php echo $i; ?>" <?php selected($rating, $i); ?>>
|
|
531
|
+
<?php echo str_repeat('★', $i); ?>
|
|
532
|
+
</option>
|
|
533
|
+
<?php endfor; ?>
|
|
534
|
+
</select>
|
|
535
|
+
</p>
|
|
536
|
+
<?php
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Save meta box data.
|
|
541
|
+
*/
|
|
542
|
+
public function save_meta_boxes($post_id, $post) {
|
|
543
|
+
// Check if nonce is set
|
|
544
|
+
if (!isset($_POST['book_review_details_nonce_field']) ||
|
|
545
|
+
!isset($_POST['book_review_rating_nonce_field'])) {
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// Verify nonces
|
|
550
|
+
if (!wp_verify_nonce($_POST['book_review_details_nonce_field'], 'book_review_details_nonce') ||
|
|
551
|
+
!wp_verify_nonce($_POST['book_review_rating_nonce_field'], 'book_review_rating_nonce')) {
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// Check if this is an autosave
|
|
556
|
+
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// Check user permissions
|
|
561
|
+
if (!current_user_can('edit_post', $post_id)) {
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// Save book details
|
|
566
|
+
if (isset($_POST['book_author'])) {
|
|
567
|
+
update_post_meta($post_id, '_book_author', sanitize_text_field($_POST['book_author']));
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
if (isset($_POST['book_isbn'])) {
|
|
571
|
+
update_post_meta($post_id, '_book_isbn', sanitize_text_field($_POST['book_isbn']));
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
if (isset($_POST['book_publisher'])) {
|
|
575
|
+
update_post_meta($post_id, '_book_publisher', sanitize_text_field($_POST['book_publisher']));
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
if (isset($_POST['book_publication_date'])) {
|
|
579
|
+
update_post_meta($post_id, '_book_publication_date', sanitize_text_field($_POST['book_publication_date']));
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// Save rating
|
|
583
|
+
if (isset($_POST['book_rating'])) {
|
|
584
|
+
$rating = intval($_POST['book_rating']);
|
|
585
|
+
if ($rating >= 1 && $rating <= 5) {
|
|
586
|
+
update_post_meta($post_id, '_book_rating', $rating);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Add custom columns to the book review list.
|
|
593
|
+
*/
|
|
594
|
+
public function add_custom_columns($columns) {
|
|
595
|
+
$new_columns = array();
|
|
596
|
+
|
|
597
|
+
foreach ($columns as $key => $value) {
|
|
598
|
+
$new_columns[$key] = $value;
|
|
599
|
+
|
|
600
|
+
// Add custom columns after title
|
|
601
|
+
if ($key === 'title') {
|
|
602
|
+
$new_columns['book_author'] = __('Author', 'book-reviews');
|
|
603
|
+
$new_columns['book_rating'] = __('Rating', 'book-reviews');
|
|
604
|
+
$new_columns['book_genre'] = __('Genre', 'book-reviews');
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
return $new_columns;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Render custom column content.
|
|
613
|
+
*/
|
|
614
|
+
public function render_custom_columns($column, $post_id) {
|
|
615
|
+
switch ($column) {
|
|
616
|
+
case 'book_author':
|
|
617
|
+
$author = get_post_meta($post_id, '_book_author', true);
|
|
618
|
+
echo esc_html($author);
|
|
619
|
+
break;
|
|
620
|
+
|
|
621
|
+
case 'book_rating':
|
|
622
|
+
$rating = get_post_meta($post_id, '_book_rating', true);
|
|
623
|
+
if ($rating) {
|
|
624
|
+
echo str_repeat('★', intval($rating));
|
|
625
|
+
} else {
|
|
626
|
+
echo '—';
|
|
627
|
+
}
|
|
628
|
+
break;
|
|
629
|
+
|
|
630
|
+
case 'book_genre':
|
|
631
|
+
$terms = get_the_terms($post_id, 'book_genre');
|
|
632
|
+
if ($terms && !is_wp_error($terms)) {
|
|
633
|
+
$genre_names = array();
|
|
634
|
+
foreach ($terms as $term) {
|
|
635
|
+
$genre_names[] = esc_html($term->name);
|
|
636
|
+
}
|
|
637
|
+
echo implode(', ', $genre_names);
|
|
638
|
+
} else {
|
|
639
|
+
echo '—';
|
|
640
|
+
}
|
|
641
|
+
break;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
### Public Class with Shortcode
|
|
648
|
+
|
|
649
|
+
**File**: `public/class-book-reviews-public.php`
|
|
650
|
+
|
|
651
|
+
```php
|
|
652
|
+
<?php
|
|
653
|
+
/**
|
|
654
|
+
* The public-facing functionality of the plugin.
|
|
655
|
+
*/
|
|
656
|
+
class Book_Reviews_Public {
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* The ID of this plugin.
|
|
660
|
+
*/
|
|
661
|
+
private $plugin_name;
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* The version of this plugin.
|
|
665
|
+
*/
|
|
666
|
+
private $version;
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* Initialize the class and set its properties.
|
|
670
|
+
*/
|
|
671
|
+
public function __construct($plugin_name, $version) {
|
|
672
|
+
$this->plugin_name = $plugin_name;
|
|
673
|
+
$this->version = $version;
|
|
674
|
+
|
|
675
|
+
add_shortcode('book_reviews', array($this, 'book_reviews_shortcode'));
|
|
676
|
+
add_filter('the_content', array($this, 'add_rating_to_content'));
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* Register the stylesheets for the public-facing side of the site.
|
|
681
|
+
*/
|
|
682
|
+
public function enqueue_styles() {
|
|
683
|
+
wp_enqueue_style(
|
|
684
|
+
$this->plugin_name,
|
|
685
|
+
BOOK_REVIEWS_PLUGIN_URL . 'public/css/book-reviews-public.css',
|
|
686
|
+
array(),
|
|
687
|
+
$this->version,
|
|
688
|
+
'all'
|
|
689
|
+
);
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* Register the JavaScript for the public-facing side of the site.
|
|
694
|
+
*/
|
|
695
|
+
public function enqueue_scripts() {
|
|
696
|
+
wp_enqueue_script(
|
|
697
|
+
$this->plugin_name,
|
|
698
|
+
BOOK_REVIEWS_PLUGIN_URL . 'public/js/book-reviews-public.js',
|
|
699
|
+
array('jquery'),
|
|
700
|
+
$this->version,
|
|
701
|
+
false
|
|
702
|
+
);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
* Shortcode to display book reviews.
|
|
707
|
+
*
|
|
708
|
+
* Usage: [book_reviews genre="fiction" limit="5"]
|
|
709
|
+
*/
|
|
710
|
+
public function book_reviews_shortcode($atts) {
|
|
711
|
+
$atts = shortcode_atts(array(
|
|
712
|
+
'genre' => '',
|
|
713
|
+
'limit' => 10,
|
|
714
|
+
'orderby' => 'date',
|
|
715
|
+
'order' => 'DESC',
|
|
716
|
+
), $atts, 'book_reviews');
|
|
717
|
+
|
|
718
|
+
$args = array(
|
|
719
|
+
'post_type' => 'book_review',
|
|
720
|
+
'posts_per_page' => intval($atts['limit']),
|
|
721
|
+
'orderby' => sanitize_text_field($atts['orderby']),
|
|
722
|
+
'order' => sanitize_text_field($atts['order']),
|
|
723
|
+
);
|
|
724
|
+
|
|
725
|
+
// Add genre filter if specified
|
|
726
|
+
if (!empty($atts['genre'])) {
|
|
727
|
+
$args['tax_query'] = array(
|
|
728
|
+
array(
|
|
729
|
+
'taxonomy' => 'book_genre',
|
|
730
|
+
'field' => 'slug',
|
|
731
|
+
'terms' => sanitize_text_field($atts['genre']),
|
|
732
|
+
),
|
|
733
|
+
);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
$query = new WP_Query($args);
|
|
737
|
+
|
|
738
|
+
ob_start();
|
|
739
|
+
|
|
740
|
+
if ($query->have_posts()) {
|
|
741
|
+
echo '<div class="book-reviews-grid">';
|
|
742
|
+
|
|
743
|
+
while ($query->have_posts()) {
|
|
744
|
+
$query->the_post();
|
|
745
|
+
$this->render_book_review_card(get_the_ID());
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
echo '</div>';
|
|
749
|
+
} else {
|
|
750
|
+
echo '<p>' . esc_html__('No book reviews found.', 'book-reviews') . '</p>';
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
wp_reset_postdata();
|
|
754
|
+
|
|
755
|
+
return ob_get_clean();
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
/**
|
|
759
|
+
* Render a single book review card.
|
|
760
|
+
*/
|
|
761
|
+
private function render_book_review_card($post_id) {
|
|
762
|
+
$author = get_post_meta($post_id, '_book_author', true);
|
|
763
|
+
$rating = get_post_meta($post_id, '_book_rating', true);
|
|
764
|
+
$genres = get_the_terms($post_id, 'book_genre');
|
|
765
|
+
|
|
766
|
+
?>
|
|
767
|
+
<div class="book-review-card">
|
|
768
|
+
<?php if (has_post_thumbnail($post_id)) : ?>
|
|
769
|
+
<div class="book-cover">
|
|
770
|
+
<?php echo get_the_post_thumbnail($post_id, 'medium'); ?>
|
|
771
|
+
</div>
|
|
772
|
+
<?php endif; ?>
|
|
773
|
+
|
|
774
|
+
<div class="book-info">
|
|
775
|
+
<h3 class="book-title">
|
|
776
|
+
<a href="<?php echo esc_url(get_permalink($post_id)); ?>">
|
|
777
|
+
<?php echo esc_html(get_the_title($post_id)); ?>
|
|
778
|
+
</a>
|
|
779
|
+
</h3>
|
|
780
|
+
|
|
781
|
+
<?php if ($author) : ?>
|
|
782
|
+
<p class="book-author">
|
|
783
|
+
<?php _e('by', 'book-reviews'); ?>
|
|
784
|
+
<strong><?php echo esc_html($author); ?></strong>
|
|
785
|
+
</p>
|
|
786
|
+
<?php endif; ?>
|
|
787
|
+
|
|
788
|
+
<?php if ($rating) : ?>
|
|
789
|
+
<div class="book-rating">
|
|
790
|
+
<?php echo str_repeat('★', intval($rating)); ?>
|
|
791
|
+
<?php echo str_repeat('☆', 5 - intval($rating)); ?>
|
|
792
|
+
</div>
|
|
793
|
+
<?php endif; ?>
|
|
794
|
+
|
|
795
|
+
<?php if ($genres && !is_wp_error($genres)) : ?>
|
|
796
|
+
<div class="book-genres">
|
|
797
|
+
<?php foreach ($genres as $genre) : ?>
|
|
798
|
+
<span class="genre-tag"><?php echo esc_html($genre->name); ?></span>
|
|
799
|
+
<?php endforeach; ?>
|
|
800
|
+
</div>
|
|
801
|
+
<?php endif; ?>
|
|
802
|
+
|
|
803
|
+
<div class="book-excerpt">
|
|
804
|
+
<?php echo wp_trim_words(get_the_excerpt($post_id), 20); ?>
|
|
805
|
+
</div>
|
|
806
|
+
|
|
807
|
+
<a href="<?php echo esc_url(get_permalink($post_id)); ?>" class="read-more">
|
|
808
|
+
<?php _e('Read Review', 'book-reviews'); ?>
|
|
809
|
+
</a>
|
|
810
|
+
</div>
|
|
811
|
+
</div>
|
|
812
|
+
<?php
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
/**
|
|
816
|
+
* Add rating to single book review content.
|
|
817
|
+
*/
|
|
818
|
+
public function add_rating_to_content($content) {
|
|
819
|
+
if (!is_singular('book_review')) {
|
|
820
|
+
return $content;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
$post_id = get_the_ID();
|
|
824
|
+
$author = get_post_meta($post_id, '_book_author', true);
|
|
825
|
+
$rating = get_post_meta($post_id, '_book_rating', true);
|
|
826
|
+
$isbn = get_post_meta($post_id, '_book_isbn', true);
|
|
827
|
+
$publisher = get_post_meta($post_id, '_book_publisher', true);
|
|
828
|
+
$publication_date = get_post_meta($post_id, '_book_publication_date', true);
|
|
829
|
+
|
|
830
|
+
ob_start();
|
|
831
|
+
?>
|
|
832
|
+
<div class="book-review-details">
|
|
833
|
+
<div class="book-meta">
|
|
834
|
+
<?php if ($author) : ?>
|
|
835
|
+
<p><strong><?php _e('Author:', 'book-reviews'); ?></strong> <?php echo esc_html($author); ?></p>
|
|
836
|
+
<?php endif; ?>
|
|
837
|
+
|
|
838
|
+
<?php if ($publisher) : ?>
|
|
839
|
+
<p><strong><?php _e('Publisher:', 'book-reviews'); ?></strong> <?php echo esc_html($publisher); ?></p>
|
|
840
|
+
<?php endif; ?>
|
|
841
|
+
|
|
842
|
+
<?php if ($publication_date) : ?>
|
|
843
|
+
<p><strong><?php _e('Publication Date:', 'book-reviews'); ?></strong> <?php echo esc_html($publication_date); ?></p>
|
|
844
|
+
<?php endif; ?>
|
|
845
|
+
|
|
846
|
+
<?php if ($isbn) : ?>
|
|
847
|
+
<p><strong><?php _e('ISBN:', 'book-reviews'); ?></strong> <?php echo esc_html($isbn); ?></p>
|
|
848
|
+
<?php endif; ?>
|
|
849
|
+
|
|
850
|
+
<?php if ($rating) : ?>
|
|
851
|
+
<div class="book-rating-large">
|
|
852
|
+
<strong><?php _e('Rating:', 'book-reviews'); ?></strong>
|
|
853
|
+
<span class="stars">
|
|
854
|
+
<?php echo str_repeat('★', intval($rating)); ?>
|
|
855
|
+
<?php echo str_repeat('☆', 5 - intval($rating)); ?>
|
|
856
|
+
</span>
|
|
857
|
+
<span class="rating-number"><?php echo esc_html($rating); ?>/5</span>
|
|
858
|
+
</div>
|
|
859
|
+
<?php endif; ?>
|
|
860
|
+
</div>
|
|
861
|
+
</div>
|
|
862
|
+
<?php
|
|
863
|
+
$details = ob_get_clean();
|
|
864
|
+
|
|
865
|
+
return $details . $content;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
### CSS Styling
|
|
871
|
+
|
|
872
|
+
**File**: `public/css/book-reviews-public.css`
|
|
873
|
+
|
|
874
|
+
```css
|
|
875
|
+
/* Book Reviews Grid */
|
|
876
|
+
.book-reviews-grid {
|
|
877
|
+
display: grid;
|
|
878
|
+
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
|
879
|
+
gap: 2rem;
|
|
880
|
+
margin: 2rem 0;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
/* Book Review Card */
|
|
884
|
+
.book-review-card {
|
|
885
|
+
border: 1px solid #ddd;
|
|
886
|
+
border-radius: 8px;
|
|
887
|
+
overflow: hidden;
|
|
888
|
+
transition: box-shadow 0.3s ease;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
.book-review-card:hover {
|
|
892
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
.book-cover {
|
|
896
|
+
width: 100%;
|
|
897
|
+
height: 400px;
|
|
898
|
+
overflow: hidden;
|
|
899
|
+
background: #f5f5f5;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
.book-cover img {
|
|
903
|
+
width: 100%;
|
|
904
|
+
height: 100%;
|
|
905
|
+
object-fit: cover;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
.book-info {
|
|
909
|
+
padding: 1.5rem;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
.book-title {
|
|
913
|
+
margin: 0 0 0.5rem;
|
|
914
|
+
font-size: 1.25rem;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
.book-title a {
|
|
918
|
+
color: #333;
|
|
919
|
+
text-decoration: none;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
.book-title a:hover {
|
|
923
|
+
color: #0073aa;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
.book-author {
|
|
927
|
+
margin: 0 0 1rem;
|
|
928
|
+
color: #666;
|
|
929
|
+
font-size: 0.95rem;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
.book-rating {
|
|
933
|
+
margin: 0 0 1rem;
|
|
934
|
+
color: #f39c12;
|
|
935
|
+
font-size: 1.2rem;
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
.book-genres {
|
|
939
|
+
margin: 0 0 1rem;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
.genre-tag {
|
|
943
|
+
display: inline-block;
|
|
944
|
+
padding: 0.25rem 0.75rem;
|
|
945
|
+
margin: 0 0.5rem 0.5rem 0;
|
|
946
|
+
background: #e8f4f8;
|
|
947
|
+
color: #0073aa;
|
|
948
|
+
border-radius: 4px;
|
|
949
|
+
font-size: 0.85rem;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
.book-excerpt {
|
|
953
|
+
margin: 0 0 1rem;
|
|
954
|
+
color: #666;
|
|
955
|
+
line-height: 1.6;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
.read-more {
|
|
959
|
+
display: inline-block;
|
|
960
|
+
padding: 0.5rem 1rem;
|
|
961
|
+
background: #0073aa;
|
|
962
|
+
color: #fff;
|
|
963
|
+
text-decoration: none;
|
|
964
|
+
border-radius: 4px;
|
|
965
|
+
transition: background 0.3s ease;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
.read-more:hover {
|
|
969
|
+
background: #005a87;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
/* Single Book Review */
|
|
973
|
+
.book-review-details {
|
|
974
|
+
padding: 1.5rem;
|
|
975
|
+
margin: 0 0 2rem;
|
|
976
|
+
background: #f9f9f9;
|
|
977
|
+
border-left: 4px solid #0073aa;
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
.book-meta p {
|
|
981
|
+
margin: 0.5rem 0;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
.book-rating-large {
|
|
985
|
+
margin: 1rem 0 0;
|
|
986
|
+
font-size: 1.1rem;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
.book-rating-large .stars {
|
|
990
|
+
color: #f39c12;
|
|
991
|
+
font-size: 1.5rem;
|
|
992
|
+
margin: 0 0.5rem;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
.rating-number {
|
|
996
|
+
color: #666;
|
|
997
|
+
font-size: 0.95rem;
|
|
998
|
+
}
|
|
999
|
+
```
|
|
1000
|
+
|
|
1001
|
+
## Usage Examples
|
|
1002
|
+
|
|
1003
|
+
### Display All Book Reviews
|
|
1004
|
+
|
|
1005
|
+
```
|
|
1006
|
+
[book_reviews]
|
|
1007
|
+
```
|
|
1008
|
+
|
|
1009
|
+
### Display Fiction Book Reviews
|
|
1010
|
+
|
|
1011
|
+
```
|
|
1012
|
+
[book_reviews genre="fiction" limit="5"]
|
|
1013
|
+
```
|
|
1014
|
+
|
|
1015
|
+
### Display Latest 3 Reviews
|
|
1016
|
+
|
|
1017
|
+
```
|
|
1018
|
+
[book_reviews limit="3" orderby="date" order="DESC"]
|
|
1019
|
+
```
|
|
1020
|
+
|
|
1021
|
+
## Security Features
|
|
1022
|
+
|
|
1023
|
+
### 1. Nonce Verification
|
|
1024
|
+
|
|
1025
|
+
All meta box saves verify nonces:
|
|
1026
|
+
|
|
1027
|
+
```php
|
|
1028
|
+
if (!wp_verify_nonce($_POST['book_review_details_nonce_field'], 'book_review_details_nonce')) {
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
```
|
|
1032
|
+
|
|
1033
|
+
### 2. Capability Checks
|
|
1034
|
+
|
|
1035
|
+
Only users with edit permissions can save:
|
|
1036
|
+
|
|
1037
|
+
```php
|
|
1038
|
+
if (!current_user_can('edit_post', $post_id)) {
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
```
|
|
1042
|
+
|
|
1043
|
+
### 3. Input Sanitization
|
|
1044
|
+
|
|
1045
|
+
All inputs are sanitized:
|
|
1046
|
+
|
|
1047
|
+
```php
|
|
1048
|
+
update_post_meta($post_id, '_book_author', sanitize_text_field($_POST['book_author']));
|
|
1049
|
+
```
|
|
1050
|
+
|
|
1051
|
+
### 4. Output Escaping
|
|
1052
|
+
|
|
1053
|
+
All outputs are escaped:
|
|
1054
|
+
|
|
1055
|
+
```php
|
|
1056
|
+
echo esc_html($author);
|
|
1057
|
+
echo esc_url(get_permalink($post_id));
|
|
1058
|
+
echo esc_attr($isbn);
|
|
1059
|
+
```
|
|
1060
|
+
|
|
1061
|
+
### 5. Data Validation
|
|
1062
|
+
|
|
1063
|
+
Rating values are validated:
|
|
1064
|
+
|
|
1065
|
+
```php
|
|
1066
|
+
$rating = intval($_POST['book_rating']);
|
|
1067
|
+
if ($rating >= 1 && $rating <= 5) {
|
|
1068
|
+
update_post_meta($post_id, '_book_rating', $rating);
|
|
1069
|
+
}
|
|
1070
|
+
```
|
|
1071
|
+
|
|
1072
|
+
## Key Takeaways
|
|
1073
|
+
|
|
1074
|
+
- **Custom post types** provide structured content management
|
|
1075
|
+
- **Custom taxonomies** enable categorization and filtering
|
|
1076
|
+
- **Meta boxes** allow additional custom fields
|
|
1077
|
+
- **Custom columns** improve admin list view usability
|
|
1078
|
+
- **Shortcodes** enable flexible frontend display
|
|
1079
|
+
- **Security** is implemented at every level (nonces, capabilities, sanitization, escaping)
|
|
1080
|
+
- **Rewrite rules** must be flushed on activation
|
|
1081
|
+
- **Template hierarchy** can be used for custom templates
|
|
1082
|
+
- **REST API support** enables Gutenberg block editor integration
|
|
1083
|
+
|