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

453 lines 17.3 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 // Picker-facing, so retired designs stay out of it. The renderer
299 // still needs get_all(); see TemplateManager::get_for_picker().
300 'campaignTemplates' => array_values( TemplateManager::get_for_picker() ),
301 'adminBaseUrl' => admin_url(),
302 ];
303 }
304
305 /**
306 * Get the site's user roles as a role_key => display_name map.
307 *
308 * Used by the Settings page (E-commerce > Subscription) role selects.
309 *
310 * @since 2.3.2
311 * @return array
312 */
313 private function get_user_roles_list()
314 {
315 $roles = [];
316
317 foreach (wp_roles()->roles as $role_key => $role) {
318 $roles[$role_key] = translate_user_role($role['name']);
319 }
320
321 return $roles;
322 }
323
324 /**
325 * Customize admin footer text and version display
326 *
327 * @since 1.5.0
328 */
329 public function customize_admin_footer($hook)
330 {
331 // Only apply to Better Payment admin pages — including the campaign
332 // builder page, whose hook is suffixed with 'bp-campaign-builder'
333 // rather than the '-admin' slug, so it shares the same branded footer.
334 if (
335 strpos($hook, $this->page_slug_prefix . '-admin') === false
336 && strpos($hook, 'bp-campaign-builder') === false
337 ) {
338 return;
339 }
340
341 add_filter('admin_footer_text', [$this, 'get_footer_text']);
342 add_filter('update_footer', [$this, 'get_footer_version']);
343 }
344
345 /**
346 * Get custom footer text
347 *
348 * @since 1.5.0
349 * @return string
350 */
351 public function get_footer_text()
352 {
353 return __('Thank you for using <a href="https://betterpayment.co/" target="_blank">Better Payment</a>', 'better-payment');
354 }
355
356 /**
357 * Get footer version text
358 *
359 * @since 1.5.0
360 * @return string
361 */
362 public function get_footer_version()
363 {
364 $free_version = BETTER_PAYMENT_VERSION;
365
366 // Check if Pro version is installed and active
367 if ($this->pro_enabled && defined('BETTER_PAYMENT_PRO_VERSION')) {
368 $pro_version = BETTER_PAYMENT_PRO_VERSION;
369 return sprintf(
370 __('<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'),
371 $free_version,
372 $pro_version
373 );
374 }
375
376 return sprintf(
377 __('<span class="bp-free-version"><span>Free Version</span> <span class="bp-footer-version">%s</span> </span>', 'better-payment'),
378 $free_version
379 );
380 }
381
382 /**
383 * Enqueue admin assets for React app
384 *
385 * @since 1.5.0
386 */
387 public function enqueue_admin_assets($hook)
388 {
389 if (strpos($hook, $this->page_slug_prefix . '-admin') === false) {
390 return;
391 }
392
393 wp_enqueue_script('wp-element');
394 wp_enqueue_script('wp-api-fetch');
395 wp_enqueue_script('wp-components');
396 wp_enqueue_script('wp-i18n');
397
398 wp_enqueue_script('wp-element');
399 wp_enqueue_script('wp-hooks');
400 wp_enqueue_script('wp-i18n');
401 wp_enqueue_script('wp-api-fetch');
402 wp_enqueue_script('react');
403 wp_enqueue_script('react-dom');
404
405 $admin_js_file = BETTER_PAYMENT_ASSETS . '/admin/admin.min.js';
406 $admin_css_file = BETTER_PAYMENT_ASSETS . '/admin/admin.min.css';
407
408 if (!file_exists(BETTER_PAYMENT_PATH . '/assets/admin/admin.min.js')) {
409 $admin_js_file = BETTER_PAYMENT_ASSETS . '/admin/admin.js';
410 }
411
412 if (!file_exists(BETTER_PAYMENT_PATH . '/assets/admin/admin.min.css')) {
413 $admin_css_file = BETTER_PAYMENT_ASSETS . '/admin/admin.css';
414 }
415
416 wp_enqueue_script(
417 'better-payment-react-admin',
418 $admin_js_file,
419 ['wp-element', 'wp-hooks', 'wp-i18n', 'wp-api-fetch', 'react', 'react-dom'],
420 $this->file_version,
421 true
422 );
423
424 wp_enqueue_style(
425 'better-payment-react-admin',
426 $admin_css_file,
427 [],
428 $this->file_version
429 );
430
431 // Campaign-list CSS contains the template-select modal styles needed on this page too.
432 $campaign_list_css = BETTER_PAYMENT_ASSETS . '/admin/campaign-list/campaign-list.min.css';
433 if ( file_exists( BETTER_PAYMENT_PATH . '/assets/admin/campaign-list/campaign-list.min.css' ) ) {
434 wp_enqueue_style(
435 'bp-campaign-list',
436 $campaign_list_css,
437 [],
438 $this->file_version
439 );
440 }
441
442 wp_add_inline_script(
443 'better-payment-react-admin',
444 'wp.apiFetch.use(wp.apiFetch.createNonceMiddleware("' . wp_create_nonce('wp_rest') . '"));',
445 'before'
446 );
447 $page_slug = $this->page_slug_prefix . '-admin';
448 $admin_base_url = admin_url('admin.php?page=' . $this->page_slug_prefix . '-admin');
449 $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})();";
450 wp_add_inline_script('better-payment-react-admin', $inline_spa_nav, 'after');
451 }
452 }
453