Completed
Branch master (91e8f4)
by
unknown
04:43
created
PaymentMethods/PayPalCommerce/modules/EED_PayPalOnboard.module.php 1 patch
Indentation   +669 added lines, -669 removed lines patch added patch discarded remove patch
@@ -20,673 +20,673 @@
 block discarded – undo
20 20
  */
21 21
 class EED_PayPalOnboard extends EED_Module
22 22
 {
23
-    /**
24
-     * @return EED_Module
25
-     * @throws EE_Error
26
-     * @throws ReflectionException
27
-     */
28
-    public static function instance(): EED_Module
29
-    {
30
-        return parent::get_instance(__CLASS__);
31
-    }
32
-
33
-
34
-    /**
35
-     * Run - initial module setup.
36
-     *
37
-     * @param WP $WP
38
-     * @return void
39
-     */
40
-    public function run($WP)
41
-    {
42
-    }
43
-
44
-
45
-    /**
46
-     * For hooking into EE Admin Core and other modules.
47
-     *
48
-     * @return void
49
-     */
50
-    public static function set_hooks_admin(): void
51
-    {
52
-        if (DbStatus::isOnline()) {
53
-            // Get onboarding URL.
54
-            add_action('wp_ajax_eeaPpGetOnboardingUrl', [__CLASS__, 'getOnboardingUrl']);
55
-            // Catch the return/redirect from PayPal onboarding page.
56
-            add_action('init', [__CLASS__, 'updateOnboardingStatus'], 10);
57
-            // Return the connection/onboard status.
58
-            add_action('wp_ajax_eeaPpGetOnboardStatus', [__CLASS__, 'getOnboardStatus']);
59
-            // Revoke access.
60
-            add_action('wp_ajax_eeaPpOffboard', [__CLASS__, 'offboard']);
61
-            // Admin notice.
62
-            add_action('admin_init', [__CLASS__, 'adminNotice']);
63
-        }
64
-    }
65
-
66
-
67
-    /**
68
-     * Get the onboarding URL.
69
-     * (AJAX)
70
-     *
71
-     * @return void
72
-     */
73
-    public static function getOnboardingUrl(): void
74
-    {
75
-        $signup_link = '';
76
-        try {
77
-            $paypal_pm = EED_PayPalCommerce::getPaymentMethod();
78
-            if (! $paypal_pm instanceof EE_Payment_Method) {
79
-                PayPalLogger::errorLogAndExit(
80
-                    esc_html__('No payment method.', 'event_espresso'),
81
-                    EED_Module::getRequest()->postParams(),
82
-                    $paypal_pm
83
-                );
84
-            }
85
-            PayPalExtraMetaManager::updateDebugMode($paypal_pm, EED_Module::getRequest()->postParams());
86
-            // $signup_link   = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_ONBOARDING_URL);
87
-            // $token_expired = EED_PayPalOnboard::partnerAccessTokenExpired($paypal_pm);
88
-            // if (! $signup_link || $token_expired) {
89
-            $signup_link = EED_PayPalOnboard::requestOnboardingUrl($paypal_pm);
90
-            // }
91
-            if (! $signup_link) {
92
-                $err_msg = esc_html__('Error! Could not generate a sign-up link.', 'event_espresso');
93
-                PayPalLogger::errorLogAndExit($err_msg, ['signup_link' => $signup_link], $paypal_pm);
94
-            }
95
-            PayPalExtraMetaManager::savePmOption($paypal_pm, Domain::META_KEY_ONBOARDING_URL, $signup_link);
96
-        } catch (Exception $exception) {
97
-            PayPalLogger::errorLogAndExit($exception->getMessage());
98
-        }
99
-        // Is it empty (can happen if we didn't get the URL through the API).
100
-        $signup_link = $signup_link ? $signup_link . '?&displayMode=minibrowser' : '#';
101
-        wp_send_json(
102
-            [
103
-                'signup_link' => $signup_link,
104
-            ]
105
-        );
106
-    }
107
-
108
-
109
-    /**
110
-     * Request the sign-up link from PayPal.
111
-     *
112
-     * @param EE_Payment_Method $paypal_pm
113
-     * @param bool              $one_time_request
114
-     * @return string
115
-     * @throws EE_Error
116
-     * @throws Exception
117
-     */
118
-    public static function requestOnboardingUrl(EE_Payment_Method $paypal_pm, bool $one_time_request = false): string
119
-    {
120
-        $signup_link = '';
121
-        // Get the access token.
122
-        $access_token = EED_PayPalOnboard::getPartnerAccessToken($paypal_pm);
123
-        if (! $access_token) {
124
-            $err_msg = esc_html__('Error! No access token.', 'event_espresso');
125
-            PayPalLogger::errorLog($err_msg, ['access_token' => $access_token], $paypal_pm);
126
-            return '';
127
-        }
128
-        // Request the access token.
129
-        $body_params = EED_PayPalOnboard::signupLinkRequestBody($paypal_pm);
130
-        $bn_code     = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_BN_CODE);
131
-        $post_params = [
132
-            'method'  => 'POST',
133
-            'headers' => [
134
-                'User-Agent'                    => sanitize_text_field($_SERVER['HTTP_USER_AGENT']),
135
-                'Content-Type'                  => 'application/json',
136
-                'Authorization'                 => 'Bearer ' . $access_token,
137
-                'PayPal-Partner-Attribution-Id' => $bn_code,
138
-            ],
139
-            'body'    => $body_params,
140
-        ];
141
-        $request_url = Domain::getPayPalApiUrl($paypal_pm) . '/v2/customer/partner-referrals';
142
-        $response    = EED_PayPalOnboard::sendRequest($paypal_pm, $request_url, $post_params);
143
-        // Check the data we received.
144
-        if (isset($response['error']) || empty($response['links'])) {
145
-            // Did the original access token get replaced by any chance ?
146
-            if (! $one_time_request
147
-                && ! empty($response['message'])
148
-                && $response['message'] === 'Access Token not found in cache'
149
-            ) {
150
-                // Clear all PM metadata and try getting the access token One more time.
151
-                PayPalExtraMetaManager::deleteAllData($paypal_pm);
152
-                return EED_PayPalOnboard::requestOnboardingUrl($paypal_pm, true);
153
-            }
154
-            $err_msg = esc_html__('Incoming sign-up link parameter validation failed.', 'event_espresso');
155
-            PayPalLogger::errorLog($err_msg, $response, $paypal_pm);
156
-            return '';
157
-        }
158
-        // Now retrieve that sign-up link.
159
-        foreach ($response['links'] as $link) {
160
-            if ($link['rel'] === 'action_url') {
161
-                $signup_link = $link['href'] ?? '';
162
-            }
163
-        }
164
-        return $signup_link;
165
-    }
166
-
167
-
168
-    /**
169
-     * Get the return URL.
170
-     *
171
-     * @param EE_Payment_Method $paypal_pm
172
-     * @return string
173
-     * @throws Exception
174
-     */
175
-    public static function signupLinkRequestBody(EE_Payment_Method $paypal_pm): string
176
-    {
177
-        $identifier_string = new OneTimeString($paypal_pm->debug_mode());
178
-        $tracking_id       = $identifier_string->value();
179
-        $request           = LoaderFactory::getLoader()->getShared(RequestInterface::class);
180
-        $checkout_type     = $request->getRequestParam('checkout_type', 'EXPRESS_CHECKOUT', DataType::STRING);
181
-        // Save the identifier for future use.
182
-        PayPalExtraMetaManager::savePmOption($paypal_pm, Domain::META_KEY_TRACKING_ID, $tracking_id);
183
-        // Assemble the return URL.
184
-        $return_url = EED_PayPalOnboard::getReturnUrl($paypal_pm);
185
-        return json_encode([
186
-            'tracking_id'             => $tracking_id,
187
-            'operations'              => [
188
-                [
189
-                    'operation'                  => 'API_INTEGRATION',
190
-                    'api_integration_preference' => [
191
-                        'rest_api_integration' => [
192
-                            'integration_method'  => 'PAYPAL',
193
-                            'integration_type'    => 'THIRD_PARTY',
194
-                            'third_party_details' => [
195
-                                'features' => ['PAYMENT', 'REFUND'],
196
-                            ],
197
-                        ],
198
-                    ],
199
-                ],
200
-            ],
201
-            'products'                => [$checkout_type],
202
-            'legal_consents'          => [
203
-                [
204
-                    'type'    => 'SHARE_DATA_CONSENT',
205
-                    'granted' => true,
206
-                ],
207
-            ],
208
-            'partner_config_override' => [
209
-                'return_url' => $return_url,
210
-            ],
211
-        ]);
212
-    }
213
-
214
-
215
-    /**
216
-     * Get the return URL.
217
-     *
218
-     * @param EE_Payment_Method $paypal_pm
219
-     * @return string
220
-     * @throws EE_Error
221
-     * @throws ReflectionException
222
-     */
223
-    public static function getReturnUrl(EE_Payment_Method $paypal_pm): string
224
-    {
225
-        $wp_nonce = EED_Module::getRequest()->getRequestParam('wp_nonce');
226
-        $nonce    = wp_create_nonce(Domain::NONCE_NAME_ONBOARDING_RETURN);
227
-        return add_query_arg(
228
-            [
229
-                'page'                        => 'espresso_payment_settings',
230
-                'webhook_action'              => 'eea_pp_commerce_merchant_onboard',
231
-                'payment_method'              => $paypal_pm->slug(),
232
-                '_wpnonce'                    => $wp_nonce,
233
-                'nonce'                       => $nonce,
234
-                Domain::META_KEY_SANDBOX_MODE => $paypal_pm->debug_mode() ? '1' : '0',
235
-            ],
236
-            admin_url('admin.php')
237
-        );
238
-    }
239
-
240
-
241
-    /**
242
-     * Redirect to the payment method (PP) settings home page.
243
-     *
244
-     * @return void
245
-     */
246
-    public static function redirectToPmSettingsHome(): void
247
-    {
248
-        $get_params = EED_Module::getRequest()->getParams();
249
-        if (empty($get_params['payment_method'])) {
250
-            // Simply do not redirect.
251
-            return;
252
-        }
253
-        $args_to_add = [
254
-            'page'           => 'espresso_payment_settings',
255
-            'payment_method' => $get_params['payment_method'],
256
-        ];
257
-        if (isset($get_params['sandbox_mode'])) {
258
-            $args_to_add[ Domain::META_KEY_SANDBOX_MODE ] = $get_params['sandbox_mode'];
259
-        }
260
-        $home_url = add_query_arg($args_to_add, admin_url('admin.php'));
261
-        wp_redirect($home_url);
262
-        exit;
263
-    }
264
-
265
-
266
-    /**
267
-     * Check user’s onboarding status.
268
-     * This will handle the user return from the auth page and also check the status via the API.
269
-     *
270
-     * @return void
271
-     * @throws EE_Error
272
-     * @throws ReflectionException
273
-     */
274
-    public static function updateOnboardingStatus(): void
275
-    {
276
-        // Check if this is the webhook from PayPal.
277
-        if (! isset($_GET['webhook_action'], $_GET['nonce'])
278
-            || $_GET['webhook_action'] !== 'eea_pp_commerce_merchant_onboard'
279
-        ) {
280
-            return;  // Ignore.
281
-        }
282
-        $get_params = EED_Module::getRequest()->getParams();
283
-        // Get the payment method.
284
-        $paypal_pm = EED_PayPalCommerce::getPaymentMethod();
285
-        // Check the response (GET) parameters.
286
-        if (! EED_PayPalOnboard::onboardingStatusResponseValid($get_params, $paypal_pm)) {
287
-            // Missing parameters. Can't proceed.
288
-            PayPalLogger::errorLog(
289
-                esc_html__('Missing required onboarding parameters.', 'event_espresso'),
290
-                $get_params,
291
-                $paypal_pm
292
-            );
293
-            EED_PayPalOnboard::redirectToPmSettingsHome();
294
-            return;
295
-        }
296
-        // Were the requested permissions granted ?
297
-        if (empty($get_params[ Domain::API_PARAM_PERMISSIONS_GRANTED ])) {
298
-            $error_message = esc_html__(
299
-                'Permissions not granted by merchant or email not confirmed.',
300
-                'event_espresso'
301
-            );
302
-            PayPalLogger::errorLog($error_message, $get_params, $paypal_pm);
303
-            EED_PayPalOnboard::redirectToPmSettingsHome();
304
-            return;
305
-        }
306
-        // Check on the onboarding status (recommended by PP).
307
-        $onboarding_status = EED_PayPalOnboard::trackSellerOnboarding(
308
-            $paypal_pm,
309
-            $get_params[ Domain::META_KEY_SELLER_MERCHANT_ID ]
310
-        );
311
-        if (! isset($onboarding_status['valid']) || ! $onboarding_status['valid']) {
312
-            PayPalLogger::errorLog(
313
-                $onboarding_status['message'],
314
-                array_merge($get_params, $onboarding_status),
315
-                $paypal_pm
316
-            );
317
-            EED_PayPalOnboard::redirectToPmSettingsHome();
318
-            return;
319
-        }
320
-        // Start saving the setup and info.
321
-        PayPalExtraMetaManager::parseAndSaveOptions($paypal_pm, $onboarding_status);
322
-        // Save the credentials.
323
-        PayPalExtraMetaManager::saveSellerApiCredentials($paypal_pm, $get_params);
324
-        // If onboarded successfully, remove the onboarding URL.
325
-        PayPalExtraMetaManager::deletePmOption($paypal_pm, Domain::META_KEY_ONBOARDING_URL);
326
-        // Also clen GET params by redirecting, because PP auto redirects to the return_url on closing the onboarding window.
327
-        EED_PayPalOnboard::redirectToPmSettingsHome();
328
-    }
329
-
330
-
331
-    /**
332
-     * Check if all required parameters for the onboarding status check are present.
333
-     *
334
-     * @param array $data
335
-     * @param mixed $paypal_pm
336
-     * @return bool
337
-     */
338
-    public static function onboardingStatusResponseValid(array $data, $paypal_pm): bool
339
-    {
340
-        // Check that we have all the required parameters and the nonce is ok.
341
-        if ($paypal_pm instanceof EE_Payment_Method
342
-            && wp_verify_nonce($data['nonce'], Domain::NONCE_NAME_ONBOARDING_RETURN)
343
-            && ! empty($data[ Domain::API_PARAM_PARTNER_ID ])
344
-            && ! empty($data[ Domain::META_KEY_SELLER_MERCHANT_ID ])
345
-            && isset($data[ Domain::API_PARAM_PERMISSIONS_GRANTED ])
346
-            && isset($data[ Domain::API_PARAM_EMAIL_CONFIRMED ])
347
-        ) {
348
-            return true;
349
-        }
350
-        return false;
351
-    }
352
-
353
-
354
-    /**
355
-     * Get partner access token.
356
-     *
357
-     * @param EE_Payment_Method $paypal_pm
358
-     * @return string
359
-     * @throws EE_Error
360
-     * @throws ReflectionException
361
-     */
362
-    public static function getPartnerAccessToken(EE_Payment_Method $paypal_pm): string
363
-    {
364
-        // Do we have it saved ?
365
-        $access_token = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_ACCESS_TOKEN);
366
-        $expired      = EED_PayPalOnboard::partnerAccessTokenExpired($paypal_pm);
367
-        // If we don't have it, request/update it.
368
-        if (! $access_token || $expired) {
369
-            return EED_PayPalOnboard::requestPartnerAccessToken($paypal_pm);
370
-        }
371
-        // Access token is saved as encrypted, so return decrypted.
372
-        return $access_token;
373
-    }
374
-
375
-
376
-    /**
377
-     * Get partner access token.
378
-     *
379
-     * @param EE_Payment_Method $paypal_pm
380
-     * @return bool
381
-     */
382
-    public static function partnerAccessTokenExpired(EE_Payment_Method $paypal_pm): bool
383
-    {
384
-        $expires_at = (int) PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_EXPIRES_IN);
385
-        if (! $expires_at) {
386
-            return true;
387
-        }
388
-        // Validate the token expiration date.
389
-        $now          = time();
390
-        $minutes_left = round(($expires_at - $now) / 60);
391
-        // Count as expired if less than 60 minutes till expiration left.
392
-        if ($minutes_left <= 60) {
393
-            return true;
394
-        }
395
-        return false;
396
-    }
397
-
398
-
399
-    /**
400
-     * Request the partner access token from PayPal and save/update it.
401
-     *
402
-     * @param EE_Payment_Method $paypal_pm
403
-     * @return string
404
-     * @throws EE_Error
405
-     * @throws ReflectionException
406
-     */
407
-    public static function requestPartnerAccessToken(EE_Payment_Method $paypal_pm): string
408
-    {
409
-        $nonce = wp_create_nonce('eea_pp_commerce_get_access_token');
410
-        // Request the access token.
411
-        $post_args = [
412
-            'method' => 'POST',
413
-            'body'   => [
414
-                'nonce'                       => $nonce,
415
-                'api_version'                 => 'v1',
416
-                Domain::META_KEY_SANDBOX_MODE => $paypal_pm->debug_mode() ? '1' : '0',
417
-            ],
418
-        ];
419
-        if (defined('LOCAL_MIDDLEMAN_SERVER')) {
420
-            $post_args['sslverify'] = false;
421
-        }
422
-        $post_url = EED_PayPalOnboard::getMiddlemanBaseUrl($paypal_pm) . 'get_token';
423
-        $response = EED_PayPalOnboard::sendRequest($paypal_pm, $post_url, $post_args);
424
-        if (isset($response['error'])) {
425
-            return '';
426
-        }
427
-        // Check the data we received.
428
-        if (! EED_PayPalOnboard::partnerTokenResponseValid($response, $paypal_pm)) {
429
-            return '';
430
-        }
431
-        // If we are here all seems to be ok. Save the token and it's data.
432
-        $saved = PayPalExtraMetaManager::savePartnerAccessToken($paypal_pm, $response);
433
-        if (! $saved) {
434
-            return '';
435
-        }
436
-        return $response['access_token'];
437
-    }
438
-
439
-
440
-    /**
441
-     * Request seller onboarding status from PayPal.
442
-     *
443
-     * @param EE_Payment_Method $paypal_pm
444
-     * @param string            $merchant_id
445
-     * @return array
446
-     */
447
-    public static function trackSellerOnboarding(EE_Payment_Method $paypal_pm, string $merchant_id): array
448
-    {
449
-        $track_onboarding = EED_PayPalOnboard::getTrackOnboardingApi($paypal_pm, $merchant_id);
450
-        if (! $track_onboarding instanceof TrackSellerOnboarding) {
451
-            $err_msg = esc_html__('Failed to track seller onboarding.', 'event_espresso');
452
-            return ['error' => 'TRACK_ONBOARDING_FAILED', 'message' => $err_msg];
453
-        }
454
-        return $track_onboarding->isValid();
455
-    }
456
-
457
-
458
-    /**
459
-     * Returns the Track Seller Onboarding API.
460
-     *
461
-     * @param EE_Payment_Method $paypal_pm
462
-     * @param string            $merchant_id
463
-     * @return TrackSellerOnboarding|null
464
-     * @throws EE_Error
465
-     * @throws ReflectionException
466
-     */
467
-    public static function getTrackOnboardingApi(
468
-        EE_Payment_Method $paypal_pm,
469
-        string            $merchant_id
470
-    ): ?TrackSellerOnboarding {
471
-        $partner_id = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_PARTNER_MERCHANT_ID);
472
-        $paypal_api = EED_PayPalCommerce::getPayPalApi($paypal_pm);
473
-        if (! $paypal_api instanceof PayPalApi || ! $partner_id) {
474
-            return null;
475
-        }
476
-        return new TrackSellerOnboarding($paypal_api, $partner_id, $merchant_id, $paypal_pm->debug_mode());
477
-    }
478
-
479
-
480
-    /**
481
-     * Check the onboard status and return the result.
482
-     * (AJAX)
483
-     *
484
-     * @return void
485
-     */
486
-    public static function getOnboardStatus(): void
487
-    {
488
-        $paypal_pm = EED_PayPalCommerce::getPaymentMethod();
489
-        if (! $paypal_pm instanceof EE_Payment_Method) {
490
-            $err_msg = esc_html__('Could not specify the payment method.', 'event_espresso');
491
-            PayPalLogger::errorLog($err_msg, EED_Module::getRequest()->postParams(), $paypal_pm);
492
-            wp_send_json(['on_board' => false]);
493
-        }
494
-        try {
495
-            $seller_id = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_SELLER_MERCHANT_ID) ?? '--';
496
-        } catch (Exception $e) {
497
-            $seller_id = '--';
498
-        }
499
-        wp_send_json(
500
-            [
501
-                'on_board'  => EED_PayPalOnboard::isOnboard($paypal_pm),
502
-                'seller_id' => $seller_id,
503
-            ]
504
-        );
505
-    }
506
-
507
-
508
-    /**
509
-     * De-authorize the seller. Remove all API credentials.
510
-     * (AJAX)
511
-     *
512
-     * @return void
513
-     */
514
-    public static function offboard(): void
515
-    {
516
-        $paypal_pm = EED_PayPalCommerce::getPaymentMethod();
517
-        if (! $paypal_pm instanceof EE_Payment_Method) {
518
-            wp_send_json([
519
-                'error'   => 'INVALID_PM',
520
-                'message' => esc_html__(
521
-                    'Invalid payment method. Please refresh the page and try again.',
522
-                    'event_espresso'
523
-                ),
524
-            ]);
525
-        }
526
-        PayPalExtraMetaManager::deleteAllData($paypal_pm);
527
-        wp_send_json(['success' => true]);
528
-    }
529
-
530
-
531
-    /**
532
-     * Checks if already onboard.
533
-     *
534
-     * @param EE_Payment_Method $payment_method
535
-     * @return boolean
536
-     */
537
-    public static function isOnboard(EE_Payment_Method $payment_method): bool
538
-    {
539
-        $pp_meta_data = PayPalExtraMetaManager::getAllData($payment_method);
540
-        return
541
-            // onborded with a third party integration ?
542
-            (! empty($pp_meta_data[ Domain::META_KEY_SELLER_MERCHANT_ID ])
543
-                && ! empty($pp_meta_data[ Domain::META_KEY_ACCESS_TOKEN ])
544
-            )
545
-            // or with the first party integration ?
546
-            || (! empty($pp_meta_data[ Domain::META_KEY_CLIENT_ID ])
547
-                && ! empty($pp_meta_data[ Domain::META_KEY_CLIENT_SECRET ])
548
-                && ! empty($pp_meta_data[ Domain::META_KEY_PAYER_ID ])
549
-            );
550
-    }
551
-
552
-
553
-    /**
554
-     * Send a request and return a decoded response body.
555
-     *
556
-     * @param EE_Payment_Method $paypal_pm
557
-     * @param string            $request_url
558
-     * @param array             $request_args
559
-     * @return array
560
-     */
561
-    public static function sendRequest(EE_Payment_Method $paypal_pm, string $request_url, array $request_args): array
562
-    {
563
-        $error_return = ['error' => true];
564
-        $response     = wp_remote_request($request_url, $request_args);
565
-        if (is_wp_error($response)) {
566
-            $message = $response->get_error_message();
567
-            PayPalLogger::errorLog($message, [$request_url, $request_args, $response], $paypal_pm);
568
-            $error_return['message'] = $message;
569
-            return $error_return;
570
-        }
571
-        $response_body = (isset($response['body']) && $response['body']) ? json_decode($response['body'], true) : [];
572
-        if (empty($response_body) || isset($response_body['error'])) {
573
-            $message = $response_body['error_description']
574
-                       ?? sprintf(
575
-                           esc_html__('Unknown response received while sending a request to: %1$s', 'event_espresso'),
576
-                           $request_url
577
-                       );
578
-            PayPalLogger::errorLog($message, [$request_url, $request_args, $response], $paypal_pm);
579
-            $error_return['message'] = $message;
580
-            return $error_return;
581
-        }
582
-        return $response_body;
583
-    }
584
-
585
-
586
-    /**
587
-     * Check the response for a partner token request.
588
-     *
589
-     * @param                   $response
590
-     * @param EE_Payment_Method $paypal_pm
591
-     * @return bool
592
-     */
593
-    public static function partnerTokenResponseValid($response, EE_Payment_Method $paypal_pm): bool
594
-    {
595
-        // Check the data we received.
596
-        if (
597
-            empty($response['nonce'])
598
-            || ! wp_verify_nonce($response['nonce'], 'eea_pp_commerce_get_access_token')
599
-            || empty($response['access_token'])
600
-            || empty($response['app_id'])
601
-            || empty($response['expires_in'])
602
-            || empty($response['partner_client_id'])
603
-            || empty($response['partner_merchant_id'])
604
-        ) {
605
-            // This is an error.
606
-            $err_msg = esc_html__('Incoming parameter validation failed.', 'event_espresso');
607
-            PayPalLogger::errorLog($err_msg, (array) $response, $paypal_pm);
608
-            return false;
609
-        }
610
-        return true;
611
-    }
612
-
613
-
614
-    /**
615
-     * Returns the base URL to the middleman server.
616
-     * If LOCAL_MIDDLEMAN_SERVER is defined, requests will be sent to connect.eventespresso.test
617
-     *
618
-     * @param EE_Payment_Method $payment_method
619
-     * @return string
620
-     * @throws EE_Error
621
-     * @throws ReflectionException
622
-     */
623
-    public static function getMiddlemanBaseUrl(EE_Payment_Method $payment_method): string
624
-    {
625
-        $target = defined('LOCAL_MIDDLEMAN_SERVER') ? 'test' : 'com';
626
-        // If this PM is used under different provider accounts, you might need an account indicator.
627
-        $account = defined('EE_PAYPAL_COMMERCE_ACCOUNT_INDICATOR') ? EE_PAYPAL_COMMERCE_ACCOUNT_INDICATOR : '';
628
-        $postfix = $payment_method->debug_mode() ? '_sandbox' : '';
629
-        $path    = 'paypal_commerce' . $account . $postfix;
630
-        return 'https://connect.eventespresso.' . $target . '/' . $path . '/';
631
-    }
632
-
633
-
634
-    /**
635
-     * This Payment Method admin notices.
636
-     *
637
-     * @return void
638
-     * @throws EE_Error
639
-     * @throws ReflectionException
640
-     */
641
-    public static function adminNotice()
642
-    {
643
-        // A notice to re-connect if still connected through the first party integration.
644
-        $pp_commerce = EEM_Payment_Method::instance()->get_one_by_slug('paypalcheckout');
645
-        // Show the notice only if PayPal Commerce is active.
646
-        if (! $pp_commerce instanceof EE_Payment_Method
647
-            || ! EED_PayPalOnboard::isOnboard($pp_commerce)
648
-            || EED_PayPalCommerce::isThirdParty($pp_commerce)
649
-        ) {
650
-            return;
651
-        }
652
-        add_action('admin_notices', [__CLASS__, 'reConnectNotice']);
653
-    }
654
-
655
-
656
-    /**
657
-     * Re-connect notice contents.
658
-     *
659
-     * @return void
660
-     * @throws EE_Error
661
-     * @throws ReflectionException
662
-     */
663
-    public static function reConnectNotice()
664
-    {
665
-        $open_anchor = $close_anchor = '';
666
-        $pp_commerce = EEM_Payment_Method::instance()->get_one_by_slug('paypalcheckout');
667
-        if ($pp_commerce instanceof EE_Payment_Method) {
668
-            $pm_page = add_query_arg(
669
-                [
670
-                    'page'           => 'espresso_payment_settings',
671
-                    'webhook_action' => 'eea_pp_commerce_merchant_onboard',
672
-                    'payment_method' => $pp_commerce->slug(),
673
-                ],
674
-                admin_url('admin.php')
675
-            );
676
-            $open_anchor  = "<a href='$pm_page'>";
677
-            $close_anchor = "</a>";
678
-        }
679
-        echo '<div class="error"><p>'
680
-        . sprintf(
681
-            esc_html__(
682
-                '%1$sPayPal Commerce%2$s has updated the API integration type to allow more flexibility with payments. Please disconnect and re-Connect on the %3$sPayment Methods admin%4$s page to update the credentials and allow advanced payment type options.',
683
-                'event_espresso'
684
-            ),
685
-            '<strong>',
686
-            '</strong>',
687
-            $open_anchor,
688
-            $close_anchor
689
-         )
690
-         . '</p></div>';
691
-    }
23
+	/**
24
+	 * @return EED_Module
25
+	 * @throws EE_Error
26
+	 * @throws ReflectionException
27
+	 */
28
+	public static function instance(): EED_Module
29
+	{
30
+		return parent::get_instance(__CLASS__);
31
+	}
32
+
33
+
34
+	/**
35
+	 * Run - initial module setup.
36
+	 *
37
+	 * @param WP $WP
38
+	 * @return void
39
+	 */
40
+	public function run($WP)
41
+	{
42
+	}
43
+
44
+
45
+	/**
46
+	 * For hooking into EE Admin Core and other modules.
47
+	 *
48
+	 * @return void
49
+	 */
50
+	public static function set_hooks_admin(): void
51
+	{
52
+		if (DbStatus::isOnline()) {
53
+			// Get onboarding URL.
54
+			add_action('wp_ajax_eeaPpGetOnboardingUrl', [__CLASS__, 'getOnboardingUrl']);
55
+			// Catch the return/redirect from PayPal onboarding page.
56
+			add_action('init', [__CLASS__, 'updateOnboardingStatus'], 10);
57
+			// Return the connection/onboard status.
58
+			add_action('wp_ajax_eeaPpGetOnboardStatus', [__CLASS__, 'getOnboardStatus']);
59
+			// Revoke access.
60
+			add_action('wp_ajax_eeaPpOffboard', [__CLASS__, 'offboard']);
61
+			// Admin notice.
62
+			add_action('admin_init', [__CLASS__, 'adminNotice']);
63
+		}
64
+	}
65
+
66
+
67
+	/**
68
+	 * Get the onboarding URL.
69
+	 * (AJAX)
70
+	 *
71
+	 * @return void
72
+	 */
73
+	public static function getOnboardingUrl(): void
74
+	{
75
+		$signup_link = '';
76
+		try {
77
+			$paypal_pm = EED_PayPalCommerce::getPaymentMethod();
78
+			if (! $paypal_pm instanceof EE_Payment_Method) {
79
+				PayPalLogger::errorLogAndExit(
80
+					esc_html__('No payment method.', 'event_espresso'),
81
+					EED_Module::getRequest()->postParams(),
82
+					$paypal_pm
83
+				);
84
+			}
85
+			PayPalExtraMetaManager::updateDebugMode($paypal_pm, EED_Module::getRequest()->postParams());
86
+			// $signup_link   = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_ONBOARDING_URL);
87
+			// $token_expired = EED_PayPalOnboard::partnerAccessTokenExpired($paypal_pm);
88
+			// if (! $signup_link || $token_expired) {
89
+			$signup_link = EED_PayPalOnboard::requestOnboardingUrl($paypal_pm);
90
+			// }
91
+			if (! $signup_link) {
92
+				$err_msg = esc_html__('Error! Could not generate a sign-up link.', 'event_espresso');
93
+				PayPalLogger::errorLogAndExit($err_msg, ['signup_link' => $signup_link], $paypal_pm);
94
+			}
95
+			PayPalExtraMetaManager::savePmOption($paypal_pm, Domain::META_KEY_ONBOARDING_URL, $signup_link);
96
+		} catch (Exception $exception) {
97
+			PayPalLogger::errorLogAndExit($exception->getMessage());
98
+		}
99
+		// Is it empty (can happen if we didn't get the URL through the API).
100
+		$signup_link = $signup_link ? $signup_link . '?&displayMode=minibrowser' : '#';
101
+		wp_send_json(
102
+			[
103
+				'signup_link' => $signup_link,
104
+			]
105
+		);
106
+	}
107
+
108
+
109
+	/**
110
+	 * Request the sign-up link from PayPal.
111
+	 *
112
+	 * @param EE_Payment_Method $paypal_pm
113
+	 * @param bool              $one_time_request
114
+	 * @return string
115
+	 * @throws EE_Error
116
+	 * @throws Exception
117
+	 */
118
+	public static function requestOnboardingUrl(EE_Payment_Method $paypal_pm, bool $one_time_request = false): string
119
+	{
120
+		$signup_link = '';
121
+		// Get the access token.
122
+		$access_token = EED_PayPalOnboard::getPartnerAccessToken($paypal_pm);
123
+		if (! $access_token) {
124
+			$err_msg = esc_html__('Error! No access token.', 'event_espresso');
125
+			PayPalLogger::errorLog($err_msg, ['access_token' => $access_token], $paypal_pm);
126
+			return '';
127
+		}
128
+		// Request the access token.
129
+		$body_params = EED_PayPalOnboard::signupLinkRequestBody($paypal_pm);
130
+		$bn_code     = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_BN_CODE);
131
+		$post_params = [
132
+			'method'  => 'POST',
133
+			'headers' => [
134
+				'User-Agent'                    => sanitize_text_field($_SERVER['HTTP_USER_AGENT']),
135
+				'Content-Type'                  => 'application/json',
136
+				'Authorization'                 => 'Bearer ' . $access_token,
137
+				'PayPal-Partner-Attribution-Id' => $bn_code,
138
+			],
139
+			'body'    => $body_params,
140
+		];
141
+		$request_url = Domain::getPayPalApiUrl($paypal_pm) . '/v2/customer/partner-referrals';
142
+		$response    = EED_PayPalOnboard::sendRequest($paypal_pm, $request_url, $post_params);
143
+		// Check the data we received.
144
+		if (isset($response['error']) || empty($response['links'])) {
145
+			// Did the original access token get replaced by any chance ?
146
+			if (! $one_time_request
147
+				&& ! empty($response['message'])
148
+				&& $response['message'] === 'Access Token not found in cache'
149
+			) {
150
+				// Clear all PM metadata and try getting the access token One more time.
151
+				PayPalExtraMetaManager::deleteAllData($paypal_pm);
152
+				return EED_PayPalOnboard::requestOnboardingUrl($paypal_pm, true);
153
+			}
154
+			$err_msg = esc_html__('Incoming sign-up link parameter validation failed.', 'event_espresso');
155
+			PayPalLogger::errorLog($err_msg, $response, $paypal_pm);
156
+			return '';
157
+		}
158
+		// Now retrieve that sign-up link.
159
+		foreach ($response['links'] as $link) {
160
+			if ($link['rel'] === 'action_url') {
161
+				$signup_link = $link['href'] ?? '';
162
+			}
163
+		}
164
+		return $signup_link;
165
+	}
166
+
167
+
168
+	/**
169
+	 * Get the return URL.
170
+	 *
171
+	 * @param EE_Payment_Method $paypal_pm
172
+	 * @return string
173
+	 * @throws Exception
174
+	 */
175
+	public static function signupLinkRequestBody(EE_Payment_Method $paypal_pm): string
176
+	{
177
+		$identifier_string = new OneTimeString($paypal_pm->debug_mode());
178
+		$tracking_id       = $identifier_string->value();
179
+		$request           = LoaderFactory::getLoader()->getShared(RequestInterface::class);
180
+		$checkout_type     = $request->getRequestParam('checkout_type', 'EXPRESS_CHECKOUT', DataType::STRING);
181
+		// Save the identifier for future use.
182
+		PayPalExtraMetaManager::savePmOption($paypal_pm, Domain::META_KEY_TRACKING_ID, $tracking_id);
183
+		// Assemble the return URL.
184
+		$return_url = EED_PayPalOnboard::getReturnUrl($paypal_pm);
185
+		return json_encode([
186
+			'tracking_id'             => $tracking_id,
187
+			'operations'              => [
188
+				[
189
+					'operation'                  => 'API_INTEGRATION',
190
+					'api_integration_preference' => [
191
+						'rest_api_integration' => [
192
+							'integration_method'  => 'PAYPAL',
193
+							'integration_type'    => 'THIRD_PARTY',
194
+							'third_party_details' => [
195
+								'features' => ['PAYMENT', 'REFUND'],
196
+							],
197
+						],
198
+					],
199
+				],
200
+			],
201
+			'products'                => [$checkout_type],
202
+			'legal_consents'          => [
203
+				[
204
+					'type'    => 'SHARE_DATA_CONSENT',
205
+					'granted' => true,
206
+				],
207
+			],
208
+			'partner_config_override' => [
209
+				'return_url' => $return_url,
210
+			],
211
+		]);
212
+	}
213
+
214
+
215
+	/**
216
+	 * Get the return URL.
217
+	 *
218
+	 * @param EE_Payment_Method $paypal_pm
219
+	 * @return string
220
+	 * @throws EE_Error
221
+	 * @throws ReflectionException
222
+	 */
223
+	public static function getReturnUrl(EE_Payment_Method $paypal_pm): string
224
+	{
225
+		$wp_nonce = EED_Module::getRequest()->getRequestParam('wp_nonce');
226
+		$nonce    = wp_create_nonce(Domain::NONCE_NAME_ONBOARDING_RETURN);
227
+		return add_query_arg(
228
+			[
229
+				'page'                        => 'espresso_payment_settings',
230
+				'webhook_action'              => 'eea_pp_commerce_merchant_onboard',
231
+				'payment_method'              => $paypal_pm->slug(),
232
+				'_wpnonce'                    => $wp_nonce,
233
+				'nonce'                       => $nonce,
234
+				Domain::META_KEY_SANDBOX_MODE => $paypal_pm->debug_mode() ? '1' : '0',
235
+			],
236
+			admin_url('admin.php')
237
+		);
238
+	}
239
+
240
+
241
+	/**
242
+	 * Redirect to the payment method (PP) settings home page.
243
+	 *
244
+	 * @return void
245
+	 */
246
+	public static function redirectToPmSettingsHome(): void
247
+	{
248
+		$get_params = EED_Module::getRequest()->getParams();
249
+		if (empty($get_params['payment_method'])) {
250
+			// Simply do not redirect.
251
+			return;
252
+		}
253
+		$args_to_add = [
254
+			'page'           => 'espresso_payment_settings',
255
+			'payment_method' => $get_params['payment_method'],
256
+		];
257
+		if (isset($get_params['sandbox_mode'])) {
258
+			$args_to_add[ Domain::META_KEY_SANDBOX_MODE ] = $get_params['sandbox_mode'];
259
+		}
260
+		$home_url = add_query_arg($args_to_add, admin_url('admin.php'));
261
+		wp_redirect($home_url);
262
+		exit;
263
+	}
264
+
265
+
266
+	/**
267
+	 * Check user’s onboarding status.
268
+	 * This will handle the user return from the auth page and also check the status via the API.
269
+	 *
270
+	 * @return void
271
+	 * @throws EE_Error
272
+	 * @throws ReflectionException
273
+	 */
274
+	public static function updateOnboardingStatus(): void
275
+	{
276
+		// Check if this is the webhook from PayPal.
277
+		if (! isset($_GET['webhook_action'], $_GET['nonce'])
278
+			|| $_GET['webhook_action'] !== 'eea_pp_commerce_merchant_onboard'
279
+		) {
280
+			return;  // Ignore.
281
+		}
282
+		$get_params = EED_Module::getRequest()->getParams();
283
+		// Get the payment method.
284
+		$paypal_pm = EED_PayPalCommerce::getPaymentMethod();
285
+		// Check the response (GET) parameters.
286
+		if (! EED_PayPalOnboard::onboardingStatusResponseValid($get_params, $paypal_pm)) {
287
+			// Missing parameters. Can't proceed.
288
+			PayPalLogger::errorLog(
289
+				esc_html__('Missing required onboarding parameters.', 'event_espresso'),
290
+				$get_params,
291
+				$paypal_pm
292
+			);
293
+			EED_PayPalOnboard::redirectToPmSettingsHome();
294
+			return;
295
+		}
296
+		// Were the requested permissions granted ?
297
+		if (empty($get_params[ Domain::API_PARAM_PERMISSIONS_GRANTED ])) {
298
+			$error_message = esc_html__(
299
+				'Permissions not granted by merchant or email not confirmed.',
300
+				'event_espresso'
301
+			);
302
+			PayPalLogger::errorLog($error_message, $get_params, $paypal_pm);
303
+			EED_PayPalOnboard::redirectToPmSettingsHome();
304
+			return;
305
+		}
306
+		// Check on the onboarding status (recommended by PP).
307
+		$onboarding_status = EED_PayPalOnboard::trackSellerOnboarding(
308
+			$paypal_pm,
309
+			$get_params[ Domain::META_KEY_SELLER_MERCHANT_ID ]
310
+		);
311
+		if (! isset($onboarding_status['valid']) || ! $onboarding_status['valid']) {
312
+			PayPalLogger::errorLog(
313
+				$onboarding_status['message'],
314
+				array_merge($get_params, $onboarding_status),
315
+				$paypal_pm
316
+			);
317
+			EED_PayPalOnboard::redirectToPmSettingsHome();
318
+			return;
319
+		}
320
+		// Start saving the setup and info.
321
+		PayPalExtraMetaManager::parseAndSaveOptions($paypal_pm, $onboarding_status);
322
+		// Save the credentials.
323
+		PayPalExtraMetaManager::saveSellerApiCredentials($paypal_pm, $get_params);
324
+		// If onboarded successfully, remove the onboarding URL.
325
+		PayPalExtraMetaManager::deletePmOption($paypal_pm, Domain::META_KEY_ONBOARDING_URL);
326
+		// Also clen GET params by redirecting, because PP auto redirects to the return_url on closing the onboarding window.
327
+		EED_PayPalOnboard::redirectToPmSettingsHome();
328
+	}
329
+
330
+
331
+	/**
332
+	 * Check if all required parameters for the onboarding status check are present.
333
+	 *
334
+	 * @param array $data
335
+	 * @param mixed $paypal_pm
336
+	 * @return bool
337
+	 */
338
+	public static function onboardingStatusResponseValid(array $data, $paypal_pm): bool
339
+	{
340
+		// Check that we have all the required parameters and the nonce is ok.
341
+		if ($paypal_pm instanceof EE_Payment_Method
342
+			&& wp_verify_nonce($data['nonce'], Domain::NONCE_NAME_ONBOARDING_RETURN)
343
+			&& ! empty($data[ Domain::API_PARAM_PARTNER_ID ])
344
+			&& ! empty($data[ Domain::META_KEY_SELLER_MERCHANT_ID ])
345
+			&& isset($data[ Domain::API_PARAM_PERMISSIONS_GRANTED ])
346
+			&& isset($data[ Domain::API_PARAM_EMAIL_CONFIRMED ])
347
+		) {
348
+			return true;
349
+		}
350
+		return false;
351
+	}
352
+
353
+
354
+	/**
355
+	 * Get partner access token.
356
+	 *
357
+	 * @param EE_Payment_Method $paypal_pm
358
+	 * @return string
359
+	 * @throws EE_Error
360
+	 * @throws ReflectionException
361
+	 */
362
+	public static function getPartnerAccessToken(EE_Payment_Method $paypal_pm): string
363
+	{
364
+		// Do we have it saved ?
365
+		$access_token = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_ACCESS_TOKEN);
366
+		$expired      = EED_PayPalOnboard::partnerAccessTokenExpired($paypal_pm);
367
+		// If we don't have it, request/update it.
368
+		if (! $access_token || $expired) {
369
+			return EED_PayPalOnboard::requestPartnerAccessToken($paypal_pm);
370
+		}
371
+		// Access token is saved as encrypted, so return decrypted.
372
+		return $access_token;
373
+	}
374
+
375
+
376
+	/**
377
+	 * Get partner access token.
378
+	 *
379
+	 * @param EE_Payment_Method $paypal_pm
380
+	 * @return bool
381
+	 */
382
+	public static function partnerAccessTokenExpired(EE_Payment_Method $paypal_pm): bool
383
+	{
384
+		$expires_at = (int) PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_EXPIRES_IN);
385
+		if (! $expires_at) {
386
+			return true;
387
+		}
388
+		// Validate the token expiration date.
389
+		$now          = time();
390
+		$minutes_left = round(($expires_at - $now) / 60);
391
+		// Count as expired if less than 60 minutes till expiration left.
392
+		if ($minutes_left <= 60) {
393
+			return true;
394
+		}
395
+		return false;
396
+	}
397
+
398
+
399
+	/**
400
+	 * Request the partner access token from PayPal and save/update it.
401
+	 *
402
+	 * @param EE_Payment_Method $paypal_pm
403
+	 * @return string
404
+	 * @throws EE_Error
405
+	 * @throws ReflectionException
406
+	 */
407
+	public static function requestPartnerAccessToken(EE_Payment_Method $paypal_pm): string
408
+	{
409
+		$nonce = wp_create_nonce('eea_pp_commerce_get_access_token');
410
+		// Request the access token.
411
+		$post_args = [
412
+			'method' => 'POST',
413
+			'body'   => [
414
+				'nonce'                       => $nonce,
415
+				'api_version'                 => 'v1',
416
+				Domain::META_KEY_SANDBOX_MODE => $paypal_pm->debug_mode() ? '1' : '0',
417
+			],
418
+		];
419
+		if (defined('LOCAL_MIDDLEMAN_SERVER')) {
420
+			$post_args['sslverify'] = false;
421
+		}
422
+		$post_url = EED_PayPalOnboard::getMiddlemanBaseUrl($paypal_pm) . 'get_token';
423
+		$response = EED_PayPalOnboard::sendRequest($paypal_pm, $post_url, $post_args);
424
+		if (isset($response['error'])) {
425
+			return '';
426
+		}
427
+		// Check the data we received.
428
+		if (! EED_PayPalOnboard::partnerTokenResponseValid($response, $paypal_pm)) {
429
+			return '';
430
+		}
431
+		// If we are here all seems to be ok. Save the token and it's data.
432
+		$saved = PayPalExtraMetaManager::savePartnerAccessToken($paypal_pm, $response);
433
+		if (! $saved) {
434
+			return '';
435
+		}
436
+		return $response['access_token'];
437
+	}
438
+
439
+
440
+	/**
441
+	 * Request seller onboarding status from PayPal.
442
+	 *
443
+	 * @param EE_Payment_Method $paypal_pm
444
+	 * @param string            $merchant_id
445
+	 * @return array
446
+	 */
447
+	public static function trackSellerOnboarding(EE_Payment_Method $paypal_pm, string $merchant_id): array
448
+	{
449
+		$track_onboarding = EED_PayPalOnboard::getTrackOnboardingApi($paypal_pm, $merchant_id);
450
+		if (! $track_onboarding instanceof TrackSellerOnboarding) {
451
+			$err_msg = esc_html__('Failed to track seller onboarding.', 'event_espresso');
452
+			return ['error' => 'TRACK_ONBOARDING_FAILED', 'message' => $err_msg];
453
+		}
454
+		return $track_onboarding->isValid();
455
+	}
456
+
457
+
458
+	/**
459
+	 * Returns the Track Seller Onboarding API.
460
+	 *
461
+	 * @param EE_Payment_Method $paypal_pm
462
+	 * @param string            $merchant_id
463
+	 * @return TrackSellerOnboarding|null
464
+	 * @throws EE_Error
465
+	 * @throws ReflectionException
466
+	 */
467
+	public static function getTrackOnboardingApi(
468
+		EE_Payment_Method $paypal_pm,
469
+		string            $merchant_id
470
+	): ?TrackSellerOnboarding {
471
+		$partner_id = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_PARTNER_MERCHANT_ID);
472
+		$paypal_api = EED_PayPalCommerce::getPayPalApi($paypal_pm);
473
+		if (! $paypal_api instanceof PayPalApi || ! $partner_id) {
474
+			return null;
475
+		}
476
+		return new TrackSellerOnboarding($paypal_api, $partner_id, $merchant_id, $paypal_pm->debug_mode());
477
+	}
478
+
479
+
480
+	/**
481
+	 * Check the onboard status and return the result.
482
+	 * (AJAX)
483
+	 *
484
+	 * @return void
485
+	 */
486
+	public static function getOnboardStatus(): void
487
+	{
488
+		$paypal_pm = EED_PayPalCommerce::getPaymentMethod();
489
+		if (! $paypal_pm instanceof EE_Payment_Method) {
490
+			$err_msg = esc_html__('Could not specify the payment method.', 'event_espresso');
491
+			PayPalLogger::errorLog($err_msg, EED_Module::getRequest()->postParams(), $paypal_pm);
492
+			wp_send_json(['on_board' => false]);
493
+		}
494
+		try {
495
+			$seller_id = PayPalExtraMetaManager::getPmOption($paypal_pm, Domain::META_KEY_SELLER_MERCHANT_ID) ?? '--';
496
+		} catch (Exception $e) {
497
+			$seller_id = '--';
498
+		}
499
+		wp_send_json(
500
+			[
501
+				'on_board'  => EED_PayPalOnboard::isOnboard($paypal_pm),
502
+				'seller_id' => $seller_id,
503
+			]
504
+		);
505
+	}
506
+
507
+
508
+	/**
509
+	 * De-authorize the seller. Remove all API credentials.
510
+	 * (AJAX)
511
+	 *
512
+	 * @return void
513
+	 */
514
+	public static function offboard(): void
515
+	{
516
+		$paypal_pm = EED_PayPalCommerce::getPaymentMethod();
517
+		if (! $paypal_pm instanceof EE_Payment_Method) {
518
+			wp_send_json([
519
+				'error'   => 'INVALID_PM',
520
+				'message' => esc_html__(
521
+					'Invalid payment method. Please refresh the page and try again.',
522
+					'event_espresso'
523
+				),
524
+			]);
525
+		}
526
+		PayPalExtraMetaManager::deleteAllData($paypal_pm);
527
+		wp_send_json(['success' => true]);
528
+	}
529
+
530
+
531
+	/**
532
+	 * Checks if already onboard.
533
+	 *
534
+	 * @param EE_Payment_Method $payment_method
535
+	 * @return boolean
536
+	 */
537
+	public static function isOnboard(EE_Payment_Method $payment_method): bool
538
+	{
539
+		$pp_meta_data = PayPalExtraMetaManager::getAllData($payment_method);
540
+		return
541
+			// onborded with a third party integration ?
542
+			(! empty($pp_meta_data[ Domain::META_KEY_SELLER_MERCHANT_ID ])
543
+				&& ! empty($pp_meta_data[ Domain::META_KEY_ACCESS_TOKEN ])
544
+			)
545
+			// or with the first party integration ?
546
+			|| (! empty($pp_meta_data[ Domain::META_KEY_CLIENT_ID ])
547
+				&& ! empty($pp_meta_data[ Domain::META_KEY_CLIENT_SECRET ])
548
+				&& ! empty($pp_meta_data[ Domain::META_KEY_PAYER_ID ])
549
+			);
550
+	}
551
+
552
+
553
+	/**
554
+	 * Send a request and return a decoded response body.
555
+	 *
556
+	 * @param EE_Payment_Method $paypal_pm
557
+	 * @param string            $request_url
558
+	 * @param array             $request_args
559
+	 * @return array
560
+	 */
561
+	public static function sendRequest(EE_Payment_Method $paypal_pm, string $request_url, array $request_args): array
562
+	{
563
+		$error_return = ['error' => true];
564
+		$response     = wp_remote_request($request_url, $request_args);
565
+		if (is_wp_error($response)) {
566
+			$message = $response->get_error_message();
567
+			PayPalLogger::errorLog($message, [$request_url, $request_args, $response], $paypal_pm);
568
+			$error_return['message'] = $message;
569
+			return $error_return;
570
+		}
571
+		$response_body = (isset($response['body']) && $response['body']) ? json_decode($response['body'], true) : [];
572
+		if (empty($response_body) || isset($response_body['error'])) {
573
+			$message = $response_body['error_description']
574
+					   ?? sprintf(
575
+						   esc_html__('Unknown response received while sending a request to: %1$s', 'event_espresso'),
576
+						   $request_url
577
+					   );
578
+			PayPalLogger::errorLog($message, [$request_url, $request_args, $response], $paypal_pm);
579
+			$error_return['message'] = $message;
580
+			return $error_return;
581
+		}
582
+		return $response_body;
583
+	}
584
+
585
+
586
+	/**
587
+	 * Check the response for a partner token request.
588
+	 *
589
+	 * @param                   $response
590
+	 * @param EE_Payment_Method $paypal_pm
591
+	 * @return bool
592
+	 */
593
+	public static function partnerTokenResponseValid($response, EE_Payment_Method $paypal_pm): bool
594
+	{
595
+		// Check the data we received.
596
+		if (
597
+			empty($response['nonce'])
598
+			|| ! wp_verify_nonce($response['nonce'], 'eea_pp_commerce_get_access_token')
599
+			|| empty($response['access_token'])
600
+			|| empty($response['app_id'])
601
+			|| empty($response['expires_in'])
602
+			|| empty($response['partner_client_id'])
603
+			|| empty($response['partner_merchant_id'])
604
+		) {
605
+			// This is an error.
606
+			$err_msg = esc_html__('Incoming parameter validation failed.', 'event_espresso');
607
+			PayPalLogger::errorLog($err_msg, (array) $response, $paypal_pm);
608
+			return false;
609
+		}
610
+		return true;
611
+	}
612
+
613
+
614
+	/**
615
+	 * Returns the base URL to the middleman server.
616
+	 * If LOCAL_MIDDLEMAN_SERVER is defined, requests will be sent to connect.eventespresso.test
617
+	 *
618
+	 * @param EE_Payment_Method $payment_method
619
+	 * @return string
620
+	 * @throws EE_Error
621
+	 * @throws ReflectionException
622
+	 */
623
+	public static function getMiddlemanBaseUrl(EE_Payment_Method $payment_method): string
624
+	{
625
+		$target = defined('LOCAL_MIDDLEMAN_SERVER') ? 'test' : 'com';
626
+		// If this PM is used under different provider accounts, you might need an account indicator.
627
+		$account = defined('EE_PAYPAL_COMMERCE_ACCOUNT_INDICATOR') ? EE_PAYPAL_COMMERCE_ACCOUNT_INDICATOR : '';
628
+		$postfix = $payment_method->debug_mode() ? '_sandbox' : '';
629
+		$path    = 'paypal_commerce' . $account . $postfix;
630
+		return 'https://connect.eventespresso.' . $target . '/' . $path . '/';
631
+	}
632
+
633
+
634
+	/**
635
+	 * This Payment Method admin notices.
636
+	 *
637
+	 * @return void
638
+	 * @throws EE_Error
639
+	 * @throws ReflectionException
640
+	 */
641
+	public static function adminNotice()
642
+	{
643
+		// A notice to re-connect if still connected through the first party integration.
644
+		$pp_commerce = EEM_Payment_Method::instance()->get_one_by_slug('paypalcheckout');
645
+		// Show the notice only if PayPal Commerce is active.
646
+		if (! $pp_commerce instanceof EE_Payment_Method
647
+			|| ! EED_PayPalOnboard::isOnboard($pp_commerce)
648
+			|| EED_PayPalCommerce::isThirdParty($pp_commerce)
649
+		) {
650
+			return;
651
+		}
652
+		add_action('admin_notices', [__CLASS__, 'reConnectNotice']);
653
+	}
654
+
655
+
656
+	/**
657
+	 * Re-connect notice contents.
658
+	 *
659
+	 * @return void
660
+	 * @throws EE_Error
661
+	 * @throws ReflectionException
662
+	 */
663
+	public static function reConnectNotice()
664
+	{
665
+		$open_anchor = $close_anchor = '';
666
+		$pp_commerce = EEM_Payment_Method::instance()->get_one_by_slug('paypalcheckout');
667
+		if ($pp_commerce instanceof EE_Payment_Method) {
668
+			$pm_page = add_query_arg(
669
+				[
670
+					'page'           => 'espresso_payment_settings',
671
+					'webhook_action' => 'eea_pp_commerce_merchant_onboard',
672
+					'payment_method' => $pp_commerce->slug(),
673
+				],
674
+				admin_url('admin.php')
675
+			);
676
+			$open_anchor  = "<a href='$pm_page'>";
677
+			$close_anchor = "</a>";
678
+		}
679
+		echo '<div class="error"><p>'
680
+		. sprintf(
681
+			esc_html__(
682
+				'%1$sPayPal Commerce%2$s has updated the API integration type to allow more flexibility with payments. Please disconnect and re-Connect on the %3$sPayment Methods admin%4$s page to update the credentials and allow advanced payment type options.',
683
+				'event_espresso'
684
+			),
685
+			'<strong>',
686
+			'</strong>',
687
+			$open_anchor,
688
+			$close_anchor
689
+		 )
690
+		 . '</p></div>';
691
+	}
692 692
 }
