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

431 lines 17.0 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 // How `payment_date` (UTC) is shown: the site's timezone, an IANA name or a
274 // fixed offset such as `+06:00` (never '' — the `timezone_string` option is
275 // empty on a site set to a manual UTC offset).
276 'siteTimezone' => wp_timezone_string(),
277 ];
278 }
279
280 /**
281 * Get the site's user roles as a role_key => display_name map.
282 *
283 * Used by the Settings page (E-commerce > Subscription) role selects.
284 *
285 * @since 2.3.2
286 * @return array
287 */
288 private function get_user_roles_list()
289 {
290 $roles = [];
291
292 foreach (wp_roles()->roles as $role_key => $role) {
293 $roles[$role_key] = translate_user_role($role['name']);
294 }
295
296 return $roles;
297 }
298
299 /**
300 * Customize admin footer text and version display
301 *
302 * @since 1.5.0
303 */
304 public function customize_admin_footer($hook)
305 {
306 // Only apply to Better Payment admin pages — including the campaign
307 // builder page, whose hook is suffixed with 'bp-campaign-builder'
308 // rather than the '-admin' slug, so it shares the same branded footer.
309 if (
310 strpos($hook, $this->page_slug_prefix . '-admin') === false
311 && strpos($hook, 'bp-campaign-builder') === false
312 ) {
313 return;
314 }
315
316 add_filter('admin_footer_text', [$this, 'get_footer_text']);
317 add_filter('update_footer', [$this, 'get_footer_version']);
318 }
319
320 /**
321 * Get custom footer text
322 *
323 * @since 1.5.0
324 * @return string
325 */
326 public function get_footer_text()
327 {
328 return __('Thank you for using <a href="https://betterpayment.co/" target="_blank">Better Payment</a>', 'better-payment');
329 }
330
331 /**
332 * Get footer version text
333 *
334 * @since 1.5.0
335 * @return string
336 */
337 public function get_footer_version()
338 {
339 $free_version = BETTER_PAYMENT_VERSION;
340
341 // Check if Pro version is installed and active
342 if ($this->pro_enabled && defined('BETTER_PAYMENT_PRO_VERSION')) {
343 $pro_version = BETTER_PAYMENT_PRO_VERSION;
344 return sprintf(
345 __('<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'),
346 $free_version,
347 $pro_version
348 );
349 }
350
351 return sprintf(
352 __('<span class="bp-free-version"><span>Free Version</span> <span class="bp-footer-version">%s</span> </span>', 'better-payment'),
353 $free_version
354 );
355 }
356
357 /**
358 * Enqueue admin assets for React app
359 *
360 * @since 1.5.0
361 */
362 public function enqueue_admin_assets($hook)
363 {
364 if (strpos($hook, $this->page_slug_prefix . '-admin') === false) {
365 return;
366 }
367
368 wp_enqueue_script('wp-element');
369 wp_enqueue_script('wp-api-fetch');
370 wp_enqueue_script('wp-components');
371 wp_enqueue_script('wp-i18n');
372
373 wp_enqueue_script('wp-element');
374 wp_enqueue_script('wp-hooks');
375 wp_enqueue_script('wp-i18n');
376 wp_enqueue_script('wp-api-fetch');
377 wp_enqueue_script('react');
378 wp_enqueue_script('react-dom');
379
380 $admin_js_file = BETTER_PAYMENT_ASSETS . '/admin/admin.min.js';
381 $admin_css_file = BETTER_PAYMENT_ASSETS . '/admin/admin.min.css';
382
383 if (!file_exists(BETTER_PAYMENT_PATH . '/assets/admin/admin.min.js')) {
384 $admin_js_file = BETTER_PAYMENT_ASSETS . '/admin/admin.js';
385 }
386
387 if (!file_exists(BETTER_PAYMENT_PATH . '/assets/admin/admin.min.css')) {
388 $admin_css_file = BETTER_PAYMENT_ASSETS . '/admin/admin.css';
389 }
390
391 wp_enqueue_script(
392 'better-payment-react-admin',
393 $admin_js_file,
394 ['wp-element', 'wp-hooks', 'wp-i18n', 'wp-api-fetch', 'react', 'react-dom'],
395 $this->file_version,
396 true
397 );
398
399 wp_enqueue_style(
400 'better-payment-react-admin',
401 $admin_css_file,
402 [],
403 $this->file_version
404 );
405
406 // Styles for the screens only Pro renders (receipt modal, Analytics, refund card,
407 // licence page) ship with Better Payment Pro, which depends on this handle.
408
409 // Campaign-list CSS contains the template-select modal styles needed on this page too.
410 $campaign_list_css = BETTER_PAYMENT_ASSETS . '/admin/campaign-list/campaign-list.min.css';
411 if ( file_exists( BETTER_PAYMENT_PATH . '/assets/admin/campaign-list/campaign-list.min.css' ) ) {
412 wp_enqueue_style(
413 'bp-campaign-list',
414 $campaign_list_css,
415 [],
416 $this->file_version
417 );
418 }
419
420 wp_add_inline_script(
421 'better-payment-react-admin',
422 'wp.apiFetch.use(wp.apiFetch.createNonceMiddleware("' . wp_create_nonce('wp_rest') . '"));',
423 'before'
424 );
425 $page_slug = $this->page_slug_prefix . '-admin';
426 $admin_base_url = admin_url('admin.php?page=' . $this->page_slug_prefix . '-admin');
427 $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})();";
428 wp_add_inline_script('better-payment-react-admin', $inline_spa_nav, 'after');
429 }
430 }
431