PluginProbe
Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More / 2.4.0
Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More v2.4.0
2.4.1 2.4.0 2.3.4 2.3.3 2.3.2 2.3.1 2.3.0 2.2.2 2.2.1 2.2.0 2.1.2 2.1.1 trunk 0.0.1 0.0.2 0.0.3 0.0.4 0.0.5 0.0.6 0.0.7 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 All 68 releases
better-payment / includes / Admin / ReactAdmin.php

ReactAdmin.php in Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More 2.4.0, at includes/Admin/ReactAdmin.php

428 lines 16.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Better_Payment\Lite\Admin;
4
5 use Better_Payment\Lite\Controller;
6 use Better_Payment\Lite\Admin\DB;
7 use Better_Payment\Lite\Traits\Helper as TraitsHelper;
8 use Better_Payment\Lite\Campaign\Templates\TemplateManager;
9
10 /**
11 * Exit if accessed directly
12 */
13 if (!defined('ABSPATH')) {
14 exit;
15 }
16
17 /**
18 * React Admin handler class
19 *
20 * @since 1.5.0
21 */
22 class ReactAdmin extends Controller
23 {
24 use TraitsHelper;
25
26 protected $pro_enabled;
27 protected $page_slug_prefix;
28 protected $file_version;
29
30 /**
31 * Constructor
32 *
33 * @since 1.5.0
34 */
35 public function __construct($page_slug_prefix = 'better-payment')
36 {
37 $this->page_slug_prefix = $page_slug_prefix;
38 $this->file_version = defined('WP_DEBUG') && WP_DEBUG ? time() : BETTER_PAYMENT_VERSION;
39 $this->pro_enabled = apply_filters('better_payment/pro_enabled', false);
40
41 add_action('init', [$this, 'handle_old_page_redirect']);
42 add_action('admin_menu', [$this, 'register_menu']);
43 add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_assets']);
44 add_action('admin_enqueue_scripts', [$this, 'customize_admin_footer']);
45
46 //EL Editor Style
47 add_action('elementor/editor/before_enqueue_scripts', [$this, 'editor_enqueue_scripts']);
48 }
49
50 /**
51 * Handle old page redirect
52 *
53 * @since 2.0.0
54 */
55 public function handle_old_page_redirect()
56 {
57 if (isset($_GET['page']) && trim(sanitize_text_field($_GET['page'])) === 'better-payment-settings') {
58 wp_safe_redirect(admin_url('admin.php?page=' . $this->page_slug_prefix . '-admin&tab=dashboard'));
59 exit;
60 }
61 }
62
63 /**
64 * Enqueue editor scripts
65 *
66 * @since 0.0.1
67 */
68 public function editor_enqueue_scripts()
69 {
70 // bp icon font
71 wp_enqueue_style(
72 'bp-icon-admin-editor',
73 BETTER_PAYMENT_ASSETS . '/icon/style.min.css',
74 false
75 );
76 }
77
78 /**
79 * Register admin menu for React SPA
80 *
81 * @since 1.5.0
82 */
83 public function register_menu()
84 {
85 add_menu_page(
86 __('Better Payment', 'better-payment'),
87 __('Better Payment', 'better-payment'),
88 'manage_options',
89 $this->page_slug_prefix . '-admin',
90 [$this, 'render_react_admin_page'],
91 BETTER_PAYMENT_ASSETS . '/img/better-payment-icon-white-small.png',
92 64
93 );
94
95 $submenu_page_list = apply_filters('better_payment/admin/get_submenu_page_list', array(
96 $this->page_slug_prefix . '-admin&tab=dashboard' => array(
97 'title' => __('Dashboard', 'better-payment'),
98 'capability' => 'manage_options',
99 'callback' => [$this, 'render_react_admin_page'],
100 ),
101 $this->page_slug_prefix . '-admin&tab=campaigns' => array(
102 'title' => __('Campaigns', 'better-payment'),
103 'capability' => 'manage_options',
104 'callback' => [$this, 'render_react_admin_page'],
105 ),
106 $this->page_slug_prefix . '-admin&tab=transactions' => array(
107 'title' => __('Transactions', 'better-payment'),
108 'capability' => 'manage_options',
109 'callback' => [$this, 'render_react_admin_page'],
110 ),
111 $this->page_slug_prefix . '-admin&tab=subscriptions' => array(
112 'title' => __('Subscriptions', 'better-payment'),
113 'capability' => 'manage_options',
114 'callback' => [$this, 'render_react_admin_page'],
115 ),
116 $this->page_slug_prefix . '-admin&tab=analytics' => array(
117 'title' => __('Analytics', 'better-payment'),
118 'capability' => 'manage_options',
119 'callback' => [$this, 'render_react_admin_page'],
120 ),
121 $this->page_slug_prefix . '-admin&tab=settings' => array(
122 'title' => __('Settings', 'better-payment'),
123 'capability' => 'manage_options',
124 'callback' => [$this, 'render_react_admin_page'],
125 ),
126 ), $this->page_slug_prefix );
127
128 foreach( $submenu_page_list as $slug => $setting ) {
129 add_submenu_page(
130 $this->page_slug_prefix . '-admin',
131 $setting['title'],
132 $setting['title'],
133 $setting['capability'],
134 $slug,
135 $setting['callback']
136 );
137 }
138
139 remove_submenu_page($this->page_slug_prefix . '-admin', $this->page_slug_prefix . '-admin');
140 }
141
142 /**
143 * Render the React admin page
144 *
145 * @since 1.5.0
146 */
147 public function render_react_admin_page()
148 {
149 $initial_data = $this->get_initial_admin_data();
150
151 add_filter('admin_body_class', function($classes) {
152 return $classes . ' better-payment-admin-page';
153 });
154
155 remove_all_actions('admin_notices');
156 remove_all_actions('all_admin_notices');
157
158 ?>
159 <div class="wrap">
160 <div id="better-payment-admin-root">
161 <!-- <div class="bp-loading-placeholder">
162 <div class="bp-loading-spinner"></div>
163 <p><?php //_e('Loading Better Payment Admin...', 'better-payment'); ?></p>
164 </div> -->
165 </div>
166 </div>
167
168 <script type="text/javascript">
169 window.betterPaymentAdmin = <?php echo wp_json_encode($initial_data); ?>;
170 </script>
171
172 <style>
173 .bp-loading-placeholder {
174 display: flex;
175 flex-direction: column;
176 align-items: center;
177 justify-content: center;
178 min-height: 400px;
179 gap: 16px;
180 color: #646970;
181 }
182
183 .bp-loading-spinner {
184 width: 32px;
185 height: 32px;
186 border: 3px solid #f3f3f3;
187 border-top: 3px solid #0073aa;
188 border-radius: 50%;
189 animation: bp-spin 1s linear infinite;
190 }
191
192 @keyframes bp-spin {
193 0% { transform: rotate(0deg); }
194 100% { transform: rotate(360deg); }
195 }
196
197 .better-payment-admin {
198 background: #f0f0f1;
199 min-height: calc(100vh - 32px);
200 margin: -20px -20px -20px -2px;
201 padding: 0;
202 }
203
204 .wrap .better-payment-admin {
205 margin: -20px -20px -20px -2px;
206 }
207
208 .better-payment-admin-page .notice,
209 .better-payment-admin-page .error,
210 .better-payment-admin-page .updated {
211 display: none !important;
212 }
213
214 .better-payment-admin-page #adminmenumain {
215 display: block !important;
216 }
217
218 .better-payment-admin-page #wpadminbar {
219 display: block !important;
220 }
221
222 .better-payment-admin-page #wpfooter {
223 display: block !important;
224 }
225
226 @media (max-width: 960px) {
227 .folded .better-payment-admin {
228 margin-left: -2px;
229 }
230 }
231
232 @media (max-width: 782px) {
233 .auto-fold .better-payment-admin {
234 margin-left: -2px;
235 }
236 }
237 </style>
238 <?php
239 }
240
241 /**
242 * Get initial data for React admin app
243 *
244 * @since 1.5.0
245 * @return array Initial data
246 */
247 private function get_initial_admin_data()
248 {
249 $settings = DB::get_settings();
250
251 return [
252 'apiUrl' => rest_url('better-payment/v1/'),
253 'ajaxUrl' => admin_url('admin-ajax.php'),
254 'nonce' => wp_create_nonce('wp_rest'),
255 'importNonce' => wp_create_nonce('better_payment_transaction_import_nonce'),
256 'logoUrl' => BETTER_PAYMENT_ASSETS . '/img/better-payment-icon-white-small.png',
257 'adminUrl' => admin_url('admin.php?page=' . $this->page_slug_prefix . '-admin'),
258 'adminPostUrl' => admin_url('admin-post.php'),
259 'settings' => $settings,
260 'proEnabled' => $this->pro_enabled,
261 'currentUser' => [
262 'id' => get_current_user_id(),
263 'name' => wp_get_current_user()->display_name,
264 'email' => wp_get_current_user()->user_email
265 ],
266 'currencies' => $this->get_currency_list(),
267 'userRoles' => $this->get_user_roles_list(),
268 'currencySymbol' => $this->get_currency_symbol( $settings['better_payment_settings_general_general_currency'] ),
269 // Picker-facing, so retired designs stay out of it. The renderer
270 // still needs get_all(); see TemplateManager::get_for_picker().
271 'campaignTemplates' => array_values( TemplateManager::get_for_picker() ),
272 'adminBaseUrl' => admin_url(),
273 ];
274 }
275
276 /**
277 * Get the site's user roles as a role_key => display_name map.
278 *
279 * Used by the Settings page (E-commerce > Subscription) role selects.
280 *
281 * @since 2.3.2
282 * @return array
283 */
284 private function get_user_roles_list()
285 {
286 $roles = [];
287
288 foreach (wp_roles()->roles as $role_key => $role) {
289 $roles[$role_key] = translate_user_role($role['name']);
290 }
291
292 return $roles;
293 }
294
295 /**
296 * Customize admin footer text and version display
297 *
298 * @since 1.5.0
299 */
300 public function customize_admin_footer($hook)
301 {
302 // Only apply to Better Payment admin pages — including the campaign
303 // builder page, whose hook is suffixed with 'bp-campaign-builder'
304 // rather than the '-admin' slug, so it shares the same branded footer.
305 if (
306 strpos($hook, $this->page_slug_prefix . '-admin') === false
307 && strpos($hook, 'bp-campaign-builder') === false
308 ) {
309 return;
310 }
311
312 add_filter('admin_footer_text', [$this, 'get_footer_text']);
313 add_filter('update_footer', [$this, 'get_footer_version']);
314 }
315
316 /**
317 * Get custom footer text
318 *
319 * @since 1.5.0
320 * @return string
321 */
322 public function get_footer_text()
323 {
324 return __('Thank you for using <a href="https://betterpayment.co/" target="_blank">Better Payment</a>', 'better-payment');
325 }
326
327 /**
328 * Get footer version text
329 *
330 * @since 1.5.0
331 * @return string
332 */
333 public function get_footer_version()
334 {
335 $free_version = BETTER_PAYMENT_VERSION;
336
337 // Check if Pro version is installed and active
338 if ($this->pro_enabled && defined('BETTER_PAYMENT_PRO_VERSION')) {
339 $pro_version = BETTER_PAYMENT_PRO_VERSION;
340 return sprintf(
341 __('<span class="bp-footer-version-divider">Free Version <span class="bp-footer-version">%s</span></span> <span>Pro Version <span class="bp-footer-version">%s</span></span>', 'better-payment'),
342 $free_version,
343 $pro_version
344 );
345 }
346
347 return sprintf(
348 __('<span class="bp-free-version"><span>Free Version</span> <span class="bp-footer-version">%s</span> </span>', 'better-payment'),
349 $free_version
350 );
351 }
352
353 /**
354 * Enqueue admin assets for React app
355 *
356 * @since 1.5.0
357 */
358 public function enqueue_admin_assets($hook)
359 {
360 if (strpos($hook, $this->page_slug_prefix . '-admin') === false) {
361 return;
362 }
363
364 wp_enqueue_script('wp-element');
365 wp_enqueue_script('wp-api-fetch');
366 wp_enqueue_script('wp-components');
367 wp_enqueue_script('wp-i18n');
368
369 wp_enqueue_script('wp-element');
370 wp_enqueue_script('wp-hooks');
371 wp_enqueue_script('wp-i18n');
372 wp_enqueue_script('wp-api-fetch');
373 wp_enqueue_script('react');
374 wp_enqueue_script('react-dom');
375
376 $admin_js_file = BETTER_PAYMENT_ASSETS . '/admin/admin.min.js';
377 $admin_css_file = BETTER_PAYMENT_ASSETS . '/admin/admin.min.css';
378
379 if (!file_exists(BETTER_PAYMENT_PATH . '/assets/admin/admin.min.js')) {
380 $admin_js_file = BETTER_PAYMENT_ASSETS . '/admin/admin.js';
381 }
382
383 if (!file_exists(BETTER_PAYMENT_PATH . '/assets/admin/admin.min.css')) {
384 $admin_css_file = BETTER_PAYMENT_ASSETS . '/admin/admin.css';
385 }
386
387 wp_enqueue_script(
388 'better-payment-react-admin',
389 $admin_js_file,
390 ['wp-element', 'wp-hooks', 'wp-i18n', 'wp-api-fetch', 'react', 'react-dom'],
391 $this->file_version,
392 true
393 );
394
395 wp_enqueue_style(
396 'better-payment-react-admin',
397 $admin_css_file,
398 [],
399 $this->file_version
400 );
401
402 // Styles for the screens only Pro renders (receipt modal, Analytics, refund card,
403 // licence page): Lite's copy until Pro declares `react_admin_pro_styles`.
404 \Better_Payment\Lite\Classes\ProAdminStyles::enqueue( $this->file_version );
405
406 // Campaign-list CSS contains the template-select modal styles needed on this page too.
407 $campaign_list_css = BETTER_PAYMENT_ASSETS . '/admin/campaign-list/campaign-list.min.css';
408 if ( file_exists( BETTER_PAYMENT_PATH . '/assets/admin/campaign-list/campaign-list.min.css' ) ) {
409 wp_enqueue_style(
410 'bp-campaign-list',
411 $campaign_list_css,
412 [],
413 $this->file_version
414 );
415 }
416
417 wp_add_inline_script(
418 'better-payment-react-admin',
419 'wp.apiFetch.use(wp.apiFetch.createNonceMiddleware("' . wp_create_nonce('wp_rest') . '"));',
420 'before'
421 );
422 $page_slug = $this->page_slug_prefix . '-admin';
423 $admin_base_url = admin_url('admin.php?page=' . $this->page_slug_prefix . '-admin');
424 $inline_spa_nav = "(function(){\n\n var PAGE_SLUG = '" . esc_js($page_slug) . "';\n var BASE_URL = '" . esc_url($admin_base_url) . "';\n\n function getTabFromUrl(){\n var params = new URLSearchParams(window.location.search);\n return params.get('tab') || 'dashboard';\n }\n\n function buildUrl(tab){\n var url = new URL(BASE_URL, window.location.origin);\n url.searchParams.set('tab', tab);\n return url.toString();\n }\n\n function setActiveSubmenu(tab){\n try {\n var top = document.getElementById('toplevel_page_'+PAGE_SLUG);\n if(!top) return;\n var submenu = top.querySelector('.wp-submenu');\n if(!submenu) return;\n // remove current from all\n submenu.querySelectorAll('li').forEach(function(li){ li.classList.remove('current'); });\n // match link by tab param\n var target = Array.prototype.find.call(submenu.querySelectorAll('a'), function(a){ return a.href && a.href.indexOf('page='+PAGE_SLUG) !== -1 && a.href.indexOf('tab='+tab) !== -1; });\n if(target && target.parentElement){ target.parentElement.classList.add('current'); }\n // ensure top-level marked as current submenu\n top.classList.add('wp-has-current-submenu');\n top.classList.add('current');\n } catch(e){}\n }\n\n // Global navigate helper used by WP menu interceptors and can be used by React too\n window.betterPaymentNavigate = function(tab, method){\n if(!tab){ tab = 'dashboard'; }\n var m = method === 'replace' ? 'replaceState' : 'pushState';\n var url = buildUrl(tab);\n try { window.history[m]({}, '', url); } catch(e){}\n setActiveSubmenu(tab);\n window.dispatchEvent(new CustomEvent('bp:navigate', { detail: { tab: tab } }));\n };\n\n // Intercept WP submenu clicks under our menu only\n function clickHandler(e){\n var link = e.target.closest('#toplevel_page_'+PAGE_SLUG+' .wp-submenu a');\n if(!link) return;\n var href = link.getAttribute('href') || '';\n if(href.indexOf('page='+PAGE_SLUG) === -1) return;\n e.preventDefault();\n var url = new URL(href, window.location.origin);\n var tab = url.searchParams.get('tab') || 'dashboard';\n window.betterPaymentNavigate(tab, 'push');\n }\n document.addEventListener('click', clickHandler, true);\n\n // Reflect browser back/forward\n window.addEventListener('popstate', function(){ setActiveSubmenu(getTabFromUrl()); });\n\n // Reflect initial state on load\n document.addEventListener('DOMContentLoaded', function(){ setActiveSubmenu(getTabFromUrl()); });\n\n // Also respond to React-driven page changes (optional hook)\n window.addEventListener('bp:page-changed', function(e){ if(e && e.detail && e.detail.tab){ setActiveSubmenu(e.detail.tab); } });\n\n})();";
425 wp_add_inline_script('better-payment-react-admin', $inline_spa_nav, 'after');
426 }
427 }
428