Please login to merge, or discard this patch.
PaymentMethods/PayPalCommerce/tools/extra_meta/PayPalExtraMetaManager.php 1 patch
Indentation   +267 added lines, -267 removed lines patch added patch discarded remove patch
@@ -22,295 +22,295 @@
 block discarded – undo
22 22
  */
23 23
 class PayPalExtraMetaManager
24 24
 {
25
-    /**
26
-     * Get payment method option/extra meta
27
-     *
28
-     * @param EE_Payment_Method $paypal_pm
29
-     * @param string            $option_name
30
-     * @return mixed
31
-     */
32
-    public static function getPmOption(EE_Payment_Method $paypal_pm, string $option_name)
33
-    {
34
-        $pp_meta_data = self::extraMeta($paypal_pm);
35
-        $option_value = $pp_meta_data->getOption($option_name);
36
-        // Decrypt the encrypted options.
37
-        if (
38
-            $option_name === Domain::META_KEY_ACCESS_TOKEN
39
-            || $option_name === Domain::META_KEY_PARTNER_MERCHANT_ID
40
-            || $option_name === Domain::META_KEY_CLIENT_SECRET
41
-        ) {
42
-            $option_value = self::decryptString($option_value, $paypal_pm);
43
-        }
44
-        return $option_value;
45
-    }
25
+	/**
26
+	 * Get payment method option/extra meta
27
+	 *
28
+	 * @param EE_Payment_Method $paypal_pm
29
+	 * @param string            $option_name
30
+	 * @return mixed
31
+	 */
32
+	public static function getPmOption(EE_Payment_Method $paypal_pm, string $option_name)
33
+	{
34
+		$pp_meta_data = self::extraMeta($paypal_pm);
35
+		$option_value = $pp_meta_data->getOption($option_name);
36
+		// Decrypt the encrypted options.
37
+		if (
38
+			$option_name === Domain::META_KEY_ACCESS_TOKEN
39
+			|| $option_name === Domain::META_KEY_PARTNER_MERCHANT_ID
40
+			|| $option_name === Domain::META_KEY_CLIENT_SECRET
41
+		) {
42
+			$option_value = self::decryptString($option_value, $paypal_pm);
43
+		}
44
+		return $option_value;
45
+	}
46 46
 
47 47
 
48
-    /**
49
-     * Save payment method option/extra meta
50
-     *
51
-     * @param EE_Payment_Method $paypal_pm
52
-     * @param string            $option_name
53
-     * @param                   $option_value
54
-     * @return bool
55
-     */
56
-    public static function savePmOption(EE_Payment_Method $paypal_pm, string $option_name, $option_value): bool
57
-    {
58
-        $pp_meta_data = self::extraMeta($paypal_pm);
59
-        return $pp_meta_data->saveOption($option_name, $option_value);
60
-    }
48
+	/**
49
+	 * Save payment method option/extra meta
50
+	 *
51
+	 * @param EE_Payment_Method $paypal_pm
52
+	 * @param string            $option_name
53
+	 * @param                   $option_value
54
+	 * @return bool
55
+	 */
56
+	public static function savePmOption(EE_Payment_Method $paypal_pm, string $option_name, $option_value): bool
57
+	{
58
+		$pp_meta_data = self::extraMeta($paypal_pm);
59
+		return $pp_meta_data->saveOption($option_name, $option_value);
60
+	}
61 61
 
62 62
 
63
-    /**
64
-     * Save a list of payment method options/extra meta.
65
-     *
66
-     * @param EE_Payment_Method $paypal_pm
67
-     * @param array             $options_list
68
-     * @return bool
69
-     */
70
-    public static function savePmOptions(EE_Payment_Method $paypal_pm, array $options_list): bool
71
-    {
72
-        $pp_meta_data = self::extraMeta($paypal_pm);
73
-        return $pp_meta_data->saveBatch($options_list);
74
-    }
63
+	/**
64
+	 * Save a list of payment method options/extra meta.
65
+	 *
66
+	 * @param EE_Payment_Method $paypal_pm
67
+	 * @param array             $options_list
68
+	 * @return bool
69
+	 */
70
+	public static function savePmOptions(EE_Payment_Method $paypal_pm, array $options_list): bool
71
+	{
72
+		$pp_meta_data = self::extraMeta($paypal_pm);
73
+		return $pp_meta_data->saveBatch($options_list);
74
+	}
75 75
 
76 76
 
77
-    /**
78
-     * Delete payment method option/extra meta
79
-     *
80
-     * @param EE_Payment_Method $paypal_pm
81
-     * @param string            $option_name
82
-     * @return bool
83
-     */
84
-    public static function deletePmOption(EE_Payment_Method $paypal_pm, string $option_name): bool
85
-    {
86
-        $pp_meta_data = self::extraMeta($paypal_pm);
87
-        return $pp_meta_data->deleteOption($option_name);
88
-    }
77
+	/**
78
+	 * Delete payment method option/extra meta
79
+	 *
80
+	 * @param EE_Payment_Method $paypal_pm
81
+	 * @param string            $option_name
82
+	 * @return bool
83
+	 */
84
+	public static function deletePmOption(EE_Payment_Method $paypal_pm, string $option_name): bool
85
+	{
86
+		$pp_meta_data = self::extraMeta($paypal_pm);
87
+		return $pp_meta_data->deleteOption($option_name);
88
+	}
89 89
 
90 90
 
91
-    /**
92
-     * Get all options for payment method.
93
-     *
94
-     * @param EE_Payment_Method $paypal_pm
95
-     * @return array|bool
96
-     */
97
-    public static function getAllData(EE_Payment_Method $paypal_pm)
98
-    {
99
-        $pp_meta_data = self::extraMeta($paypal_pm);
100
-        return $pp_meta_data->getMetaData();
101
-    }
91
+	/**
92
+	 * Get all options for payment method.
93
+	 *
94
+	 * @param EE_Payment_Method $paypal_pm
95
+	 * @return array|bool
96
+	 */
97
+	public static function getAllData(EE_Payment_Method $paypal_pm)
98
+	{
99
+		$pp_meta_data = self::extraMeta($paypal_pm);
100
+		return $pp_meta_data->getMetaData();
101
+	}
102 102
 
103 103
 
104
-    /**
105
-     * Delete all options for this payment method.
106
-     *
107
-     * @param EE_Payment_Method $paypal_pm
108
-     * @return bool
109
-     */
110
-    public static function deleteAllData(EE_Payment_Method $paypal_pm): bool
111
-    {
112
-        $pp_meta_data = self::extraMeta($paypal_pm);
113
-        return $pp_meta_data->deleteMetaData();
114
-    }
104
+	/**
105
+	 * Delete all options for this payment method.
106
+	 *
107
+	 * @param EE_Payment_Method $paypal_pm
108
+	 * @return bool
109
+	 */
110
+	public static function deleteAllData(EE_Payment_Method $paypal_pm): bool
111
+	{
112
+		$pp_meta_data = self::extraMeta($paypal_pm);
113
+		return $pp_meta_data->deleteMetaData();
114
+	}
115 115
 
116 116
 
117
-    /**
118
-     * Save the debug mode option if it changed.
119
-     *
120
-     * @param EE_Payment_Method $paypal_pm
121
-     * @param array             $request_data
122
-     * @return bool             Updated or not.
123
-     */
124
-    public static function updateDebugMode(EE_Payment_Method $paypal_pm, array $request_data): bool
125
-    {
126
-        if (
127
-            isset($request_data['sandbox_mode'])
128
-            && in_array($request_data['sandbox_mode'], ['0', '1'], true)
129
-            && $paypal_pm->debug_mode() !== (bool) $request_data['sandbox_mode']
130
-        ) {
131
-            try {
132
-                $paypal_pm->save(['PMD_debug_mode' => $request_data['sandbox_mode']]);
133
-            } catch (EE_Error $e) {
134
-                $err_msg = sprintf(
135
-                    esc_html__('Note, debug mode not saved ! %1$s', 'event_espresso'),
136
-                    $e->getMessage()
137
-                );
138
-                PayPalLogger::errorLog($err_msg, $request_data, $paypal_pm);
139
-                return false;
140
-            }
141
-             return true;
142
-        }
143
-        return false;
144
-    }
117
+	/**
118
+	 * Save the debug mode option if it changed.
119
+	 *
120
+	 * @param EE_Payment_Method $paypal_pm
121
+	 * @param array             $request_data
122
+	 * @return bool             Updated or not.
123
+	 */
124
+	public static function updateDebugMode(EE_Payment_Method $paypal_pm, array $request_data): bool
125
+	{
126
+		if (
127
+			isset($request_data['sandbox_mode'])
128
+			&& in_array($request_data['sandbox_mode'], ['0', '1'], true)
129
+			&& $paypal_pm->debug_mode() !== (bool) $request_data['sandbox_mode']
130
+		) {
131
+			try {
132
+				$paypal_pm->save(['PMD_debug_mode' => $request_data['sandbox_mode']]);
133
+			} catch (EE_Error $e) {
134
+				$err_msg = sprintf(
135
+					esc_html__('Note, debug mode not saved ! %1$s', 'event_espresso'),
136
+					$e->getMessage()
137
+				);
138
+				PayPalLogger::errorLog($err_msg, $request_data, $paypal_pm);
139
+				return false;
140
+			}
141
+			 return true;
142
+		}
143
+		return false;
144
+	}
145 145
 
146 146
 
147
-    /**
148
-     * Save partner access token and parameters.
149
-     *
150
-     * @param EE_Payment_Method $paypal_pm
151
-     * @param array             $response
152
-     * @return bool
153
-     */
154
-    public static function savePartnerAccessToken(EE_Payment_Method $paypal_pm, array $response): bool
155
-    {
156
-        $paypal_data         = [];
157
-        $expected_parameters = [
158
-            Domain::META_KEY_ACCESS_TOKEN,
159
-            Domain::META_KEY_EXPIRES_IN,
160
-            Domain::META_KEY_APP_ID,
161
-            Domain::META_KEY_PARTNER_CLIENT_ID,
162
-            Domain::META_KEY_PARTNER_MERCHANT_ID,
163
-            Domain::META_KEY_BN_CODE,
164
-        ];
165
-        foreach ($expected_parameters as $api_key) {
166
-            if (! isset($response[ $api_key ])) {
167
-                // Don't want to try saving data that doesn't exist.
168
-                continue;
169
-            }
170
-            try {
171
-                switch ($api_key) {
172
-                    case Domain::META_KEY_ACCESS_TOKEN:
173
-                    case Domain::META_KEY_PARTNER_MERCHANT_ID:
174
-                        $paypal_data[ $api_key ] = self::encryptString($response[ $api_key ], $paypal_pm);
175
-                        break;
176
-                    case Domain::META_KEY_EXPIRES_IN:
177
-                        $paypal_data[ $api_key ] = time() + (int) sanitize_key($response[ $api_key ]);
178
-                        break;
179
-                    default:
180
-                        $paypal_data[ $api_key ] = sanitize_text_field($response[ $api_key ]);
181
-                }
182
-            } catch (Exception $exception) {
183
-                PayPalLogger::errorLog($exception->getMessage(), $response, $paypal_pm);
184
-                return false;
185
-            }
186
-        }
187
-        return self::savePmOptions($paypal_pm, $paypal_data);
188
-    }
147
+	/**
148
+	 * Save partner access token and parameters.
149
+	 *
150
+	 * @param EE_Payment_Method $paypal_pm
151
+	 * @param array             $response
152
+	 * @return bool
153
+	 */
154
+	public static function savePartnerAccessToken(EE_Payment_Method $paypal_pm, array $response): bool
155
+	{
156
+		$paypal_data         = [];
157
+		$expected_parameters = [
158
+			Domain::META_KEY_ACCESS_TOKEN,
159
+			Domain::META_KEY_EXPIRES_IN,
160
+			Domain::META_KEY_APP_ID,
161
+			Domain::META_KEY_PARTNER_CLIENT_ID,
162
+			Domain::META_KEY_PARTNER_MERCHANT_ID,
163
+			Domain::META_KEY_BN_CODE,
164
+		];
165
+		foreach ($expected_parameters as $api_key) {
166
+			if (! isset($response[ $api_key ])) {
167
+				// Don't want to try saving data that doesn't exist.
168
+				continue;
169
+			}
170
+			try {
171
+				switch ($api_key) {
172
+					case Domain::META_KEY_ACCESS_TOKEN:
173
+					case Domain::META_KEY_PARTNER_MERCHANT_ID:
174
+						$paypal_data[ $api_key ] = self::encryptString($response[ $api_key ], $paypal_pm);
175
+						break;
176
+					case Domain::META_KEY_EXPIRES_IN:
177
+						$paypal_data[ $api_key ] = time() + (int) sanitize_key($response[ $api_key ]);
178
+						break;
179
+					default:
180
+						$paypal_data[ $api_key ] = sanitize_text_field($response[ $api_key ]);
181
+				}
182
+			} catch (Exception $exception) {
183
+				PayPalLogger::errorLog($exception->getMessage(), $response, $paypal_pm);
184
+				return false;
185
+			}
186
+		}
187
+		return self::savePmOptions($paypal_pm, $paypal_data);
188
+	}
189 189
 
190 190
 
191
-    /**
192
-     * Save merchant/seller API credentials.
193
-     *
194
-     * @param EE_Payment_Method $paypal_pm
195
-     * @param array             $response
196
-     * @return bool
197
-     */
198
-    public static function saveSellerApiCredentials(EE_Payment_Method $paypal_pm, array $response): bool
199
-    {
200
-        $api_credentials     = [];
201
-        $expected_parameters = [
202
-            Domain::META_KEY_SELLER_MERCHANT_ID,
203
-        ];
204
-        foreach ($expected_parameters as $api_key) {
205
-            if (! isset($response[ $api_key ])) {
206
-                // Don't want to try saving data that doesn't exist.
207
-                continue;
208
-            }
209
-            try {
210
-                $api_credentials[ $api_key ] = $response[ $api_key ];
211
-            } catch (Exception $exception) {
212
-                PayPalLogger::errorLog($exception->getMessage(), $response, $paypal_pm);
213
-                return false;
214
-            }
215
-        }
216
-        return self::savePmOptions($paypal_pm, $api_credentials);
217
-    }
191
+	/**
192
+	 * Save merchant/seller API credentials.
193
+	 *
194
+	 * @param EE_Payment_Method $paypal_pm
195
+	 * @param array             $response
196
+	 * @return bool
197
+	 */
198
+	public static function saveSellerApiCredentials(EE_Payment_Method $paypal_pm, array $response): bool
199
+	{
200
+		$api_credentials     = [];
201
+		$expected_parameters = [
202
+			Domain::META_KEY_SELLER_MERCHANT_ID,
203
+		];
204
+		foreach ($expected_parameters as $api_key) {
205
+			if (! isset($response[ $api_key ])) {
206
+				// Don't want to try saving data that doesn't exist.
207
+				continue;
208
+			}
209
+			try {
210
+				$api_credentials[ $api_key ] = $response[ $api_key ];
211
+			} catch (Exception $exception) {
212
+				PayPalLogger::errorLog($exception->getMessage(), $response, $paypal_pm);
213
+				return false;
214
+			}
215
+		}
216
+		return self::savePmOptions($paypal_pm, $api_credentials);
217
+	}
218 218
 
219 219
 
220
-    /**
221
-     * Save other payment method related settings from a data array.
222
-     *
223
-     * @param EE_Payment_Method $paypal_pm
224
-     * @param array             $data
225
-     * @return bool
226
-     * @throws EE_Error
227
-     * @throws ReflectionException
228
-     */
229
-    public static function parseAndSaveOptions(EE_Payment_Method $paypal_pm, array $data): bool
230
-    {
231
-        $allowed_checkout_type = 'express_checkout';
232
-        // Note, although PayPal shows that this should include PPCP_CUSTOM or EXPRESS_CHECKOUT only,
233
-        // in reality, it will also include other products like MOBILE_PAYMENT_ACCEPTANCE etc.
234
-        if (! empty($data['response']['products'][0]['name']) && is_array($data['response']['products'])) {
235
-            foreach ($data['response']['products'] as $product) {
236
-                if (str_contains($product['name'], 'PPCP')) {
237
-                    // This merchant has PPCP in the products list, so we can enable both (all) checkout types.
238
-                    $allowed_checkout_type = 'all';
239
-                    break;
240
-                }
241
-            }
242
-        }
243
-        // Set the Checkout type (a PM option), just in case merchant doesn't save PM options manually.
244
-        $checkout_type = $paypal_pm->get_extra_meta(Domain::META_KEY_CHECKOUT_TYPE, true, false);
245
-        if (! $checkout_type) {
246
-            $paypal_pm->update_extra_meta(Domain::META_KEY_CHECKOUT_TYPE, $allowed_checkout_type);
247
-        }
248
-        return PayPalExtraMetaManager::savePmOption(
249
-            $paypal_pm,
250
-            Domain::META_KEY_ALLOWED_CHECKOUT_TYPE,
251
-            $allowed_checkout_type
252
-        );
253
-    }
220
+	/**
221
+	 * Save other payment method related settings from a data array.
222
+	 *
223
+	 * @param EE_Payment_Method $paypal_pm
224
+	 * @param array             $data
225
+	 * @return bool
226
+	 * @throws EE_Error
227
+	 * @throws ReflectionException
228
+	 */
229
+	public static function parseAndSaveOptions(EE_Payment_Method $paypal_pm, array $data): bool
230
+	{
231
+		$allowed_checkout_type = 'express_checkout';
232
+		// Note, although PayPal shows that this should include PPCP_CUSTOM or EXPRESS_CHECKOUT only,
233
+		// in reality, it will also include other products like MOBILE_PAYMENT_ACCEPTANCE etc.
234
+		if (! empty($data['response']['products'][0]['name']) && is_array($data['response']['products'])) {
235
+			foreach ($data['response']['products'] as $product) {
236
+				if (str_contains($product['name'], 'PPCP')) {
237
+					// This merchant has PPCP in the products list, so we can enable both (all) checkout types.
238
+					$allowed_checkout_type = 'all';
239
+					break;
240
+				}
241
+			}
242
+		}
243
+		// Set the Checkout type (a PM option), just in case merchant doesn't save PM options manually.
244
+		$checkout_type = $paypal_pm->get_extra_meta(Domain::META_KEY_CHECKOUT_TYPE, true, false);
245
+		if (! $checkout_type) {
246
+			$paypal_pm->update_extra_meta(Domain::META_KEY_CHECKOUT_TYPE, $allowed_checkout_type);
247
+		}
248
+		return PayPalExtraMetaManager::savePmOption(
249
+			$paypal_pm,
250
+			Domain::META_KEY_ALLOWED_CHECKOUT_TYPE,
251
+			$allowed_checkout_type
252
+		);
253
+	}
254 254
 
255 255
 
256
-    /**
257
-     * Get PayPal extra meta helper.
258
-     *
259
-     * @param EE_Payment_Method $paypal_pm
260
-     * @return PayPalExtraMeta
261
-     */
262
-    public static function extraMeta(EE_Payment_Method $paypal_pm): PayPalExtraMeta
263
-    {
264
-        return LoaderFactory::getLoader()->getShared(PayPalExtraMeta::class, [$paypal_pm]);
265
-    }
256
+	/**
257
+	 * Get PayPal extra meta helper.
258
+	 *
259
+	 * @param EE_Payment_Method $paypal_pm
260
+	 * @return PayPalExtraMeta
261
+	 */
262
+	public static function extraMeta(EE_Payment_Method $paypal_pm): PayPalExtraMeta
263
+	{
264
+		return LoaderFactory::getLoader()->getShared(PayPalExtraMeta::class, [$paypal_pm]);
265
+	}
266 266
 
267 267
 
268
-    /**
269
-     * Encrypt a text field.
270
-     *
271
-     * @param string            $text
272
-     * @param EE_Payment_Method $paypal_pm
273
-     * @return string|null
274
-     */
275
-    public static function encryptString(string $text, EE_Payment_Method $paypal_pm): ?string
276
-    {
277
-        // We sure we are getting something ?
278
-        if (! $text) {
279
-            return $text;
280
-        }
281
-        // Do encrypt.
282
-        $encryptor      = LoaderFactory::getLoader()->getShared(OpenSSLEncryption::class, [new Base64Encoder()]);
283
-        $sanitized_text = sanitize_text_field($text);
284
-        $key_identifier = $paypal_pm->debug_mode()
285
-            ? PPCommerceEncryptionKeyManager::SANDBOX_ENCRYPTION_KEY_ID
286
-            : PPCommerceEncryptionKeyManager::PRODUCTION_ENCRYPTION_KEY_ID;
287
-        return $encryptor->encrypt($sanitized_text, $key_identifier);
288
-    }
268
+	/**
269
+	 * Encrypt a text field.
270
+	 *
271
+	 * @param string            $text
272
+	 * @param EE_Payment_Method $paypal_pm
273
+	 * @return string|null
274
+	 */
275
+	public static function encryptString(string $text, EE_Payment_Method $paypal_pm): ?string
276
+	{
277
+		// We sure we are getting something ?
278
+		if (! $text) {
279
+			return $text;
280
+		}
281
+		// Do encrypt.
282
+		$encryptor      = LoaderFactory::getLoader()->getShared(OpenSSLEncryption::class, [new Base64Encoder()]);
283
+		$sanitized_text = sanitize_text_field($text);
284
+		$key_identifier = $paypal_pm->debug_mode()
285
+			? PPCommerceEncryptionKeyManager::SANDBOX_ENCRYPTION_KEY_ID
286
+			: PPCommerceEncryptionKeyManager::PRODUCTION_ENCRYPTION_KEY_ID;
287
+		return $encryptor->encrypt($sanitized_text, $key_identifier);
288
+	}
289 289
 
290 290
 
291
-    /**
292
-     * Decrypt a string.
293
-     *
294
-     * @param string            $text
295
-     * @param EE_Payment_Method $paypal_pm
296
-     * @return string
297
-     */
298
-    public static function decryptString(string $text, EE_Payment_Method $paypal_pm): string
299
-    {
300
-        // Are we even getting something ?
301
-        if (! $text) {
302
-            return $text;
303
-        }
304
-        // Try decrypting.
305
-        try {
306
-            $encryptor      = LoaderFactory::getLoader()->getShared(OpenSSLEncryption::class, [new Base64Encoder()]);
307
-            $key_identifier = $paypal_pm->debug_mode()
308
-                ? PPCommerceEncryptionKeyManager::SANDBOX_ENCRYPTION_KEY_ID
309
-                : PPCommerceEncryptionKeyManager::PRODUCTION_ENCRYPTION_KEY_ID;
310
-            $decrypted      = $encryptor->decrypt($text, $key_identifier);
311
-        } catch (Exception $e) {
312
-            return $text;
313
-        }
314
-        return $decrypted ?? $text;
315
-    }
291
+	/**
292
+	 * Decrypt a string.
293
+	 *
294
+	 * @param string            $text
295
+	 * @param EE_Payment_Method $paypal_pm
296
+	 * @return string
297
+	 */
298
+	public static function decryptString(string $text, EE_Payment_Method $paypal_pm): string
299
+	{
300
+		// Are we even getting something ?
301
+		if (! $text) {
302
+			return $text;
303
+		}
304
+		// Try decrypting.
305
+		try {
306
+			$encryptor      = LoaderFactory::getLoader()->getShared(OpenSSLEncryption::class, [new Base64Encoder()]);
307
+			$key_identifier = $paypal_pm->debug_mode()
308
+				? PPCommerceEncryptionKeyManager::SANDBOX_ENCRYPTION_KEY_ID
309
+				: PPCommerceEncryptionKeyManager::PRODUCTION_ENCRYPTION_KEY_ID;
310
+			$decrypted      = $encryptor->decrypt($text, $key_identifier);
311
+		} catch (Exception $e) {
312
+			return $text;
313
+		}
314
+		return $decrypted ?? $text;
315
+	}
316 316
 }
