PluginProbe
Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More / 2.2.1
Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More v2.2.1
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 1.0.5 1.0.6 All 66 releases
better-payment / includes / Admin / ReactAdmin.php

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

426 lines 16.4 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=analytics' => array(
112 'title' => __('Analytics', 'better-payment'),
113 'capability' => 'manage_options',
114 'callback' => [$this, 'render_react_admin_page'],
115 ),
116 $this->page_slug_prefix . '-admin&tab=settings' => array(
117 'title' => __('Settings', 'better-payment'),
118 'capability' => 'manage_options',
119 'callback' => [$this, 'render_react_admin_page'],
120 ),
121 ), $this->page_slug_prefix );
122
123 foreach( $submenu_page_list as $slug => $setting ) {
124 add_submenu_page(
125 $this->page_slug_prefix . '-admin',
126 $setting['title'],
127 $setting['title'],
128 $setting['capability'],
129 $slug,
130 $setting['callback']
131 );
132 }
133
134 remove_submenu_page($this->page_slug_prefix . '-admin', $this->page_slug_prefix . '-admin');
135 }
136
137 /**
138 * Render the React admin page
139 *
140 * @since 1.5.0
141 */
142 public function render_react_admin_page()
143 {
144 $initial_data = $this->get_initial_admin_data();
145
146 add_filter('admin_body_class', function($classes) {
147 return $classes . ' better-payment-admin-page';
148 });
149
150 remove_all_actions('admin_notices');
151 remove_all_actions('all_admin_notices');
152
153 ?>
154 <div class="wrap">
155 <div id="better-payment-admin-root">
156 <!-- <div class="bp-loading-placeholder">
157 <div class="bp-loading-spinner"></div>
158 <p><?php //_e('Loading Better Payment Admin...', 'better-payment'); ?></p>
159 </div> -->
160 </div>
161 </div>
162
163 <script type="text/javascript">
164 window.betterPaymentAdmin = <?php echo wp_json_encode($initial_data); ?>;
165 </script>
166
167 <style>
168 .bp-loading-placeholder {
169 display: flex;
170 flex-direction: column;
171 align-items: center;
172 justify-content: center;
173 min-height: 400px;
174 gap: 16px;
175 color: #646970;
176 }
177
178 .bp-loading-spinner {
179 width: 32px;
180 height: 32px;
181 border: 3px solid #f3f3f3;
182 border-top: 3px solid #0073aa;
183 border-radius: 50%;
184 animation: bp-spin 1s linear infinite;
185 }
186
187 @keyframes bp-spin {
188 0% { transform: rotate(0deg); }
189 100% { transform: rotate(360deg); }
190 }
191
192 .better-payment-admin {
193 background: #f0f0f1;
194 min-height: calc(100vh - 32px);
195 margin: -20px -20px -20px -2px;
196 padding: 0;
197 }
198
199 .wrap .better-payment-admin {
200 margin: -20px -20px -20px -2px;
201 }
202
203 .better-payment-admin-page .notice,
204 .better-payment-admin-page .error,
205 .better-payment-admin-page .updated {
206 display: none !important;
207 }
208
209 .better-payment-admin-page #adminmenumain {
210 display: block !important;
211 }
212
213 .better-payment-admin-page #wpadminbar {
214 display: block !important;
215 }
216
217 .better-payment-admin-page #wpfooter {
218 display: block !important;
219 }
220
221 @media (max-width: 960px) {
222 .folded .better-payment-admin {
223 margin-left: -2px;
224 }
225 }
226
227 @media (max-width: 782px) {
228 .auto-fold .better-payment-admin {
229 margin-left: -2px;
230 }
231 }
232
233 @media print {
234 body {
235 transform: scale(1);
236 background: white !important;
237 }
238
239 body, body *, .wpcontent {
240 visibility: hidden !important;
241 }
242
243 .bp-invoice-body,
244 .bp-invoice-body * {
245 visibility: visible !important;
246 }
247
248 /* Make modal fill the full page */
249 .bp-invoice-body {
250 position: absolute !important;
251 left: 0;
252 top: 100;
253 width: 100% !important;
254 height: auto !important;
255 }
256
257 .bp-button-wrapper {
258 display: none !important;
259 }
260 }
261 </style>
262 <?php
263 }
264
265 /**
266 * Get initial data for React admin app
267 *
268 * @since 1.5.0
269 * @return array Initial data
270 */
271 private function get_initial_admin_data()
272 {
273 $settings = DB::get_settings();
274
275 return [
276 'apiUrl' => rest_url('better-payment/v1/'),
277 'ajaxUrl' => admin_url('admin-ajax.php'),
278 'nonce' => wp_create_nonce('wp_rest'),
279 'importNonce' => wp_create_nonce('better_payment_transaction_import_nonce'),
280 'logoUrl' => BETTER_PAYMENT_ASSETS . '/img/better-payment-icon-white-small.png',
281 'adminUrl' => admin_url('admin.php?page=' . $this->page_slug_prefix . '-admin'),
282 'adminPostUrl' => admin_url('admin-post.php'),
283 'settings' => $settings,
284 'proEnabled' => $this->pro_enabled,
285 'currentUser' => [
286 'id' => get_current_user_id(),
287 'name' => wp_get_current_user()->display_name,
288 'email' => wp_get_current_user()->user_email
289 ],
290 'currencies' => $this->get_currency_list(),
291 'currencySymbol' => $this->get_currency_symbol( $settings['better_payment_settings_general_general_currency'] ),
292 'campaignTemplates' => array_values( TemplateManager::get_all() ),
293 'adminBaseUrl' => admin_url(),
294 ];
295 }
296
297 /**
298 * Customize admin footer text and version display
299 *
300 * @since 1.5.0
301 */
302 public function customize_admin_footer($hook)
303 {
304 // Only apply to Better Payment admin pages — including the campaign
305 // builder page, whose hook is suffixed with 'bp-campaign-builder'
306 // rather than the '-admin' slug, so it shares the same branded footer.
307 if (
308 strpos($hook, $this->page_slug_prefix . '-admin') === false
309 && strpos($hook, 'bp-campaign-builder') === false
310 ) {
311 return;
312 }
313
314 add_filter('admin_footer_text', [$this, 'get_footer_text']);
315 add_filter('update_footer', [$this, 'get_footer_version']);
316 }
317
318 /**
319 * Get custom footer text
320 *
321 * @since 1.5.0
322 * @return string
323 */
324 public function get_footer_text()
325 {
326 return __('Thank you for using <a href="https://betterpayment.co/" target="_blank">Better Payment</a>', 'better-payment');
327 }
328
329 /**
330 * Get footer version text
331 *
332 * @since 1.5.0
333 * @return string
334 */
335 public function get_footer_version()
336 {
337 $free_version = BETTER_PAYMENT_VERSION;
338
339 // Check if Pro version is installed and active
340 if ($this->pro_enabled && defined('BETTER_PAYMENT_PRO_VERSION')) {
341 $pro_version = BETTER_PAYMENT_PRO_VERSION;
342 return sprintf(
343 __('<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'),
344 $free_version,
345 $pro_version
346 );
347 }
348
349 return sprintf(
350 __('<span class="bp-free-version"><span>Free Version</span> <span class="bp-footer-version">%s</span> </span>', 'better-payment'),
351 $free_version
352 );
353 }
354
355 /**
356 * Enqueue admin assets for React app
357 *
358 * @since 1.5.0
359 */
360 public function enqueue_admin_assets($hook)
361 {
362 if (strpos($hook, $this->page_slug_prefix . '-admin') === false) {
363 return;
364 }
365
366 wp_enqueue_script('wp-element');
367 wp_enqueue_script('wp-api-fetch');
368 wp_enqueue_script('wp-components');
369 wp_enqueue_script('wp-i18n');
370
371 wp_enqueue_script('wp-element');
372 wp_enqueue_script('wp-hooks');
373 wp_enqueue_script('wp-i18n');
374 wp_enqueue_script('wp-api-fetch');
375 wp_enqueue_script('react');
376 wp_enqueue_script('react-dom');
377
378 $admin_js_file = BETTER_PAYMENT_ASSETS . '/admin/admin.min.js';
379 $admin_css_file = BETTER_PAYMENT_ASSETS . '/admin/admin.min.css';
380
381 if (!file_exists(BETTER_PAYMENT_PATH . '/assets/admin/admin.min.js')) {
382 $admin_js_file = BETTER_PAYMENT_ASSETS . '/admin/admin.js';
383 }
384
385 if (!file_exists(BETTER_PAYMENT_PATH . '/assets/admin/admin.min.css')) {
386 $admin_css_file = BETTER_PAYMENT_ASSETS . '/admin/admin.css';
387 }
388
389 wp_enqueue_script(
390 'better-payment-react-admin',
391 $admin_js_file,
392 ['wp-element', 'wp-hooks', 'wp-i18n', 'wp-api-fetch', 'react', 'react-dom'],
393 $this->file_version,
394 true
395 );
396
397 wp_enqueue_style(
398 'better-payment-react-admin',
399 $admin_css_file,
400 [],
401 $this->file_version
402 );
403
404 // Campaign-list CSS contains the template-select modal styles needed on this page too.
405 $campaign_list_css = BETTER_PAYMENT_ASSETS . '/admin/campaign-list/campaign-list.min.css';
406 if ( file_exists( BETTER_PAYMENT_PATH . '/assets/admin/campaign-list/campaign-list.min.css' ) ) {
407 wp_enqueue_style(
408 'bp-campaign-list',
409 $campaign_list_css,
410 [],
411 $this->file_version
412 );
413 }
414
415 wp_add_inline_script(
416 'better-payment-react-admin',
417 'wp.apiFetch.use(wp.apiFetch.createNonceMiddleware("' . wp_create_nonce('wp_rest') . '"));',
418 'before'
419 );
420 $page_slug = $this->page_slug_prefix . '-admin';
421 $admin_base_url = admin_url('admin.php?page=' . $this->page_slug_prefix . '-admin');
422 $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})();";
423 wp_add_inline_script('better-payment-react-admin', $inline_spa_nav, 'after');
424 }
425 }
426