PluginProbe
Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More / 2.3.2
Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More v2.3.2
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.3.2, at includes/Admin/ReactAdmin.php

451 lines 17.2 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
238 @media print {
239 body {
240 transform: scale(1);
241 background: white !important;
242 }
243
244 body, body *, .wpcontent {
245 visibility: hidden !important;
246 }
247
248 .bp-invoice-body,
249 .bp-invoice-body * {
250 visibility: visible !important;
251 }
252
253 /* Make modal fill the full page */
254 .bp-invoice-body {
255 position: absolute !important;
256 left: 0;
257 top: 100;
258 width: 100% !important;
259 height: auto !important;
260 }
261
262 .bp-button-wrapper {
263 display: none !important;
264 }
265 }
266 </style>
267 <?php
268 }
269
270 /**
271 * Get initial data for React admin app
272 *
273 * @since 1.5.0
274 * @return array Initial data
275 */
276 private function get_initial_admin_data()
277 {
278 $settings = DB::get_settings();
279
280 return [
281 'apiUrl' => rest_url('better-payment/v1/'),
282 'ajaxUrl' => admin_url('admin-ajax.php'),
283 'nonce' => wp_create_nonce('wp_rest'),
284 'importNonce' => wp_create_nonce('better_payment_transaction_import_nonce'),
285 'logoUrl' => BETTER_PAYMENT_ASSETS . '/img/better-payment-icon-white-small.png',
286 'adminUrl' => admin_url('admin.php?page=' . $this->page_slug_prefix . '-admin'),
287 'adminPostUrl' => admin_url('admin-post.php'),
288 'settings' => $settings,
289 'proEnabled' => $this->pro_enabled,
290 'currentUser' => [
291 'id' => get_current_user_id(),
292 'name' => wp_get_current_user()->display_name,
293 'email' => wp_get_current_user()->user_email
294 ],
295 'currencies' => $this->get_currency_list(),
296 'userRoles' => $this->get_user_roles_list(),
297 'currencySymbol' => $this->get_currency_symbol( $settings['better_payment_settings_general_general_currency'] ),
298 'campaignTemplates' => array_values( TemplateManager::get_all() ),
299 'adminBaseUrl' => admin_url(),
300 ];
301 }
302
303 /**
304 * Get the site's user roles as a role_key => display_name map.
305 *
306 * Used by the Settings page (E-commerce > Subscription) role selects.
307 *
308 * @since 2.3.2
309 * @return array
310 */
311 private function get_user_roles_list()
312 {
313 $roles = [];
314
315 foreach (wp_roles()->roles as $role_key => $role) {
316 $roles[$role_key] = translate_user_role($role['name']);
317 }
318
319 return $roles;
320 }
321
322 /**
323 * Customize admin footer text and version display
324 *
325 * @since 1.5.0
326 */
327 public function customize_admin_footer($hook)
328 {
329 // Only apply to Better Payment admin pages — including the campaign
330 // builder page, whose hook is suffixed with 'bp-campaign-builder'
331 // rather than the '-admin' slug, so it shares the same branded footer.
332 if (
333 strpos($hook, $this->page_slug_prefix . '-admin') === false
334 && strpos($hook, 'bp-campaign-builder') === false
335 ) {
336 return;
337 }
338
339 add_filter('admin_footer_text', [$this, 'get_footer_text']);
340 add_filter('update_footer', [$this, 'get_footer_version']);
341 }
342
343 /**
344 * Get custom footer text
345 *
346 * @since 1.5.0
347 * @return string
348 */
349 public function get_footer_text()
350 {
351 return __('Thank you for using <a href="https://betterpayment.co/" target="_blank">Better Payment</a>', 'better-payment');
352 }
353
354 /**
355 * Get footer version text
356 *
357 * @since 1.5.0
358 * @return string
359 */
360 public function get_footer_version()
361 {
362 $free_version = BETTER_PAYMENT_VERSION;
363
364 // Check if Pro version is installed and active
365 if ($this->pro_enabled && defined('BETTER_PAYMENT_PRO_VERSION')) {
366 $pro_version = BETTER_PAYMENT_PRO_VERSION;
367 return sprintf(
368 __('<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'),
369 $free_version,
370 $pro_version
371 );
372 }
373
374 return sprintf(
375 __('<span class="bp-free-version"><span>Free Version</span> <span class="bp-footer-version">%s</span> </span>', 'better-payment'),
376 $free_version
377 );
378 }
379
380 /**
381 * Enqueue admin assets for React app
382 *
383 * @since 1.5.0
384 */
385 public function enqueue_admin_assets($hook)
386 {
387 if (strpos($hook, $this->page_slug_prefix . '-admin') === false) {
388 return;
389 }
390
391 wp_enqueue_script('wp-element');
392 wp_enqueue_script('wp-api-fetch');
393 wp_enqueue_script('wp-components');
394 wp_enqueue_script('wp-i18n');
395
396 wp_enqueue_script('wp-element');
397 wp_enqueue_script('wp-hooks');
398 wp_enqueue_script('wp-i18n');
399 wp_enqueue_script('wp-api-fetch');
400 wp_enqueue_script('react');
401 wp_enqueue_script('react-dom');
402
403 $admin_js_file = BETTER_PAYMENT_ASSETS . '/admin/admin.min.js';
404 $admin_css_file = BETTER_PAYMENT_ASSETS . '/admin/admin.min.css';
405
406 if (!file_exists(BETTER_PAYMENT_PATH . '/assets/admin/admin.min.js')) {
407 $admin_js_file = BETTER_PAYMENT_ASSETS . '/admin/admin.js';
408 }
409
410 if (!file_exists(BETTER_PAYMENT_PATH . '/assets/admin/admin.min.css')) {
411 $admin_css_file = BETTER_PAYMENT_ASSETS . '/admin/admin.css';
412 }
413
414 wp_enqueue_script(
415 'better-payment-react-admin',
416 $admin_js_file,
417 ['wp-element', 'wp-hooks', 'wp-i18n', 'wp-api-fetch', 'react', 'react-dom'],
418 $this->file_version,
419 true
420 );
421
422 wp_enqueue_style(
423 'better-payment-react-admin',
424 $admin_css_file,
425 [],
426 $this->file_version
427 );
428
429 // Campaign-list CSS contains the template-select modal styles needed on this page too.
430 $campaign_list_css = BETTER_PAYMENT_ASSETS . '/admin/campaign-list/campaign-list.min.css';
431 if ( file_exists( BETTER_PAYMENT_PATH . '/assets/admin/campaign-list/campaign-list.min.css' ) ) {
432 wp_enqueue_style(
433 'bp-campaign-list',
434 $campaign_list_css,
435 [],
436 $this->file_version
437 );
438 }
439
440 wp_add_inline_script(
441 'better-payment-react-admin',
442 'wp.apiFetch.use(wp.apiFetch.createNonceMiddleware("' . wp_create_nonce('wp_rest') . '"));',
443 'before'
444 );
445 $page_slug = $this->page_slug_prefix . '-admin';
446 $admin_base_url = admin_url('admin.php?page=' . $this->page_slug_prefix . '-admin');
447 $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})();";
448 wp_add_inline_script('better-payment-react-admin', $inline_spa_nav, 'after');
449 }
450 }
451