Please login to merge, or discard this patch.
espresso.php 1 patch
Indentation   +107 added lines, -107 removed lines patch added patch discarded remove patch
@@ -37,138 +37,138 @@
 block discarded – undo
37 37
  * @since           4.0
38 38
  */
39 39
 if (function_exists('espresso_version')) {
40
-    if (! function_exists('espresso_duplicate_plugin_error')) {
41
-        /**
42
-         *    espresso_duplicate_plugin_error
43
-         *    displays if more than one version of EE is activated at the same time.
44
-         */
45
-        function espresso_duplicate_plugin_error()
46
-        {
47
-            ?>
40
+	if (! function_exists('espresso_duplicate_plugin_error')) {
41
+		/**
42
+		 *    espresso_duplicate_plugin_error
43
+		 *    displays if more than one version of EE is activated at the same time.
44
+		 */
45
+		function espresso_duplicate_plugin_error()
46
+		{
47
+			?>
48 48
 <div class="error">
49 49
 	<p>
50 50
 		<?php
51
-                    echo esc_html__(
52
-                        'Can not run multiple versions of Event Espresso! One version has been automatically deactivated. Please verify that you have the correct version you want still active.',
53
-                        'event_espresso'
54
-                    ); ?>
51
+					echo esc_html__(
52
+						'Can not run multiple versions of Event Espresso! One version has been automatically deactivated. Please verify that you have the correct version you want still active.',
53
+						'event_espresso'
54
+					); ?>
55 55
 	</p>
56 56
 </div>
57 57
 <?php
58
-            espresso_deactivate_plugin(plugin_basename(__FILE__));
59
-        }
60
-    }
61
-    add_action('admin_notices', 'espresso_duplicate_plugin_error', 1);
58
+			espresso_deactivate_plugin(plugin_basename(__FILE__));
59
+		}
60
+	}
61
+	add_action('admin_notices', 'espresso_duplicate_plugin_error', 1);
62 62
 } else {
63
-    define('EE_MIN_PHP_VER_REQUIRED', '7.4.0');
64
-    if (! version_compare(PHP_VERSION, EE_MIN_PHP_VER_REQUIRED, '>=')) {
65
-        /**
66
-         * espresso_minimum_php_version_error
67
-         *
68
-         * @return void
69
-         */
70
-        function espresso_minimum_php_version_error()
71
-        {
72
-            ?>
63
+	define('EE_MIN_PHP_VER_REQUIRED', '7.4.0');
64
+	if (! version_compare(PHP_VERSION, EE_MIN_PHP_VER_REQUIRED, '>=')) {
65
+		/**
66
+		 * espresso_minimum_php_version_error
67
+		 *
68
+		 * @return void
69
+		 */
70
+		function espresso_minimum_php_version_error()
71
+		{
72
+			?>
73 73
 <div class="error">
74 74
 	<p>
75 75
 		<?php
76
-                    printf(
77
-                        esc_html__(
78
-                            'We\'re sorry, but Event Espresso requires PHP version %1$s or greater in order to operate. You are currently running version %2$s.%3$sIn order to update your version of PHP, you will need to contact your current hosting provider.%3$sFor information on stable PHP versions, please go to %4$s.',
79
-                            'event_espresso'
80
-                        ),
81
-                        EE_MIN_PHP_VER_REQUIRED,
82
-                        PHP_VERSION,
83
-                        '<br/>',
84
-                        '<a href="https://www.php.net/downloads.php">https://php.net/downloads.php</a>'
85
-                    );
86
-                    ?>
76
+					printf(
77
+						esc_html__(
78
+							'We\'re sorry, but Event Espresso requires PHP version %1$s or greater in order to operate. You are currently running version %2$s.%3$sIn order to update your version of PHP, you will need to contact your current hosting provider.%3$sFor information on stable PHP versions, please go to %4$s.',
79
+							'event_espresso'
80
+						),
81
+						EE_MIN_PHP_VER_REQUIRED,
82
+						PHP_VERSION,
83
+						'<br/>',
84
+						'<a href="https://www.php.net/downloads.php">https://php.net/downloads.php</a>'
85
+					);
86
+					?>
87 87
 	</p>
88 88
 </div>
89 89
 <?php
90
-            espresso_deactivate_plugin(plugin_basename(__FILE__));
91
-        }
90
+			espresso_deactivate_plugin(plugin_basename(__FILE__));
91
+		}
92 92
 
93
-        add_action('admin_notices', 'espresso_minimum_php_version_error', 1);
94
-    } else {
95
-        define('EVENT_ESPRESSO_MAIN_FILE', __FILE__);
93
+		add_action('admin_notices', 'espresso_minimum_php_version_error', 1);
94
+	} else {
95
+		define('EVENT_ESPRESSO_MAIN_FILE', __FILE__);
96 96
 
97
-        require_once __DIR__ . '/vendor/autoload.php';
97
+		require_once __DIR__ . '/vendor/autoload.php';
98 98
 
99
-        /**
100
-         * espresso_version
101
-         * Returns the plugin version
102
-         *
103
-         * @return string
104
-         */
105
-        function espresso_version(): string
106
-        {
107
-            return apply_filters('FHEE__espresso__espresso_version', '5.0.14.rc.006');
108
-        }
99
+		/**
100
+		 * espresso_version
101
+		 * Returns the plugin version
102
+		 *
103
+		 * @return string
104
+		 */
105
+		function espresso_version(): string
106
+		{
107
+			return apply_filters('FHEE__espresso__espresso_version', '5.0.14.rc.006');
108
+		}
109 109
 
110
-        /**
111
-         * espresso_plugin_activation
112
-         * adds a wp-option to indicate that EE has been activated via the WP admin plugins page
113
-         */
114
-        function espresso_plugin_activation()
115
-        {
116
-            update_option('ee_espresso_activation', true);
117
-            update_option('event-espresso-core_allow_tracking', 'no');
118
-            update_option('event-espresso-core_tracking_notice', 'hide');
119
-            // Run WP GraphQL activation callback
120
-            espressoLoadWpGraphQL();
121
-            graphql_activation_callback();
122
-        }
110
+		/**
111
+		 * espresso_plugin_activation
112
+		 * adds a wp-option to indicate that EE has been activated via the WP admin plugins page
113
+		 */
114
+		function espresso_plugin_activation()
115
+		{
116
+			update_option('ee_espresso_activation', true);
117
+			update_option('event-espresso-core_allow_tracking', 'no');
118
+			update_option('event-espresso-core_tracking_notice', 'hide');
119
+			// Run WP GraphQL activation callback
120
+			espressoLoadWpGraphQL();
121
+			graphql_activation_callback();
122
+		}
123 123
 
124
-        register_activation_hook(EVENT_ESPRESSO_MAIN_FILE, 'espresso_plugin_activation');
124
+		register_activation_hook(EVENT_ESPRESSO_MAIN_FILE, 'espresso_plugin_activation');
125 125
 
126
-        /**
127
-         * espresso_plugin_deactivation
128
-         */
129
-        function espresso_plugin_deactivation()
130
-        {
131
-            // Run WP GraphQL deactivation callback
132
-            espressoLoadWpGraphQL();
133
-            graphql_deactivation_callback();
134
-            delete_option('event-espresso-core_allow_tracking');
135
-            delete_option('event-espresso-core_tracking_notice');
136
-        }
137
-        register_deactivation_hook(EVENT_ESPRESSO_MAIN_FILE, 'espresso_plugin_deactivation');
126
+		/**
127
+		 * espresso_plugin_deactivation
128
+		 */
129
+		function espresso_plugin_deactivation()
130
+		{
131
+			// Run WP GraphQL deactivation callback
132
+			espressoLoadWpGraphQL();
133
+			graphql_deactivation_callback();
134
+			delete_option('event-espresso-core_allow_tracking');
135
+			delete_option('event-espresso-core_tracking_notice');
136
+		}
137
+		register_deactivation_hook(EVENT_ESPRESSO_MAIN_FILE, 'espresso_plugin_deactivation');
138 138
 
139
-        require_once __DIR__ . '/core/bootstrap_espresso.php';
140
-        bootstrap_espresso();
141
-    }
139
+		require_once __DIR__ . '/core/bootstrap_espresso.php';
140
+		bootstrap_espresso();
141
+	}
142 142
 }
