PluginProbe
Subscriptions for WooCommerce with Stripe Recurring Payments / 2.2.0
Subscriptions for WooCommerce with Stripe Recurring Payments v2.2.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
subscription / includes / Admin / Plans.php

Plans.php in Subscriptions for WooCommerce with Stripe Recurring Payments 2.2.0, at includes/Admin/Plans.php

248 lines 8.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plans - admin class.
4 *
5 * Registers the Subscription Plans admin screens (list + detail) and enqueues
6 * their assets. Data is read live from PlanRepository via PlanPresenter; all
7 * writes go through the wpsubscription/v1 REST API (admin-only). The UI is
8 * built entirely on the shared `wpsubs-*` component system (admin-components).
9 *
10 * @package SpringDevs\Subscription\Admin
11 */
12
13 namespace SpringDevs\Subscription\Admin;
14
15 /**
16 * Plans - admin class.
17 */
18 class Plans {
19
20 /**
21 * Admin page slug.
22 */
23 const SLUG = 'wp-subscription-plans';
24
25 /**
26 * The page hook returned by add_submenu_page (for the enqueue gate).
27 *
28 * @var string
29 */
30 private $hook = '';
31
32 /**
33 * Initialize the class.
34 */
35 public function __construct() {
36 add_action( 'admin_menu', array( $this, 'register_page' ), 11 );
37 add_filter( 'subscrpt_submenu_order', array( $this, 'position_submenu' ) );
38 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
39 }
40
41 /**
42 * Register the Plans submenu under the WPSubscription top menu.
43 *
44 * @return void
45 */
46 public function register_page() {
47 $this->hook = (string) add_submenu_page(
48 'wp-subscription',
49 __( 'Plans', 'subscription' ),
50 __( 'Plans', 'subscription' ),
51 'manage_options',
52 self::SLUG,
53 array( $this, 'render_page' )
54 );
55 }
56
57 /**
58 * Position the Plans item within the WPSubscription submenu order.
59 *
60 * @param array $order Ordered slug => position map.
61 * @return array
62 */
63 public function position_submenu( $order ) {
64 if ( is_array( $order ) ) {
65 $order[ self::SLUG ] = 10;
66 }
67 return $order;
68 }
69
70 /**
71 * Enqueue Plans assets only on the Plans page.
72 *
73 * Reuses the shared `subscrpt_admin_components` bundle (registered by
74 * Assets) for all styling; adds only the Plans interaction script on top.
75 *
76 * @param string $hook Current admin page hook.
77 * @return void
78 */
79 public function enqueue_assets( $hook ) {
80 if ( ! $this->hook || $hook !== $this->hook ) {
81 return;
82 }
83
84 wp_enqueue_style( 'subscrpt_admin_components' );
85 wp_enqueue_script( 'subscrpt_admin_components' );
86
87 // Shared plan-group + selling-plan modal logic.
88 wp_enqueue_script( 'subscrpt_plan_forms_js' );
89 wp_localize_script( 'subscrpt_plan_forms_js', 'subscrptPlanForms', self::plan_forms_config() );
90
91 wp_enqueue_script(
92 'subscrpt_admin_plans_js',
93 SUBSCRPT_ASSETS . '/js/admin/plans.js',
94 array( 'subscrpt_admin_components', 'subscrpt_plan_forms_js' ),
95 SUBSCRPT_VERSION,
96 true
97 );
98
99 wp_localize_script(
100 'subscrpt_admin_plans_js',
101 'subscrptPlans',
102 array(
103 'restUrl' => esc_url_raw( rest_url( 'wpsubscription/v1/plans' ) ),
104 'nonce' => wp_create_nonce( 'wp_rest' ),
105 'listUrl' => admin_url( 'admin.php?page=' . self::SLUG ),
106 'currency' => function_exists( 'get_woocommerce_currency_symbol' )
107 ? html_entity_decode( get_woocommerce_currency_symbol(), ENT_QUOTES, 'UTF-8' )
108 : '$',
109 'i18n' => array(
110 'saved' => __( 'Saved.', 'subscription' ),
111 'deleted' => __( 'Deleted.', 'subscription' ),
112 'confirmPlan' => __( 'Delete this plan and all its durations? This cannot be undone.', 'subscription' ),
113 /* translators: %d: number of selected plans. */
114 'confirmBulkDelete' => __( 'Delete %d selected plan(s) and all their durations? This cannot be undone.', 'subscription' ),
115 'selectPlans' => __( 'Please select at least one plan.', 'subscription' ),
116 'confirmTerm' => __( 'Delete this duration?', 'subscription' ),
117 'confirmRemoveProduct' => __( 'Remove this product from the plan? It will be detached from every duration.', 'subscription' ),
118 'confirmRemoveVariation' => __( 'Remove this variation from the plan? It will be detached from every duration.', 'subscription' ),
119 'genericError' => __( 'Something went wrong. Please try again.', 'subscription' ),
120 'nameRequired' => __( 'Please enter a name.', 'subscription' ),
121 'addTerm' => __( 'Add Duration', 'subscription' ),
122 'editTerm' => __( 'Edit Duration', 'subscription' ),
123 'draft' => __( 'Draft', 'subscription' ),
124 'setDraft' => __( 'Set as Draft', 'subscription' ),
125 'setActive' => __( 'Set as Active', 'subscription' ),
126 'termActivated' => __( 'Duration activated.', 'subscription' ),
127 'pricesSaved' => __( 'Prices saved.', 'subscription' ),
128 'bulkApplied' => __( 'Prices updated.', 'subscription' ),
129 'bulkNoPrice' => __( 'Enter a regular price.', 'subscription' ),
130 'bulkNoProducts' => __( 'Select at least one product.', 'subscription' ),
131 'bulkNoTerms' => __( 'Select at least one duration.', 'subscription' ),
132 /* translators: %d: number of selected items. */
133 'bulkSelected' => __( '%d selected', 'subscription' ),
134 'oneTimeOn' => __( 'One-time purchase turned on.', 'subscription' ),
135 'oneTimeOff' => __( 'One-time purchase turned off.', 'subscription' ),
136 /* translators: %d: number of products ticked in the picker. */
137 'picked' => __( '%d product(s) on this plan', 'subscription' ),
138 'pickedNone' => __( 'No products on this plan', 'subscription' ),
139 'productsUpdated' => __( 'Products updated.', 'subscription' ),
140 'productRemoved' => __( 'Product removed from this plan.', 'subscription' ),
141 'termDrafted' => __( 'Duration set to draft.', 'subscription' ),
142 'orderSaved' => __( 'Order saved.', 'subscription' ),
143 /* translators: %1$s: first item number, %2$s: last item number, %3$s: total. */
144 'showingRange' => __( 'Showing %1-%2 of %3', 'subscription' ),
145 ),
146 )
147 );
148 }
149
150 /**
151 * Config for the shared plan-forms.js module (create group + selling-plan
152 * modals). Shared by the Plans screen and the product-editor Subscription
153 * tab so the term payload contract is localized identically on both.
154 *
155 * @return array
156 */
157 public static function plan_forms_config() {
158 return array(
159 'restUrl' => esc_url_raw( rest_url( 'wpsubscription/v1/plans' ) ),
160 'nonce' => wp_create_nonce( 'wp_rest' ),
161 'i18n' => array(
162 'genericError' => __( 'Something went wrong. Please try again.', 'subscription' ),
163 'nameRequired' => __( 'Please enter a name.', 'subscription' ),
164 'addTerm' => __( 'Add Duration', 'subscription' ),
165 'editTerm' => __( 'Edit Duration', 'subscription' ),
166 ),
167 );
168 }
169
170 /**
171 * Render the page: header + routed view (list or detail).
172 *
173 * @return void
174 */
175 public function render_page() {
176 // phpcs:disable WordPress.Security.NonceVerification.Recommended -- read-only view routing, no state change.
177 $view = isset( $_GET['view'] ) ? sanitize_key( wp_unslash( $_GET['view'] ) ) : 'list';
178 $plan_id = isset( $_GET['plan'] ) ? absint( wp_unslash( $_GET['plan'] ) ) : 0;
179 // phpcs:enable WordPress.Security.NonceVerification.Recommended
180
181 $plans = PlanPresenter::all();
182 $list_url = admin_url( 'admin.php?page=' . self::SLUG );
183
184 if ( 'detail' === $view && isset( $plans[ $plan_id ] ) ) {
185 $plan = $plans[ $plan_id ];
186
187 $this->render_header(
188 array(
189 array(
190 'label' => __( 'Plans', 'subscription' ),
191 'url' => $list_url,
192 ),
193 array( 'label' => $plan['name'] ),
194 )
195 );
196
197 include __DIR__ . '/views/plans/detail.php';
198 return;
199 }
200
201 $this->render_header( array( array( 'label' => __( 'Plans', 'subscription' ) ) ) );
202 include __DIR__ . '/views/plans/list.php';
203 }
204
205 /**
206 * Render the shared sticky admin header via the Menu class.
207 *
208 * @param array $breadcrumbs Ordered breadcrumb segments.
209 * @return void
210 */
211 private function render_header( $breadcrumbs ) {
212 $menu = new Menu();
213 if ( method_exists( $menu, 'render_admin_header' ) ) {
214 $menu->render_admin_header( __( 'Plans', 'subscription' ), '', $breadcrumbs );
215 }
216 }
217
218 /**
219 * Human label for a plan type.
220 *
221 * @param string $type Plan type key.
222 * @return string
223 */
224 public static function type_label( $type ) {
225 $labels = array(
226 'recurring' => __( 'Recurring Payment', 'subscription' ),
227 'subscribe_save' => __( 'Recurring Delivery', 'subscription' ),
228 'installments' => __( 'Split Payment', 'subscription' ),
229 );
230 return isset( $labels[ $type ] ) ? $labels[ $type ] : ucfirst( $type );
231 }
232
233 /**
234 * Dashicon class for a plan type.
235 *
236 * @param string $type Plan type key.
237 * @return string
238 */
239 public static function type_icon( $type ) {
240 $icons = array(
241 'recurring' => 'dashicons-update',
242 'subscribe_save' => 'dashicons-cart',
243 'installments' => 'dashicons-money-alt',
244 );
245 return isset( $icons[ $type ] ) ? $icons[ $type ] : 'dashicons-screenoptions';
246 }
247 }
248