PluginProbe
Subscriptions for WooCommerce with Stripe Recurring Payments / 2.4.0
Subscriptions for WooCommerce with Stripe Recurring Payments v2.4.0
2.4.0 2.3.0 2.2.0 2.1.0 2.0.0 1.11.2 1.11.1 1.11.0 1.10.9 1.10.8 1.10.7 1.10.6 1.10.5 1.10.4 1.10.3 1.10.2 1.10.1 1.10.0 1.9.6 1.9.5 trunk 1.3.0 1.3.1 1.3.2 1.4.0 All 65 releases
← All changes | includes/Admin/Settings.php +181 -47 1.10.8 → 2.4.0 View file →
@@ -1,8 +1,10 @@
1 1 <?php
2 2
3 3 namespace SpringDevs\Subscription\Admin;
4 4
5 +use SpringDevs\Subscription\Ajax;
6 +
5 7 /**
6 8 * Class Settings
7 9 *
8 10 * @package SpringDevs\Subscription\Admin
@@ -24,8 +26,9 @@
24 26
25 27 add_action( 'admin_menu', array( $this, 'admin_menu' ) );
26 28 add_action( 'admin_init', array( $this, 'register_settings' ) );
27 29 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_wc_admin_styles' ) );
30 + add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_installer_script' ) );
28 31 }
29 32
30 33 /**
31 34 * Register submenu on `Subscriptions` menu.
@@ -61,17 +64,17 @@
61 64 // Setting fields.
62 65 $settings_fields = [
63 66 [
64 67 'type' => 'heading',
65 - 'group' => 'main',
68 + 'group' => 'renewals',
66 69 'priority' => 0,
67 70 'field_data' => [
68 - 'title' => __( 'General Settings', 'subscription' ),
71 + 'title' => __( 'Renewals', 'subscription' ),
69 72 ],
70 73 ],
71 74 [
72 75 'type' => 'select',
73 - 'group' => 'main',
76 + 'group' => 'renewals',
74 77 'priority' => 1,
75 78 'field_data' => [
76 79 'id' => 'wp_subscription_renewal_process',
77 80 'title' => __( 'Renewal Process', 'subscription' ),
@@ -79,36 +82,44 @@
79 82 'options' => [
80 83 'auto' => __( 'Automatic', 'subscription' ),
81 84 'manual' => __( 'Manual', 'subscription' ),
82 85 ],
83 - 'selected' => esc_attr( get_option( 'wp_subscription_renewal_process', 'auto' ) ),
86 + 'selected' => esc_attr( subscrpt_get_renewal_process() ),
84 87 ],
85 88 ],
86 89 [
87 90 'type' => 'input',
88 - 'group' => 'main',
91 + 'group' => 'renewals',
89 92 'priority' => 2,
90 93 'field_data' => [
91 94 'id' => 'wp_subscription_manual_renew_cart_notice',
92 95 'title' => __( 'Renewal Cart Notice', 'subscription' ),
93 96 'description' => __( 'Display Notice when Renewal Subscription product add to cart. Only available for Manual Renewal Process.', 'subscription' ),
94 - 'value' => esc_attr( get_option( 'wp_subscription_manual_renew_cart_notice' ) ),
97 + 'value' => esc_attr( subscrpt_get_manual_renew_cart_notice() ),
95 98 ],
96 99 ],
97 100 [
98 101 'type' => 'toggle',
99 - 'group' => 'main',
102 + 'group' => 'renewals',
100 103 'priority' => 3,
101 104 'field_data' => [
102 105 'id' => 'wp_subscription_stripe_auto_renew',
103 106 'title' => __( 'Stripe Auto Renewal', 'subscription' ),
104 107 'label' => __( 'Accept Stripe Auto Renewals', 'subscription' ),
105 - 'description' => sprintf(
106 - /* translators: HTML tags */
107 - __( '%1$s WooCommerce Stripe Payment Gateway %2$s plugin is required!', 'subscription' ),
108 - '<a href="https://wordpress.org/plugins/woocommerce-gateway-stripe/" target="_blank">',
109 - '</a>'
110 - ),
108 + 'description' => class_exists( 'WC_Stripe' )
109 + ? sprintf(
110 + /* translators: 1: opening plugin link tag, 2: closing link tag, 3: opening settings link tag */
111 + __( '&#10003; %1$sWooCommerce Stripe Payment Gateway%2$s is active. %3$sConfigure%2$s', 'subscription' ),
112 + '<a href="https://wordpress.org/plugins/woocommerce-gateway-stripe/" target="_blank">',
113 + '</a>',
114 + '<a href="' . esc_url( admin_url( 'admin.php?page=wc-settings&tab=checkout&section=stripe' ) ) . '">'
115 + )
116 + : sprintf(
117 + /* translators: HTML tags */
118 + __( '%1$s WooCommerce Stripe Payment Gateway %2$s plugin is required!', 'subscription' ),
119 + '<a href="https://wordpress.org/plugins/woocommerce-gateway-stripe/" target="_blank">',
120 + '</a>'
121 + ) . $this->get_stripe_plugin_action(),
111 122 'value' => '1',
112 123 'checked' => '1' === get_option( 'wp_subscription_stripe_auto_renew', '1' ),
113 124 ],
114 125 ],
@@ -113,9 +124,9 @@
113 124 ],
114 125 ],
115 126 [
116 127 'type' => 'toggle',
117 - 'group' => 'main',
128 + 'group' => 'renewals',
118 129 'priority' => 4,
119 130 'field_data' => [
120 131 'id' => 'wp_subscription_auto_renewal_toggle',
121 132 'title' => __( 'Auto Renewal Toggle', 'subscription' ),
@@ -125,11 +136,61 @@
125 136 'checked' => '1' === get_option( 'wp_subscription_auto_renewal_toggle', '1' ),
126 137 ],
127 138 ],
128 139 [
140 + 'type' => 'heading',
141 + 'group' => 'stock_management',
142 + 'priority' => 0,
143 + 'field_data' => [
144 + 'title' => __( 'Stock Management', 'subscription' ),
145 + ],
146 + ],
147 + [
148 + 'type' => 'toggle',
149 + 'group' => 'stock_management',
150 + 'priority' => 1,
151 + 'field_data' => [
152 + 'id' => 'wp_subscription_stock_pause',
153 + 'title' => __( 'Pause on Low Stock', 'subscription' ),
154 + 'label' => __( 'Pause subscriptions when the product runs low on stock', 'subscription' ),
155 + 'description' => __( 'Active subscriptions of a product are put on hold when its stock reaches the threshold, and the customer is emailed, so no renewal is charged for something you cannot ship.', 'subscription' ),
156 + 'value' => '1',
157 + 'checked' => '1' === get_option( 'wp_subscription_stock_pause', '' ),
158 + ],
159 + ],
160 + [
161 + 'type' => 'input',
162 + 'group' => 'stock_management',
163 + 'priority' => 2,
164 + 'field_data' => [
165 + 'id' => 'wp_subscription_stock_pause_threshold',
166 + 'title' => __( 'Stock Threshold', 'subscription' ),
167 + 'description' => __( 'Pause when the stock quantity is at or below this number. Products that do not manage a quantity pause only when out of stock.', 'subscription' ),
168 + 'type' => 'number',
169 + 'value' => esc_attr( get_option( 'wp_subscription_stock_pause_threshold', 0 ) ),
170 + 'attributes' => [
171 + 'min' => 0,
172 + 'step' => 1,
173 + ],
174 + ],
175 + ],
176 + [
177 + 'type' => 'toggle',
178 + 'group' => 'stock_management',
179 + 'priority' => 3,
180 + 'field_data' => [
181 + 'id' => 'wp_subscription_stock_auto_resume',
182 + 'title' => __( 'Auto Resume', 'subscription' ),
183 + 'label' => __( 'Resume paused subscriptions when stock recovers', 'subscription' ),
184 + 'description' => __( 'Only subscriptions paused because of stock are resumed; ones you put on hold yourself stay on hold.', 'subscription' ),
185 + 'value' => '1',
186 + 'checked' => '1' === get_option( 'wp_subscription_stock_auto_resume', '1' ),
187 + ],
188 + ],
189 + [
129 190 'type' => 'select',
130 - 'group' => 'main',
131 - 'priority' => 5,
191 + 'group' => 'role_based_settings',
192 + 'priority' => 2,
132 193 'field_data' => [
133 194 'id' => 'wp_subscription_active_role',
134 195 'title' => __( 'Subscriber Default Role', 'subscription' ),
135 196 'description' => __( 'When a subscription is activated, either manually or after a successful purchase, new users will be assigned this role.', 'subscription' ),
@@ -138,10 +199,10 @@
138 199 ],
139 200 ],
140 201 [
141 202 'type' => 'select',
142 - 'group' => 'main',
143 - 'priority' => 6,
203 + 'group' => 'role_based_settings',
204 + 'priority' => 3,
144 205 'field_data' => [
145 206 'id' => 'wp_subscription_unactive_role',
146 207 'title' => __( 'Subscriber Inactive Role', 'subscription' ),
147 208 'description' => __( "If a subscriber's subscription is manually cancelled or expires, they will be assigned this role.", 'subscription' ),
@@ -148,34 +209,8 @@
148 209 'options' => $roles,
149 210 'selected' => esc_attr( get_option( 'wp_subscription_unactive_role', 'customer' ) ),
150 211 ],
151 212 ],
152 - [
153 - 'type' => 'toggle',
154 - 'group' => 'main',
155 - 'priority' => 9.5,
156 - 'field_data' => [
157 - 'id' => 'subscrpt_cancellation_feedback_enabled',
158 - 'title' => __( 'Cancellation Survey', 'subscription' ),
159 - 'label' => __( 'Ask customers why they are cancelling', 'subscription' ),
160 - 'description' => __( 'Show a short cancellation survey when a customer cancels a subscription, and record the reason for churn tracking.', 'subscription' ),
161 - 'value' => '1',
162 - 'checked' => '1' === get_option( 'subscrpt_cancellation_feedback_enabled', '1' ),
163 - ],
164 - ],
165 - [
166 - 'type' => 'toggle',
167 - 'group' => 'main',
168 - 'priority' => 9.6,
169 - 'field_data' => [
170 - 'id' => 'subscrpt_cancellation_feedback_comment',
171 - 'title' => __( 'Survey Comment Box', 'subscription' ),
172 - 'label' => __( 'Allow an additional comment', 'subscription' ),
173 - 'description' => __( 'Show an optional free-text comment field in the cancellation survey.', 'subscription' ),
174 - 'value' => '1',
175 - 'checked' => '1' === get_option( 'subscrpt_cancellation_feedback_comment', '1' ),
176 - ],
177 - ],
178 213 ];
179 214
180 215 // Allow other modules to add/modify settings fields.
181 216 $settings_fields = apply_filters( 'subscrpt_settings_fields', $settings_fields );
@@ -187,8 +222,61 @@
187 222 $this->settings_fields = $settings_fields;
188 223 }
189 224
190 225 /**
226 + * Install / Activate link for the Stripe gateway plugin.
227 + *
228 + * Rendered inside a field description, which goes through `wp_kses_post()`,
229 + * so the link carries data attributes rather than an inline handler;
230 + * `integration_settings.js` picks it up.
231 + *
232 + * @return string Link HTML, or an empty string when the user cannot act on it.
233 + */
234 + private function get_stripe_plugin_action() {
235 + $slug = 'woocommerce-gateway-stripe';
236 + $installed = '' !== Ajax::get_installed_plugin_file( $slug );
237 +
238 + if ( $installed && current_user_can( 'activate_plugins' ) ) {
239 + $label = __( 'Activate', 'subscription' );
240 + $busy_label = __( 'Activating…', 'subscription' );
241 + } elseif ( ! $installed && current_user_can( 'install_plugins' ) ) {
242 + $label = __( 'Install & Activate', 'subscription' );
243 + $busy_label = __( 'Installing…', 'subscription' );
244 + } else {
245 + return '';
246 + }
247 +
248 + return sprintf(
249 + ' <a href="#" data-subscrpt-install-plugin="%1$s" data-busy-label="%2$s">%3$s</a>',
250 + esc_attr( $slug ),
251 + esc_attr( $busy_label ),
252 + esc_html( $label )
253 + );
254 + }
255 +
256 + /**
257 + * Enqueue the plugin installer on the settings page.
258 + *
259 + * @param string $hook The current admin page hook.
260 + */
261 + public function enqueue_installer_script( $hook ) {
262 + if ( false === strpos( $hook, 'wp-subscription-settings' ) ) {
263 + return;
264 + }
265 +
266 + wp_enqueue_script( 'subscrpt-integrations', SUBSCRPT_ASSETS . '/js/integration_settings.js', [], SUBSCRPT_VERSION, true );
267 +
268 + wp_localize_script(
269 + 'subscrpt-integrations',
270 + 'subscrptIntegrations',
271 + array(
272 + 'nonce' => wp_create_nonce( 'subscrpt_integration_install_nonce' ),
273 + 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
274 + )
275 + );
276 + }
277 +
278 + /**
191 279 * Register settings options.
192 280 **/
193 281 public function register_settings() {
194 282 register_setting(
@@ -232,9 +320,9 @@
232 320 )
233 321 );
234 322 register_setting(
235 323 'wp_subscription_settings',
236 - 'wp_subscription_auto_renewal_toggle',
324 + 'wp_subscription_stock_pause',
237 325 array(
238 326 'type' => 'string',
239 327 'sanitize_callback' => 'sanitize_text_field',
240 328 )
@@ -240,10 +328,18 @@
240 328 )
241 329 );
242 330 register_setting(
243 331 'wp_subscription_settings',
244 - 'subscrpt_cancellation_feedback_enabled',
332 + 'wp_subscription_stock_pause_threshold',
245 333 array(
334 + 'type' => 'integer',
335 + 'sanitize_callback' => 'absint',
336 + )
337 + );
338 + register_setting(
339 + 'wp_subscription_settings',
340 + 'wp_subscription_stock_auto_resume',
341 + array(
246 342 'type' => 'string',
247 343 'sanitize_callback' => 'sanitize_text_field',
248 344 )
249 345 );
@@ -248,9 +344,9 @@
248 344 )
249 345 );
250 346 register_setting(
251 347 'wp_subscription_settings',
252 - 'subscrpt_cancellation_feedback_comment',
348 + 'wp_subscription_auto_renewal_toggle',
253 349 array(
254 350 'type' => 'string',
255 351 'sanitize_callback' => 'sanitize_text_field',
256 352 )
@@ -263,8 +359,10 @@
263 359 * Settings HTML.
264 360 */
265 361 public function settings_content() {
266 362 $settings_fields = $this->settings_fields;
363 + $category_groups = SettingsHelper::category_groups( $settings_fields );
364 + $active_cat = $this->get_active_category( $category_groups );
267 365
268 366 // Header.
269 367 $menu = new Menu();
270 368 $menu->render_admin_header( __( 'Settings', 'subscription' ) );
@@ -272,8 +370,44 @@
272 370 include 'views/settings.php';
273 371
274 372 // Footer.
275 373 $menu->render_admin_footer();
374 + }
375 +
376 + /**
377 + * Which settings section the page opens on.
378 + *
379 + * Read from `?cat=`, and it has to be a query argument rather than a
380 + * fragment: the form posts to `options.php`, which redirects back to
381 + * `_wp_http_referer`, and a fragment never reaches the server. Keeping the
382 + * section in the query string is what returns you to the panel you saved
383 + * from.
384 + *
385 + * Validated against the sections that actually hold groups, so an unknown
386 + * or hostile value — or one naming a section only Pro fills — opens the
387 + * first real section rather than an empty panel.
388 + *
389 + * @param array<string,string[]> $category_groups Section key => group keys.
390 + * @return string Section key, or an empty string when there are no groups.
391 + */
392 + private function get_active_category( array $category_groups ) {
393 + $cats = array_keys( $category_groups );
394 +
395 + if ( empty( $cats ) ) {
396 + return '';
397 + }
398 +
399 + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only section selection, validated against the list above.
400 + $requested = isset( $_GET['cat'] ) ? sanitize_key( wp_unslash( $_GET['cat'] ) ) : '';
401 +
402 + // `all` stacks every section on one page; a known section opens just that
403 + // one. With nothing requested (or something invalid), open the first
404 + // section rather than the stacked "All Settings" view.
405 + if ( 'all' === $requested || in_array( $requested, $cats, true ) ) {
406 + return $requested;
407 + }
408 +
409 + return $cats[0];
276 410 }
277 411
278 412 /**
279 413 * Enqueue WooCommerce admin styles for settings page.