143 143
 
144 144
 if (! function_exists('espresso_deactivate_plugin')) {
145
-    /**
146
-     *    deactivate_plugin
147
-     * usage:  espresso_deactivate_plugin( plugin_basename( __FILE__ ));
148
-     *
149
-     * @access public
150
-     * @param string $plugin_basename - the results of plugin_basename( __FILE__ ) for the plugin's main file
151
-     * @return    void
152
-     */
153
-    function espresso_deactivate_plugin(string $plugin_basename = '')
154
-    {
155
-        if (! function_exists('deactivate_plugins')) {
156
-            require_once ABSPATH . 'wp-admin/includes/plugin.php';
157
-        }
158
-        unset($_GET['activate'], $_REQUEST['activate']);
159
-        deactivate_plugins($plugin_basename);
160
-    }
145
+	/**
146
+	 *    deactivate_plugin
147
+	 * usage:  espresso_deactivate_plugin( plugin_basename( __FILE__ ));
148
+	 *
149
+	 * @access public
150
+	 * @param string $plugin_basename - the results of plugin_basename( __FILE__ ) for the plugin's main file
151
+	 * @return    void
152
+	 */
153
+	function espresso_deactivate_plugin(string $plugin_basename = '')
154
+	{
155
+		if (! function_exists('deactivate_plugins')) {
156
+			require_once ABSPATH . 'wp-admin/includes/plugin.php';
157
+		}
158
+		unset($_GET['activate'], $_REQUEST['activate']);
159
+		deactivate_plugins($plugin_basename);
160
+	}
161 161
 }
162 162
 
163 163
 
164 164
 if (! function_exists('espressoLoadWpGraphQL')) {
165
-    function espressoLoadWpGraphQL()
166
-    {
167
-        if (
168
-            ! class_exists('WPGraphQL')
169
-            && is_readable(__DIR__ . '/vendor/wp-graphql/wp-graphql/wp-graphql.php')
170
-        ) {
171
-            require_once __DIR__ . '/vendor/wp-graphql/wp-graphql/wp-graphql.php';
172
-        }
173
-    }
165
+	function espressoLoadWpGraphQL()
166
+	{
167
+		if (
168
+			! class_exists('WPGraphQL')
169
+			&& is_readable(__DIR__ . '/vendor/wp-graphql/wp-graphql/wp-graphql.php')
170
+		) {
171
+			require_once __DIR__ . '/vendor/wp-graphql/wp-graphql/wp-graphql.php';
172
+		}
173
+	}
174 174
 }
Please login to merge, or discard this patch.
core/EE_Capabilities.core.php 1 patch
Indentation   +1340 added lines, -1340 removed lines patch added patch discarded remove patch
@@ -15,967 +15,967 @@  discard block
 block discarded – undo
15 15
 final class EE_Capabilities extends EE_Base
