Changeset 3472097
- Timestamp:
- 03/01/2026 02:23:59 PM (8 days ago)
- Location:
- editorial-workflow-manager/trunk
- Files:
-
- 8 edited
-
editorial-workflow-manager.php (modified) (2 diffs)
-
includes/class-ediworman-default-templates.php (modified) (1 diff)
-
includes/class-ediworman-editor-assets.php (modified) (1 diff)
-
includes/class-ediworman-meta.php (modified) (1 diff)
-
includes/class-ediworman-settings.php (modified) (1 diff)
-
includes/class-ediworman-templates-cpt.php (modified) (1 diff)
-
readme.txt (modified) (2 diffs)
-
uninstall.php (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
editorial-workflow-manager/trunk/editorial-workflow-manager.php
r3457373 r3472097 1 1 <?php 2 3 2 /** 4 3 * Plugin Name: Editorial Workflow Manager 5 4 * Description: Add editorial checklists and approvals to the WordPress editor. 6 * Version: 0.3. 45 * Version: 0.3.5 7 6 * Author: Vasileios Zisis 8 7 * Author URI: https://profiles.wordpress.org/vzisis/ … … 12 11 * License: GPL-2.0-or-later 13 12 * License URI: https://www.gnu.org/licenses/gpl-2.0.html 13 * 14 * @package EditorialWorkflowManager 14 15 */ 15 16 16 if (! defined('ABSPATH')) { 17 exit; // Exit if accessed directly. 18 } 19 final class EDIWORMAN_Plugin 20 { 21 22 const VERSION = '0.3.3'; 23 24 private static $instance = null; 25 26 /** @var EDIWORMAN_Templates_CPT */ 27 private $templates_cpt; 28 29 /** @var EDIWORMAN_Settings */ 30 private $settings; 31 32 /** @var EDIWORMAN_Meta */ 33 private $meta; 34 35 /** @var EDIWORMAN_Editor_Assets */ 36 private $editor_assets; 37 38 public static function instance() 39 { 40 if (null === self::$instance) { 41 self::$instance = new self(); 42 } 43 44 return self::$instance; 45 } 46 47 private function __construct() 48 { 49 $this->define_constants(); 50 51 // Load classes. 52 require_once EDIWORMAN_PATH . 'includes/class-ediworman-templates-cpt.php'; 53 require_once EDIWORMAN_PATH . 'includes/class-ediworman-settings.php'; 54 require_once EDIWORMAN_PATH . 'includes/class-ediworman-meta.php'; 55 require_once EDIWORMAN_PATH . 'includes/class-ediworman-editor-assets.php'; 56 require_once EDIWORMAN_PATH . 'includes/class-ediworman-default-templates.php'; 57 58 // Instantiate. 59 $this->templates_cpt = new EDIWORMAN_Templates_CPT(); 60 $this->settings = new EDIWORMAN_Settings(); 61 $this->meta = new EDIWORMAN_Meta(); 62 $this->editor_assets = new EDIWORMAN_Editor_Assets(); 63 64 // Hooks. 65 add_action('init', [$this, 'on_init']); 66 } 67 68 private function define_constants() 69 { 70 define('EDIWORMAN_VERSION', self::VERSION); 71 define('EDIWORMAN_FILE', __FILE__); 72 define('EDIWORMAN_PATH', plugin_dir_path(__FILE__)); 73 define('EDIWORMAN_URL', plugin_dir_url(__FILE__)); 74 } 75 76 public function on_init() 77 { 78 // other init stuff later (if needed) 79 } 80 81 public static function activate() 82 { 83 EDIWORMAN_Default_Templates::create_on_activation(); 84 } 17 if ( ! defined( 'ABSPATH' ) ) { 18 exit; // Exit if accessed directly. 85 19 } 86 20 87 register_activation_hook(__FILE__, ['EDIWORMAN_Plugin', 'activate']); 21 /** 22 * Plugin bootstrap and service wiring. 23 */ 24 final class EDIWORMAN_Plugin { 25 26 const VERSION = '0.3.5'; 27 28 /** 29 * Singleton plugin instance. 30 * 31 * @var EDIWORMAN_Plugin|null 32 */ 33 private static $instance = null; 34 35 /** 36 * Checklist template CPT handler. 37 * 38 * @var EDIWORMAN_Templates_CPT 39 */ 40 private $templates_cpt; 41 42 /** 43 * Plugin settings handler. 44 * 45 * @var EDIWORMAN_Settings 46 */ 47 private $settings; 48 49 /** 50 * Post meta registration and save hooks. 51 * 52 * @var EDIWORMAN_Meta 53 */ 54 private $meta; 55 56 /** 57 * Block editor asset loader. 58 * 59 * @var EDIWORMAN_Editor_Assets 60 */ 61 private $editor_assets; 62 63 /** 64 * Get plugin singleton instance. 65 * 66 * @return EDIWORMAN_Plugin 67 */ 68 public static function instance() { 69 if ( null === self::$instance ) { 70 self::$instance = new self(); 71 } 72 73 return self::$instance; 74 } 75 76 /** 77 * Construct plugin services and hooks. 78 */ 79 private function __construct() { 80 $this->define_constants(); 81 82 // Load classes. 83 require_once EDIWORMAN_PATH . 'includes/class-ediworman-templates-cpt.php'; 84 require_once EDIWORMAN_PATH . 'includes/class-ediworman-settings.php'; 85 require_once EDIWORMAN_PATH . 'includes/class-ediworman-meta.php'; 86 require_once EDIWORMAN_PATH . 'includes/class-ediworman-editor-assets.php'; 87 require_once EDIWORMAN_PATH . 'includes/class-ediworman-default-templates.php'; 88 89 // Instantiate. 90 $this->templates_cpt = new EDIWORMAN_Templates_CPT(); 91 $this->settings = new EDIWORMAN_Settings(); 92 $this->meta = new EDIWORMAN_Meta(); 93 $this->editor_assets = new EDIWORMAN_Editor_Assets(); 94 95 // Hooks. 96 add_action( 'init', array( $this, 'on_init' ) ); 97 } 98 99 /** 100 * Define plugin constants used across the codebase. 101 * 102 * @return void 103 */ 104 private function define_constants() { 105 define( 'EDIWORMAN_VERSION', self::VERSION ); 106 define( 'EDIWORMAN_FILE', __FILE__ ); 107 define( 'EDIWORMAN_PATH', plugin_dir_path( __FILE__ ) ); 108 define( 'EDIWORMAN_URL', plugin_dir_url( __FILE__ ) ); 109 } 110 111 /** 112 * Run init-time plugin behavior. 113 * 114 * @return void 115 */ 116 public function on_init() { 117 // Reserved for future init-time hooks. 118 } 119 120 /** 121 * Activation callback. 122 * 123 * @return void 124 */ 125 public static function activate() { 126 EDIWORMAN_Default_Templates::create_on_activation(); 127 } 128 } 129 130 register_activation_hook( __FILE__, array( 'EDIWORMAN_Plugin', 'activate' ) ); 88 131 89 132 // Bootstrap the plugin. -
editorial-workflow-manager/trunk/includes/class-ediworman-default-templates.php
r3433414 r3472097 1 1 <?php 2 /** 3 * Create default checklist templates on plugin activation. 4 * 5 * @package EditorialWorkflowManager 6 */ 7 8 if ( ! defined( 'ABSPATH' ) ) { 9 exit; 10 } 2 11 3 12 /** 4 * Create default checklist templates on plugin activation.13 * Creates default template posts and default post type mappings. 5 14 */ 15 class EDIWORMAN_Default_Templates { 6 16 7 if (! defined('ABSPATH')) { 8 exit; 17 /** 18 * Create default templates and map Blog Post SOP to "post". 19 */ 20 public static function create_on_activation() { 21 // Ensure the CPT is registered so the admin will see these nicely. 22 $cpt = new EDIWORMAN_Templates_CPT(); 23 $cpt->register_cpt(); 24 25 // 1) Define the templates and their items. 26 $definitions = array( 27 'Blog Post SOP' => array( 28 'Set featured image', 29 'Write excerpt / meta description', 30 'Add at least 2 internal links', 31 'Check external links (open in new tab if needed)', 32 'Spellcheck and grammar check', 33 'Confirm category and tags', 34 ), 35 'Landing Page QA' => array( 36 'Check layout on mobile', 37 'Test primary CTA button/link', 38 'Test form submission (if any)', 39 'Confirm thank-you page or message', 40 'Confirm analytics / pixel tracking', 41 'Check page speed (basic)', 42 ), 43 'Announcement / News Post' => array( 44 'Verify dates, names, and key facts', 45 'Add internal link to relevant product/service page', 46 'Add featured image or banner', 47 'Check tone and brand voice', 48 'Confirm any required disclaimer', 49 'Prepare or schedule social share copy', 50 ), 51 ); 52 53 $created_ids = array(); 54 55 foreach ( $definitions as $title => $items ) { 56 // Try to find an existing template with this title. 57 $existing = get_posts( 58 array( 59 'post_type' => 'ediworman_template', 60 'title' => $title, 61 'post_status' => 'any', 62 'posts_per_page' => 1, 63 'orderby' => 'ID', 64 'order' => 'ASC', 65 'fields' => 'ids', 66 'no_found_rows' => true, 67 ) 68 ); 69 70 if ( ! empty( $existing ) ) { 71 $template_id = (int) $existing[0]; 72 } else { 73 // Create a new template. 74 $template_id = wp_insert_post( 75 array( 76 'post_type' => 'ediworman_template', 77 'post_status' => 'publish', 78 'post_title' => $title, 79 ) 80 ); 81 } 82 83 if ( $template_id && ! is_wp_error( $template_id ) ) { 84 // Store / update the checklist items meta. 85 update_post_meta( $template_id, '_ediworman_items', $items ); 86 $created_ids[ $title ] = (int) $template_id; 87 } 88 } 89 90 // 2) Map "Blog Post SOP" to the "post" post type, if nothing set yet. 91 if ( isset( $created_ids['Blog Post SOP'] ) ) { 92 $settings = get_option( EDIWORMAN_Settings::OPTION_NAME, array() ); 93 94 if ( ! isset( $settings['post_type_templates'] ) || ! is_array( $settings['post_type_templates'] ) ) { 95 $settings['post_type_templates'] = array(); 96 } 97 98 // Only set if there is no mapping yet (don't overwrite user choices). 99 if ( empty( $settings['post_type_templates']['post'] ) ) { 100 $settings['post_type_templates']['post'] = $created_ids['Blog Post SOP']; 101 update_option( EDIWORMAN_Settings::OPTION_NAME, $settings ); 102 } 103 } 104 } 9 105 } 10 11 class EDIWORMAN_Default_Templates12 {13 14 /**15 * Create default templates and map Blog Post SOP to "post".16 */17 public static function create_on_activation()18 {19 // Ensure the CPT is registered so the admin will see these nicely.20 if (class_exists('EDIWORMAN_Templates_CPT')) {21 $cpt = new EDIWORMAN_Templates_CPT();22 $cpt->register_cpt();23 }24 25 // 1) Define the templates and their items.26 $definitions = [27 'Blog Post SOP' => [28 'Set featured image',29 'Write excerpt / meta description',30 'Add at least 2 internal links',31 'Check external links (open in new tab if needed)',32 'Spellcheck and grammar check',33 'Confirm category and tags',34 ],35 'Landing Page QA' => [36 'Check layout on mobile',37 'Test primary CTA button/link',38 'Test form submission (if any)',39 'Confirm thank-you page or message',40 'Confirm analytics / pixel tracking',41 'Check page speed (basic)',42 ],43 'Announcement / News Post' => [44 'Verify dates, names, and key facts',45 'Add internal link to relevant product/service page',46 'Add featured image or banner',47 'Check tone and brand voice',48 'Confirm any required disclaimer',49 'Prepare or schedule social share copy',50 ],51 ];52 53 $created_ids = [];54 55 foreach ($definitions as $title => $items) {56 // Try to find an existing template with this title.57 $existing = get_posts(58 [59 'post_type' => 'ediworman_template',60 'title' => $title,61 'post_status' => 'any',62 'posts_per_page' => 1,63 'orderby' => 'ID',64 'order' => 'ASC',65 'fields' => 'ids',66 'no_found_rows' => true,67 ]68 );69 70 if (! empty($existing)) {71 $template_id = (int) $existing[0];72 } else {73 // Create a new template.74 $template_id = wp_insert_post(75 [76 'post_type' => 'ediworman_template',77 'post_status' => 'publish',78 'post_title' => $title,79 ]80 );81 }82 83 if ($template_id && ! is_wp_error($template_id)) {84 // Store / update the checklist items meta.85 update_post_meta($template_id, '_ediworman_items', $items);86 $created_ids[$title] = (int) $template_id;87 }88 }89 90 // 2) Map "Blog Post SOP" to the "post" post type, if nothing set yet.91 if (class_exists('EDIWORMAN_Settings') && isset($created_ids['Blog Post SOP'])) {92 $settings = get_option(EDIWORMAN_Settings::OPTION_NAME, []);93 94 if (! isset($settings['post_type_templates']) || ! is_array($settings['post_type_templates'])) {95 $settings['post_type_templates'] = [];96 }97 98 // Only set if there is no mapping yet (don't overwrite user choices).99 if (empty($settings['post_type_templates']['post'])) {100 $settings['post_type_templates']['post'] = $created_ids['Blog Post SOP'];101 update_option(EDIWORMAN_Settings::OPTION_NAME, $settings);102 }103 }104 }105 } -
editorial-workflow-manager/trunk/includes/class-ediworman-editor-assets.php
r3457373 r3472097 1 1 <?php 2 /** 3 * Enqueue block editor assets (Gutenberg sidebar). 4 * 5 * @package EditorialWorkflowManager 6 */ 7 8 if ( ! defined( 'ABSPATH' ) ) { 9 exit; 10 } 2 11 3 12 /** 4 * Enqueue block editor assets (Gutenberg sidebar).13 * Enqueues editor scripts and localizes checklist data. 5 14 */ 15 class EDIWORMAN_Editor_Assets { 6 16 7 if (! defined('ABSPATH')) { 8 exit; 17 /** 18 * Register editor enqueue hook. 19 * 20 * @return void 21 */ 22 public function __construct() { 23 // This hook only runs in the block editor. 24 add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue' ) ); 25 } 26 27 /** 28 * Enqueue the sidebar JavaScript and pass checklist data to it. 29 */ 30 public function enqueue() { 31 // Always enqueue the JS in the block editor. 32 wp_enqueue_script( 33 'ediworman-sidebar', 34 EDIWORMAN_URL . 'assets/js/sidebar.js', 35 array( 36 'wp-plugins', 37 'wp-edit-post', 38 'wp-element', 39 'wp-components', 40 'wp-data', 41 'wp-core-data', 42 ), 43 EDIWORMAN_VERSION, 44 true 45 ); 46 47 // Try to detect the current post type. 48 if ( ! function_exists( 'get_current_screen' ) ) { 49 return; 50 } 51 52 $screen = get_current_screen(); 53 54 // We only care about actual post editing screens. 55 if ( ! $screen || empty( $screen->post_type ) || 'post' !== $screen->base ) { 56 return; 57 } 58 59 $post_type = sanitize_key( $screen->post_type ); 60 if ( ! $post_type || ! post_type_exists( $post_type ) ) { 61 return; 62 } 63 64 $template_id = null; 65 $items = array(); 66 67 $template_id = EDIWORMAN_Settings::get_template_for_post_type( $post_type ); 68 69 if ( $template_id ) { 70 $template_id = absint( $template_id ); 71 $stored_items = get_post_meta( $template_id, '_ediworman_items', true ); 72 if ( is_array( $stored_items ) ) { 73 $items = array_values( 74 array_filter( 75 array_map( 'sanitize_text_field', $stored_items ), 76 'strlen' 77 ) 78 ); 79 } 80 } 81 82 // Pass data to JS (even if items is empty). 83 wp_localize_script( 84 'ediworman-sidebar', 85 'EDIWORMAN_CHECKLIST_DATA', 86 array( 87 'templateId' => $template_id, 88 'postType' => $post_type, 89 'items' => $items, 90 ) 91 ); 92 } 9 93 } 10 11 class EDIWORMAN_Editor_Assets12 {13 14 public function __construct()15 {16 // This hook only runs in the block editor.17 add_action('enqueue_block_editor_assets', [$this, 'enqueue']);18 }19 20 /**21 * Enqueue the sidebar JavaScript and pass checklist data to it.22 */23 public function enqueue()24 {25 // Always enqueue the JS in the block editor.26 wp_enqueue_script(27 'ediworman-sidebar',28 EDIWORMAN_URL . 'assets/js/sidebar.js',29 [30 'wp-plugins',31 'wp-edit-post',32 'wp-element',33 'wp-components',34 'wp-data',35 'wp-core-data',36 ],37 EDIWORMAN_VERSION,38 true39 );40 41 // Try to detect the current post type.42 if (! function_exists('get_current_screen')) {43 return;44 }45 46 $screen = get_current_screen();47 48 // We only care about actual post editing screens.49 if (! $screen || empty($screen->post_type) || $screen->base !== 'post') {50 return;51 }52 53 $post_type = sanitize_key($screen->post_type);54 if (! $post_type || ! post_type_exists($post_type)) {55 return;56 }57 58 $template_id = null;59 $items = [];60 61 if (class_exists('EDIWORMAN_Settings')) {62 $template_id = EDIWORMAN_Settings::get_template_for_post_type($post_type);63 }64 65 if ($template_id) {66 $template_id = absint($template_id);67 $stored_items = get_post_meta($template_id, '_ediworman_items', true);68 if (is_array($stored_items)) {69 $items = array_values(70 array_filter(71 array_map('sanitize_text_field', $stored_items),72 'strlen'73 )74 );75 }76 }77 78 // Pass data to JS (even if items is empty).79 wp_localize_script(80 'ediworman-sidebar',81 'EDIWORMAN_CHECKLIST_DATA',82 [83 'templateId' => $template_id,84 'postType' => $post_type,85 'items' => $items,86 ]87 );88 }89 } -
editorial-workflow-manager/trunk/includes/class-ediworman-meta.php
r3457373 r3472097 1 1 <?php 2 /** 3 * Post meta for storing checklist state per post. 4 * 5 * @package EditorialWorkflowManager 6 */ 7 8 if ( ! defined( 'ABSPATH' ) ) { 9 exit; 10 } 2 11 3 12 /** 4 * Post meta for storing checklist state per post.13 * Registers and maintains editorial checklist post meta. 5 14 */ 15 class EDIWORMAN_Meta { 6 16 7 if (! defined('ABSPATH')) { 8 exit; 17 /** 18 * Register meta and save handlers. 19 * 20 * @return void 21 */ 22 public function __construct() { 23 add_action( 'init', array( $this, 'register_meta' ) ); 24 add_action( 'save_post', array( $this, 'capture_last_editor' ), 10, 1 ); 25 } 26 27 /** 28 * Sanitize the checked items meta. 29 * 30 * @param mixed $value Raw meta value. 31 * @return array 32 */ 33 public function sanitize_checked_items( $value ) { 34 if ( ! is_array( $value ) ) { 35 if ( is_string( $value ) && '' !== $value ) { 36 $value = array( $value ); 37 } else { 38 return array(); 39 } 40 } 41 42 $sanitized = array(); 43 44 foreach ( $value as $item ) { 45 if ( ! is_scalar( $item ) ) { 46 continue; 47 } 48 49 $label = sanitize_text_field( wp_unslash( (string) $item ) ); 50 if ( '' === $label ) { 51 continue; 52 } 53 54 $sanitized[] = $label; 55 } 56 57 return array_values( array_unique( $sanitized ) ); 58 } 59 60 /** 61 * Register post meta used by the plugin. 62 * 63 * - _ediworman_checked_items: array of checklist item labels that are checked. 64 * - _ediworman_last_editor: user ID of the last editor (for display only). 65 * 66 * @return void 67 */ 68 public function register_meta() { 69 70 // Checklist state. 71 register_post_meta( 72 '', 73 '_ediworman_checked_items', 74 array( 75 'single' => true, 76 'type' => 'array', 77 'default' => array(), 78 'show_in_rest' => array( 79 'schema' => array( 80 'type' => 'array', 81 'items' => array( 82 'type' => 'string', 83 ), 84 ), 85 ), 86 'sanitize_callback' => array( $this, 'sanitize_checked_items' ), 87 'auth_callback' => static function ( $allowed, $meta_key, $post_id ) { 88 if ( false === $allowed || '_ediworman_checked_items' !== $meta_key ) { 89 return false; 90 } 91 92 $post_id = (int) $post_id; 93 if ( $post_id <= 0 ) { 94 return false; 95 } 96 97 return current_user_can( 'edit_post', $post_id ); 98 }, 99 ) 100 ); 101 102 // Last editor (user ID). 103 register_post_meta( 104 '', 105 '_ediworman_last_editor', 106 array( 107 'single' => true, 108 'type' => 'integer', 109 'default' => 0, 110 'show_in_rest' => true, 111 'sanitize_callback' => 'absint', 112 'auth_callback' => static function ( $allowed, $meta_key, $post_id ) { 113 if ( false === $allowed || '_ediworman_last_editor' !== $meta_key ) { 114 return false; 115 } 116 117 $post_id = (int) $post_id; 118 if ( $post_id <= 0 ) { 119 return false; 120 } 121 122 return current_user_can( 'edit_post', $post_id ); 123 }, 124 ) 125 ); 126 } 127 128 /** 129 * Store the last editor's user ID when a post is saved. 130 * 131 * @param int $post_id Post ID being saved. 132 * @return void 133 */ 134 public function capture_last_editor( $post_id ) { 135 // Don't run on autosave / revisions. 136 if ( wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id ) ) { 137 return; 138 } 139 140 // Only for real posts/pages/etc. 141 142 // Permission check. 143 if ( ! current_user_can( 'edit_post', $post_id ) ) { 144 return; 145 } 146 147 $user_id = get_current_user_id(); 148 if ( $user_id ) { 149 update_post_meta( $post_id, '_ediworman_last_editor', (int) $user_id ); 150 } 151 } 9 152 } 10 11 if (! class_exists('EDIWORMAN')) {12 13 class EDIWORMAN_Meta14 {15 16 public function __construct()17 {18 add_action('init', [$this, 'register_meta']);19 add_action('save_post', [$this, 'capture_last_editor'], 10, 3);20 }21 22 /**23 * Sanitize the checked items meta.24 *25 * @param mixed $value26 * @param string $meta_key27 * @param string $object_type28 * @return array29 */30 public function sanitize_checked_items($value, $meta_key, $object_type)31 {32 if (! is_array($value)) {33 if (is_string($value) && $value !== '') {34 $value = [$value];35 } else {36 return [];37 }38 }39 40 $sanitized = [];41 42 foreach ($value as $item) {43 if (! is_scalar($item)) {44 continue;45 }46 47 $label = sanitize_text_field(wp_unslash((string) $item));48 if ($label === '') {49 continue;50 }51 52 $sanitized[] = $label;53 }54 55 return array_values(array_unique($sanitized));56 }57 58 /**59 * Register post meta used by the plugin.60 *61 * - _ediworman_checked_items: array of checklist item labels that are checked.62 * - _ediworman_last_editor: user ID of the last editor (for display only).63 */64 public function register_meta()65 {66 67 // Checklist state.68 register_post_meta(69 '',70 '_ediworman_checked_items',71 [72 'single' => true,73 'type' => 'array',74 'default' => [],75 'show_in_rest' => [76 'schema' => [77 'type' => 'array',78 'items' => [79 'type' => 'string',80 ],81 ],82 ],83 'sanitize_callback' => [$this, 'sanitize_checked_items'],84 'auth_callback' => static function ($allowed, $meta_key, $post_id, $user_id = 0, $cap = '', $caps = []) {85 $post_id = (int) $post_id;86 if ($post_id <= 0) {87 return false;88 }89 90 return current_user_can('edit_post', $post_id);91 },92 ]93 );94 95 // Last editor (user ID).96 register_post_meta(97 '',98 '_ediworman_last_editor',99 [100 'single' => true,101 'type' => 'integer',102 'default' => 0,103 'show_in_rest' => true,104 'sanitize_callback' => 'absint',105 'auth_callback' => static function ($allowed, $meta_key, $post_id, $user_id = 0, $cap = '', $caps = []) {106 $post_id = (int) $post_id;107 if ($post_id <= 0) {108 return false;109 }110 111 return current_user_can('edit_post', $post_id);112 },113 ]114 );115 }116 117 /**118 * Store the last editor's user ID when a post is saved.119 *120 * @param int $post_id121 * @param WP_Post $post122 * @param bool $update123 */124 public function capture_last_editor($post_id, $post, $update)125 {126 // Don't run on autosave / revisions.127 if (wp_is_post_autosave($post_id) || wp_is_post_revision($post_id)) {128 return;129 }130 131 // Only for real posts/pages/etc (not our template CPT if you prefer to exclude).132 // If you want to skip templates, uncomment:133 // if ( $post->post_type === 'ediworman_template' ) {134 // return;135 // }136 137 // Permission check.138 if (! current_user_can('edit_post', $post_id)) {139 return;140 }141 142 $user_id = get_current_user_id();143 if ($user_id) {144 update_post_meta($post_id, '_ediworman_last_editor', (int) $user_id);145 }146 }147 }148 } -
editorial-workflow-manager/trunk/includes/class-ediworman-settings.php
r3457373 r3472097 1 1 <?php 2 3 2 /** 4 3 * Plugin settings: map post types to checklist templates. 4 * 5 * @package EditorialWorkflowManager 5 6 */ 6 7 7 if ( ! defined('ABSPATH')) {8 exit;8 if ( ! defined( 'ABSPATH' ) ) { 9 exit; 9 10 } 10 11 11 class EDIWORMAN_Settings 12 { 13 /** 14 * Option name. 15 */ 16 const OPTION_NAME = 'ediworman_settings'; 17 18 public function __construct() 19 { 20 add_action('admin_menu', [$this, 'register_menu']); 21 add_action('admin_init', [$this, 'register_settings']); 22 } 23 24 /** 25 * Add a settings page under "Settings". 26 */ 27 public function register_menu() 28 { 29 add_options_page( 30 __('Editorial Workflow', 'editorial-workflow-manager'), 31 __('Editorial Workflow', 'editorial-workflow-manager'), 32 'manage_options', 33 'ediworman-settings', 34 [$this, 'render_page'] 35 ); 36 } 37 38 /** 39 * Register the settings. 40 */ 41 public function register_settings() 42 { 43 register_setting( 44 'ediworman_settings_group', 45 self::OPTION_NAME, 46 [ 47 'type' => 'array', 48 'sanitize_callback' => [$this, 'sanitize_settings'], 49 'default' => [], 50 ] 51 ); 52 } 53 54 /** 55 * Sanitize settings on save. 56 * 57 * @param array $input 58 * @return array 59 */ 60 public function sanitize_settings($input) 61 { 62 $output = []; 63 64 $output['post_type_templates'] = []; 65 66 if (! is_array($input)) { 67 return $output; 68 } 69 70 if (! empty($input['post_type_templates']) && is_array($input['post_type_templates'])) { 71 foreach ($input['post_type_templates'] as $post_type => $template_id) { 72 $post_type = sanitize_key($post_type); 73 $template_id = absint($template_id); 74 75 if (! $post_type || ! post_type_exists($post_type)) { 76 continue; 77 } 78 79 // Prevent assigning our own CPT or attachments. 80 if ($post_type === 'ediworman_template' || $post_type === 'attachment') { 81 continue; 82 } 83 84 if ($template_id > 0) { 85 $template = get_post($template_id); 86 87 if (! $template || $template->post_type !== 'ediworman_template') { 88 continue; 89 } 90 91 // If it's in trash, ignore the mapping. 92 if ($template->post_status === 'trash') { 93 continue; 94 } 95 96 $output['post_type_templates'][$post_type] = (int) $template_id; 97 } 98 } 99 } 100 101 return $output; 102 } 103 104 /** 105 * Render the settings page. 106 */ 107 public function render_page() 108 { 109 if (! current_user_can('manage_options')) { 110 return; 111 } 112 113 $settings = get_option(self::OPTION_NAME, []); 114 if (! is_array($settings)) { 115 $settings = []; 116 } 117 118 $mappings = isset($settings['post_type_templates']) ? $settings['post_type_templates'] : []; 119 120 // Get public post types that have UI. 121 $post_types = get_post_types( 122 [ 123 'show_ui' => true, 124 'public' => true, 125 ], 126 'objects' 127 ); 128 129 // Exclude our own CPT. 130 unset($post_types['ediworman_template']); 131 132 // Exclude Media (attachments) – checklists for media items don't make sense. 133 unset($post_types['attachment']); 134 135 // Get all checklist templates. 136 $templates = get_posts( 137 [ 138 'post_type' => 'ediworman_template', 139 'post_status' => 'any', 140 'posts_per_page' => -1, 141 'orderby' => 'title', 142 'order' => 'ASC', 143 ] 144 ); 145 ?> 146 <div class="wrap"> 147 <h1><?php esc_html_e('Editorial Workflow Settings', 'editorial-workflow-manager'); ?></h1> 148 149 <p> 150 <?php esc_html_e('Use checklist templates to enforce a consistent review process before publishing content.', 'editorial-workflow-manager'); ?> 151 </p> 152 153 <div class="notice notice-info"> 154 <p><strong><?php esc_html_e('Getting started', 'editorial-workflow-manager'); ?></strong></p> 155 <ol> 156 <li> 157 <?php 158 printf( 159 /* translators: %s: menu label "Checklist Templates" */ 160 esc_html__('Go to %s and create or edit checklist templates (or use the defaults).', 'editorial-workflow-manager'), 161 '<em>' . esc_html__('Checklist Templates → Add New', 'editorial-workflow-manager') . '</em>' 162 ); 163 ?> 164 </li> 165 <li> 166 <?php esc_html_e('Return to this page and map each post type to a checklist template.', 'editorial-workflow-manager'); ?> 167 </li> 168 <li> 169 <?php esc_html_e('Edit a post or page and open the “Editorial Checklist” sidebar in the block editor to see and tick off the checklist items.', 'editorial-workflow-manager'); ?> 170 </li> 171 <li> 172 <?php esc_html_e('If you delete a checklist template, come back here to assign a new template to any post types that were using it.', 'editorial-workflow-manager'); ?> 173 </li> 174 </ol> 175 </div> 176 177 <form method="post" action="options.php"> 178 <?php 179 settings_fields('ediworman_settings_group'); 180 ?> 181 182 <table class="form-table" role="presentation"> 183 <tbody> 184 <tr> 185 <th scope="row"> 186 <label><?php esc_html_e('Template per post type', 'editorial-workflow-manager'); ?></label> 187 </th> 188 <td> 189 <p class="description"> 190 <?php esc_html_e('Choose which checklist template should be used by default for each post type.', 'editorial-workflow-manager'); ?> 191 </p> 192 193 <table> 194 <thead> 195 <tr> 196 <th style="text-align:left;"><?php esc_html_e('Post type', 'editorial-workflow-manager'); ?></th> 197 <th style="text-align:left;"><?php esc_html_e('Checklist template', 'editorial-workflow-manager'); ?></th> 198 </tr> 199 </thead> 200 <tbody> 201 <?php foreach ($post_types as $post_type => $obj) : ?> 202 <tr> 203 <td> 204 <?php echo esc_html($obj->labels->singular_name); ?> 205 <br> 206 <code><?php echo esc_html($post_type); ?></code> 207 </td> 208 <td> 209 <select name="ediworman_settings[post_type_templates][<?php echo esc_attr($post_type); ?>]"> 210 <option value="0"> 211 <?php esc_html_e('None', 'editorial-workflow-manager'); ?> 212 </option> 213 <?php foreach ($templates as $template) : ?> 214 <?php 215 $selected = isset($mappings[$post_type]) && (int) $mappings[$post_type] === (int) $template->ID 216 ? 'selected' 217 : ''; 218 ?> 219 <option value="<?php echo esc_attr($template->ID); ?>" <?php selected((int) ($mappings[$post_type] ?? 0), (int) $template->ID); ?>> 220 <?php echo esc_html($template->post_title); ?> 221 </option> 222 <?php endforeach; ?> 223 </select> 224 </td> 225 </tr> 226 <?php endforeach; ?> 227 </tbody> 228 </table> 229 230 </td> 231 </tr> 232 </tbody> 233 </table> 234 235 <?php submit_button(); ?> 236 </form> 237 </div> 238 <?php 239 } 240 241 /** 242 * Helper: get template ID for a given post type. 243 * 244 * Returns null if no valid template exists anymore (e.g. deleted or trashed). 245 * 246 * @param string $post_type 247 * @return int|null 248 */ 249 public static function get_template_for_post_type($post_type) 250 { 251 $post_type = sanitize_key($post_type); 252 if (! $post_type || ! post_type_exists($post_type)) { 253 return null; 254 } 255 256 $settings = get_option(self::OPTION_NAME, []); 257 if (! is_array($settings)) { 258 $settings = []; 259 } 260 261 if (empty($settings['post_type_templates'][$post_type])) { 262 return null; 263 } 264 265 $template_id = (int) $settings['post_type_templates'][$post_type]; 266 if ($template_id <= 0) { 267 return null; 268 } 269 270 $template = get_post($template_id); 271 272 // If the template is gone or not our CPT, treat as no template. 273 if (! $template || $template->post_type !== 'ediworman_template') { 274 return null; 275 } 276 277 // If it's in trash, also treat as no template. 278 if ($template->post_status === 'trash') { 279 return null; 280 } 281 282 return $template_id; 283 } 12 /** 13 * Registers and renders plugin settings. 14 */ 15 class EDIWORMAN_Settings { 16 17 /** 18 * Option name. 19 */ 20 const OPTION_NAME = 'ediworman_settings'; 21 22 /** 23 * Register admin menu and settings hooks. 24 * 25 * @return void 26 */ 27 public function __construct() { 28 add_action( 'admin_menu', array( $this, 'register_menu' ) ); 29 add_action( 'admin_init', array( $this, 'register_settings' ) ); 30 } 31 32 /** 33 * Add a settings page under "Settings". 34 */ 35 public function register_menu() { 36 add_options_page( 37 __( 'Editorial Workflow', 'editorial-workflow-manager' ), 38 __( 'Editorial Workflow', 'editorial-workflow-manager' ), 39 'manage_options', 40 'ediworman-settings', 41 array( $this, 'render_page' ) 42 ); 43 } 44 45 /** 46 * Register the settings. 47 */ 48 public function register_settings() { 49 register_setting( 50 'ediworman_settings_group', 51 self::OPTION_NAME, 52 array( 53 'type' => 'array', 54 'sanitize_callback' => array( $this, 'sanitize_settings' ), 55 'default' => array(), 56 ) 57 ); 58 } 59 60 /** 61 * Sanitize settings on save. 62 * 63 * @param array $input Raw settings input. 64 * @return array 65 */ 66 public function sanitize_settings( $input ) { 67 $output = array(); 68 69 $output['post_type_templates'] = array(); 70 71 if ( ! is_array( $input ) ) { 72 return $output; 73 } 74 75 if ( ! empty( $input['post_type_templates'] ) && is_array( $input['post_type_templates'] ) ) { 76 foreach ( $input['post_type_templates'] as $post_type => $template_id ) { 77 $post_type = sanitize_key( $post_type ); 78 $template_id = absint( $template_id ); 79 80 if ( ! $post_type || ! post_type_exists( $post_type ) ) { 81 continue; 82 } 83 84 // Prevent assigning our own CPT or attachments. 85 if ( 'ediworman_template' === $post_type || 'attachment' === $post_type ) { 86 continue; 87 } 88 89 if ( $template_id > 0 ) { 90 $template = get_post( $template_id ); 91 92 if ( ! $template || 'ediworman_template' !== $template->post_type ) { 93 continue; 94 } 95 96 // If it's in trash, ignore the mapping. 97 if ( 'trash' === $template->post_status ) { 98 continue; 99 } 100 101 $output['post_type_templates'][ $post_type ] = (int) $template_id; 102 } 103 } 104 } 105 106 return $output; 107 } 108 109 /** 110 * Render the settings page. 111 */ 112 public function render_page() { 113 if ( ! current_user_can( 'manage_options' ) ) { 114 return; 115 } 116 117 $settings = get_option( self::OPTION_NAME, array() ); 118 if ( ! is_array( $settings ) ) { 119 $settings = array(); 120 } 121 122 $mappings = isset( $settings['post_type_templates'] ) ? $settings['post_type_templates'] : array(); 123 124 // Get public post types that have UI. 125 $post_types = get_post_types( 126 array( 127 'show_ui' => true, 128 'public' => true, 129 ), 130 'objects' 131 ); 132 133 // Exclude our own CPT. 134 unset( $post_types['ediworman_template'] ); 135 136 // Exclude Media (attachments) – checklists for media items don't make sense. 137 unset( $post_types['attachment'] ); 138 139 // Get all checklist templates. 140 $templates = get_posts( 141 array( 142 'post_type' => 'ediworman_template', 143 'post_status' => 'any', 144 'posts_per_page' => -1, 145 'orderby' => 'title', 146 'order' => 'ASC', 147 ) 148 ); 149 ?> 150 <div class="wrap"> 151 <h1><?php esc_html_e( 'Editorial Workflow Settings', 'editorial-workflow-manager' ); ?></h1> 152 153 <p> 154 <?php esc_html_e( 'Use checklist templates to enforce a consistent review process before publishing content.', 'editorial-workflow-manager' ); ?> 155 </p> 156 157 <div class="notice notice-info"> 158 <p><strong><?php esc_html_e( 'Getting started', 'editorial-workflow-manager' ); ?></strong></p> 159 <ol> 160 <li> 161 <?php 162 printf( 163 /* translators: %s: menu label "Checklist Templates" */ 164 esc_html__( 'Go to %s and create or edit checklist templates (or use the defaults).', 'editorial-workflow-manager' ), 165 '<em>' . esc_html__( 'Checklist Templates → Add New', 'editorial-workflow-manager' ) . '</em>' 166 ); 167 ?> 168 </li> 169 <li> 170 <?php esc_html_e( 'Return to this page and map each post type to a checklist template.', 'editorial-workflow-manager' ); ?> 171 </li> 172 <li> 173 <?php esc_html_e( 'Edit a post or page and open the “Editorial Checklist” sidebar in the block editor to see and tick off the checklist items.', 'editorial-workflow-manager' ); ?> 174 </li> 175 <li> 176 <?php esc_html_e( 'If you delete a checklist template, come back here to assign a new template to any post types that were using it.', 'editorial-workflow-manager' ); ?> 177 </li> 178 </ol> 179 </div> 180 181 <form method="post" action="options.php"> 182 <?php 183 settings_fields( 'ediworman_settings_group' ); 184 ?> 185 186 <table class="form-table" role="presentation"> 187 <tbody> 188 <tr> 189 <th scope="row"> 190 <label><?php esc_html_e( 'Template per post type', 'editorial-workflow-manager' ); ?></label> 191 </th> 192 <td> 193 <p class="description"> 194 <?php esc_html_e( 'Choose which checklist template should be used by default for each post type.', 'editorial-workflow-manager' ); ?> 195 </p> 196 197 <table> 198 <thead> 199 <tr> 200 <th style="text-align:left;"><?php esc_html_e( 'Post type', 'editorial-workflow-manager' ); ?></th> 201 <th style="text-align:left;"><?php esc_html_e( 'Checklist template', 'editorial-workflow-manager' ); ?></th> 202 </tr> 203 </thead> 204 <tbody> 205 <?php foreach ( $post_types as $post_type => $obj ) : ?> 206 <tr> 207 <td> 208 <?php echo esc_html( $obj->labels->singular_name ); ?> 209 <br> 210 <code><?php echo esc_html( $post_type ); ?></code> 211 </td> 212 <td> 213 <select name="ediworman_settings[post_type_templates][<?php echo esc_attr( $post_type ); ?>]"> 214 <option value="0"> 215 <?php esc_html_e( 'None', 'editorial-workflow-manager' ); ?> 216 </option> 217 <?php foreach ( $templates as $template ) : ?> 218 <?php 219 $selected = isset( $mappings[ $post_type ] ) && (int) $mappings[ $post_type ] === (int) $template->ID 220 ? 'selected' 221 : ''; 222 ?> 223 <option value="<?php echo esc_attr( $template->ID ); ?>" <?php selected( (int) ( $mappings[ $post_type ] ?? 0 ), (int) $template->ID ); ?>> 224 <?php echo esc_html( $template->post_title ); ?> 225 </option> 226 <?php endforeach; ?> 227 </select> 228 </td> 229 </tr> 230 <?php endforeach; ?> 231 </tbody> 232 </table> 233 234 </td> 235 </tr> 236 </tbody> 237 </table> 238 239 <?php submit_button(); ?> 240 </form> 241 </div> 242 <?php 243 } 244 245 /** 246 * Helper: get template ID for a given post type. 247 * 248 * Returns null if no valid template exists anymore (e.g. deleted or trashed). 249 * 250 * @param string $post_type Post type slug. 251 * @return int|null 252 */ 253 public static function get_template_for_post_type( $post_type ) { 254 $post_type = sanitize_key( $post_type ); 255 if ( ! $post_type || ! post_type_exists( $post_type ) ) { 256 return null; 257 } 258 259 $settings = get_option( self::OPTION_NAME, array() ); 260 if ( ! is_array( $settings ) ) { 261 $settings = array(); 262 } 263 264 if ( empty( $settings['post_type_templates'][ $post_type ] ) ) { 265 return null; 266 } 267 268 $template_id = (int) $settings['post_type_templates'][ $post_type ]; 269 if ( $template_id <= 0 ) { 270 return null; 271 } 272 273 $template = get_post( $template_id ); 274 275 // If the template is gone or not our CPT, treat as no template. 276 if ( ! $template || 'ediworman_template' !== $template->post_type ) { 277 return null; 278 } 279 280 // If it's in trash, also treat as no template. 281 if ( 'trash' === $template->post_status ) { 282 return null; 283 } 284 285 return $template_id; 286 } 284 287 } -
editorial-workflow-manager/trunk/includes/class-ediworman-templates-cpt.php
r3433414 r3472097 1 1 <?php 2 /** 3 * Checklist Templates CPT + meta box for checklist items. 4 * 5 * @package EditorialWorkflowManager 6 */ 7 8 if ( ! defined( 'ABSPATH' ) ) { 9 exit; 10 } 2 11 3 12 /** 4 * Checklist Templates CPT + meta box for checklist items.13 * Registers the checklist template post type and meta box persistence. 5 14 */ 15 class EDIWORMAN_Templates_CPT { 6 16 7 if (! defined('ABSPATH')) { 8 exit; 17 /** 18 * Register CPT and meta box hooks. 19 * 20 * @return void 21 */ 22 public function __construct() { 23 // Register CPT on init. 24 add_action( 'init', array( $this, 'register_cpt' ) ); 25 26 // Meta box for checklist items. 27 add_action( 'add_meta_boxes', array( $this, 'register_metaboxes' ) ); 28 29 // Save checklist items. 30 add_action( 'save_post_ediworman_template', array( $this, 'save_template_meta' ), 10, 2 ); 31 } 32 33 /** 34 * Register the "Checklist Template" custom post type. 35 */ 36 public function register_cpt() { 37 $labels = array( 38 'name' => __( 'Checklist Templates', 'editorial-workflow-manager' ), 39 'singular_name' => __( 'Checklist Template', 'editorial-workflow-manager' ), 40 'add_new' => __( 'Add New', 'editorial-workflow-manager' ), 41 'add_new_item' => __( 'Add New Checklist Template', 'editorial-workflow-manager' ), 42 'edit_item' => __( 'Edit Checklist Template', 'editorial-workflow-manager' ), 43 'new_item' => __( 'New Checklist Template', 'editorial-workflow-manager' ), 44 'view_item' => __( 'View Checklist Template', 'editorial-workflow-manager' ), 45 'search_items' => __( 'Search Checklist Templates', 'editorial-workflow-manager' ), 46 'not_found' => __( 'No checklist templates found.', 'editorial-workflow-manager' ), 47 'not_found_in_trash' => __( 'No checklist templates found in Trash.', 'editorial-workflow-manager' ), 48 'menu_name' => __( 'Checklist Templates', 'editorial-workflow-manager' ), 49 ); 50 51 $args = array( 52 'labels' => $labels, 53 'public' => false, 54 'show_ui' => true, 55 'show_in_menu' => true, 56 'show_in_nav_menus' => false, 57 'show_in_admin_bar' => false, 58 'exclude_from_search' => true, 59 'publicly_queryable' => false, 60 'has_archive' => false, 61 'rewrite' => false, 62 'supports' => array( 'title' ), 63 'capability_type' => 'post', 64 ); 65 66 register_post_type( 'ediworman_template', $args ); 67 } 68 69 /** 70 * Register meta boxes for checklist templates. 71 */ 72 public function register_metaboxes() { 73 add_meta_box( 74 'ediworman_template_items', 75 __( 'Checklist Items', 'editorial-workflow-manager' ), 76 array( $this, 'render_items_metabox' ), 77 'ediworman_template', 78 'normal', 79 'high' 80 ); 81 } 82 83 /** 84 * Render the checklist items meta box. 85 * 86 * @param WP_Post $post Current checklist template post. 87 */ 88 public function render_items_metabox( $post ) { 89 // Security nonce. 90 wp_nonce_field( 'ediworman_save_template_items', 'ediworman_template_items_nonce' ); 91 92 $items = get_post_meta( $post->ID, '_ediworman_items', true ); 93 if ( ! is_array( $items ) ) { 94 $items = array(); 95 } 96 97 $value = implode( "\n", $items ); 98 ?> 99 <p> 100 <?php esc_html_e( 'Enter one checklist item per line. These items will appear in the editor sidebar for posts using this template.', 'editorial-workflow-manager' ); ?> 101 </p> 102 <textarea 103 name="ediworman_template_items" 104 id="ediworman_template_items" 105 style="width: 100%; min-height: 200px;"><?php echo esc_textarea( $value ); ?></textarea> 106 <?php 107 } 108 109 /** 110 * Save the checklist items when the template is saved. 111 * 112 * @param int $post_id Post ID being saved. 113 * @param WP_Post $post Post object being saved. 114 * @return void 115 */ 116 public function save_template_meta( $post_id, $post ) { 117 // Autosave? Bail. 118 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { 119 return; 120 } 121 122 // Right post type? 123 if ( 'ediworman_template' !== $post->post_type ) { 124 return; 125 } 126 127 // Nonce check. 128 $nonce = isset( $_POST['ediworman_template_items_nonce'] ) 129 ? sanitize_text_field( wp_unslash( $_POST['ediworman_template_items_nonce'] ) ) 130 : ''; 131 132 if ( ! $nonce || ! wp_verify_nonce( $nonce, 'ediworman_save_template_items' ) ) { 133 return; 134 } 135 136 // Capability check. 137 if ( ! current_user_can( 'edit_post', $post_id ) ) { 138 return; 139 } 140 141 // If field missing, delete meta (or keep existing - your choice). 142 if ( ! isset( $_POST['ediworman_template_items'] ) ) { 143 delete_post_meta( $post_id, '_ediworman_items' ); 144 return; 145 } 146 147 $raw = sanitize_textarea_field( wp_unslash( $_POST['ediworman_template_items'] ) ); 148 149 $lines = preg_split( '/\R/', $raw ); // Split on any newline type. 150 $items = array_filter( 151 array_map( 152 static function ( $line ) { 153 return sanitize_text_field( trim( $line ) ); 154 }, 155 $lines 156 ) 157 ); 158 159 update_post_meta( $post_id, '_ediworman_items', $items ); 160 } 9 161 } 10 11 class EDIWORMAN_Templates_CPT12 {13 14 public function __construct()15 {16 // Register CPT on init.17 add_action('init', [$this, 'register_cpt']);18 19 // Meta box for checklist items.20 add_action('add_meta_boxes', [$this, 'register_metaboxes']);21 22 // Save checklist items.23 add_action('save_post_ediworman_template', [$this, 'save_template_meta'], 10, 2);24 }25 26 /**27 * Register the "Checklist Template" custom post type.28 */29 public function register_cpt()30 {31 $labels = [32 'name' => __('Checklist Templates', 'editorial-workflow-manager'),33 'singular_name' => __('Checklist Template', 'editorial-workflow-manager'),34 'add_new' => __('Add New', 'editorial-workflow-manager'),35 'add_new_item' => __('Add New Checklist Template', 'editorial-workflow-manager'),36 'edit_item' => __('Edit Checklist Template', 'editorial-workflow-manager'),37 'new_item' => __('New Checklist Template', 'editorial-workflow-manager'),38 'view_item' => __('View Checklist Template', 'editorial-workflow-manager'),39 'search_items' => __('Search Checklist Templates', 'editorial-workflow-manager'),40 'not_found' => __('No checklist templates found.', 'editorial-workflow-manager'),41 'not_found_in_trash' => __('No checklist templates found in Trash.', 'editorial-workflow-manager'),42 'menu_name' => __('Checklist Templates', 'editorial-workflow-manager'),43 ];44 45 $args = [46 'labels' => $labels,47 'public' => false,48 'show_ui' => true,49 'show_in_menu' => true,50 'show_in_nav_menus' => false,51 'show_in_admin_bar' => false,52 'exclude_from_search' => true,53 'publicly_queryable' => false,54 'has_archive' => false,55 'rewrite' => false,56 'supports' => ['title'],57 'capability_type' => 'post',58 ];59 60 register_post_type('ediworman_template', $args);61 }62 63 /**64 * Register meta boxes for checklist templates.65 */66 public function register_metaboxes()67 {68 add_meta_box(69 'ediworman_template_items',70 __('Checklist Items', 'editorial-workflow-manager'),71 [$this, 'render_items_metabox'],72 'ediworman_template',73 'normal',74 'high'75 );76 }77 78 /**79 * Render the checklist items meta box.80 *81 * @param WP_Post $post82 */83 public function render_items_metabox($post)84 {85 // Security nonce.86 wp_nonce_field('ediworman_save_template_items', 'ediworman_template_items_nonce');87 88 $items = get_post_meta($post->ID, '_ediworman_items', true);89 if (! is_array($items)) {90 $items = [];91 }92 93 $value = implode("\n", $items);94 ?>95 <p>96 <?php esc_html_e('Enter one checklist item per line. These items will appear in the editor sidebar for posts using this template.', 'editorial-workflow-manager'); ?>97 </p>98 <textarea99 name="ediworman_template_items"100 id="ediworman_template_items"101 style="width: 100%; min-height: 200px;"><?php echo esc_textarea($value); ?></textarea>102 <?php103 }104 105 /**106 * Save the checklist items when the template is saved.107 *108 * @param int $post_id109 * @param WP_Post $post110 */111 public function save_template_meta($post_id, $post)112 {113 // Autosave? Bail.114 if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {115 return;116 }117 118 // Right post type?119 if ($post->post_type !== 'ediworman_template') {120 return;121 }122 123 // Nonce check.124 $nonce = isset($_POST['ediworman_template_items_nonce'])125 ? sanitize_text_field(wp_unslash($_POST['ediworman_template_items_nonce']))126 : '';127 128 if (! $nonce || ! wp_verify_nonce($nonce, 'ediworman_save_template_items')) {129 return;130 }131 132 // Capability check.133 if (! current_user_can('edit_post', $post_id)) {134 return;135 }136 137 // If field missing, delete meta (or keep existing - your choice).138 if (! isset($_POST['ediworman_template_items'])) {139 delete_post_meta($post_id, '_ediworman_items');140 return;141 }142 143 $raw = sanitize_textarea_field(wp_unslash($_POST['ediworman_template_items']));144 145 $lines = preg_split('/\R/', $raw); // split on any newline type146 $items = array_filter(147 array_map(148 static function ($line) {149 return sanitize_text_field(trim($line));150 },151 $lines152 )153 );154 155 update_post_meta($post_id, '_ediworman_items', $items);156 }157 } -
editorial-workflow-manager/trunk/readme.txt
r3457373 r3472097 5 5 Tested up to: 6.9 6 6 Requires PHP: 7.4 7 Stable tag: 0.3. 47 Stable tag: 0.3.5 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 101 101 == Changelog == 102 102 103 = 0.3.5 = 104 * Maintenance release: improved PHPCS compliance (PHPDoc comments, Yoda conditions, line endings, and callback parameter cleanup). 105 103 106 = 0.3.4 = 104 107 * Various security improvements (Better data validation and sanitization). -
editorial-workflow-manager/trunk/uninstall.php
r3442109 r3472097 4 4 * 5 5 * Runs when the plugin is deleted from WordPress. 6 * 7 * @package EditorialWorkflowManager 6 8 */ 7 9 8 if ( ! defined('WP_UNINSTALL_PLUGIN')) {9 exit;10 if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) { 11 exit; 10 12 } 11 13 … … 18 20 * @return void 19 21 */ 20 function ediworman_uninstall_cleanup_site() 21 { 22 // Delete settings. 23 delete_option('ediworman_settings'); 22 function ediworman_uninstall_cleanup_site() { 23 // Delete settings. 24 delete_option( 'ediworman_settings' ); 24 25 25 // Remove post meta stored on edited content.26 delete_post_meta_by_key('_ediworman_checked_items');27 delete_post_meta_by_key('_ediworman_last_editor');26 // Remove post meta stored on edited content. 27 delete_post_meta_by_key( '_ediworman_checked_items' ); 28 delete_post_meta_by_key( '_ediworman_last_editor' ); 28 29 29 // Remove checklist template item meta (in case templates are left behind).30 delete_post_meta_by_key('_ediworman_items');30 // Remove checklist template item meta (in case templates are left behind). 31 delete_post_meta_by_key( '_ediworman_items' ); 31 32 32 // Delete checklist templates CPT posts.33 $limit = 100;33 // Delete checklist templates CPT posts. 34 $limit = 100; 34 35 35 do {36 $template_ids = get_posts(37 [ 38 'post_type' => 'ediworman_template',39 'post_status' => 'any',40 'posts_per_page' => $limit,41 'fields' => 'ids',42 'no_found_rows' => true,43 'orderby' => 'ID',44 'order' => 'ASC',45 ] 46 );36 do { 37 $template_ids = get_posts( 38 array( 39 'post_type' => 'ediworman_template', 40 'post_status' => 'any', 41 'posts_per_page' => $limit, 42 'fields' => 'ids', 43 'no_found_rows' => true, 44 'orderby' => 'ID', 45 'order' => 'ASC', 46 ) 47 ); 47 48 48 foreach ($template_ids as $template_id) {49 wp_delete_post((int) $template_id, true);50 }51 } while (! empty($template_ids));49 foreach ( $template_ids as $template_id ) { 50 wp_delete_post( (int) $template_id, true ); 51 } 52 } while ( ! empty( $template_ids ) ); 52 53 } 53 54 … … 57 58 * @return void 58 59 */ 59 function ediworman_uninstall() 60 { 61 if (is_multisite()) { 62 $ediworman_site_ids = get_sites( 63 [ 64 'fields' => 'ids', 65 ] 66 ); 60 function ediworman_uninstall() { 61 if ( is_multisite() ) { 62 $ediworman_site_ids = get_sites( 63 array( 64 'fields' => 'ids', 65 ) 66 ); 67 67 68 foreach ($ediworman_site_ids as $ediworman_site_id) {69 switch_to_blog((int) $ediworman_site_id);70 ediworman_uninstall_cleanup_site();71 restore_current_blog();72 }68 foreach ( $ediworman_site_ids as $ediworman_site_id ) { 69 switch_to_blog( (int) $ediworman_site_id ); 70 ediworman_uninstall_cleanup_site(); 71 restore_current_blog(); 72 } 73 73 74 // In case anything was stored at network level in the future.75 delete_site_option('ediworman_settings');76 return;77 }74 // In case anything was stored at network level in the future. 75 delete_site_option( 'ediworman_settings' ); 76 return; 77 } 78 78 79 ediworman_uninstall_cleanup_site();79 ediworman_uninstall_cleanup_site(); 80 80 } 81 81
Note: See TracChangeset
for help on using the changeset viewer.