PluginProbe
Subscriptions for WooCommerce with Stripe Recurring Payments / 2.0.0
Subscriptions for WooCommerce with Stripe Recurring Payments v2.0.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 1.4.1 1.4.2 1.5.0 1.5.1 All 61 releases
← All changes | includes/Admin/Product.php +147 -55 1.4.12.0.0 View file →
@@ -1,5 +1,10 @@
1 1 <?php
2 +/**
3 + * Product editor integration (Subscription tab).
4 + *
5 + * @package SpringDevs\Subscription\Admin
6 + */
2 7
3 8 namespace SpringDevs\Subscription\Admin;
4 9
5 10 use SpringDevs\Subscription\Illuminate\Helper;
@@ -21,9 +26,8 @@
21 26 public function __construct() {
22 27 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
23 28 add_filter( 'woocommerce_product_data_tabs', array( $this, 'register_tab' ) );
24 29 add_action( 'woocommerce_product_data_panels', array( $this, 'subscription_forms' ) );
25 - add_filter( 'product_type_options', array( $this, 'add_product_type_options' ) );
26 30 add_action( 'save_post_product', array( $this, 'save_subscrpt_data' ) );
27 31 add_filter( 'woocommerce_get_price_html', array( $this, 'change_price_html' ), 10, 2 );
28 32 }
29 33
@@ -41,16 +45,17 @@
41 45 }
42 46
43 47 $enabled = $product->get_meta( '_subscrpt_enabled' );
44 48 if ( $enabled ) :
45 - $type = Helper::get_typos( 1, $product->get_meta( '_subscrpt_timing_option' ) );
49 + $type = Helper::get_typos( 1, $product->get_meta( '_subscrpt_timing_option' ), true );
46 50 $meta_trial_time = $product->get_meta( '_subscrpt_trial_timing_per' );
47 51 $trial = null;
48 52 if ( ! empty( $meta_trial_time ) && $meta_trial_time > 0 ) {
49 - $trial = '<br/> + Get ' . $meta_trial_time . ' ' . Helper::get_typos( $meta_trial_time, $product->get_meta( '_subscrpt_trial_timing_option' ) ) . ' free trial!';
53 + $trial_type = Helper::get_typos( $meta_trial_time, $product->get_meta( '_subscrpt_trial_timing_option' ), true );
54 + $trial = '<br/> + Get ' . $meta_trial_time . ' ' . ucfirst( $trial_type ) . ' free trial!';
50 55 }
51 56
52 - $price_html = $price . ' / ' . $type . $trial;
57 + $price_html = $price . ' / ' . ucfirst( $type ) . $trial;
53 58 return $price_html;
54 59 else :
55 60 return $price;
56 61 endif;
@@ -60,8 +65,57 @@
60 65 * Enqueue Assets.
61 66 */
62 67 public function enqueue_assets() {
63 68 wp_enqueue_script( 'sdevs_subscription_admin' );
69 +
70 + // Plan view assets on the product editor. Enqueued whether or not Pro is
71 + // active: with Pro off, free renders its own panel; with Pro on, the plan
72 + // view mounts on Pro's `subscrpt_simple_plan_panel` hook.
73 + $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
74 + if ( ! $screen || 'product' !== $screen->id ) {
75 + return;
76 + }
77 +
78 + wp_enqueue_style( 'subscrpt_admin_components' );
79 + wp_enqueue_script( 'subscrpt_admin_components' );
80 +
81 + // Shared plan-group + selling-plan modal logic. With Pro active the
82 + // Subscription tab exposes "+ New plan group", which drives these modals.
83 + wp_enqueue_script( 'subscrpt_plan_forms_js' );
84 + wp_localize_script( 'subscrpt_plan_forms_js', 'subscrptPlanForms', \SpringDevs\Subscription\Admin\Plans::plan_forms_config() );
85 +
86 + wp_enqueue_script(
87 + 'subscrpt_product_plans_js',
88 + SUBSCRPT_ASSETS . '/js/admin/product-plans.js',
89 + array( 'subscrpt_admin_components', 'subscrpt_plan_forms_js' ),
90 + SUBSCRPT_VERSION,
91 + true
92 + );
93 +
94 + wp_localize_script(
95 + 'subscrpt_product_plans_js',
96 + 'subscrptProductPlans',
97 + array(
98 + 'restUrl' => esc_url_raw( rest_url( 'wpsubscription/v1/plans' ) ),
99 + 'nonce' => wp_create_nonce( 'wp_rest' ),
100 + 'currency' => function_exists( 'get_woocommerce_currency_symbol' )
101 + ? html_entity_decode( get_woocommerce_currency_symbol(), ENT_QUOTES, 'UTF-8' )
102 + : '$',
103 + 'i18n' => array(
104 + 'connectError' => __( 'Could not connect the plan. Please try again.', 'subscription' ),
105 + 'pickPlan' => __( 'Please select a plan.', 'subscription' ),
106 + 'confirmDetach' => __( 'Detach this product from the plan?', 'subscription' ),
107 + 'loading' => __( 'Loading plans…', 'subscription' ),
108 + 'step1' => __( 'Step 1 of 2 · Plan', 'subscription' ),
109 + 'step2' => __( 'Step 2 of 2 · Plan', 'subscription' ),
110 + 'wizardNext' => __( 'Continue', 'subscription' ),
111 + 'wizardBack' => __( 'Back', 'subscription' ),
112 + 'wizardCreate' => __( 'Create', 'subscription' ),
113 + 'addPlan' => __( 'Add plan', 'subscription' ),
114 + 'copyLinkPrompt' => __( 'Copy this direct checkout link:', 'subscription' ),
115 + ),
116 + )
117 + );
64 118 }
65 119
66 120 /**
67 121 * Register "Subscription" option tab.
@@ -71,10 +125,13 @@
71 125 * @return array
72 126 */
73 127 public function register_tab( $tabs ) {
74 128 $tabs['sdevs_subscription'] = array(
75 - 'label' => __( 'Subscription', 'sdevs_subscrpt' ),
76 - 'class' => array( 'show_if_simple', 'show_if_subscription' ),
129 + 'label' => __( 'Subscription', 'subscription' ),
130 + // Shown for simple and variable products. The panel content differs
131 + // by type (see subscription_forms): simple renders here in free;
132 + // variable is filled by Pro via `subscrpt_variable_subscription_panel`.
133 + 'class' => array( 'show_if_simple', 'show_if_variable' ),
77 134 'target' => 'sdevs_subscription_options',
78 135 'priority' => 11,
79 136 );
80 137 return $tabs;
@@ -80,51 +137,65 @@
80 137 return $tabs;
81 138 }
82 139
83 140 /**
84 - * Display Enable Subscription Checkbox on product data tab.
85 - *
86 - * @param array $product_type_options Product Type options on Data tab.
87 - *
88 - * @return array
141 + * Display forms on product create/edit.
89 142 */
90 - public function add_product_type_options( $product_type_options ) {
91 - $screen = get_current_screen();
92 - $value = 'no';
93 - if ( 'edit' === $screen->parent_base ) {
94 - $product = wc_get_product( get_the_ID() );
95 - if ( $product ) {
96 - $value = $product->get_meta( '_subscrpt_enabled' ) ? 'yes' : 'no';
97 - }
98 - }
143 + public function subscription_forms() {
144 + $screen = get_current_screen();
145 + $subscrpt_current = ( $screen && 'edit' === $screen->parent_base ) ? wc_get_product( get_the_ID() ) : null;
99 146
100 - $wrapper_class = apply_filters( 'subscrpt_simple_enable_checkbox_classes', 'show_if_simple' );
101 - $product_type_options['subscrpt_enable'] = array(
102 - 'id' => 'subscrpt_enable',
103 - 'wrapper_class' => $wrapper_class,
104 - 'label' => __( 'Subscription', 'sdevs_subscrpt' ),
105 - 'description' => __( 'Enable Subscriptions', 'sdevs_subscrpt' ),
106 - 'default' => $value,
107 - );
147 + // Variable products: free renders the panel shell; the content is filled
148 + // by Pro (per-variation sections) via the action below. Free itself is
149 + // simple-only, so with Pro inactive it shows an upgrade note.
150 + if ( $subscrpt_current && $subscrpt_current->is_type( 'variable' ) ) {
151 + ?>
152 + <div id="sdevs_subscription_options" class="panel woocommerce_options_panel option_group sdevs-form sdevs_panel show_if_variable" style="padding:10px;">
153 + <?php
154 + /**
155 + * Fires inside the Subscription tab for a variable product. Pro
156 + * hooks this to render its per-variation subscription sections.
157 + *
158 + * @param \WC_Product $subscrpt_current Variable product being edited.
159 + */
160 + do_action( 'subscrpt_variable_subscription_panel', $subscrpt_current );
108 161
109 - return $product_type_options;
110 - }
162 + if ( ! has_action( 'subscrpt_variable_subscription_panel' ) ) {
163 + ?>
164 + <div style="text-align:center;padding:30px 20px;">
165 + <span style="display:inline-flex;align-items:center;justify-content:center;width:48px;height:48px;border-radius:50%;background:var(--wpsubs-brand-light,#fff1eb);color:var(--wpsubs-brand,#ff4d00);">
166 + <span class="dashicons dashicons-lock" style="font-size:24px;width:24px;height:24px;"></span>
167 + </span>
168 + <h3 style="margin:14px 0 6px;font-size:15px;color:var(--wpsubs-text,#1d2327);display:flex;align-items:center;justify-content:center;gap:8px;">
169 + <?php esc_html_e( 'Variable product subscriptions', 'subscription' ); ?>
170 + <span class="wpsubs-badge wpsubs-badge--pro"><?php esc_html_e( 'Pro', 'subscription' ); ?></span>
171 + </h3>
172 + <p style="margin:0 auto;max-width:380px;font-size:13px;line-height:1.6;color:var(--wpsubs-text-muted,#646970);">
173 + <?php esc_html_e( 'Sell each variation as its own subscription, with per-variation billing, trials and sign-up fees.', 'subscription' ); ?>
174 + </p>
175 + <a href="https://wpsubscription.co/?utm_source=plugin&utm_medium=admin&utm_campaign=upgrade_pro" target="_blank" rel="noopener noreferrer" class="wpsubs-btn wpsubs-btn--primary" style="margin-top:16px;">
176 + <?php esc_html_e( 'Upgrade to Pro', 'subscription' ); ?>
177 + </a>
178 + </div>
179 + <?php
180 + }
181 + ?>
182 + </div>
183 + <?php
184 + return;
185 + }
111 186
112 - /**
113 - * Display forms on product create/edit.
114 - */
115 - public function subscription_forms() {
116 187 if ( function_exists( 'subscrpt_pro_activated' ) ) {
117 188 if ( subscrpt_pro_activated() ) {
118 189 do_action( 'subscrpt_simple_pro_fields', get_the_ID() );
119 190 } else {
120 191 $timing_types = array(
121 - 'days' => __( 'Daily', 'sdevs_subscrpt' ),
122 - 'weeks' => __( 'Weekly', 'sdevs_subscrpt' ),
123 - 'months' => __( 'Monthly', 'sdevs_subscrpt' ),
124 - 'years' => __( 'Yearly', 'sdevs_subscrpt' ),
192 + 'days' => __( 'Daily', 'subscription' ),
193 + 'weeks' => __( 'Weekly', 'subscription' ),
194 + 'months' => __( 'Monthly', 'subscription' ),
195 + 'years' => __( 'Yearly', 'subscription' ),
125 196 );
126 - $trial_timing_types = get_timing_types();
197 + $trial_timing_types = wps_subscription_get_timing_types();
127 198 $subscrpt_timing = null;
128 199 $subscrpt_trial_time = null;
129 200 $subscrpt_trial_timing = null;
130 201 $subscrpt_cart_txt = 'subscribe';
@@ -129,22 +200,43 @@
129 200 $subscrpt_trial_timing = null;
130 201 $subscrpt_cart_txt = 'subscribe';
131 202 $subscrpt_user_cancell = 'yes';
132 203 $subscrpt_limit = 'one';
204 + $subscrpt_enabled = false;
133 205
134 - $screen = get_current_screen();
206 + $subscrpt_product = null;
207 + $screen = get_current_screen();
135 208 if ( 'edit' === $screen->parent_base ) {
136 - $product = wc_get_product( get_the_ID() );
137 - if ( $product ) {
138 - $subscrpt_timing = $product->get_meta( '_subscrpt_timing_option' );
139 - $subscrpt_trial_time = $product->get_meta( '_subscrpt_trial_timing_per' );
140 - $subscrpt_trial_timing = $product->get_meta( '_subscrpt_trial_timing_option' );
141 - $subscrpt_cart_txt = $product->get_meta( '_subscrpt_cart_btn_label' );
142 - $subscrpt_user_cancell = $product->get_meta( '_subscrpt_user_cancel' );
143 - $subscrpt_limit = $product->get_meta( '_subscrpt_limit' );
209 + $subscrpt_product = wc_get_product( get_the_ID() );
210 + if ( $subscrpt_product ) {
211 + $subscrpt_enabled = (bool) $subscrpt_product->get_meta( '_subscrpt_enabled' );
212 + $subscrpt_timing = $subscrpt_product->get_meta( '_subscrpt_timing_option' );
213 + $subscrpt_trial_time = $subscrpt_product->get_meta( '_subscrpt_trial_timing_per' );
214 + $subscrpt_trial_timing = $subscrpt_product->get_meta( '_subscrpt_trial_timing_option' );
215 + $subscrpt_cart_txt = $subscrpt_product->get_meta( '_subscrpt_cart_btn_label' );
216 + $subscrpt_user_cancell = $subscrpt_product->get_meta( '_subscrpt_user_cancel' );
217 + $subscrpt_limit = $subscrpt_product->get_meta( '_subscrpt_limit' );
144 218 }
145 219 }
146 - include 'views/product-form.php';
220 +
221 + // Simple products: plan view (default) + hidden classic settings.
222 + // Variable/other products keep the classic-only panel unchanged.
223 + if ( $subscrpt_product && $subscrpt_product->is_type( 'simple' ) && class_exists( '\SpringDevs\Subscription\Admin\Product\Plans' ) ) {
224 + ?>
225 + <div id="sdevs_subscription_options" class="panel woocommerce_options_panel option_group sdevs-form sdevs_panel show_if_simple" style="padding:10px;" data-subscrpt-product-plans data-product-id="<?php echo esc_attr( $subscrpt_product->get_id() ); ?>"<?php echo Product\Plans::should_default_classic( $subscrpt_product ) ? ' data-subscrpt-default-classic="1"' : ''; ?>>
226 + <?php Product\Plans::render_toolbar( $subscrpt_product ); ?>
227 + <div data-subscrpt-plan-view>
228 + <?php Product\Plans::render_plan_view( $subscrpt_product ); ?>
229 + </div>
230 + <div data-subscrpt-classic-view style="display:none;">
231 + <?php require __DIR__ . '/views/product-classic-fields.php'; ?>
232 + </div>
233 + </div>
234 + <?php
235 + Product\Plans::render_checkout_link_modal( $subscrpt_product );
236 + } else {
237 + include 'views/product-form.php';
238 + }
147 239 }
148 240 }
149 241 }
150 242
@@ -161,9 +253,9 @@
161 253 return;
162 254 }
163 255 }
164 256
165 - if ( ! isset( $_POST['_subscript_nonce'], $_POST['subscrpt_timing'], $_POST['subscrpt_cart_txt'], $_POST['subscrpt_user_cancel'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_subscript_nonce'] ) ), '_subscript_edit_product_nonce' ) ) {
257 + if ( ! isset( $_POST['_subscript_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_subscript_nonce'] ) ), '_subscript_edit_product_nonce' ) ) {
166 258 return;
167 259 }
168 260
169 261 remove_action( 'save_post_product', array( $this, 'save_subscrpt_data' ) );
@@ -168,13 +260,13 @@
168 260
169 261 remove_action( 'save_post_product', array( $this, 'save_subscrpt_data' ) );
170 262
171 263 $subscrpt_enable = isset( $_POST['subscrpt_enable'] );
172 - $subscrpt_timing = sanitize_text_field( wp_unslash( $_POST['subscrpt_timing'] ) );
173 - $subscrpt_trial_time = sanitize_text_field( wp_unslash( $_POST['subscrpt_trial_time'] ) );
174 - $subscrpt_trial_timing = sanitize_text_field( wp_unslash( $_POST['subscrpt_trial_timing'] ) );
175 - $subscrpt_cart_txt = sanitize_text_field( wp_unslash( $_POST['subscrpt_cart_txt'] ) );
176 - $subscrpt_user_cancel = sanitize_text_field( wp_unslash( $_POST['subscrpt_user_cancel'] ) );
264 + $subscrpt_timing = isset( $_POST['subscrpt_timing'] ) ? sanitize_text_field( wp_unslash( $_POST['subscrpt_timing'] ) ) : '';
265 + $subscrpt_trial_time = isset( $_POST['subscrpt_trial_time'] ) ? sanitize_text_field( wp_unslash( $_POST['subscrpt_trial_time'] ) ) : '';
266 + $subscrpt_trial_timing = isset( $_POST['subscrpt_trial_timing'] ) ? sanitize_text_field( wp_unslash( $_POST['subscrpt_trial_timing'] ) ) : '';
267 + $subscrpt_cart_txt = isset( $_POST['subscrpt_cart_txt'] ) ? sanitize_text_field( wp_unslash( $_POST['subscrpt_cart_txt'] ) ) : '';
268 + $subscrpt_user_cancel = isset( $_POST['subscrpt_user_cancel'] ) ? sanitize_text_field( wp_unslash( $_POST['subscrpt_user_cancel'] ) ) : '';
177 269 $subscrpt_limit = isset( $_POST['subscrpt_limit'] ) ? sanitize_text_field( wp_unslash( $_POST['subscrpt_limit'] ) ) : null;
178 270
179 271 $product = wc_get_product( $product_id );
180 272 $product->update_meta_data( '_subscrpt_enabled', $subscrpt_enable );