16 16
 {
17 17
 
18
-    const ROLE_ADMINISTRATOR = 'administrator';
19
-
20
-    const ROLE_EVENTS_ADMINISTRATOR = 'ee_events_administrator';
21
-
22
-
23
-    /**
24
-     * the name of the wp option used to store caps previously initialized
25
-     */
26
-    const option_name = 'ee_caps_initialized';
27
-
28
-    private static ?EE_Capabilities $_instance = null;
29
-
30
-
31
-    /**
32
-     * This is a map of caps that correspond to a default WP_Role.
33
-     * Array is indexed by Role and values are ee capabilities.
34
-     *
35
-     * @since 4.5.0
36
-     * @var array
37
-     */
38
-    private array $capabilities_map = [];
39
-
40
-    /**
41
-     * This used to hold an array of EE_Meta_Capability_Map objects
42
-     * that define the granular capabilities mapped to for a user depending on context.
43
-     *
44
-     * @var EE_Meta_Capability_Map[]
45
-     */
46
-    private array $_meta_caps = [];
47
-
48
-    /**
49
-     * The internal $capabilities_map needs to be initialized before it can be used.
50
-     * This flag tracks whether that has happened or not.
51
-     * But for this to work, we need three states to indicate:
52
-     *      initialization has not occurred at all
53
-     *      initialization has started but is not complete
54
-     *      initialization is complete
55
-     * The reason this is needed is because the addCaps() method
56
-     * normally requires the $capabilities_map to be initialized,
57
-     * but is also used during the initialization process.
58
-     * So:
59
-     *      If initialized === null, init_caps() will be called before any other methods will run.
60
-     *      If initialized === false, then init_caps() is in the process of running it's logic.
61
-     *      If initialized === true, then init_caps() has completed the initialization process.
62
-     *
63
-     * @var boolean|null $initialized
64
-     */
65
-    private ?bool $initialized = null;
66
-
67
-    /**
68
-     * @var boolean $reset
69
-     */
70
-    private bool $reset = false;
71
-
72
-
73
-    /**
74
-     * singleton method used to instantiate class object
75
-     *
76
-     * @return EE_Capabilities
77
-     * @since 4.5.0
78
-     */
79
-    public static function instance(): EE_Capabilities
80
-    {
81
-        // check if instantiated, and if not do so.
82
-        if (! self::$_instance instanceof EE_Capabilities) {
83
-            self::$_instance = new self();
84
-        }
85
-        return self::$_instance;
86
-    }
87
-
88
-
89
-    /**
90
-     * private constructor
91
-     *
92
-     * @since 4.5.0
93
-     */
94
-    private function __construct()
95
-    {
96
-    }
97
-
98
-
99
-    /**
100
-     * This delays the initialization of the capabilities class until EE_System core is loaded and ready.
101
-     *
102
-     * @param bool $reset allows for resetting the default capabilities saved on roles.  Note that this doesn't
103
-     *                    actually REMOVE any capabilities from existing roles, it just resaves defaults roles and
104
-     *                    ensures that they are up to date.
105
-     * @since 4.5.0
106
-     * @return bool
107
-     * @throws EE_Error
108
-     */
109
-    public function init_caps(?bool $reset = false): bool
110
-    {
111
-        if (DbStatus::isOffline()) {
112
-            return false;
113
-        }
114
-        $this->reset = filter_var($reset, FILTER_VALIDATE_BOOLEAN);
115
-        // if reset, then completely delete the cache option and clear the $capabilities_map property.
116
-        if ($this->reset) {
117
-            $this->initialized      = null;
118
-            $this->capabilities_map = [];
119
-            delete_option(self::option_name);
120
-        }
121
-        if ($this->initialized === null) {
122
-            $this->initialized = false;
123
-            do_action(
124
-                'AHEE__EE_Capabilities__init_caps__before_initialization',
125
-                $this->reset
126
-            );
127
-            $this->addCaps($this->_init_caps_map());
128
-            $this->_set_meta_caps();
129
-            do_action(
130
-                'AHEE__EE_Capabilities__init_caps__after_initialization',
131
-                $this->capabilities_map
132
-            );
133
-            $this->initialized = true;
134
-        }
135
-        // reset $this->reset so that it's not stuck on true if init_caps() gets called again
136
-        $this->reset = false;
137
-        return true;
138
-    }
139
-
140
-
141
-    /**
142
-     * This sets the meta caps property.
143
-     *
144
-     * @return void
145
-     * @throws EE_Error
146
-     * @since 4.5.0
147
-     */
148
-    private function _set_meta_caps()
149
-    {
150
-        // get default meta caps and filter the returned array
151
-        $this->_meta_caps = apply_filters(
152
-            'FHEE__EE_Capabilities___set_meta_caps__meta_caps',
153
-            $this->_get_default_meta_caps_array()
154
-        );
155
-        // add filter for map_meta_caps but only if models can query.
156
-        if (! has_filter('map_meta_cap', [$this, 'map_meta_caps'])) {
157
-            add_filter('map_meta_cap', [$this, 'map_meta_caps'], 10, 4);
158
-        }
159
-    }
160
-
161
-
162
-    /**
163
-     * This builds and returns the default meta_caps array only once.
164
-     *
165
-     * @return array
166
-     * @throws EE_Error
167
-     * @since  4.8.28.rc.012
168
-     */
169
-    private function _get_default_meta_caps_array(): array
170
-    {
171
-        static $default_meta_caps = [];
172
-        // make sure we're only ever initializing the default _meta_caps array once if it's empty.
173
-        if (empty($default_meta_caps)) {
174
-            $default_meta_caps = [
175
-                // edits
176
-                new EE_Meta_Capability_Map_Edit(
177
-                    'ee_edit_event',
178
-                    ['Event', 'ee_edit_published_events', 'ee_edit_others_events', 'ee_edit_private_events']
179
-                ),
180
-                new EE_Meta_Capability_Map_Edit(
181
-                    'ee_edit_venue',
182
-                    ['Venue', 'ee_edit_published_venues', 'ee_edit_others_venues', 'ee_edit_private_venues']
183
-                ),
184
-                new EE_Meta_Capability_Map_Edit(
185
-                    'ee_edit_registration',
186
-                    ['Registration', '', 'ee_edit_others_registrations', '']
187
-                ),
188
-                new EE_Meta_Capability_Map_Edit(
189
-                    'ee_edit_checkin',
190
-                    ['Registration', '', 'ee_edit_others_checkins', '']
191
-                ),
192
-                new EE_Meta_Capability_Map_Messages_Cap(
193
-                    'ee_edit_message',
194
-                    ['Message_Template_Group', '', 'ee_edit_others_messages', 'ee_edit_global_messages']
195
-                ),
196
-                new EE_Meta_Capability_Map_Edit(
197
-                    'ee_edit_default_ticket',
198
-                    ['Ticket', '', 'ee_edit_others_default_tickets', '']
199
-                ),
200
-                new EE_Meta_Capability_Map_Registration_Form_Cap(
201
-                    'ee_edit_question',
202
-                    ['Question', '', '', 'ee_edit_system_questions']
203
-                ),
204
-                new EE_Meta_Capability_Map_Registration_Form_Cap(
205
-                    'ee_edit_question_group',
206
-                    ['Question_Group', '', '', 'ee_edit_system_question_groups']
207
-                ),
208
-                new EE_Meta_Capability_Map_Edit(
209
-                    'ee_edit_payment_method',
210
-                    ['Payment_Method', '', 'ee_edit_others_payment_methods', '']
211
-                ),
212
-                // reads
213
-                new EE_Meta_Capability_Map_Read(
214
-                    'ee_read_event',
215
-                    ['Event', '', 'ee_read_others_events', 'ee_read_private_events']
216
-                ),
217
-                new EE_Meta_Capability_Map_Read(
218
-                    'ee_read_venue',
219
-                    ['Venue', '', 'ee_read_others_venues', 'ee_read_private_venues']
220
-                ),
221
-                new EE_Meta_Capability_Map_Read(
222
-                    'ee_read_registration',
223
-                    ['Registration', '', 'ee_read_others_registrations', '']
224
-                ),
225
-                new EE_Meta_Capability_Map_Read(
226
-                    'ee_read_checkin',
227
-                    ['Registration', '', 'ee_read_others_checkins', '']
228
-                ),
229
-                new EE_Meta_Capability_Map_Messages_Cap(
230
-                    'ee_read_message',
231
-                    ['Message_Template_Group', '', 'ee_read_others_messages', 'ee_read_global_messages']
232
-                ),
233
-                new EE_Meta_Capability_Map_Read(
234
-                    'ee_read_default_ticket',
235
-                    ['Ticket', '', 'ee_read_others_default_tickets', '']
236
-                ),
237
-                new EE_Meta_Capability_Map_Read(
238
-                    'ee_read_payment_method',
239
-                    ['Payment_Method', '', 'ee_read_others_payment_methods', '']
240
-                ),
241
-                // deletes
242
-                new EE_Meta_Capability_Map_Delete(
243
-                    'ee_delete_event',
244
-                    [
245
-                        'Event',
246
-                        'ee_delete_published_events',
247
-                        'ee_delete_others_events',
248
-                        'ee_delete_private_events',
249
-                    ]
250
-                ),
251
-                new EE_Meta_Capability_Map_Delete(
252
-                    'ee_delete_venue',
253
-                    [
254
-                        'Venue',
255
-                        'ee_delete_published_venues',
256
-                        'ee_delete_others_venues',
257
-                        'ee_delete_private_venues',
258
-                    ]
259
-                ),
260
-                new EE_Meta_Capability_Map_Delete(
261
-                    'ee_delete_registration',
262
-                    ['Registration', '', 'ee_delete_others_registrations', '']
263
-                ),
264
-                new EE_Meta_Capability_Map_Delete(
265
-                    'ee_delete_checkin',
266
-                    ['Registration', '', 'ee_delete_others_checkins', '']
267
-                ),
268
-                new EE_Meta_Capability_Map_Messages_Cap(
269
-                    'ee_delete_message',
270
-                    ['Message_Template_Group', '', 'ee_delete_others_messages', 'ee_delete_global_messages']
271
-                ),
272
-                new EE_Meta_Capability_Map_Delete(
273
-                    'ee_delete_default_ticket',
274
-                    ['Ticket', '', 'ee_delete_others_default_tickets', '']
275
-                ),
276
-                new EE_Meta_Capability_Map_Registration_Form_Cap(
277
-                    'ee_delete_question',
278
-                    ['Question', '', '', 'delete_system_questions']
279
-                ),
280
-                new EE_Meta_Capability_Map_Registration_Form_Cap(
281
-                    'ee_delete_question_group',
282
-                    ['Question_Group', '', '', 'delete_system_question_groups']
283
-                ),
284
-                new EE_Meta_Capability_Map_Delete(
285
-                    'ee_delete_payment_method',
286
-                    ['Payment_Method', '', 'ee_delete_others_payment_methods', '']
287
-                ),
288
-            ];
289
-        }
290
-        return $default_meta_caps;
291
-    }
292
-
293
-
294
-    /**
295
-     * This is the callback for the wp map_meta_caps() function which allows for ensuring certain caps that act as a
296
-     * "meta" for other caps ( i.e. ee_edit_event is a meta for ee_edit_others_events ) work as expected.
297
-     * The actual logic is carried out by implementer classes in their definition of _map_meta_caps.
298
-     *
299
-     * @param array       $caps    actual users capabilities
300
-     * @param string|null $cap     initial capability name that is being checked (the "map" key)
301
-     * @param int         $user_id The user id
302
-     * @param array       $args    Adds context to the cap. Typically the object ID.
303
-     * @return array actual users capabilities
304
-     * @throws EE_Error
305
-     * @throws ReflectionException
306
-     * @since 4.5.0
307
-     * @see   wp-includes/capabilities.php
308
-     */
309
-    public function map_meta_caps(array $caps, ?string $cap, int $user_id, array $args): array
310
-    {
311
-        if (! $cap || ! did_action('AHEE__EE_System__load_espresso_addons__complete')) {
312
-            return $caps;
313
-        }
314
-        // loop through our _meta_caps array
315
-        foreach ($this->_meta_caps as $meta_map) {
316
-            if (! $meta_map instanceof EE_Meta_Capability_Map) {
317
-                continue;
318
-            }
319
-            // don't load models if there is no object ID in the args
320
-            if (! empty($args[0])) {
321
-                $meta_map->ensure_is_model();
322
-            }
323
-            $caps = $meta_map->map_meta_caps($caps, $cap, $user_id, $args);
324
-        }
325
-        return $caps;
326
-    }
327
-
328
-
329
-    /**
330
-     * This sets up and returns the initial capabilities map for Event Espresso
331
-     * Note this array is filtered.
332
-     * It is assumed that all available EE capabilities are assigned to the administrator role.
333
-     *
334
-     * @return array
335
-     * @since 4.5.0
336
-     */
337
-    private function _init_caps_map(): array
338
-    {
339
-        return apply_filters(
340
-            'FHEE__EE_Capabilities__init_caps_map__caps',
341
-            [
342
-                EE_Capabilities::ROLE_ADMINISTRATOR        => [
343
-                    // basic access
344
-                    'ee_read_ee',
345
-                    // gateways
346
-                    // note that with payment method capabilities, although we've implemented
347
-                    // capability mapping which will be used for accessing payment methods owned by
348
-                    // other users.  This is not fully implemented yet in the payment method ui.
349
-                    // Currently only the "plural" caps are in active use.
350
-                    // (Specific payment method caps are in use as well).
351
-                    'ee_manage_gateways',
352
-                    'ee_read_payment_methods',
353
-                    'ee_read_others_payment_methods',
354
-                    'ee_edit_payment_methods',
355
-                    'ee_edit_others_payment_methods',
356
-                    'ee_delete_payment_methods',
357
-                    // events
358
-                    'ee_publish_events',
359
-                    'ee_read_private_events',
360
-                    'ee_read_others_events',
361
-                    'ee_read_events',
362
-                    'ee_edit_events',
363
-                    'ee_edit_published_events',
364
-                    'ee_edit_others_events',
365
-                    'ee_edit_private_events',
366
-                    'ee_delete_published_events',
367
-                    'ee_delete_private_events',
368
-                    'ee_delete_events',
369
-                    'ee_delete_others_events',
370
-                    // event categories
371
-                    'ee_manage_event_categories',
372
-                    'ee_edit_event_category',
373
-                    'ee_delete_event_category',
374
-                    'ee_assign_event_category',
375
-                    // venues
376
-                    'ee_publish_venues',
377
-                    'ee_read_venues',
378
-                    'ee_read_others_venues',
379
-                    'ee_read_private_venues',
380
-                    'ee_edit_venues',
381
-                    'ee_edit_others_venues',
382
-                    'ee_edit_published_venues',
383
-                    'ee_edit_private_venues',
384
-                    'ee_delete_venues',
385
-                    'ee_delete_others_venues',
386
-                    'ee_delete_private_venues',
387
-                    'ee_delete_published_venues',
388
-                    // venue categories
389
-                    'ee_manage_venue_categories',
390
-                    'ee_edit_venue_category',
391
-                    'ee_delete_venue_category',
392
-                    'ee_assign_venue_category',
393
-                    // contacts
394
-                    'ee_read_contacts',
395
-                    'ee_edit_contacts',
396
-                    'ee_delete_contacts',
397
-                    // registrations
398
-                    'ee_read_registrations',
399
-                    'ee_read_others_registrations',
400
-                    'ee_edit_registrations',
401
-                    'ee_edit_others_registrations',
402
-                    'ee_delete_registrations',
403
-                    'ee_delete_others_registrations',
404
-                    // checkins
405
-                    'ee_read_others_checkins',
406
-                    'ee_read_checkins',
407
-                    'ee_edit_checkins',
408
-                    'ee_edit_others_checkins',
409
-                    'ee_delete_checkins',
410
-                    'ee_delete_others_checkins',
411
-                    // transactions && payments
412
-                    'ee_read_transaction',
413
-                    'ee_read_transactions',
414
-                    'ee_edit_payments',
415
-                    'ee_delete_payments',
416
-                    // messages
417
-                    'ee_read_messages',
418
-                    'ee_read_others_messages',
419
-                    'ee_read_global_messages',
420
-                    'ee_edit_global_messages',
421
-                    'ee_edit_messages',
422
-                    'ee_edit_others_messages',
423
-                    'ee_delete_messages',
424
-                    'ee_delete_others_messages',
425
-                    'ee_delete_global_messages',
426
-                    'ee_send_message',
427
-                    // tickets
428
-                    'ee_read_default_tickets',
429
-                    'ee_read_others_default_tickets',
430
-                    'ee_edit_default_tickets',
431
-                    'ee_edit_others_default_tickets',
432
-                    'ee_delete_default_tickets',
433
-                    'ee_delete_others_default_tickets',
434
-                    // prices
435
-                    'ee_edit_default_price',
436
-                    'ee_edit_default_prices',
437
-                    'ee_delete_default_price',
438
-                    'ee_delete_default_prices',
439
-                    'ee_edit_default_price_type',
440
-                    'ee_edit_default_price_types',
441
-                    'ee_delete_default_price_type',
442
-                    'ee_delete_default_price_types',
443
-                    'ee_read_default_prices',
444
-                    'ee_read_default_price_types',
445
-                    // registration form
446
-                    'ee_edit_questions',
447
-                    'ee_edit_system_questions',
448
-                    'ee_read_questions',
449
-                    'ee_delete_questions',
450
-                    'ee_edit_question_groups',
451
-                    'ee_read_question_groups',
452
-                    'ee_edit_system_question_groups',
453
-                    'ee_delete_question_groups',
454
-                    // event_type taxonomy
455
-                    'ee_assign_event_type',
456
-                    'ee_manage_event_types',
457
-                    'ee_edit_event_type',
458
-                    'ee_delete_event_type',
459
-                ],
460
-                EE_Capabilities::ROLE_EVENTS_ADMINISTRATOR => [
461
-                    // core wp caps
462
-                    'read',
463
-                    'read_private_pages',
464
-                    'read_private_posts',
465
-                    'edit_users',
466
-                    'edit_posts',
467
-                    'edit_pages',
468
-                    'edit_published_posts',
469
-                    'edit_published_pages',
470
-                    'edit_private_pages',
471
-                    'edit_private_posts',
472
-                    'edit_others_posts',
473
-                    'edit_others_pages',
474
-                    'publish_posts',
475
-                    'publish_pages',
476
-                    'delete_posts',
477
-                    'delete_pages',
478
-                    'delete_private_pages',
479
-                    'delete_private_posts',
480
-                    'delete_published_pages',
481
-                    'delete_published_posts',
482
-                    'delete_others_posts',
483
-                    'delete_others_pages',
484
-                    'manage_categories',
485
-                    'manage_links',
486
-                    'moderate_comments',
487
-                    'unfiltered_html',
488
-                    'upload_files',
489
-                    'export',
490
-                    'import',
491
-                    'list_users',
492
-                    'level_1', // required if user with this role shows up in author dropdowns
493
-                    // basic ee access
494
-                    'ee_read_ee',
495
-                    // events
496
-                    'ee_publish_events',
497
-                    'ee_read_private_events',
498
-                    'ee_read_others_events',
499
-                    'ee_read_event',
500
-                    'ee_read_events',
501
-                    'ee_edit_event',
502
-                    'ee_edit_events',
503
-                    'ee_edit_published_events',
504
-                    'ee_edit_others_events',
505
-                    'ee_edit_private_events',
506
-                    'ee_delete_published_events',
507
-                    'ee_delete_private_events',
508
-                    'ee_delete_event',
509
-                    'ee_delete_events',
510
-                    'ee_delete_others_events',
511
-                    // event categories
512
-                    'ee_manage_event_categories',
513
-                    'ee_edit_event_category',
514
-                    'ee_delete_event_category',
515
-                    'ee_assign_event_category',
516
-                    // venues
517
-                    'ee_publish_venues',
518
-                    'ee_read_venue',
519
-                    'ee_read_venues',
520
-                    'ee_read_others_venues',
521
-                    'ee_read_private_venues',
522
-                    'ee_edit_venue',
523
-                    'ee_edit_venues',
524
-                    'ee_edit_others_venues',
525
-                    'ee_edit_published_venues',
526
-                    'ee_edit_private_venues',
527
-                    'ee_delete_venue',
528
-                    'ee_delete_venues',
529
-                    'ee_delete_others_venues',
530
-                    'ee_delete_private_venues',
531
-                    'ee_delete_published_venues',
532
-                    // venue categories
533
-                    'ee_manage_venue_categories',
534
-                    'ee_edit_venue_category',
535
-                    'ee_delete_venue_category',
536
-                    'ee_assign_venue_category',
537
-                    // contacts
538
-                    'ee_read_contacts',
539
-                    'ee_edit_contacts',
540
-                    'ee_delete_contacts',
541
-                    // registrations
542
-                    'ee_read_registrations',
543
-                    'ee_read_others_registrations',
544
-                    'ee_edit_registration',
545
-                    'ee_edit_registrations',
546
-                    'ee_edit_others_registrations',
547
-                    'ee_delete_registration',
548
-                    'ee_delete_registrations',
549
-                    'ee_delete_others_registrations',
550
-                    // checkins
551
-                    'ee_read_others_checkins',
552
-                    'ee_read_checkins',
553
-                    'ee_edit_checkins',
554
-                    'ee_edit_others_checkins',
555
-                    'ee_delete_checkins',
556
-                    'ee_delete_others_checkins',
557
-                    // transactions && payments
558
-                    'ee_read_transaction',
559
-                    'ee_read_transactions',
560
-                    'ee_edit_payments',
561
-                    'ee_delete_payments',
562
-                    // messages
563
-                    'ee_read_messages',
564
-                    'ee_read_others_messages',
565
-                    'ee_read_global_messages',
566
-                    'ee_edit_global_messages',
567
-                    'ee_edit_messages',
568
-                    'ee_edit_others_messages',
569
-                    'ee_delete_messages',
570
-                    'ee_delete_others_messages',
571
-                    'ee_delete_global_messages',
572
-                    'ee_send_message',
573
-                    // tickets
574
-                    'ee_read_default_tickets',
575
-                    'ee_read_others_default_tickets',
576
-                    'ee_edit_default_tickets',
577
-                    'ee_edit_others_default_tickets',
578
-                    'ee_delete_default_tickets',
579
-                    'ee_delete_others_default_tickets',
580
-                    // prices
581
-                    'ee_edit_default_price',
582
-                    'ee_edit_default_prices',
583
-                    'ee_delete_default_price',
584
-                    'ee_delete_default_prices',
585
-                    'ee_edit_default_price_type',
586
-                    'ee_edit_default_price_types',
587
-                    'ee_delete_default_price_type',
588
-                    'ee_delete_default_price_types',
589
-                    'ee_read_default_prices',
590
-                    'ee_read_default_price_types',
591
-                    // registration form
592
-                    'ee_edit_questions',
593
-                    'ee_edit_system_questions',
594
-                    'ee_read_questions',
595
-                    'ee_delete_questions',
596
-                    'ee_edit_question_groups',
597
-                    'ee_read_question_groups',
598
-                    'ee_edit_system_question_groups',
599
-                    'ee_delete_question_groups',
600
-                    // event_type taxonomy
601
-                    'ee_assign_event_type',
602
-                    'ee_manage_event_types',
603
-                    'ee_edit_event_type',
604
-                    'ee_delete_event_type',
605
-                ],
606
-            ]
607
-        );
608
-    }
609
-
610
-
611
-    /**
612
-     * @return bool
613
-     * @throws EE_Error
614
-     */
615
-    private function setupCapabilitiesMap(): bool
616
-    {
617
-        // if the initialization process hasn't even started, then we need to call init_caps()
618
-        if ($this->initialized === null) {
619
-            return $this->init_caps();
620
-        }
621
-        // unless resetting, get caps from db if we haven't already
622
-        $this->capabilities_map = $this->reset || ! empty($this->capabilities_map)
623
-            ? $this->capabilities_map
624
-            : get_option(self::option_name, []);
625
-        return true;
626
-    }
627
-
628
-
629
-    /**
630
-     * @param bool $update
631
-     * @return bool
632
-     */
633
-    private function updateCapabilitiesMap(bool $update = true): bool
634
-    {
635
-        return $update && update_option(self::option_name, $this->capabilities_map);
636
-    }
637
-
638
-
639
-    /**
640
-     * Adds capabilities to roles.
641
-     *
642
-     * @param array $capabilities_to_add array of capabilities to add, indexed by roles.
643
-     *                                   Note that this should ONLY be called on activation hook
644
-     *                                   otherwise the caps will be added on every request.
645
-     * @return bool
646
-     * @throws EE_Error
647
-     * @since 4.9.42
648
-     */
649
-    public function addCaps(array $capabilities_to_add): bool
650
-    {
651
-        // don't do anything if the capabilities map can not be initialized
652
-        if (! $this->setupCapabilitiesMap()) {
653
-            return false;
654
-        }
655
-        // and filter the array so others can get in on the fun during resets
656
-        $capabilities_to_add     = apply_filters(
657
-            'FHEE__EE_Capabilities__addCaps__capabilities_to_add',
658
-            $capabilities_to_add,
659
-            $this->reset,
660
-            $this->capabilities_map
661
-        );
662
-        $update_capabilities_map = false;
663
-        // if not reset, see what caps are new for each role. if they're new, add them.
664
-        foreach ($capabilities_to_add as $role => $caps_for_role) {
665
-            if (is_array($caps_for_role)) {
666
-                foreach ($caps_for_role as $cap) {
667
-                    if (
668
-                        ! $this->capHasBeenAddedToRole($role, $cap)
669
-                        && $this->add_cap_to_role($role, $cap, true, false)
670
-                    ) {
671
-                        $update_capabilities_map = true;
672
-                    }
673
-                }
674
-            }
675
-        }
676
-        // now let's just save the cap that has been set but only if there's been a change.
677
-        $updated = $this->updateCapabilitiesMap($update_capabilities_map);
678
-        $this->flushWpUser($updated);
679
-        do_action('AHEE__EE_Capabilities__addCaps__complete', $this->capabilities_map, $updated);
680
-        return $updated;
681
-    }
682
-
683
-
684
-    /**
685
-     * Loops through the capabilities map and removes the role caps specified by the incoming array
686
-     *
687
-     * @param array $caps_map map of capabilities to be removed (indexed by roles)
688
-     * @return bool
689
-     * @throws EE_Error
690
-     */
691
-    public function removeCaps(array $caps_map): bool
692
-    {
693
-        // don't do anything if the capabilities map can not be initialized
694
-        if (! $this->setupCapabilitiesMap()) {
695
-            return false;
696
-        }
697
-        $update_capabilities_map = false;
698
-        foreach ($caps_map as $role => $caps_for_role) {
699
-            if (is_array($caps_for_role)) {
700
-                foreach ($caps_for_role as $cap) {
701
-                    if (
702
-                        $this->capHasBeenAddedToRole($role, $cap)
703
-                        && $this->remove_cap_from_role($role, $cap, false)
704
-                    ) {
705
-                        $update_capabilities_map = true;
706
-                    }
707
-                }
708
-            }
709
-        }
710
-        // maybe resave the caps
711
-        $updated = $this->updateCapabilitiesMap($update_capabilities_map);
712
-        $this->flushWpUser($updated);
713
-        return $updated;
714
-    }
715
-
716
-
717
-    /**
718
-     * This ensures that the WP User object cached on the $current_user global in WP has the latest capabilities from
719
-     * the roles on that user.
720
-     *
721
-     * @param bool $flush Default is to flush the WP_User object.  If false, then this method effectively does nothing.
722
-     */
723
-    private function flushWpUser(bool $flush = true)
724
-    {
725
-        if ($flush) {
726
-            $user = wp_get_current_user();
727
-            if ($user instanceof WP_User) {
728
-                $user->get_role_caps();
729
-            }
730
-        }
731
-    }
732
-
733
-
734
-    /**
735
-     * This method sets a capability on a role.  Note this should only be done on activation, or if you have something
736
-     * specific to prevent the cap from being added on every page load (adding caps are persistent to the db). Note.
737
-     * this is a wrapper for $wp_role->add_cap()
738
-     *
739
-     * @param string|WP_Role $role  A WordPress role the capability is being added to
740
-     * @param string         $cap   The capability being added to the role
741
-     * @param bool           $grant Whether to grant access to this cap on this role.
742
-     * @param bool           $update_capabilities_map
743
-     * @return bool
744
-     * @throws EE_Error
745
-     * @see   wp-includes/capabilities.php
746
-     * @since 4.5.0
747
-     */
748
-    public function add_cap_to_role(
749
-        $role,
750
-        string $cap,
751
-        bool $grant = true,
752
-        bool $update_capabilities_map = true
753
-    ): bool {
754
-        // capture incoming value for $role because we may need it to create a new WP_Role
755
-        $orig_role = $role;
756
-        $role      = $role instanceof WP_Role ? $role : get_role($role);
757
-        // if the role isn't available then we create it.
758
-        if (! $role instanceof WP_Role) {
759
-            // if a plugin wants to create a specific role name then they should create the role before
760
-            // EE_Capabilities does.  Otherwise this function will create the role name from the slug:
761
-            // - removes any `ee_` namespacing from the start of the slug.
762
-            // - replaces `_` with ` ` (empty space).
763
-            // - sentence case on the resulting string.
764
-            $role_label = ucwords(str_replace(['ee_', '_'], ['', ' '], $orig_role));
765
-            $role       = add_role($orig_role, $role_label);
766
-        }
767
-        if ($role instanceof WP_Role) {
768
-            // don't do anything if the capabilities map can not be initialized
769
-            if (! $this->setupCapabilitiesMap()) {
770
-                return false;
771
-            }
772
-            if (! $this->capHasBeenAddedToRole($role->name, $cap)) {
773
-                $role->add_cap($cap, $grant);
774
-                $this->capabilities_map[ $role->name ][] = $cap;
775
-                $this->updateCapabilitiesMap($update_capabilities_map);
776
-                return true;
777
-            }
778
-        }
779
-        return false;
780
-    }
781
-
782
-
783
-    /**
784
-     * Functions similarly to add_cap_to_role except removes cap from given role.
785
-     * Wrapper for $wp_role->remove_cap()
786
-     *
787
-     * @param string|WP_Role $role A WordPress role the capability is being removed from.
788
-     * @param string         $cap  The capability being removed
789
-     * @param bool           $update_capabilities_map
790
-     * @return bool
791
-     * @throws EE_Error
792
-     * @since 4.5.0
793
-     * @see   wp-includes/capabilities.php
794
-     */
795
-    public function remove_cap_from_role($role, string $cap, bool $update_capabilities_map = true): bool
796
-    {
797
-        // don't do anything if the capabilities map can not be initialized
798
-        if (! $this->setupCapabilitiesMap()) {
799
-            return false;
800
-        }
801
-
802
-        $role = $role instanceof WP_Role ? $role : get_role($role);
803
-        if ($role instanceof WP_Role && $index = $this->capHasBeenAddedToRole($role->name, $cap, true)) {
804
-            $role->remove_cap($cap);
805
-            unset($this->capabilities_map[ $role->name ][ $index ]);
806
-            $this->updateCapabilitiesMap($update_capabilities_map);
807
-            return true;
808
-        }
809
-        return false;
810
-    }
811
-
812
-
813
-    /**
814
-     * @param string $role_name
815
-     * @param string $cap
816
-     * @param bool   $get_index
817
-     * @return bool|int|string
818
-     */
819
-    private function capHasBeenAddedToRole(string $role_name = '', string $cap = '', bool $get_index = false)
820
-    {
821
-        if (
822
-            isset($this->capabilities_map[ $role_name ])
823
-            && ($index = array_search($cap, $this->capabilities_map[ $role_name ], true)) !== false
824
-        ) {
825
-            return $get_index ? $index : true;
826
-        }
827
-        return false;
828
-    }
829
-
830
-
831
-    /**
832
-     * Wrapper for the native WP current_user_can() method.
833
-     * This is provided as a handy method for a couple things:
834
-     * 1. Using the context string it allows for targeted filtering by addons for a specific check (without having to
835
-     * write those filters wherever current_user_can is called).
836
-     * 2. Explicit passing of $id from a given context ( useful in the cases of map_meta_cap filters )
837
-     *
838
-     * @param string     $cap     The cap being checked.
839
-     * @param string     $context The context where the current_user_can is being called from.
840
-     * @param int|string $id      [optional] ID for entity where current_user_can() is being called from
841
-     *                            (used in map_meta_cap() filters).
842
-     * @return bool  Whether user can or not.
843
-     * @since 4.5.0
844
-     */
845
-    public function current_user_can(string $cap, string $context, $id = 0): bool
846
-    {
847
-        // apply filters (both a global on just the cap, and context specific.  Global overrides context specific)
848
-        $filtered_cap = apply_filters(
849
-            'FHEE__EE_Capabilities__current_user_can__cap',
850
-            apply_filters('FHEE__EE_Capabilities__current_user_can__cap__' . $context, $cap, $id),
851
-            $context,
852
-            $cap,
853
-            $id
854
-        );
855
-        return ! empty($id) ? current_user_can($filtered_cap, $id) : current_user_can($filtered_cap);
856
-    }
857
-
858
-
859
-    /**
860
-     * This is a wrapper for the WP user_can() function and follows the same style as the other wrappers in this class.
861
-     *
862
-     * @param int|WP_User $user    Either the user_id or a WP_User object
863
-     * @param string      $cap     The capability string being checked
864
-     * @param string      $context The context where the user_can is being called from (used in filters).
865
-     * @param int         $id      Optional. Id for item where user_can is being called from ( used in map_meta_cap()
866
-     *                             filters)
867
-     * @return bool Whether user can or not.
868
-     */
869
-    public function user_can($user, string $cap, string $context, int $id = 0): bool
870
-    {
871
-        // apply filters (both a global on just the cap, and context specific.  Global overrides context specific)
872
-        $filtered_cap = apply_filters('FHEE__EE_Capabilities__user_can__cap__' . $context, $cap, $user, $id);
873
-        $filtered_cap = apply_filters(
874
-            'FHEE__EE_Capabilities__user_can__cap',
875
-            $filtered_cap,
876
-            $context,
877
-            $cap,
878
-            $user,
879
-            $id
880
-        );
881
-        return ! empty($id) ? user_can($user, $filtered_cap, $id) : user_can($user, $filtered_cap);
882
-    }
883
-
884
-
885
-    /**
886
-     * Wrapper for the native WP current_user_can_for_blog() method.
887
-     * This is provided as a handy method for a couple things:
888
-     * 1. Using the context string it allows for targeted filtering by addons for a specific check (without having to
889
-     * write those filters wherever current_user_can is called).
890
-     * 2. Explicit passing of $id from a given context ( useful in the cases of map_meta_cap filters )
891
-     *
892
-     * @param int    $blog_id The blog id that is being checked for.
893
-     * @param string $cap     The cap being checked.
894
-     * @param string $context The context where the current_user_can is being called from.
895
-     * @param int    $id      Optional. Id for item where current_user_can is being called from (used in map_meta_cap()
896
-     *                        filters.
897
-     * @return bool  Whether user can or not.
898
-     * @since 4.5.0
899
-     */
900
-    public function current_user_can_for_blog(int $blog_id, string $cap, string $context, int $id = 0): bool
901
-    {
902
-        $user_can = ! empty($id) ? current_user_can_for_blog($blog_id, $cap, $id) : current_user_can($blog_id, $cap);
903
-        // apply filters (both a global on just the cap, and context specific.  Global overrides context specific)
904
-        $user_can = apply_filters(
905
-            'FHEE__EE_Capabilities__current_user_can_for_blog__user_can__' . $context,
906
-            $user_can,
907
-            $blog_id,
908
-            $cap,
909
-            $id
910
-        );
911
-        return apply_filters(
912
-            'FHEE__EE_Capabilities__current_user_can_for_blog__user_can',
913
-            $user_can,
914
-            $context,
915
-            $blog_id,
916
-            $cap,
917
-            $id
918
-        );
919
-    }
920
-
921
-
922
-    /**
923
-     * This helper method just returns an array of registered EE capabilities.
924
-     *
925
-     * @param string|null $role If empty then the entire role/capability map is returned.
926
-     *                          Otherwise just the capabilities for the given role are returned.
927
-     * @return array
928
-     * @throws EE_Error
929
-     * @since 4.5.0
930
-     */
931
-    public function get_ee_capabilities(?string $role = EE_Capabilities::ROLE_ADMINISTRATOR): array
932
-    {
933
-        if (! $this->initialized) {
934
-            $this->init_caps();
935
-        }
936
-        if ($role === '') {
937
-            return $this->capabilities_map;
938
-        }
939
-        return $this->capabilities_map[ $role ] ?? [];
940
-    }
941
-
942
-
943
-    /**
944
-     * @param bool  $reset      If you need to reset Event Espresso's capabilities,
945
-     *                          then please use the init_caps() method with the "$reset" parameter set to "true"
946
-     * @param array $caps_map   Optional.
947
-     *                          Can be used to send a custom map of roles and capabilities for setting them up.
948
-     *                          Note that this should ONLY be called on activation hook or some other one-time
949
-     *                          task otherwise the caps will be added on every request.
950
-     * @return void
951
-     * @throws EE_Error
952
-     * @deprecated 4.9.42
953
-     */
954
-    public function init_role_caps(bool $reset = false, array $caps_map = [])
955
-    {
956
-        // If this method is called directly and reset is set as 'true',
957
-        // then display a doing it wrong notice, because we want resets to go through init_caps()
958
-        // to guarantee that everything is set up correctly.
959
-        // This prevents the capabilities map getting reset incorrectly by direct calls to this method.
960
-        if ($reset) {
961
-            EE_Error::doing_it_wrong(
962
-                __METHOD__,
963
-                sprintf(
964
-                    esc_html__(
965
-                        'The "%1$s" parameter for the "%2$s" method is deprecated. If you need to reset Event Espresso\'s capabilities, then please use the "%3$s" method with the "%1$s" parameter set to "%4$s".',
966
-                        'event_espresso'
967
-                    ),
968
-                    '$reset',
969
-                    __METHOD__ . '()',
970
-                    'EE_Capabilities::init_caps()',
971
-                    'true'
972
-                ),
973
-                '4.9.42',
974
-                '5.0.0'
975
-            );
976
-        }
977
-        $this->addCaps($caps_map);
978
-    }
18
+	const ROLE_ADMINISTRATOR = 'administrator';
19
+
20
+	const ROLE_EVENTS_ADMINISTRATOR = 'ee_events_administrator';
21
+
22
+
23
+	/**
24
+	 * the name of the wp option used to store caps previously initialized
25
+	 */
26
+	const option_name = 'ee_caps_initialized';
27
+
28
+	private static ?EE_Capabilities $_instance = null;
29
+
30
+
31
+	/**
32
+	 * This is a map of caps that correspond to a default WP_Role.
33
+	 * Array is indexed by Role and values are ee capabilities.
34
+	 *
35
+	 * @since 4.5.0
36
+	 * @var array
37
+	 */
38
+	private array $capabilities_map = [];
39
+
40
+	/**
41
+	 * This used to hold an array of EE_Meta_Capability_Map objects
42
+	 * that define the granular capabilities mapped to for a user depending on context.
43
+	 *
44
+	 * @var EE_Meta_Capability_Map[]
45
+	 */
46
+	private array $_meta_caps = [];
47
+
48
+	/**
49
+	 * The internal $capabilities_map needs to be initialized before it can be used.
50
+	 * This flag tracks whether that has happened or not.
51
+	 * But for this to work, we need three states to indicate:
52
+	 *      initialization has not occurred at all
53
+	 *      initialization has started but is not complete
54
+	 *      initialization is complete
55
+	 * The reason this is needed is because the addCaps() method
56
+	 * normally requires the $capabilities_map to be initialized,
57
+	 * but is also used during the initialization process.
58
+	 * So:
59
+	 *      If initialized === null, init_caps() will be called before any other methods will run.
60
+	 *      If initialized === false, then init_caps() is in the process of running it's logic.
61
+	 *      If initialized === true, then init_caps() has completed the initialization process.
62
+	 *
63
+	 * @var boolean|null $initialized
64
+	 */
65
+	private ?bool $initialized = null;
66
+
67
+	/**
68
+	 * @var boolean $reset
69
+	 */
70
+	private bool $reset = false;
71
+
72
+
73
+	/**
74
+	 * singleton method used to instantiate class object
75
+	 *
76
+	 * @return EE_Capabilities
77
+	 * @since 4.5.0
78
+	 */
79
+	public static function instance(): EE_Capabilities
80
+	{
81
+		// check if instantiated, and if not do so.
82
+		if (! self::$_instance instanceof EE_Capabilities) {
83
+			self::$_instance = new self();
84
+		}
85
+		return self::$_instance;
86
+	}
87
+
88
+
89
+	/**
90
+	 * private constructor
91
+	 *
92
+	 * @since 4.5.0
93
+	 */
94
+	private function __construct()
95
+	{
96
+	}
97
+
98
+
99
+	/**
100
+	 * This delays the initialization of the capabilities class until EE_System core is loaded and ready.
101
+	 *
102
+	 * @param bool $reset allows for resetting the default capabilities saved on roles.  Note that this doesn't
103
+	 *                    actually REMOVE any capabilities from existing roles, it just resaves defaults roles and
104
+	 *                    ensures that they are up to date.
105
+	 * @since 4.5.0
106
+	 * @return bool
107
+	 * @throws EE_Error
108
+	 */
109
+	public function init_caps(?bool $reset = false): bool
110
+	{
111
+		if (DbStatus::isOffline()) {
112
+			return false;
113
+		}
114
+		$this->reset = filter_var($reset, FILTER_VALIDATE_BOOLEAN);
115
+		// if reset, then completely delete the cache option and clear the $capabilities_map property.
116
+		if ($this->reset) {
117
+			$this->initialized      = null;
118
+			$this->capabilities_map = [];
119
+			delete_option(self::option_name);
120
+		}
121
+		if ($this->initialized === null) {
122
+			$this->initialized = false;
123
+			do_action(
124
+				'AHEE__EE_Capabilities__init_caps__before_initialization',
125
+				$this->reset
126
+			);
127
+			$this->addCaps($this->_init_caps_map());
128
+			$this->_set_meta_caps();
129
+			do_action(
130
+				'AHEE__EE_Capabilities__init_caps__after_initialization',
131
+				$this->capabilities_map
132
+			);
133
+			$this->initialized = true;
134
+		}
135
+		// reset $this->reset so that it's not stuck on true if init_caps() gets called again
136
+		$this->reset = false;
137
+		return true;
138
+	}
139
+
140
+
141
+	/**
142
+	 * This sets the meta caps property.
143
+	 *
144
+	 * @return void
145
+	 * @throws EE_Error
146
+	 * @since 4.5.0
147
+	 */
148
+	private function _set_meta_caps()
149
+	{
150
+		// get default meta caps and filter the returned array
151
+		$this->_meta_caps = apply_filters(
152
+			'FHEE__EE_Capabilities___set_meta_caps__meta_caps',
153
+			$this->_get_default_meta_caps_array()
154
+		);
155
+		// add filter for map_meta_caps but only if models can query.
156
+		if (! has_filter('map_meta_cap', [$this, 'map_meta_caps'])) {
157
+			add_filter('map_meta_cap', [$this, 'map_meta_caps'], 10, 4);
158
+		}
159
+	}
160
+
161
+
162
+	/**
163
+	 * This builds and returns the default meta_caps array only once.
164
+	 *
165
+	 * @return array
166
+	 * @throws EE_Error
167
+	 * @since  4.8.28.rc.012
168
+	 */
169
+	private function _get_default_meta_caps_array(): array
170
+	{
171
+		static $default_meta_caps = [];
172
+		// make sure we're only ever initializing the default _meta_caps array once if it's empty.
173
+		if (empty($default_meta_caps)) {
174
+			$default_meta_caps = [
175
+				// edits
176
+				new EE_Meta_Capability_Map_Edit(
177
+					'ee_edit_event',
178
+					['Event', 'ee_edit_published_events', 'ee_edit_others_events', 'ee_edit_private_events']
179
+				),
180
+				new EE_Meta_Capability_Map_Edit(
181
+					'ee_edit_venue',
182
+					['Venue', 'ee_edit_published_venues', 'ee_edit_others_venues', 'ee_edit_private_venues']
183
+				),
184
+				new EE_Meta_Capability_Map_Edit(
185
+					'ee_edit_registration',
186
+					['Registration', '', 'ee_edit_others_registrations', '']
187
+				),
188
+				new EE_Meta_Capability_Map_Edit(
189
+					'ee_edit_checkin',
190
+					['Registration', '', 'ee_edit_others_checkins', '']
191
+				),
192
+				new EE_Meta_Capability_Map_Messages_Cap(
193
+					'ee_edit_message',
194
+					['Message_Template_Group', '', 'ee_edit_others_messages', 'ee_edit_global_messages']
195
+				),
196
+				new EE_Meta_Capability_Map_Edit(
197
+					'ee_edit_default_ticket',
198
+					['Ticket', '', 'ee_edit_others_default_tickets', '']
199
+				),
200
+				new EE_Meta_Capability_Map_Registration_Form_Cap(
201
+					'ee_edit_question',
202
+					['Question', '', '', 'ee_edit_system_questions']
203
+				),
204
+				new EE_Meta_Capability_Map_Registration_Form_Cap(
205
+					'ee_edit_question_group',
206
+					['Question_Group', '', '', 'ee_edit_system_question_groups']
207
+				),
208
+				new EE_Meta_Capability_Map_Edit(
209
+					'ee_edit_payment_method',
210
+					['Payment_Method', '', 'ee_edit_others_payment_methods', '']
211
+				),
212
+				// reads
213
+				new EE_Meta_Capability_Map_Read(
214
+					'ee_read_event',
215
+					['Event', '', 'ee_read_others_events', 'ee_read_private_events']
216
+				),
217
+				new EE_Meta_Capability_Map_Read(
218
+					'ee_read_venue',
219
+					['Venue', '', 'ee_read_others_venues', 'ee_read_private_venues']
220
+				),
221
+				new EE_Meta_Capability_Map_Read(
222
+					'ee_read_registration',
223
+					['Registration', '', 'ee_read_others_registrations', '']
224
+				),
225
+				new EE_Meta_Capability_Map_Read(
226
+					'ee_read_checkin',
227
+					['Registration', '', 'ee_read_others_checkins', '']
228
+				),
229
+				new EE_Meta_Capability_Map_Messages_Cap(
230
+					'ee_read_message',
231
+					['Message_Template_Group', '', 'ee_read_others_messages', 'ee_read_global_messages']
232
+				),
233
+				new EE_Meta_Capability_Map_Read(
234
+					'ee_read_default_ticket',
235
+					['Ticket', '', 'ee_read_others_default_tickets', '']
236
+				),
237
+				new EE_Meta_Capability_Map_Read(
238
+					'ee_read_payment_method',
239
+					['Payment_Method', '', 'ee_read_others_payment_methods', '']
240
+				),
241
+				// deletes
242
+				new EE_Meta_Capability_Map_Delete(
243
+					'ee_delete_event',
244
+					[
245
+						'Event',
246
+						'ee_delete_published_events',
247
+						'ee_delete_others_events',
248
+						'ee_delete_private_events',
249
+					]
250
+				),
251
+				new EE_Meta_Capability_Map_Delete(
252
+					'ee_delete_venue',
253
+					[
254
+						'Venue',
255
+						'ee_delete_published_venues',
256
+						'ee_delete_others_venues',
257
+						'ee_delete_private_venues',
258
+					]
259
+				),
260
+				new EE_Meta_Capability_Map_Delete(
261
+					'ee_delete_registration',
262
+					['Registration', '', 'ee_delete_others_registrations', '']
263
+				),
264
+				new EE_Meta_Capability_Map_Delete(
265
+					'ee_delete_checkin',
266
+					['Registration', '', 'ee_delete_others_checkins', '']
267
+				),
268
+				new EE_Meta_Capability_Map_Messages_Cap(
269
+					'ee_delete_message',
270
+					['Message_Template_Group', '', 'ee_delete_others_messages', 'ee_delete_global_messages']
271
+				),
272
+				new EE_Meta_Capability_Map_Delete(
273
+					'ee_delete_default_ticket',
274
+					['Ticket', '', 'ee_delete_others_default_tickets', '']
275
+				),
276
+				new EE_Meta_Capability_Map_Registration_Form_Cap(
277
+					'ee_delete_question',
278
+					['Question', '', '', 'delete_system_questions']
279
+				),
280
+				new EE_Meta_Capability_Map_Registration_Form_Cap(
281
+					'ee_delete_question_group',
282
+					['Question_Group', '', '', 'delete_system_question_groups']
283
+				),
284
+				new EE_Meta_Capability_Map_Delete(
285
+					'ee_delete_payment_method',
286
+					['Payment_Method', '', 'ee_delete_others_payment_methods', '']
287
+				),
288
+			];
289
+		}
290
+		return $default_meta_caps;
291
+	}
292
+
293
+
294
+	/**
295
+	 * This is the callback for the wp map_meta_caps() function which allows for ensuring certain caps that act as a
296
+	 * "meta" for other caps ( i.e. ee_edit_event is a meta for ee_edit_others_events ) work as expected.
297
+	 * The actual logic is carried out by implementer classes in their definition of _map_meta_caps.
298
+	 *
299
+	 * @param array       $caps    actual users capabilities
300
+	 * @param string|null $cap     initial capability name that is being checked (the "map" key)
301
+	 * @param int         $user_id The user id
302
+	 * @param array       $args    Adds context to the cap. Typically the object ID.
303
+	 * @return array actual users capabilities
304
+	 * @throws EE_Error
305
+	 * @throws ReflectionException
306
+	 * @since 4.5.0
307
+	 * @see   wp-includes/capabilities.php
308
+	 */
309
+	public function map_meta_caps(array $caps, ?string $cap, int $user_id, array $args): array
310
+	{
311
+		if (! $cap || ! did_action('AHEE__EE_System__load_espresso_addons__complete')) {
312
+			return $caps;
313
+		}
314
+		// loop through our _meta_caps array
315
+		foreach ($this->_meta_caps as $meta_map) {
316
+			if (! $meta_map instanceof EE_Meta_Capability_Map) {
317
+				continue;
318
+			}
319
+			// don't load models if there is no object ID in the args
320
+			if (! empty($args[0])) {
321
+				$meta_map->ensure_is_model();
322
+			}
323
+			$caps = $meta_map->map_meta_caps($caps, $cap, $user_id, $args);
324
+		}
325
+		return $caps;
326
+	}
327
+
328
+
329
+	/**
330
+	 * This sets up and returns the initial capabilities map for Event Espresso
331
+	 * Note this array is filtered.
332
+	 * It is assumed that all available EE capabilities are assigned to the administrator role.
333
+	 *
334
+	 * @return array
335
+	 * @since 4.5.0
336
+	 */
337
+	private function _init_caps_map(): array
338
+	{
339
+		return apply_filters(
340
+			'FHEE__EE_Capabilities__init_caps_map__caps',
341
+			[
342
+				EE_Capabilities::ROLE_ADMINISTRATOR        => [
343
+					// basic access
344
+					'ee_read_ee',
345
+					// gateways
346
+					// note that with payment method capabilities, although we've implemented
347
+					// capability mapping which will be used for accessing payment methods owned by
348
+					// other users.  This is not fully implemented yet in the payment method ui.
349
+					// Currently only the "plural" caps are in active use.
350
+					// (Specific payment method caps are in use as well).
351
+					'ee_manage_gateways',
352
+					'ee_read_payment_methods',
353
+					'ee_read_others_payment_methods',
354
+					'ee_edit_payment_methods',
355
+					'ee_edit_others_payment_methods',
356
+					'ee_delete_payment_methods',
357
+					// events
358
+					'ee_publish_events',
359
+					'ee_read_private_events',
360
+					'ee_read_others_events',
361
+					'ee_read_events',
362
+					'ee_edit_events',
363
+					'ee_edit_published_events',
364
+					'ee_edit_others_events',
365
+					'ee_edit_private_events',
366
+					'ee_delete_published_events',
367
+					'ee_delete_private_events',
368
+					'ee_delete_events',
369
+					'ee_delete_others_events',
370
+					// event categories
371
+					'ee_manage_event_categories',
372
+					'ee_edit_event_category',
373
+					'ee_delete_event_category',
374
+					'ee_assign_event_category',
375
+					// venues
376
+					'ee_publish_venues',
377
+					'ee_read_venues',
378
+					'ee_read_others_venues',
379
+					'ee_read_private_venues',
380
+					'ee_edit_venues',
381
+					'ee_edit_others_venues',
382
+					'ee_edit_published_venues',
383
+					'ee_edit_private_venues',
384
+					'ee_delete_venues',
385
+					'ee_delete_others_venues',
386
+					'ee_delete_private_venues',
387
+					'ee_delete_published_venues',
388
+					// venue categories
389
+					'ee_manage_venue_categories',
390
+					'ee_edit_venue_category',
391
+					'ee_delete_venue_category',
392
+					'ee_assign_venue_category',
393
+					// contacts
394
+					'ee_read_contacts',
395
+					'ee_edit_contacts',
396
+					'ee_delete_contacts',
397
+					// registrations
398
+					'ee_read_registrations',
399
+					'ee_read_others_registrations',
400
+					'ee_edit_registrations',
401
+					'ee_edit_others_registrations',
402
+					'ee_delete_registrations',
403
+					'ee_delete_others_registrations',
404
+					// checkins
405
+					'ee_read_others_checkins',
406
+					'ee_read_checkins',
407
+					'ee_edit_checkins',
408
+					'ee_edit_others_checkins',
409
+					'ee_delete_checkins',
410
+					'ee_delete_others_checkins',
411
+					// transactions && payments
412
+					'ee_read_transaction',
413
+					'ee_read_transactions',
414
+					'ee_edit_payments',
415
+					'ee_delete_payments',
416
+					// messages
417
+					'ee_read_messages',
418
+					'ee_read_others_messages',
419
+					'ee_read_global_messages',
420
+					'ee_edit_global_messages',
421
+					'ee_edit_messages',
422
+					'ee_edit_others_messages',
423
+					'ee_delete_messages',
424
+					'ee_delete_others_messages',
425
+					'ee_delete_global_messages',
426
+					'ee_send_message',
427
+					// tickets
428
+					'ee_read_default_tickets',
429
+					'ee_read_others_default_tickets',
430
+					'ee_edit_default_tickets',
431
+					'ee_edit_others_default_tickets',
432
+					'ee_delete_default_tickets',
433
+					'ee_delete_others_default_tickets',
434
+					// prices
435
+					'ee_edit_default_price',
436
+					'ee_edit_default_prices',
437
+					'ee_delete_default_price',
438
+					'ee_delete_default_prices',
439
+					'ee_edit_default_price_type',
440
+					'ee_edit_default_price_types',
441
+					'ee_delete_default_price_type',
442
+					'ee_delete_default_price_types',
443
+					'ee_read_default_prices',
444
+					'ee_read_default_price_types',
445
+					// registration form
446
+					'ee_edit_questions',
447
+					'ee_edit_system_questions',
448
+					'ee_read_questions',
449
+					'ee_delete_questions',
450
+					'ee_edit_question_groups',
451
+					'ee_read_question_groups',
452
+					'ee_edit_system_question_groups',
453
+					'ee_delete_question_groups',
454
+					// event_type taxonomy
455
+					'ee_assign_event_type',
456
+					'ee_manage_event_types',
457
+					'ee_edit_event_type',
458
+					'ee_delete_event_type',
459
+				],
460
+				EE_Capabilities::ROLE_EVENTS_ADMINISTRATOR => [
461
+					// core wp caps
462
+					'read',
463
+					'read_private_pages',
464
+					'read_private_posts',
465
+					'edit_users',
466
+					'edit_posts',
467
+					'edit_pages',
468
+					'edit_published_posts',
469
+					'edit_published_pages',
470
+					'edit_private_pages',
471
+					'edit_private_posts',
472
+					'edit_others_posts',
473
+					'edit_others_pages',
474
+					'publish_posts',
475
+					'publish_pages',
476
+					'delete_posts',
477
+					'delete_pages',
478
+					'delete_private_pages',
479
+					'delete_private_posts',
480
+					'delete_published_pages',
481
+					'delete_published_posts',
482
+					'delete_others_posts',
483
+					'delete_others_pages',
484
+					'manage_categories',
485
+					'manage_links',
486
+					'moderate_comments',
487
+					'unfiltered_html',
488
+					'upload_files',
489
+					'export',
490
+					'import',
491
+					'list_users',
492
+					'level_1', // required if user with this role shows up in author dropdowns
493
+					// basic ee access
494
+					'ee_read_ee',
495
+					// events
496
+					'ee_publish_events',
497
+					'ee_read_private_events',
498
+					'ee_read_others_events',
499
+					'ee_read_event',
500
+					'ee_read_events',
501
+					'ee_edit_event',
502
+					'ee_edit_events',
503
+					'ee_edit_published_events',
504
+					'ee_edit_others_events',
505
+					'ee_edit_private_events',
506
+					'ee_delete_published_events',
507
+					'ee_delete_private_events',
508
+					'ee_delete_event',
509
+					'ee_delete_events',
510
+					'ee_delete_others_events',
511
+					// event categories
512
+					'ee_manage_event_categories',
513
+					'ee_edit_event_category',
514
+					'ee_delete_event_category',
515
+					'ee_assign_event_category',
516
+					// venues
517
+					'ee_publish_venues',
518
+					'ee_read_venue',
519
+					'ee_read_venues',
520
+					'ee_read_others_venues',
521
+					'ee_read_private_venues',
522
+					'ee_edit_venue',
523
+					'ee_edit_venues',
524
+					'ee_edit_others_venues',
525
+					'ee_edit_published_venues',
526
+					'ee_edit_private_venues',
527
+					'ee_delete_venue',
528
+					'ee_delete_venues',
529
+					'ee_delete_others_venues',
530
+					'ee_delete_private_venues',
531
+					'ee_delete_published_venues',
532
+					// venue categories
533
+					'ee_manage_venue_categories',
534
+					'ee_edit_venue_category',
535
+					'ee_delete_venue_category',
536
+					'ee_assign_venue_category',
537
+					// contacts
538
+					'ee_read_contacts',
539
+					'ee_edit_contacts',
540
+					'ee_delete_contacts',
541
+					// registrations
542
+					'ee_read_registrations',
543
+					'ee_read_others_registrations',
544
+					'ee_edit_registration',
545
+					'ee_edit_registrations',
546
+					'ee_edit_others_registrations',
547
+					'ee_delete_registration',
548
+					'ee_delete_registrations',
549
+					'ee_delete_others_registrations',
550
+					// checkins
551
+					'ee_read_others_checkins',
552
+					'ee_read_checkins',
553
+					'ee_edit_checkins',
554
+					'ee_edit_others_checkins',
555
+					'ee_delete_checkins',
556
+					'ee_delete_others_checkins',
557
+					// transactions && payments
558
+					'ee_read_transaction',
559
+					'ee_read_transactions',
560
+					'ee_edit_payments',
561
+					'ee_delete_payments',
562
+					// messages
563
+					'ee_read_messages',
564
+					'ee_read_others_messages',
565
+					'ee_read_global_messages',
566
+					'ee_edit_global_messages',
567
+					'ee_edit_messages',
568
+					'ee_edit_others_messages',
569
+					'ee_delete_messages',
570
+					'ee_delete_others_messages',
571
+					'ee_delete_global_messages',
572
+					'ee_send_message',
573
+					// tickets
574
+					'ee_read_default_tickets',
575
+					'ee_read_others_default_tickets',
576
+					'ee_edit_default_tickets',
577
+					'ee_edit_others_default_tickets',
578
+					'ee_delete_default_tickets',
579
+					'ee_delete_others_default_tickets',
580
+					// prices
581
+					'ee_edit_default_price',
582
+					'ee_edit_default_prices',
583
+					'ee_delete_default_price',
584
+					'ee_delete_default_prices',
585
+					'ee_edit_default_price_type',
586
+					'ee_edit_default_price_types',
587
+					'ee_delete_default_price_type',
588
+					'ee_delete_default_price_types',
589
+					'ee_read_default_prices',
590
+					'ee_read_default_price_types',
591
+					// registration form
592
+					'ee_edit_questions',
593
+					'ee_edit_system_questions',
594
+					'ee_read_questions',
595
+					'ee_delete_questions',
596
+					'ee_edit_question_groups',
597
+					'ee_read_question_groups',
598
+					'ee_edit_system_question_groups',
599
+					'ee_delete_question_groups',
600
+					// event_type taxonomy
601
+					'ee_assign_event_type',
602
+					'ee_manage_event_types',
603
+					'ee_edit_event_type',
604
+					'ee_delete_event_type',
605
+				],
606
+			]
607
+		);
608
+	}
609
+
610
+
611
+	/**
612
+	 * @return bool
613
+	 * @throws EE_Error
614
+	 */
615
+	private function setupCapabilitiesMap(): bool
616
+	{
617
+		// if the initialization process hasn't even started, then we need to call init_caps()
618
+		if ($this->initialized === null) {
619
+			return $this->init_caps();
620
+		}
621
+		// unless resetting, get caps from db if we haven't already
622
+		$this->capabilities_map = $this->reset || ! empty($this->capabilities_map)
623
+			? $this->capabilities_map
624
+			: get_option(self::option_name, []);
625
+		return true;
626
+	}
627
+
628
+
629
+	/**
630
+	 * @param bool $update
631
+	 * @return bool
632
+	 */
633
+	private function updateCapabilitiesMap(bool $update = true): bool
634
+	{
635
+		return $update && update_option(self::option_name, $this->capabilities_map);
636
+	}
637
+
638
+
639
+	/**
640
+	 * Adds capabilities to roles.
641
+	 *
642
+	 * @param array $capabilities_to_add array of capabilities to add, indexed by roles.
643
+	 *                                   Note that this should ONLY be called on activation hook
644
+	 *                                   otherwise the caps will be added on every request.
645
+	 * @return bool
646
+	 * @throws EE_Error
647
+	 * @since 4.9.42
648
+	 */
649
+	public function addCaps(array $capabilities_to_add): bool
650
+	{
651
+		// don't do anything if the capabilities map can not be initialized
652
+		if (! $this->setupCapabilitiesMap()) {
653
+			return false;
654
+		}
655
+		// and filter the array so others can get in on the fun during resets
656
+		$capabilities_to_add     = apply_filters(
657
+			'FHEE__EE_Capabilities__addCaps__capabilities_to_add',
658
+			$capabilities_to_add,
659
+			$this->reset,
660
+			$this->capabilities_map
661
+		);
662
+		$update_capabilities_map = false;
663
+		// if not reset, see what caps are new for each role. if they're new, add them.
664
+		foreach ($capabilities_to_add as $role => $caps_for_role) {
665
+			if (is_array($caps_for_role)) {
666
+				foreach ($caps_for_role as $cap) {
667
+					if (
668
+						! $this->capHasBeenAddedToRole($role, $cap)
669
+						&& $this->add_cap_to_role($role, $cap, true, false)
670
+					) {
671
+						$update_capabilities_map = true;
672
+					}
673
+				}
674
+			}
675
+		}
676
+		// now let's just save the cap that has been set but only if there's been a change.
677
+		$updated = $this->updateCapabilitiesMap($update_capabilities_map);
678
+		$this->flushWpUser($updated);
679
+		do_action('AHEE__EE_Capabilities__addCaps__complete', $this->capabilities_map, $updated);
680
+		return $updated;
681
+	}
682
+
683
+
684
+	/**
685
+	 * Loops through the capabilities map and removes the role caps specified by the incoming array
686
+	 *
687
+	 * @param array $caps_map map of capabilities to be removed (indexed by roles)
688
+	 * @return bool
689
+	 * @throws EE_Error
690
+	 */
691
+	public function removeCaps(array $caps_map): bool
692
+	{
693
+		// don't do anything if the capabilities map can not be initialized
694
+		if (! $this->setupCapabilitiesMap()) {
695
+			return false;
696
+		}
697
+		$update_capabilities_map = false;
698
+		foreach ($caps_map as $role => $caps_for_role) {
699
+			if (is_array($caps_for_role)) {
700
+				foreach ($caps_for_role as $cap) {
701
+					if (
702
+						$this->capHasBeenAddedToRole($role, $cap)
703
+						&& $this->remove_cap_from_role($role, $cap, false)
704
+					) {
705
+						$update_capabilities_map = true;
706
+					}
707
+				}
708
+			}
709
+		}
710
+		// maybe resave the caps
711
+		$updated = $this->updateCapabilitiesMap($update_capabilities_map);
712
+		$this->flushWpUser($updated);
713
+		return $updated;
714
+	}
715
+
716
+
717
+	/**
718
+	 * This ensures that the WP User object cached on the $current_user global in WP has the latest capabilities from
719
+	 * the roles on that user.
720
+	 *
721
+	 * @param bool $flush Default is to flush the WP_User object.  If false, then this method effectively does nothing.
722
+	 */
723
+	private function flushWpUser(bool $flush = true)
724
+	{
725
+		if ($flush) {
726
+			$user = wp_get_current_user();
727
+			if ($user instanceof WP_User) {
728
+				$user->get_role_caps();
729
+			}
730
+		}
731
+	}
732
+
733
+
734
+	/**
735
+	 * This method sets a capability on a role.  Note this should only be done on activation, or if you have something
736
+	 * specific to prevent the cap from being added on every page load (adding caps are persistent to the db). Note.
737
+	 * this is a wrapper for $wp_role->add_cap()
738
+	 *
739
+	 * @param string|WP_Role $role  A WordPress role the capability is being added to
740
+	 * @param string         $cap   The capability being added to the role
741
+	 * @param bool           $grant Whether to grant access to this cap on this role.
742
+	 * @param bool           $update_capabilities_map
743
+	 * @return bool
744
+	 * @throws EE_Error
745
+	 * @see   wp-includes/capabilities.php
746
+	 * @since 4.5.0
747
+	 */
748
+	public function add_cap_to_role(
749
+		$role,
750
+		string $cap,
751
+		bool $grant = true,
752
+		bool $update_capabilities_map = true
753
+	): bool {
754
+		// capture incoming value for $role because we may need it to create a new WP_Role
755
+		$orig_role = $role;
756
+		$role      = $role instanceof WP_Role ? $role : get_role($role);
757
+		// if the role isn't available then we create it.
758
+		if (! $role instanceof WP_Role) {
759
+			// if a plugin wants to create a specific role name then they should create the role before
760
+			// EE_Capabilities does.  Otherwise this function will create the role name from the slug:
761
+			// - removes any `ee_` namespacing from the start of the slug.
762
+			// - replaces `_` with ` ` (empty space).
763
+			// - sentence case on the resulting string.
764
+			$role_label = ucwords(str_replace(['ee_', '_'], ['', ' '], $orig_role));
765
+			$role       = add_role($orig_role, $role_label);
766
+		}
767
+		if ($role instanceof WP_Role) {
768
+			// don't do anything if the capabilities map can not be initialized
769
+			if (! $this->setupCapabilitiesMap()) {
770
+				return false;
771
+			}
772
+			if (! $this->capHasBeenAddedToRole($role->name, $cap)) {
773
+				$role->add_cap($cap, $grant);
774
+				$this->capabilities_map[ $role->name ][] = $cap;
775
+				$this->updateCapabilitiesMap($update_capabilities_map);
776
+				return true;
777
+			}
778
+		}
779
+		return false;
780
+	}
781
+
782
+
783
+	/**
784
+	 * Functions similarly to add_cap_to_role except removes cap from given role.
785
+	 * Wrapper for $wp_role->remove_cap()
786
+	 *
787
+	 * @param string|WP_Role $role A WordPress role the capability is being removed from.
788
+	 * @param string         $cap  The capability being removed
789
+	 * @param bool           $update_capabilities_map
790
+	 * @return bool
791
+	 * @throws EE_Error
792
+	 * @since 4.5.0
793
+	 * @see   wp-includes/capabilities.php
794
+	 */
795
+	public function remove_cap_from_role($role, string $cap, bool $update_capabilities_map = true): bool
796
+	{
797
+		// don't do anything if the capabilities map can not be initialized
798
+		if (! $this->setupCapabilitiesMap()) {
799
+			return false;
800
+		}
801
+
802
+		$role = $role instanceof WP_Role ? $role : get_role($role);
803
+		if ($role instanceof WP_Role && $index = $this->capHasBeenAddedToRole($role->name, $cap, true)) {
804
+			$role->remove_cap($cap);
805
+			unset($this->capabilities_map[ $role->name ][ $index ]);
806
+			$this->updateCapabilitiesMap($update_capabilities_map);
807
+			return true;
808
+		}
809
+		return false;
810
+	}
811
+
812
+
813
+	/**
814
+	 * @param string $role_name
815
+	 * @param string $cap
816
+	 * @param bool   $get_index
817
+	 * @return bool|int|string
818
+	 */
819
+	private function capHasBeenAddedToRole(string $role_name = '', string $cap = '', bool $get_index = false)
820
+	{
821
+		if (
822
+			isset($this->capabilities_map[ $role_name ])
823
+			&& ($index = array_search($cap, $this->capabilities_map[ $role_name ], true)) !== false
824
+		) {
825
+			return $get_index ? $index : true;
826
+		}
827
+		return false;
828
+	}
829
+
830
+
831
+	/**
832
+	 * Wrapper for the native WP current_user_can() method.
833
+	 * This is provided as a handy method for a couple things:
834
+	 * 1. Using the context string it allows for targeted filtering by addons for a specific check (without having to
835
+	 * write those filters wherever current_user_can is called).
836
+	 * 2. Explicit passing of $id from a given context ( useful in the cases of map_meta_cap filters )
837
+	 *
838
+	 * @param string     $cap     The cap being checked.
839
+	 * @param string     $context The context where the current_user_can is being called from.
840
+	 * @param int|string $id      [optional] ID for entity where current_user_can() is being called from
841
+	 *                            (used in map_meta_cap() filters).
842
+	 * @return bool  Whether user can or not.
843
+	 * @since 4.5.0
844
+	 */
845
+	public function current_user_can(string $cap, string $context, $id = 0): bool
846
+	{
847
+		// apply filters (both a global on just the cap, and context specific.  Global overrides context specific)
848
+		$filtered_cap = apply_filters(
849
+			'FHEE__EE_Capabilities__current_user_can__cap',
850
+			apply_filters('FHEE__EE_Capabilities__current_user_can__cap__' . $context, $cap, $id),
851
+			$context,
852
+			$cap,
853
+			$id
854
+		);
855
+		return ! empty($id) ? current_user_can($filtered_cap, $id) : current_user_can($filtered_cap);
856
+	}
857
+
858
+
859
+	/**
860
+	 * This is a wrapper for the WP user_can() function and follows the same style as the other wrappers in this class.
861
+	 *
862
+	 * @param int|WP_User $user    Either the user_id or a WP_User object
863
+	 * @param string      $cap     The capability string being checked
864
+	 * @param string      $context The context where the user_can is being called from (used in filters).
865
+	 * @param int         $id      Optional. Id for item where user_can is being called from ( used in map_meta_cap()
866
+	 *                             filters)
867
+	 * @return bool Whether user can or not.
868
+	 */
869
+	public function user_can($user, string $cap, string $context, int $id = 0): bool
870
+	{
871
+		// apply filters (both a global on just the cap, and context specific.  Global overrides context specific)
872
+		$filtered_cap = apply_filters('FHEE__EE_Capabilities__user_can__cap__' . $context, $cap, $user, $id);
873
+		$filtered_cap = apply_filters(
874
+			'FHEE__EE_Capabilities__user_can__cap',
875
+			$filtered_cap,
876
+			$context,
877
+			$cap,
878
+			$user,
879
+			$id
880
+		);
881
+		return ! empty($id) ? user_can($user, $filtered_cap, $id) : user_can($user, $filtered_cap);
882
+	}
883
+
884
+
885
+	/**
886
+	 * Wrapper for the native WP current_user_can_for_blog() method.
887
+	 * This is provided as a handy method for a couple things:
888
+	 * 1. Using the context string it allows for targeted filtering by addons for a specific check (without having to
889
+	 * write those filters wherever current_user_can is called).
890
+	 * 2. Explicit passing of $id from a given context ( useful in the cases of map_meta_cap filters )
891
+	 *
892
+	 * @param int    $blog_id The blog id that is being checked for.
893
+	 * @param string $cap     The cap being checked.
894
+	 * @param string $context The context where the current_user_can is being called from.
895
+	 * @param int    $id      Optional. Id for item where current_user_can is being called from (used in map_meta_cap()
896
+	 *                        filters.
897
+	 * @return bool  Whether user can or not.
898
+	 * @since 4.5.0
899
+	 */
900
+	public function current_user_can_for_blog(int $blog_id, string $cap, string $context, int $id = 0): bool
901
+	{
902
+		$user_can = ! empty($id) ? current_user_can_for_blog($blog_id, $cap, $id) : current_user_can($blog_id, $cap);
903
+		// apply filters (both a global on just the cap, and context specific.  Global overrides context specific)
904
+		$user_can = apply_filters(
905
+			'FHEE__EE_Capabilities__current_user_can_for_blog__user_can__' . $context,
906
+			$user_can,
907
+			$blog_id,
908
+			$cap,
909
+			$id
910
+		);
911
+		return apply_filters(
912
+			'FHEE__EE_Capabilities__current_user_can_for_blog__user_can',
913
+			$user_can,
914
+			$context,
915
+			$blog_id,
916
+			$cap,
917
+			$id
918
+		);
919
+	}
920
+
921
+
922
+	/**
923
+	 * This helper method just returns an array of registered EE capabilities.
924
+	 *
925
+	 * @param string|null $role If empty then the entire role/capability map is returned.
926
+	 *                          Otherwise just the capabilities for the given role are returned.
927
+	 * @return array
928
+	 * @throws EE_Error
929
+	 * @since 4.5.0
930
+	 */
931
+	public function get_ee_capabilities(?string $role = EE_Capabilities::ROLE_ADMINISTRATOR): array
932
+	{
933
+		if (! $this->initialized) {
934
+			$this->init_caps();
935
+		}
936
+		if ($role === '') {
937
+			return $this->capabilities_map;
938
+		}
939
+		return $this->capabilities_map[ $role ] ?? [];
940
+	}
941
+
942
+
943
+	/**
944
+	 * @param bool  $reset      If you need to reset Event Espresso's capabilities,
945
+	 *                          then please use the init_caps() method with the "$reset" parameter set to "true"
946
+	 * @param array $caps_map   Optional.
947
+	 *                          Can be used to send a custom map of roles and capabilities for setting them up.
948
+	 *                          Note that this should ONLY be called on activation hook or some other one-time
949
+	 *                          task otherwise the caps will be added on every request.
950
+	 * @return void
951
+	 * @throws EE_Error
952
+	 * @deprecated 4.9.42
953
+	 */
954
+	public function init_role_caps(bool $reset = false, array $caps_map = [])
955
+	{
956
+		// If this method is called directly and reset is set as 'true',
957
+		// then display a doing it wrong notice, because we want resets to go through init_caps()
958
+		// to guarantee that everything is set up correctly.
959
+		// This prevents the capabilities map getting reset incorrectly by direct calls to this method.
960
+		if ($reset) {
961
+			EE_Error::doing_it_wrong(
962
+				__METHOD__,
963
+				sprintf(
964
+					esc_html__(
965
+						'The "%1$s" parameter for the "%2$s" method is deprecated. If you need to reset Event Espresso\'s capabilities, then please use the "%3$s" method with the "%1$s" parameter set to "%4$s".',
966
+						'event_espresso'
967
+					),
968
+					'$reset',
969
+					__METHOD__ . '()',
970
+					'EE_Capabilities::init_caps()',
971
+					'true'
972
+				),
973
+				'4.9.42',
974
+				'5.0.0'
975
+			);
976
+		}
977
+		$this->addCaps($caps_map);
978
+	}
979 979
 }
