wordpress-turnstile

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

class-admin-settings.php (14507B)


      1 <?php
      2 /**
      3  * Admin Settings Page
      4  *
      5  * Handles the main plugin settings page.
      6  */
      7 
      8 if (!defined('ABSPATH')) {
      9     exit;
     10 }
     11 
     12 class Taler_Turnstile_Admin_Settings {
     13 
     14     public function __construct() {
     15         add_action('admin_init', array($this, 'register_settings'));
     16     }
     17 
     18     public function register_settings() {
     19         register_setting('taler_turnstile_settings', 'taler_turnstile_enabled_post_types', array(
     20             'type' => 'array',
     21             'sanitize_callback' => array($this, 'sanitize_post_types')
     22         ));
     23 
     24         register_setting('taler_turnstile_settings', 'taler_turnstile_payment_backend_url', array(
     25             'type' => 'string',
     26             'sanitize_callback' => array($this, 'sanitize_backend_url')
     27         ));
     28 
     29         register_setting('taler_turnstile_settings', 'taler_turnstile_access_token', array(
     30             'type' => 'string',
     31             'sanitize_callback' => array($this, 'sanitize_access_token')
     32         ));
     33 
     34         register_setting('taler_turnstile_settings', 'taler_turnstile_grant_access_on_error', array(
     35             'type' => 'boolean',
     36             'sanitize_callback' => array($this, 'sanitize_grant_access_option')
     37         ));
     38 
     39         register_setting('taler_turnstile_settings', 'taler_turnstile_subscription_prices', array(
     40             'type' => 'array',
     41             'sanitize_callback' => array($this, 'sanitize_subscription_prices'),
     42             'default' => array()
     43         ));
     44 
     45         // Add settings sections
     46         add_settings_section(
     47             'taler_turnstile_main_section',
     48             __('Basic Settings', 'taler-turnstile'),
     49             array($this, 'main_section_callback'),
     50             'taler_turnstile_settings'
     51         );
     52 
     53         add_settings_section(
     54             'taler_turnstile_backend_section',
     55             __('Payment Backend Configuration', 'taler-turnstile'),
     56             array($this, 'backend_section_callback'),
     57             'taler_turnstile_settings'
     58         );
     59 
     60         add_settings_field(
     61             'enabled_post_types',
     62             __('Enabled Post Types', 'taler-turnstile'),
     63             array($this, 'enabled_post_types_callback'),
     64             'taler_turnstile_settings',
     65             'taler_turnstile_main_section'
     66         );
     67 
     68         add_settings_field(
     69             'payment_backend_url',
     70             __('Payment Backend URL', 'taler-turnstile'),
     71             array($this, 'payment_backend_url_callback'),
     72             'taler_turnstile_settings',
     73             'taler_turnstile_backend_section'
     74         );
     75 
     76         add_settings_field(
     77             'access_token',
     78             __('Access Token', 'taler-turnstile'),
     79             array($this, 'access_token_callback'),
     80             'taler_turnstile_settings',
     81             'taler_turnstile_backend_section'
     82         );
     83 
     84         add_settings_field(
     85             'grant_access_on_error',
     86             __('Disable Turnstile on Error', 'taler-turnstile'),
     87             array($this, 'grant_access_on_error_callback'),
     88             'taler_turnstile_settings',
     89             'taler_turnstile_backend_section'
     90         );
     91     }
     92 
     93     public function main_section_callback() {
     94         echo '<p>' . esc_html__('Configure which post types should have the price field and be subject to paywall transformation.', 'taler-turnstile') . '</p>';
     95     }
     96 
     97     public function backend_section_callback() {
     98         echo '<p>' . esc_html__('Configure your GNU Taler merchant backend connection.', 'taler-turnstile') . '</p>';
     99     }
    100 
    101     public function enabled_post_types_callback() {
    102         $enabled_types = get_option('taler_turnstile_enabled_post_types', array('post'));
    103         $post_types = get_post_types(array('public' => true), 'objects');
    104 
    105         foreach ($post_types as $post_type) {
    106             $checked = in_array($post_type->name, $enabled_types) ? 'checked' : '';
    107             echo '<label style="display: block; margin-bottom: 5px;">';
    108             echo '<input type="checkbox" name="taler_turnstile_enabled_post_types[]" value="' . esc_attr($post_type->name) . '" ' . $checked . '> ';
    109             echo esc_html($post_type->label);
    110             echo '</label>';
    111         }
    112         echo '<p class="description">' . esc_html__('Select which post types should have the price field and be subject to paywall transformation.', 'taler-turnstile') . '</p>';
    113     }
    114 
    115     public function payment_backend_url_callback() {
    116         $value = get_option('taler_turnstile_payment_backend_url', '');
    117         echo '<input type="url" name="taler_turnstile_payment_backend_url" value="' . esc_attr($value) . '" class="regular-text" />';
    118         echo '<p class="description">' . esc_html__('HTTP URL for the payment backend service. Must end with a "/".', 'taler-turnstile') . '</p>';
    119     }
    120 
    121     public function access_token_callback() {
    122         $value = get_option('taler_turnstile_access_token', '');
    123         echo '<input type="text" name="taler_turnstile_access_token" value="' . esc_attr($value) . '" class="regular-text" />';
    124         echo '<p class="description">' . esc_html__('Access token for authenticating with the payment backend. Must begin with "secret-token:".', 'taler-turnstile') . '</p>';
    125     }
    126 
    127     public function grant_access_on_error_callback() {
    128         $value = get_option('taler_turnstile_grant_access_on_error', false);
    129         echo '<label>';
    130         echo '<input type="checkbox" name="taler_turnstile_grant_access_on_error" value="1" ' . checked($value, true, false) . ' />';
    131         echo ' ' . esc_html__('Allows users gratis access when Turnstile is unable to communicate with the GNU Taler merchant backend. Use this setting to avoid exposing users to configuration errors.', 'taler-turnstile');
    132         echo '</label>';
    133     }
    134 
    135     public function sanitize_post_types($input) {
    136         if (!is_array($input)) {
    137             return array();
    138         }
    139 
    140         $valid_post_types = get_post_types(array('public' => true));
    141         $new_enabled_types = array_values(array_intersect($input, array_keys($valid_post_types)));
    142 
    143         // Get old enabled types to determine what changed
    144         $old_enabled_types = get_option('taler_turnstile_enabled_post_types', array());
    145 
    146         // Find types to add and remove
    147         $types_to_add = array_diff($new_enabled_types, $old_enabled_types);
    148         $types_to_remove = array_diff($old_enabled_types, $new_enabled_types);
    149 
    150         // Add fields to newly enabled post types
    151         if (!empty($types_to_add)) {
    152             Taler_Field_Manager::add_fields_to_post_types($types_to_add);
    153 
    154             // Add success message
    155             $type_labels = array();
    156             foreach ($types_to_add as $type) {
    157                 $post_type_obj = get_post_type_object($type);
    158                 if ($post_type_obj) {
    159                     $type_labels[] = $post_type_obj->label;
    160                 }
    161             }
    162 
    163             if (!empty($type_labels)) {
    164                 add_settings_error(
    165                     'taler_turnstile_enabled_post_types',
    166                     'fields_added',
    167                     sprintf(
    168                         /* translators: placeholder is category type to which fields were added */
    169                         __('Price category fields added to: %s', 'taler-turnstile'),
    170                         implode(', ', $type_labels)
    171                     ),
    172                     'success'
    173                 );
    174             }
    175         }
    176 
    177         // Remove fields from disabled post types
    178         if (!empty($types_to_remove)) {
    179             Taler_Field_Manager::remove_fields_from_post_types($types_to_remove);
    180 
    181             // Add success message
    182             $type_labels = array();
    183             foreach ($types_to_remove as $type) {
    184                 $post_type_obj = get_post_type_object($type);
    185                 if ($post_type_obj) {
    186                     $type_labels[] = $post_type_obj->label;
    187                 }
    188             }
    189 
    190             if (!empty($type_labels)) {
    191                 add_settings_error(
    192                     'taler_turnstile_enabled_post_types',
    193                     'fields_removed',
    194                     sprintf(
    195                         /* translators: placeholder is category type to which fields were removed */
    196                         __('Price category fields removed from: %s', 'taler-turnstile'),
    197                         implode(', ', $type_labels)
    198                     ),
    199                     'success'
    200                 );
    201             }
    202         }
    203 
    204         // Cleanup field storage if no types are enabled
    205         if (empty($new_enabled_types)) {
    206             Taler_Field_Manager::cleanup_field_storage();
    207         }
    208 
    209         return $new_enabled_types;
    210     }
    211 
    212     public function sanitize_backend_url($input) {
    213         $input = trim($input);
    214 
    215         if (empty($input)) {
    216             return '';
    217         }
    218 
    219         if (!str_ends_with($input, '/')) {
    220             add_settings_error(
    221                 'taler_turnstile_payment_backend_url',
    222                 'invalid_url',
    223                 __('Payment backend URL must end with a "/".', 'taler-turnstile'),
    224                 'error'
    225             );
    226             return get_option('taler_turnstile_payment_backend_url');
    227         }
    228 
    229         if (!Taler_Merchant_API::check_config($input)) {
    230             add_settings_error(
    231                 'taler_turnstile_payment_backend_url',
    232                 'invalid_url',
    233                 __('Invalid payment backend URL', 'taler-turnstile'),
    234                 'error'
    235             );
    236             return get_option('taler_turnstile_payment_backend_url');
    237         }
    238 
    239         // Check backend access
    240         $token = get_option('taler_turnstile_access_token');
    241         $http_status = Taler_Merchant_API::check_access($input, $token);
    242 
    243         $this->validate_http_status($http_status);
    244 
    245         return esc_url_raw($input);
    246     }
    247 
    248     public function sanitize_access_token($input) {
    249         $input = trim($input);
    250 
    251         if (empty($input)) {
    252             return '';
    253         }
    254 
    255         if (!str_starts_with($input, 'secret-token:')) {
    256             add_settings_error(
    257                 'taler_turnstile_access_token',
    258                 'invalid_token',
    259                 __('Access token must begin with "secret-token:".', 'taler-turnstile'),
    260                 'error'
    261             );
    262             return get_option('taler_turnstile_access_token');
    263         }
    264 
    265         return sanitize_text_field($input);
    266     }
    267 
    268     /**
    269      * Sanitizer for the "boolean" grant access option as
    270      * requested by WP reviewers. Not sure how to really
    271      * validate that an input of type boolean is an input,
    272      * but the Internets suggest to check that the boolean
    273      * isset(), which makes some sense. So we check that the
    274      * $input isset().
    275      */
    276     public function sanitize_grant_access_option($input) {
    277       return isset($input);
    278     }
    279 
    280     public function sanitize_subscription_prices($input) {
    281       // FIXME: implement!
    282       return true;
    283     }
    284 
    285     private function validate_http_status($http_status) {
    286         if ($http_status === 200 || $http_status === 204) {
    287             return;
    288         }
    289         $messages = array(
    290             502 => __('Bad gateway (502) trying to access the merchant backend', 'taler-turnstile'),
    291             500 => __('Internal server error (500) of the merchant backend reported', 'taler-turnstile'),
    292             404 => __('The specified instance is unknown to the merchant backend', 'taler-turnstile'),
    293             403 => __('Access token not accepted by the merchant backend', 'taler-turnstile'),
    294             401 => __('Access token not accepted by the merchant backend', 'taler-turnstile'),
    295             0 => __('HTTP request failed', 'taler-turnstile')
    296         );
    297 
    298         if (isset($messages[$http_status])) {
    299             add_settings_error(
    300                 'taler_turnstile_settings',
    301                 'backend_error',
    302                 $messages[$http_status],
    303                 'error'
    304             );
    305         } else {
    306             add_settings_error(
    307                 'taler_turnstile_settings',
    308                 'backend_error',
    309                 /* translators: placeholder is the numeric HTTP status code */
    310                 sprintf(__('Unexpected response (%d) from merchant backend', 'taler-turnstile'), $http_status),
    311                 'error'
    312             );
    313         }
    314 
    315         // Warning for incomplete configuration
    316         $backend_url = get_option('taler_turnstile_payment_backend_url');
    317         $access_token = get_option('taler_turnstile_access_token');
    318         $grant_access = get_option('taler_turnstile_grant_access_on_error');
    319 
    320         if (!$grant_access && (empty($backend_url) || empty($access_token))) {
    321             add_settings_error(
    322                 'taler_turnstile_settings',
    323                 'incomplete_config',
    324                 __('Warning: Merchant backend is not configured correctly. To keep the site working, you probably should set the "Disable Turnstile when payment backend is unavailable" option!', 'taler-turnstile'),
    325                 'warning'
    326             );
    327         }
    328     }
    329 
    330     public static function render_settings_page() {
    331         if (!current_user_can('manage_options')) {
    332             return;
    333         }
    334         ?>
    335         <div class="wrap">
    336             <h1><?php echo esc_html(get_admin_page_title()); ?></h1>
    337 
    338             <?php
    339             // Check if backend is configured and show link to subscription prices
    340             $backend_url = get_option('taler_turnstile_payment_backend_url');
    341             $access_token = get_option('taler_turnstile_access_token');
    342 
    343             if (!empty($backend_url) && !empty($access_token)) {
    344                 ?>
    345                 <div class="notice notice-info">
    346                     <p>
    347                         <?php
    348                         printf(
    349                             /* translators: placeholders are the HTML to generate a link (beginning and end) to the configuration page */
    350                             esc_html__('Backend configured successfully! You can now %1$sconfigure subscription prices%2$s.', 'taler-turnstile'),
    351                             '<a href="' . esc_url(admin_url('admin.php?page=taler-subscription-prices')) . '">',
    352                             '</a>'
    353                         );
    354                         ?>
    355                     </p>
    356                 </div>
    357                 <?php
    358             }
    359             ?>
    360 
    361             <form method="post" action="options.php">
    362                 <?php
    363                 settings_fields('taler_turnstile_settings');
    364                 do_settings_sections('taler_turnstile_settings');
    365                 submit_button(__('Save Settings', 'taler-turnstile'));
    366                 ?>
    367             </form>
    368         </div>
    369         <?php
    370     }
    371 }