wordpress-turnstile

Wordpress paywall plugin
Log | Files | Refs | README | LICENSE

class-field-manager.php (6388B)


      1 <?php
      2 /**
      3  * Field Manager
      4  *
      5  * Manages the price category meta fields on post types.
      6  */
      7 
      8 if (!defined('ABSPATH')) {
      9     exit;
     10 }
     11 
     12 class Taler_Field_Manager {
     13 
     14     /**
     15      * Add price category fields to specified post types
     16      *
     17      * @param array $post_types Array of post type names
     18      */
     19     public static function add_fields_to_post_types($post_types) {
     20         foreach ($post_types as $post_type) {
     21             // Verify post type exists
     22             if (!post_type_exists($post_type)) {
     23                 continue;
     24             }
     25 
     26             self::add_price_category_field($post_type);
     27         }
     28     }
     29 
     30     /**
     31      * Add price category field to a specific post type
     32      *
     33      * @param string $post_type The post type name
     34      */
     35     protected static function add_price_category_field($post_type) {
     36         // Register the meta box for this post type
     37         add_action('add_meta_boxes', function() use ($post_type) {
     38             add_meta_box(
     39                 'taler_turnstile_price_category',
     40                 __('Taler Turnstile Price Category', 'taler-turnstile'),
     41                 array('Taler_Field_Manager', 'render_price_category_meta_box'),
     42                 $post_type,
     43                 'side',
     44                 'default'
     45             );
     46         });
     47 
     48         // Register the meta field
     49         register_post_meta($post_type, '_taler_price_category', array(
     50             'type' => 'string',
     51             'description' => __('Selected price category for this content', 'taler-turnstile'),
     52             'single' => true,
     53             'show_in_rest' => true,
     54             'sanitize_callback' => 'sanitize_key',
     55             'auth_callback' => function() {
     56                 return current_user_can('edit_posts');
     57             }
     58         ));
     59 
     60         // Save the meta field
     61         add_action('save_post_' . $post_type, array('Taler_Field_Manager', 'save_price_category_meta'), 10, 2);
     62     }
     63 
     64     /**
     65      * Render the price category meta box
     66      *
     67      * @param WP_Post $post The post object
     68      */
     69     public static function render_price_category_meta_box($post) {
     70         wp_nonce_field('taler_price_category_meta', 'taler_price_category_nonce');
     71 
     72         $selected_category = get_post_meta($post->ID, '_taler_price_category', true);
     73         $categories = Taler_Price_Category::get_all();
     74         ?>
     75         <p>
     76             <label for="taler_price_category">
     77                 <?php esc_html_e('Price Category:', 'taler-turnstile'); ?>
     78             </label>
     79             <select name="taler_price_category" id="taler_price_category" style="width: 100%;">
     80                 <option value=""><?php esc_html_e('-- None --', 'taler-turnstile'); ?></option>
     81                 <?php foreach ($categories as $id => $category): ?>
     82                     <option value="<?php echo esc_attr($id); ?>" <?php selected($selected_category, $id); ?>>
     83                         <?php echo esc_html($category['label']); ?>
     84                     </option>
     85                 <?php endforeach; ?>
     86             </select>
     87         </p>
     88         <p class="description">
     89             <?php esc_html_e('Select a price category for this content. Leave empty for free access.', 'taler-turnstile'); ?>
     90         </p>
     91         <?php
     92     }
     93 
     94     /**
     95      * Save the price category meta field
     96      *
     97      * @param int $post_id The post ID
     98      * @param WP_Post $post The post object
     99      */
    100     public static function save_price_category_meta($post_id, $post) {
    101         // Check nonce
    102         // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
    103         // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash
    104         // FIXME: reviewers say sanitization is needed here for wp_verify_nonce()
    105         // But https://wordpress.stackexchange.com/questions/256513/should-nonce-be-sanitized
    106         // says this is not needed and points to
    107         // https://github.com/WordPress/wordpress-develop/blob/6.2/src/wp-includes/pluggable.php#L1271
    108         // for an example where the wordpress core also does not do it.
    109         // I truly do not see why the reviewers believe sanitization is useful here.
    110         if (!isset($_POST['taler_price_category_nonce']) ||
    111             !wp_verify_nonce($_POST['taler_price_category_nonce'], 'taler_price_category_meta')) {
    112             return;
    113         }
    114 
    115         // Check autosave
    116         if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
    117             return;
    118         }
    119 
    120         // Check permissions
    121         if (!current_user_can('edit_post', $post_id)) {
    122             return;
    123         }
    124 
    125         // Save or delete the meta field
    126         if (isset($_POST['taler_price_category'])) {
    127             $category = sanitize_key($_POST['taler_price_category']);
    128 
    129             if (empty($category)) {
    130                 delete_post_meta($post_id, '_taler_price_category');
    131             } else {
    132                 update_post_meta($post_id, '_taler_price_category', $category);
    133             }
    134         }
    135     }
    136 
    137     /**
    138      * Remove price category fields from specified post types
    139      *
    140      * @param array $post_types Array of post type names
    141      */
    142     public static function remove_fields_from_post_types($post_types) {
    143         foreach ($post_types as $post_type) {
    144             self::remove_price_category_field($post_type);
    145         }
    146     }
    147 
    148     /**
    149      * Remove price category field from a specific post type
    150      *
    151      * @param string $post_type The post type name
    152      */
    153     protected static function remove_price_category_field($post_type) {
    154         // Unregister the meta field
    155         unregister_post_meta($post_type, '_taler_price_category');
    156 
    157         // Note: Meta boxes are removed automatically when not registered
    158         // We don't need to explicitly remove them since they're added via hooks
    159     }
    160 
    161     /**
    162      * Cleanup all price category meta when no post types are using it
    163      */
    164     public static function cleanup_field_storage() {
    165         global $wpdb;
    166 
    167         // Delete all price category meta from all posts
    168         $wpdb->delete(
    169             $wpdb->postmeta,
    170             array('meta_key' => '_taler_price_category'),
    171             array('%s')
    172         );
    173     }
    174 
    175     /**
    176      * Initialize field manager hooks
    177      */
    178     public static function init() {
    179         $enabled_types = get_option('taler_turnstile_enabled_post_types', array('post'));
    180 
    181         if (!empty($enabled_types)) {
    182             self::add_fields_to_post_types($enabled_types);
    183         }
    184     }
    185 }