980 980
 
981 981
 
@@ -991,136 +991,136 @@  discard block
 block discarded – undo
991 991
  */
992 992
 abstract class EE_Meta_Capability_Map
993 993
 {
994
-    /**
995
-     * @var EEM_Base
996
-     */
997
-    protected $_model = null;
998
-
999
-    protected string $_model_name = '';
1000
-
1001
-    public string $meta_cap = '';
1002
-
1003
-    public string $published_cap = '';
1004
-
1005
-    public string $others_cap = '';
1006
-
1007
-    public string $private_cap = '';
1008
-
1009
-
1010
-    /**
1011
-     * constructor.
1012
-     * Receives the setup arguments for the map.
1013
-     *
1014
-     * @param string $meta_cap       What meta capability is this mapping.
1015
-     * @param array  $map_values     array {
1016
-     *                               //array of values that MUST match a count of 4.  It's okay to send an empty string
1017
-     *                               for capabilities that don't get mapped to.
1018
-     * @type         $map_values     [0] string A string representing the model name. Required.  String's
1019
-     *                               should always be used when Menu Maps are registered via the
1020
-     *                               plugin API as models are not allowed to be instantiated when
1021
-     *                               in maintenance mode 2 (migrations).
1022
-     * @type         $map_values     [1] string represents the capability used for published. Optional.
1023
-     * @type         $map_values     [2] string represents the capability used for "others". Optional.
1024
-     * @type         $map_values     [3] string represents the capability used for private. Optional.
1025
-     *                               }
1026
-     * @throws EE_Error
1027
-     * @since                        4.5.0
1028
-     */
1029
-    public function __construct(string $meta_cap, array $map_values)
1030
-    {
1031
-        $this->meta_cap = $meta_cap;
1032
-        // verify there are four args in the $map_values array;
1033
-        if (count($map_values) !== 4) {
1034
-            throw new EE_Error(
1035
-                sprintf(
1036
-                    esc_html__(
1037
-                        'Incoming $map_values array should have a count of four values in it.  This is what was given: %s',
1038
-                        'event_espresso'
1039
-                    ),
1040
-                    '<br>' . print_r($map_values, true)
1041
-                )
1042
-            );
1043
-        }
1044
-        // set properties
1045
-        $this->_model        = null;
1046
-        $this->_model_name   = $map_values[0];
1047
-        $this->published_cap = (string) $map_values[1];
1048
-        $this->others_cap    = (string) $map_values[2];
1049
-        $this->private_cap   = (string) $map_values[3];
1050
-    }
1051
-
1052
-
1053
-    /**
1054
-     * Makes it so this object stops filtering caps
1055
-     */
1056
-    public function remove_filters()
1057
-    {
1058
-        remove_filter('map_meta_cap', [$this, 'map_meta_caps']);
1059
-    }
1060
-
1061
-
1062
-    /**
1063
-     * This method ensures that the $model property is converted from the model name string to a proper EEM_Base class
1064
-     *
1065
-     * @return void
1066
-     * @throws EE_Error
1067
-     * @throws ReflectionException
1068
-     * @since 4.5.0
1069
-     */
1070
-    public function ensure_is_model()
1071
-    {
1072
-        // is it already instantiated?
1073
-        if ($this->_model instanceof EEM_Base) {
1074
-            return;
1075
-        }
1076
-        // ensure model name is string
1077
-        $this->_model_name = (string) $this->_model_name;
1078
-        // error proof if the name has EEM in it
1079
-        $this->_model_name = str_replace('EEM', '', $this->_model_name);
1080
-        $this->_model      = EE_Registry::instance()->load_model($this->_model_name);
1081
-        if (! $this->_model instanceof EEM_Base) {
1082
-            throw new EE_Error(
1083
-                sprintf(
1084
-                    esc_html__(
1085
-                        'This string passed in to %s to represent a EEM_Base model class was not able to be used to instantiate the class.   Please ensure that the string is a match for the EEM_Base model name (not including the EEM_ part). This was given: %s',
1086
-                        'event_espresso'
1087
-                    ),
1088
-                    get_class($this),
1089
-                    $this->_model
1090
-                )
1091
-            );
1092
-        }
1093
-    }
1094
-
1095
-
1096
-    /**
1097
-     * @param array       $caps    actual users capabilities
1098
-     * @param string|null $cap     initial capability name that is being checked (the "map" key)
1099
-     * @param int         $user_id The user id
1100
-     * @param array       $args    Adds context to the cap. Typically the object ID.
1101
-     * @return array
1102
-     * @since 4.6.x
1103
-     * @see   EE_Meta_Capability_Map::_map_meta_caps() for docs on params.
1104
-     */
1105
-    public function map_meta_caps(array $caps, ?string $cap, int $user_id, array $args): array
1106
-    {
1107
-        return $this->_map_meta_caps($caps, $cap, $user_id, $args);
1108
-    }
1109
-
1110
-
1111
-    /**
1112
-     * This is the callback for the wp map_meta_caps() function which allows for ensuring certain caps that act as a
1113
-     * "meta" for other caps ( i.e. ee_edit_event is a meta for ee_edit_others_events ) work as expected.
1114
-     *
1115
-     * @param array  $caps    actual users capabilities
1116
-     * @param string $cap     initial capability name that is being checked (the "map" key)
1117
-     * @param int    $user_id The user id
1118
-     * @param array  $args    Adds context to the cap. Typically the object ID.
1119
-     * @return array   actual users capabilities
1120
-     * @see   wp-includes/capabilities.php
1121
-     * @since 4.5.0
1122
-     */
1123
-    abstract protected function _map_meta_caps(array $caps, string $cap, int $user_id, array $args): array;
994
+	/**
995
+	 * @var EEM_Base
996
+	 */
997
+	protected $_model = null;
998
+
999
+	protected string $_model_name = '';
1000
+
1001
+	public string $meta_cap = '';
1002
+
1003
+	public string $published_cap = '';
1004
+
1005
+	public string $others_cap = '';
1006
+
1007
+	public string $private_cap = '';
1008
+
1009
+
1010
+	/**
1011
+	 * constructor.
1012
+	 * Receives the setup arguments for the map.
1013
+	 *
1014
+	 * @param string $meta_cap       What meta capability is this mapping.
1015
+	 * @param array  $map_values     array {
1016
+	 *                               //array of values that MUST match a count of 4.  It's okay to send an empty string
1017
+	 *                               for capabilities that don't get mapped to.
1018
+	 * @type         $map_values     [0] string A string representing the model name. Required.  String's
1019
+	 *                               should always be used when Menu Maps are registered via the
1020
+	 *                               plugin API as models are not allowed to be instantiated when
1021
+	 *                               in maintenance mode 2 (migrations).
1022
+	 * @type         $map_values     [1] string represents the capability used for published. Optional.
1023
+	 * @type         $map_values     [2] string represents the capability used for "others". Optional.
1024
+	 * @type         $map_values     [3] string represents the capability used for private. Optional.
1025
+	 *                               }
1026
+	 * @throws EE_Error
1027
+	 * @since                        4.5.0
1028
+	 */
1029
+	public function __construct(string $meta_cap, array $map_values)
1030
+	{
1031
+		$this->meta_cap = $meta_cap;
1032
+		// verify there are four args in the $map_values array;
1033
+		if (count($map_values) !== 4) {
1034
+			throw new EE_Error(
1035
+				sprintf(
1036
+					esc_html__(
1037
+						'Incoming $map_values array should have a count of four values in it.  This is what was given: %s',
1038
+						'event_espresso'
1039
+					),
1040
+					'<br>' . print_r($map_values, true)
1041
+				)
1042
+			);
1043
+		}
1044
+		// set properties
1045
+		$this->_model        = null;
1046
+		$this->_model_name   = $map_values[0];
1047
+		$this->published_cap = (string) $map_values[1];
1048
+		$this->others_cap    = (string) $map_values[2];
1049
+		$this->private_cap   = (string) $map_values[3];
1050
+	}
1051
+
1052
+
1053
+	/**
1054
+	 * Makes it so this object stops filtering caps
1055
+	 */
1056
+	public function remove_filters()
1057
+	{
1058
+		remove_filter('map_meta_cap', [$this, 'map_meta_caps']);
1059
+	}
1060
+
1061
+
1062
+	/**
1063
+	 * This method ensures that the $model property is converted from the model name string to a proper EEM_Base class
1064
+	 *
1065
+	 * @return void
1066
+	 * @throws EE_Error
1067
+	 * @throws ReflectionException
1068
+	 * @since 4.5.0
1069
+	 */
1070
+	public function ensure_is_model()
1071
+	{
1072
+		// is it already instantiated?
1073
+		if ($this->_model instanceof EEM_Base) {
1074
+			return;
1075
+		}
1076
+		// ensure model name is string
1077
+		$this->_model_name = (string) $this->_model_name;
1078
+		// error proof if the name has EEM in it
1079
+		$this->_model_name = str_replace('EEM', '', $this->_model_name);
1080
+		$this->_model      = EE_Registry::instance()->load_model($this->_model_name);
1081
+		if (! $this->_model instanceof EEM_Base) {
1082
+			throw new EE_Error(
1083
+				sprintf(
1084
+					esc_html__(
1085
+						'This string passed in to %s to represent a EEM_Base model class was not able to be used to instantiate the class.   Please ensure that the string is a match for the EEM_Base model name (not including the EEM_ part). This was given: %s',
1086
+						'event_espresso'
1087
+					),
1088
+					get_class($this),
1089
+					$this->_model
1090
+				)
1091
+			);
1092
+		}
1093
+	}
1094
+
1095
+
1096
+	/**
1097
+	 * @param array       $caps    actual users capabilities
1098
+	 * @param string|null $cap     initial capability name that is being checked (the "map" key)
1099
+	 * @param int         $user_id The user id
1100
+	 * @param array       $args    Adds context to the cap. Typically the object ID.
1101
+	 * @return array
1102
+	 * @since 4.6.x
1103
+	 * @see   EE_Meta_Capability_Map::_map_meta_caps() for docs on params.
1104
+	 */
1105
+	public function map_meta_caps(array $caps, ?string $cap, int $user_id, array $args): array
1106
+	{
1107
+		return $this->_map_meta_caps($caps, $cap, $user_id, $args);
1108
+	}
1109
+
1110
+
1111
+	/**
1112
+	 * This is the callback for the wp map_meta_caps() function which allows for ensuring certain caps that act as a
1113
+	 * "meta" for other caps ( i.e. ee_edit_event is a meta for ee_edit_others_events ) work as expected.
1114
+	 *
1115
+	 * @param array  $caps    actual users capabilities
1116
+	 * @param string $cap     initial capability name that is being checked (the "map" key)
1117
+	 * @param int    $user_id The user id
1118
+	 * @param array  $args    Adds context to the cap. Typically the object ID.
1119
+	 * @return array   actual users capabilities
1120
+	 * @see   wp-includes/capabilities.php
1121
+	 * @since 4.5.0
1122
+	 */
1123
+	abstract protected function _map_meta_caps(array $caps, string $cap, int $user_id, array $args): array;
1124 1124
 }
