wordpress-turnstile

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

commit e61027f22fd27d34db4e41e8063ecd01333ecfbd
parent 5c1251397d1fc160326885fc17cd72f592d945ba
Author: Christian Grothoff <christian@grothoff.org>
Date:   Mon,  3 Nov 2025 17:51:09 +0100

sanitization

Diffstat:
MREADME.md | 12+++++++++---
Madmin/class-admin-settings.php | 6+++++-
Madmin/class-price-category-admin.php | 6+++++-
Madmin/class-subscription-prices-admin.php | 6+++++-
Mincludes/class-content-filter.php | 32+++++++++++++++++---------------
Mincludes/class-field-manager.php | 2++
Mincludes/class-price-category.php | 3+++
Mincludes/class-taler-merchant-api.php | 2+-
Mtaler-turnstile.php | 16++++------------
9 files changed, 51 insertions(+), 34 deletions(-)

diff --git a/README.md b/README.md @@ -1,5 +1,11 @@ -# GNU Taler Turnstile for WordPress - +# GNU Taler Turnstile +Contributors: grothoff +Tags: payment, monetization, privacy, subscription +Requires at least: 6.3 +Tested up to: 6.8 +Stable tag: 1.1.0 +Requires PHP: 8.0 +License: GPLv2 or later A WordPress plugin that adds price fields to posts and requires payment via GNU Taler for access. @@ -175,7 +181,7 @@ For issues and questions: ## License -This plugin is licensed under GNU GPL v3 or later. +This plugin is licensed under GNU GPL v2 or later. ## Credits diff --git a/admin/class-admin-settings.php b/admin/class-admin-settings.php @@ -163,6 +163,7 @@ class Taler_Turnstile_Admin_Settings { 'taler_turnstile_enabled_post_types', 'fields_added', sprintf( + /* translators: placeholder is category type to which fields were added */ __('Price category fields added to: %s', 'taler-turnstile'), implode(', ', $type_labels) ), @@ -189,6 +190,7 @@ class Taler_Turnstile_Admin_Settings { 'taler_turnstile_enabled_post_types', 'fields_removed', sprintf( + /* translators: placeholder is category type to which fields were removed */ __('Price category fields removed from: %s', 'taler-turnstile'), implode(', ', $type_labels) ), @@ -285,6 +287,7 @@ class Taler_Turnstile_Admin_Settings { add_settings_error( 'taler_turnstile_settings', 'backend_error', + /* translators: placeholder is the numeric HTTP status code */ sprintf(__('Unexpected response (%d) from merchant backend', 'taler-turnstile'), $http_status), 'error' ); @@ -324,7 +327,8 @@ class Taler_Turnstile_Admin_Settings { <p> <?php printf( - esc_html__('Backend configured successfully! You can now %sconfigure subscription prices%s.', 'taler-turnstile'), + /* translators: placeholders are the HTML to generate a link (beginning and end) to the configuration page */ + esc_html__('Backend configured successfully! You can now %1$sconfigure subscription prices%2$s.', 'taler-turnstile'), '<a href="' . esc_url(admin_url('admin.php?page=taler-subscription-prices')) . '">', '</a>' ); diff --git a/admin/class-price-category-admin.php b/admin/class-price-category-admin.php @@ -172,15 +172,19 @@ class Taler_Turnstile_Price_Category_Admin { public function save_price_category() { if (!current_user_can('manage_options')) { - wp_die(__('You do not have sufficient permissions to access this page.', 'taler-turnstile')); + wp_die(esc_html(__('You do not have sufficient permissions to access this page.', 'taler-turnstile'))); } check_admin_referer('taler_save_price_category'); $subscriptions = Taler_Merchant_API::get_subscriptions(); + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash $label = isset($_POST['label']) ? sanitize_text_field($_POST['label']) : ''; + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash $description = isset($_POST['description']) ? sanitize_textarea_field($_POST['description']) : ''; + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $prices = isset($_POST['prices']) ? $_POST['prices'] : array(); // Determine if this is an edit or new category diff --git a/admin/class-subscription-prices-admin.php b/admin/class-subscription-prices-admin.php @@ -71,7 +71,8 @@ class Taler_Subscription_Prices_Admin { <p> <?php printf( - esc_html__('Turnstile payment backend is not configured. Please %sconfigure the backend%s first.', 'taler-turnstile'), + /* translators: placeholders are the HTML to generate the link (beginning and end) */ + esc_html__('Turnstile payment backend is not configured. Please %1$sconfigure the backend%1$s first.', 'taler-turnstile'), '<a href="' . esc_url(admin_url('options-general.php?page=taler-turnstile-settings')) . '">', '</a>' ); @@ -167,6 +168,7 @@ class Taler_Subscription_Prices_Admin { <p class="description"> <?php printf( + /* translators: placeholder is the currency code (like "USD" or "EUR") */ esc_html__('Leave empty to prevent buying this subscription with %s.', 'taler-turnstile'), esc_html($currency_code) ); @@ -196,6 +198,8 @@ class Taler_Subscription_Prices_Admin { check_admin_referer('taler_save_subscription_prices'); + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $subscription_prices = isset($_POST['subscription_prices']) ? $_POST['subscription_prices'] : array(); // Validate and sanitize diff --git a/includes/class-content-filter.php b/includes/class-content-filter.php @@ -83,14 +83,14 @@ class Taler_Content_Filter { $full_subscriptions = Taler_Price_Category::get_full_subscriptions($price_category['prices']); foreach ($full_subscriptions as $subscription_id) { if (self::is_subscriber($subscription_id)) { - debug_log('Subscriber access granted'); + self::debug_log('Subscriber access granted'); return $content; } } // Check if this session already has access if (self::has_session_access($post->ID)) { - debug_log('Session access already granted'); + self::debug_log('Session access already granted'); return $content; } @@ -100,11 +100,11 @@ class Taler_Content_Filter { $order_status = Taler_Merchant_API::check_order_status($order_info['order_id']); if ($order_status && $order_status['paid']) { - info_log('Taler Turnstile: Order was paid, granting session access'); + self::debug_log('Taler Turnstile: Order was paid, granting session access'); self::grant_session_access($post->ID); if (!empty($order_status['subscription_slug'])) { - info_log('Taler Turnstile: Subscription was purchased, granting subscription access'); + self::debug_log('Taler Turnstile: Subscription was purchased, granting subscription access'); self::grant_subscriber_access( $order_status['subscription_slug'], $order_status['subscription_expiration'] @@ -129,7 +129,7 @@ class Taler_Content_Filter { // Need to create a new order if we don't have a valid one if (!$order_info) { - info_log('Creating new order for ' . $post->ID); + self::debug_log('Creating new order for ' . $post->ID); $order_info = Taler_Merchant_API::create_order($post->ID); } @@ -148,7 +148,7 @@ class Taler_Content_Filter { // Store order info in session self::store_order_node_mapping($post->ID, $order_info); - info_log('Showing paywall page for ' . $post->ID); + self::debug_log('Showing paywall page for ' . $post->ID); // User needs to pay - show teaser + payment button return self::render_paywall($post, $order_info); } @@ -222,6 +222,7 @@ class Taler_Content_Filter { <!-- div class="taler-payment-info"> <p class="taler-order-id"> + /* translators: placeholder is the order ID of the merchant backend */ <small><?php printf(esc_html__('Order ID: %s', 'taler-turnstile'), esc_html($order_id)); ?></small> </p> </div --> @@ -275,11 +276,7 @@ class Taler_Content_Filter { session_start(); } - if (!isset($_SESSION['taler_turnstile_subscriptions'][$subscription_slug])) { - return false; - } - - $expiration = $_SESSION['taler_turnstile_subscriptions'][$subscription_slug]; + $expiration = $_SESSION['taler_turnstile_subscriptions'][$subscription_slug] ?? 0; return $expiration >= time(); } @@ -344,10 +341,15 @@ class Taler_Content_Filter { session_start(); } - if (!isset($_SESSION['taler_turnstile_node_orders'][$post_id])) { - return null; - } + return $_SESSION['taler_turnstile_node_orders'][$post_id] ?? NULL; + } - return $_SESSION['taler_turnstile_node_orders'][$post_id]; + /** + * Helper function for logging that is easily turned off. + * + * @param string $msg The log message + */ + private static function debug_log($msg) { + // error_log($msg); } } \ No newline at end of file diff --git a/includes/class-field-manager.php b/includes/class-field-manager.php @@ -99,6 +99,8 @@ class Taler_Field_Manager { */ public static function save_price_category_meta($post_id, $post) { // Check nonce + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash if (!isset($_POST['taler_price_category_nonce']) || !wp_verify_nonce($_POST['taler_price_category_nonce'], 'taler_price_category_meta')) { return; diff --git a/includes/class-price-category.php b/includes/class-price-category.php @@ -99,6 +99,7 @@ class Taler_Price_Category { ); $description = sprintf( + /* translators: placeholder is the currency code (like "USD" or "EUR") */ __('Pay in %s with subscription', 'taler-turnstile'), $currency_code ); @@ -122,6 +123,7 @@ class Taler_Price_Category { ); $description = sprintf( + /* translators: placeholder is the currency code (like "USD" or "EUR") */ __('Buy subscription in %s', 'taler-turnstile'), $currency_code ); @@ -137,6 +139,7 @@ class Taler_Price_Category { } else { // No subscription case $description = sprintf( + /* translators: placeholder is the currency code (like "USD" or "EUR") */ __('Pay in %s', 'taler-turnstile'), $currency_code ); diff --git a/includes/class-taler-merchant-api.php b/includes/class-taler-merchant-api.php @@ -45,7 +45,7 @@ class Taler_Merchant_API { return null; } - $parsed_url = parse_url($backend_url); + $parsed_url = wp_parse_url($backend_url); $path = isset($parsed_url['path']) ? $parsed_url['path'] : '/'; $cleaned_path = preg_replace('#^/instances/[^/]+/?#', '/', $path); diff --git a/taler-turnstile.php b/taler-turnstile.php @@ -1,14 +1,13 @@ <?php /** * Plugin Name: GNU Taler Turnstile - * Plugin URI: https://taler.net + * Plugin URI: https://git.taler.net/wordpress-turnstile.git * Description: Adds price fields to posts and requires payment for access via GNU Taler. - * Version: 0.9.0 + * Version: 1.1.0 * Author: GNU Taler - * Author URI: https://taler.net - * License: GPL v3 or later + * Author URI: https://grothoff.org/christian/ + * License: GPL v2 or later * Text Domain: taler-turnstile - * Domain Path: /languages */ // Exit if accessed directly @@ -57,15 +56,8 @@ class Taler_Turnstile { } public function init() { - load_plugin_textdomain('taler-turnstile', false, dirname(plugin_basename(__FILE__)) . '/languages'); - - // Initialize field manager Taler_Field_Manager::init(); - - // Initialize content filter Taler_Content_Filter::init(); - - // Initialize admin classes if (is_admin()) { new Taler_Turnstile_Admin_Settings(); new Taler_Turnstile_Price_Category_Admin();