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 }