1125 1125
 
1126 1126
 
@@ -1135,78 +1135,78 @@  discard block
 block discarded – undo
1135 1135
  */
1136 1136
 class EE_Meta_Capability_Map_Edit extends EE_Meta_Capability_Map
1137 1137
 {
1138
-    /**
1139
-     * This is the callback for the wp map_meta_caps() function which allows for ensuring certain caps that act as a
1140
-     * "meta" for other caps ( i.e. ee_edit_event is a meta for ee_edit_others_events ) work as expected.
1141
-     *
1142
-     * @param array  $caps    actual users capabilities
1143
-     * @param string $cap     initial capability name that is being checked (the "map" key)
1144
-     * @param int    $user_id The user id
1145
-     * @param array  $args    Adds context to the cap. Typically the object ID.
1146
-     * @return array   actual users capabilities
1147
-     * @throws EE_Error
1148
-     * @throws ReflectionException
1149
-     * @since 4.5.0
1150
-     * @see   wp-includes/capabilities.php
1151
-     */
1152
-    protected function _map_meta_caps(array $caps, string $cap, int $user_id, array $args): array
1153
-    {
1154
-        // only process if we're checking our mapped_cap
1155
-        if ($cap !== $this->meta_cap) {
1156
-            return $caps;
1157
-        }
1158
-
1159
-        // okay it is a meta cap so let's first remove that cap from the $caps array.
1160
-        if (($key = array_search($cap, $caps)) !== false) {
1161
-            unset($caps[ $key ]);
1162
-        }
1163
-
1164
-        /** @var EE_Base_Class $obj */
1165
-        $obj = ! empty($args[0]) ? $this->_model->get_one_by_ID($args[0]) : null;
1166
-        // if no obj then let's just do cap
1167
-        if (! $obj instanceof EE_Base_Class) {
1168
-            $caps[] = 'do_not_allow';
1169
-            return $caps;
1170
-        }
1171
-        $caps[] = $cap . 's';
1172
-        if ($obj instanceof EE_CPT_Base) {
1173
-            // if the item author is set and the user is the author...
1174
-            if ($obj->wp_user() && $user_id === $obj->wp_user()) {
1175
-                // if obj is published...
1176
-                if ($obj->status() === 'publish') {
1177
-                    $caps[] = $this->published_cap;
1178
-                }
1179
-            } else {
1180
-                // the user is trying to edit someone else's obj
1181
-                if (! empty($this->others_cap)) {
1182
-                    $caps[] = $this->others_cap;
1183
-                }
1184
-                if (! empty($this->published_cap) && $obj->status() === 'publish') {
1185
-                    $caps[] = $this->published_cap;
1186
-                } elseif (! empty($this->private_cap) && $obj->status() === 'private') {
1187
-                    $caps[] = $this->private_cap;
1188
-                }
1189
-            }
1190
-        } else {
1191
-            // not a cpt object so handled differently
1192
-            $has_cap = false;
1193
-            try {
1194
-                $has_cap = method_exists($obj, 'wp_user')
1195
-                           && $obj->wp_user()
1196
-                           && $obj->wp_user() === $user_id;
1197
-            } catch (Exception $e) {
1198
-                if (WP_DEBUG) {
1199
-                    EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
1200
-                }
1201
-            }
1202
-            if (! $has_cap) {
1203
-                if (! empty($this->others_cap)) {
1204
-                    $caps[] = $this->others_cap;
1205
-                }
1206
-            }
1207
-        }
1208
-        return $caps;
1209
-    }
1138
+	/**
1139
+	 * This is the callback for the wp map_meta_caps() function which allows for ensuring certain caps that act as a
1140
+	 * "meta" for other caps ( i.e. ee_edit_event is a meta for ee_edit_others_events ) work as expected.
1141
+	 *
1142
+	 * @param array  $caps    actual users capabilities
1143
+	 * @param string $cap     initial capability name that is being checked (the "map" key)
1144
+	 * @param int    $user_id The user id
1145
+	 * @param array  $args    Adds context to the cap. Typically the object ID.
1146
+	 * @return array   actual users capabilities
1147
+	 * @throws EE_Error
1148
+	 * @throws ReflectionException
1149
+	 * @since 4.5.0
1150
+	 * @see   wp-includes/capabilities.php
1151
+	 */
1152
+	protected function _map_meta_caps(array $caps, string $cap, int $user_id, array $args): array
1153
+	{
1154
+		// only process if we're checking our mapped_cap
1155
+		if ($cap !== $this->meta_cap) {
1156
+			return $caps;
1157
+		}
1158
+
1159
+		// okay it is a meta cap so let's first remove that cap from the $caps array.
1160
+		if (($key = array_search($cap, $caps)) !== false) {
1161
+			unset($caps[ $key ]);
1162
+		}
1163
+
1164
+		/** @var EE_Base_Class $obj */
1165
+		$obj = ! empty($args[0]) ? $this->_model->get_one_by_ID($args[0]) : null;
1166
+		// if no obj then let's just do cap
1167
+		if (! $obj instanceof EE_Base_Class) {
1168
+			$caps[] = 'do_not_allow';
1169
+			return $caps;
1170
+		}
1171
+		$caps[] = $cap . 's';
1172
+		if ($obj instanceof EE_CPT_Base) {
1173
+			// if the item author is set and the user is the author...
1174
+			if ($obj->wp_user() && $user_id === $obj->wp_user()) {
1175
+				// if obj is published...
1176
+				if ($obj->status() === 'publish') {
1177
+					$caps[] = $this->published_cap;
1178
+				}
1179
+			} else {
1180
+				// the user is trying to edit someone else's obj
1181
+				if (! empty($this->others_cap)) {
1182
+					$caps[] = $this->others_cap;
1183
+				}
1184
+				if (! empty($this->published_cap) && $obj->status() === 'publish') {
1185
+					$caps[] = $this->published_cap;
1186
+				} elseif (! empty($this->private_cap) && $obj->status() === 'private') {
1187
+					$caps[] = $this->private_cap;
1188
+				}
1189
+			}
1190
+		} else {
1191
+			// not a cpt object so handled differently
1192
+			$has_cap = false;
1193
+			try {
1194
+				$has_cap = method_exists($obj, 'wp_user')
1195
+						   && $obj->wp_user()
1196
+						   && $obj->wp_user() === $user_id;
1197
+			} catch (Exception $e) {
1198
+				if (WP_DEBUG) {
1199
+					EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
1200
+				}
1201
+			}
1202
+			if (! $has_cap) {
1203
+				if (! empty($this->others_cap)) {
1204
+					$caps[] = $this->others_cap;
1205
+				}
1206
+			}
1207
+		}
1208
+		return $caps;
1209
+	}
1210 1210
 }
1211 1211
 
1212 1212
 
@@ -1222,24 +1222,24 @@  discard block
 block discarded – undo
1222 1222
  */
1223 1223
 class EE_Meta_Capability_Map_Delete extends EE_Meta_Capability_Map_Edit
1224 1224
 {
1225
-    /**
1226
-     * This is the callback for the wp map_meta_caps() function which allows for ensuring certain caps that act as a
1227
-     * "meta" for other caps ( i.e. ee_edit_event is a meta for ee_edit_others_events ) work as expected.
1228
-     *
1229
-     * @param array  $caps    actual users capabilities
1230
-     * @param string $cap     initial capability name that is being checked (the "map" key)
1231
-     * @param int    $user_id The user id
1232
-     * @param array  $args    Adds context to the cap. Typically the object ID.
1233
-     * @return array   actual users capabilities
1234
-     * @throws EE_Error
1235
-     * @throws ReflectionException
1236
-     * @since 4.5.0
1237
-     * @see   wp-includes/capabilities.php
1238
-     */
1239
-    protected function _map_meta_caps(array $caps, string $cap, int $user_id, array $args): array
1240
-    {
1241
-        return parent::_map_meta_caps($caps, $cap, $user_id, $args);
1242
-    }
1225
+	/**
1226
+	 * This is the callback for the wp map_meta_caps() function which allows for ensuring certain caps that act as a
1227
+	 * "meta" for other caps ( i.e. ee_edit_event is a meta for ee_edit_others_events ) work as expected.
1228
+	 *
1229
+	 * @param array  $caps    actual users capabilities
1230
+	 * @param string $cap     initial capability name that is being checked (the "map" key)
1231
+	 * @param int    $user_id The user id
1232
+	 * @param array  $args    Adds context to the cap. Typically the object ID.
1233
+	 * @return array   actual users capabilities
1234
+	 * @throws EE_Error
1235
+	 * @throws ReflectionException
1236
+	 * @since 4.5.0
1237
+	 * @see   wp-includes/capabilities.php
1238
+	 */
1239
+	protected function _map_meta_caps(array $caps, string $cap, int $user_id, array $args): array
1240
+	{
1241
+		return parent::_map_meta_caps($caps, $cap, $user_id, $args);
1242
+	}
1243 1243
 }
1244 1244
 
1245 1245
 
@@ -1254,79 +1254,79 @@  discard block
 block discarded – undo
1254 1254
  */
1255 1255
 class EE_Meta_Capability_Map_Read extends EE_Meta_Capability_Map
1256 1256
 {
1257
-    /**
1258
-     * This is the callback for the wp map_meta_caps() function which allows for ensuring certain caps that act as a
1259
-     * "meta" for other caps ( i.e. ee_edit_event is a meta for ee_edit_others_events ) work as expected.
1260
-     *
1261
-     * @param array  $caps    actual users capabilities
1262
-     * @param string $cap     initial capability name that is being checked (the "map" key)
1263
-     * @param int    $user_id The user id
1264
-     * @param array  $args    Adds context to the cap. Typically the object ID.
1265
-     * @return array   actual users capabilities
1266
-     * @throws EE_Error
1267
-     * @throws ReflectionException
1268
-     * @since 4.5.0
1269
-     * @see   wp-includes/capabilities.php
1270
-     */
1271
-    protected function _map_meta_caps(array $caps, string $cap, int $user_id, array $args): array
1272
-    {
1273
-        // only process if we're checking our mapped cap;
1274
-        if ($cap !== $this->meta_cap) {
1275
-            return $caps;
1276
-        }
1277
-
1278
-        // okay it is a meta cap so let's first remove that cap from the $caps array.
1279
-        if (($key = array_search($cap, $caps)) !== false) {
1280
-            unset($caps[ $key ]);
1281
-        }
1282
-
1283
-        $obj = ! empty($args[0]) ? $this->_model->get_one_by_ID($args[0]) : null;
1284
-        // if no obj then let's just do cap
1285
-        if (! $obj instanceof EE_Base_Class) {
1286
-            $caps[] = 'do_not_allow';
1287
-            return $caps;
1288
-        }
1289
-
1290
-        $caps[] = $cap . 's';
1291
-        if ($obj instanceof EE_CPT_Base) {
1292
-            $status_obj = get_post_status_object($obj->status());
1293
-            if ($status_obj->public) {
1294
-                return $caps;
1295
-            }
1296
-            // if the item author is set and the user is not the author...
1297
-            if ($obj->wp_user() && $obj->wp_user() !== $user_id) {
1298
-                if (! empty($this->others_cap)) {
1299
-                    $caps[] = $this->others_cap;
1300
-                }
1301
-            }
1302
-            // yes this means that if users created the private post, they are able to see it regardless of private cap.
1303
-            if ($status_obj->private && ! empty($this->private_cap) && $obj->wp_user() !== $user_id) {
1304
-                // the user is trying to view a private object for an object they don't own.
1305
-                $caps[] = $this->private_cap;
1306
-            }
1307
-        } else {
1308
-            // not a cpt object so handled differently
1309
-            $has_cap = false;
1310
-            try {
1311
-                $has_cap = method_exists($obj, 'wp_user')
1312
-                           && $obj->wp_user()
1313
-                           && $obj->wp_user() === $user_id;
1314
-            } catch (Exception $e) {
1315
-                if (WP_DEBUG) {
1316
-                    EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
1317
-                }
1318
-            }
1319
-            if (! $has_cap) {
1320
-                if (! empty($this->private_cap)) {
1321
-                    $caps[] = $this->private_cap;
1322
-                }
1323
-                if (! empty($this->others_cap)) {
1324
-                    $caps[] = $this->others_cap;
1325
-                }
1326
-            }
1327
-        }
1328
-        return $caps;
1329
-    }
1257
+	/**
1258
+	 * This is the callback for the wp map_meta_caps() function which allows for ensuring certain caps that act as a
1259
+	 * "meta" for other caps ( i.e. ee_edit_event is a meta for ee_edit_others_events ) work as expected.
1260
+	 *
1261
+	 * @param array  $caps    actual users capabilities
1262
+	 * @param string $cap     initial capability name that is being checked (the "map" key)
1263
+	 * @param int    $user_id The user id
1264
+	 * @param array  $args    Adds context to the cap. Typically the object ID.
1265
+	 * @return array   actual users capabilities
1266
+	 * @throws EE_Error
1267
+	 * @throws ReflectionException
1268
+	 * @since 4.5.0
1269
+	 * @see   wp-includes/capabilities.php
1270
+	 */
1271
+	protected function _map_meta_caps(array $caps, string $cap, int $user_id, array $args): array
1272
+	{
1273
+		// only process if we're checking our mapped cap;
1274
+		if ($cap !== $this->meta_cap) {
1275
+			return $caps;
1276
+		}
1277
+
1278
+		// okay it is a meta cap so let's first remove that cap from the $caps array.
1279
+		if (($key = array_search($cap, $caps)) !== false) {
1280
+			unset($caps[ $key ]);
1281
+		}
1282
+
1283
+		$obj = ! empty($args[0]) ? $this->_model->get_one_by_ID($args[0]) : null;
1284
+		// if no obj then let's just do cap
1285
+		if (! $obj instanceof EE_Base_Class) {
1286
+			$caps[] = 'do_not_allow';
1287
+			return $caps;
1288
+		}
1289
+
1290
+		$caps[] = $cap . 's';
1291
+		if ($obj instanceof EE_CPT_Base) {
1292
+			$status_obj = get_post_status_object($obj->status());
1293
+			if ($status_obj->public) {
1294
+				return $caps;
1295
+			}
1296
+			// if the item author is set and the user is not the author...
1297
+			if ($obj->wp_user() && $obj->wp_user() !== $user_id) {
1298
+				if (! empty($this->others_cap)) {
1299
+					$caps[] = $this->others_cap;
1300
+				}
1301
+			}
1302
+			// yes this means that if users created the private post, they are able to see it regardless of private cap.
1303
+			if ($status_obj->private && ! empty($this->private_cap) && $obj->wp_user() !== $user_id) {
1304
+				// the user is trying to view a private object for an object they don't own.
1305
+				$caps[] = $this->private_cap;
1306
+			}
1307
+		} else {
1308
+			// not a cpt object so handled differently
1309
+			$has_cap = false;
1310
+			try {
1311
+				$has_cap = method_exists($obj, 'wp_user')
1312
+						   && $obj->wp_user()
1313
+						   && $obj->wp_user() === $user_id;
1314
+			} catch (Exception $e) {
1315
+				if (WP_DEBUG) {
1316
+					EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
1317
+				}
1318
+			}
1319
+			if (! $has_cap) {
1320
+				if (! empty($this->private_cap)) {
1321
+					$caps[] = $this->private_cap;
1322
+				}
1323
+				if (! empty($this->others_cap)) {
1324
+					$caps[] = $this->others_cap;
1325
+				}
1326
+			}
1327
+		}
1328
+		return $caps;
1329
+	}
1330 1330
 }
1331 1331
 
1332 1332
 
@@ -1342,53 +1342,53 @@  discard block
 block discarded – undo
1342 1342
  */
1343 1343
 class EE_Meta_Capability_Map_Messages_Cap extends EE_Meta_Capability_Map
1344 1344
 {
1345
-    /**
1346
-     * This is the callback for the wp map_meta_caps() function which allows for ensuring certain caps that act as a
1347
-     * "meta" for other caps ( i.e. ee_edit_event is a meta for ee_edit_others_events ) work as expected.
1348
-     *
1349
-     * @param array  $caps    actual users capabilities
1350
-     * @param string $cap     initial capability name that is being checked (the "map" key)
1351
-     * @param int    $user_id The user id
1352
-     * @param array  $args    Adds context to the cap. Typically the object ID.
1353
-     * @return array   actual users capabilities
1354
-     * @throws EE_Error
1355
-     * @throws ReflectionException
1356
-     * @since 4.5.0
1357
-     * @see   wp-includes/capabilities.php
1358
-     */
1359
-    protected function _map_meta_caps(array $caps, string $cap, int $user_id, array $args): array
1360
-    {
1361
-        // only process if we're checking our mapped_cap
1362
-        if ($cap !== $this->meta_cap) {
1363
-            return $caps;
1364
-        }
1365
-
1366
-        // okay it is a meta cap so let's first remove that cap from the $caps array.
1367
-        if (($key = array_search($cap, $caps)) !== false) {
1368
-            unset($caps[ $key ]);
1369
-        }
1370
-
1371
-        $obj = ! empty($args[0]) ? $this->_model->get_one_by_ID($args[0]) : null;
1372
-        // if no obj then let's just do cap
1373
-        if (! $obj instanceof EE_Message_Template_Group) {
1374
-            $caps[] = 'do_not_allow';
1375
-            return $caps;
1376
-        }
1377
-        $caps[]    = $cap . 's';
1378
-        $is_global = $obj->is_global();
1379
-        if ($obj->wp_user() && $obj->wp_user() === $user_id) {
1380
-            if ($is_global) {
1381
-                $caps[] = $this->private_cap;
1382
-            }
1383
-        } else {
1384
-            if ($is_global) {
1385
-                $caps[] = $this->private_cap;
1386
-            } else {
1387
-                $caps[] = $this->others_cap;
1388
-            }
1389
-        }
1390
-        return $caps;
1391
-    }
1345
+	/**
1346
+	 * This is the callback for the wp map_meta_caps() function which allows for ensuring certain caps that act as a
1347
+	 * "meta" for other caps ( i.e. ee_edit_event is a meta for ee_edit_others_events ) work as expected.
1348
+	 *
1349
+	 * @param array  $caps    actual users capabilities
1350
+	 * @param string $cap     initial capability name that is being checked (the "map" key)
1351
+	 * @param int    $user_id The user id
1352
+	 * @param array  $args    Adds context to the cap. Typically the object ID.
1353
+	 * @return array   actual users capabilities
1354
+	 * @throws EE_Error
1355
+	 * @throws ReflectionException
1356
+	 * @since 4.5.0
1357
+	 * @see   wp-includes/capabilities.php
1358
+	 */
1359
+	protected function _map_meta_caps(array $caps, string $cap, int $user_id, array $args): array
1360
+	{
1361
+		// only process if we're checking our mapped_cap
1362
+		if ($cap !== $this->meta_cap) {
1363
+			return $caps;
1364
+		}
1365
+
1366
+		// okay it is a meta cap so let's first remove that cap from the $caps array.
1367
+		if (($key = array_search($cap, $caps)) !== false) {
1368
+			unset($caps[ $key ]);
1369
+		}
1370
+
1371
+		$obj = ! empty($args[0]) ? $this->_model->get_one_by_ID($args[0]) : null;
1372
+		// if no obj then let's just do cap
1373
+		if (! $obj instanceof EE_Message_Template_Group) {
1374
+			$caps[] = 'do_not_allow';
1375
+			return $caps;
1376
+		}
1377
+		$caps[]    = $cap . 's';
1378
+		$is_global = $obj->is_global();
1379
+		if ($obj->wp_user() && $obj->wp_user() === $user_id) {
1380
+			if ($is_global) {
1381
+				$caps[] = $this->private_cap;
1382
+			}
1383
+		} else {
1384
+			if ($is_global) {
1385
+				$caps[] = $this->private_cap;
1386
+			} else {
1387
+				$caps[] = $this->others_cap;
1388
+			}
1389
+		}
1390
+		return $caps;
1391
+	}
1392 1392
 }
1393 1393
 
1394 1394
 
@@ -1404,43 +1404,43 @@  discard block
 block discarded – undo
1404 1404
  */
1405 1405
 class EE_Meta_Capability_Map_Registration_Form_Cap extends EE_Meta_Capability_Map
1406 1406
 {
1407
-    /**
1408
-     * This is the callback for the wp map_meta_caps() function which allows for ensuring certain caps that act as a
1409
-     * "meta" for other caps ( i.e. ee_edit_event is a meta for ee_edit_others_events ) work as expected.
1410
-     *
1411
-     * @param array  $caps    actual users capabilities
1412
-     * @param string $cap     initial capability name that is being checked (the "map" key)
1413
-     * @param int    $user_id The user id
1414
-     * @param array  $args    Adds context to the cap. Typically the object ID.
1415
-     * @return array   actual users capabilities
1416
-     * @throws EE_Error
1417
-     * @throws EE_Error
1418
-     * @throws ReflectionException
1419
-     * @since 4.5.0
1420
-     * @see   wp-includes/capabilities.php
1421
-     */
1422
-    protected function _map_meta_caps(array $caps, string $cap, int $user_id, array $args): array
1423
-    {
1424
-        // only process if we're checking our mapped_cap
1425
-        if ($cap !== $this->meta_cap) {
1426
-            return $caps;
1427
-        }
1428
-        // okay it is a meta cap so let's first remove that cap from the $caps array.
1429
-        if (($key = array_search($cap, $caps)) !== false) {
1430
-            unset($caps[ $key ]);
1431
-        }
1432
-        $obj = ! empty($args[0]) ? $this->_model->get_one_by_ID($args[0]) : null;
1433
-        // if no obj then let's just do cap
1434
-        if (! $obj instanceof EE_Base_Class) {
1435
-            $caps[] = 'do_not_allow';
1436
-            return $caps;
1437
-        }
1438
-        $caps[]    = $cap . 's';
1439
-        $is_system = $obj instanceof EE_Question_Group ? $obj->system_group() : false;
1440
-        $is_system = $obj instanceof EE_Question ? $obj->is_system_question() : $is_system;
1441
-        if ($is_system) {
1442
-            $caps[] = $this->private_cap;
1443
-        }
1444
-        return $caps;
1445
-    }
1407
+	/**
1408
+	 * This is the callback for the wp map_meta_caps() function which allows for ensuring certain caps that act as a
1409
+	 * "meta" for other caps ( i.e. ee_edit_event is a meta for ee_edit_others_events ) work as expected.
1410
+	 *
1411
+	 * @param array  $caps    actual users capabilities
1412
+	 * @param string $cap     initial capability name that is being checked (the "map" key)
1413
+	 * @param int    $user_id The user id
1414
+	 * @param array  $args    Adds context to the cap. Typically the object ID.
1415
+	 * @return array   actual users capabilities
1416
+	 * @throws EE_Error
1417
+	 * @throws EE_Error
1418
+	 * @throws ReflectionException
1419
+	 * @since 4.5.0
1420
+	 * @see   wp-includes/capabilities.php
1421
+	 */
1422
+	protected function _map_meta_caps(array $caps, string $cap, int $user_id, array $args): array
1423
+	{
1424
+		// only process if we're checking our mapped_cap
1425
+		if ($cap !== $this->meta_cap) {
1426
+			return $caps;
1427
+		}
1428
+		// okay it is a meta cap so let's first remove that cap from the $caps array.
1429
+		if (($key = array_search($cap, $caps)) !== false) {
1430
+			unset($caps[ $key ]);
1431
+		}
1432
+		$obj = ! empty($args[0]) ? $this->_model->get_one_by_ID($args[0]) : null;
1433
+		// if no obj then let's just do cap
1434
+		if (! $obj instanceof EE_Base_Class) {
1435
+			$caps[] = 'do_not_allow';
1436
+			return $caps;
1437
+		}
1438
+		$caps[]    = $cap . 's';
1439
+		$is_system = $obj instanceof EE_Question_Group ? $obj->system_group() : false;
1440
+		$is_system = $obj instanceof EE_Question ? $obj->is_system_question() : $is_system;
1441
+		if ($is_system) {
1442
+			$caps[] = $this->private_cap;
1443
+		}
1444
+		return $caps;
1445
+	}
1446 1446
 }
Please login to merge, or discard this patch.