=> array( 'force' => array( 'type' => 'boolean', 'default' => false, 'description' => __( 'Required to be true, as revisions do not support trashing.' ), ), ), ), 'schema' => array( $this, 'get_public_item_schema' ), ) ); } /** * Gets the parent post, if the template ID is valid. * * @since 6.4.0 * * @param string $parent_template_id Supplied ID. * @return WP_Post|WP_Error Post object if ID is valid, WP_Error otherwise. */ protected function get_parent( $parent_template_id ) { $template = get_block_template( $parent_template_id, $this->parent_post_type ); if ( ! $template ) { return new WP_Error( 'rest_post_invalid_parent', __( 'Invalid template parent ID.' ), array( 'status' => WP_Http::NOT_FOUND ) ); } $parent_post_id = isset( $template->wp_id ) ? (int) $template->wp_id : 0; if ( $parent_post_id <= 0 ) { return new WP_Error( 'rest_invalid_template', __( 'Templates based on theme files can\'t have revisions.' ), array( 'status' => WP_Http::BAD_REQUEST ) ); } return get_post( $template->wp_id ); } /** * Prepares the item for the REST response. * * @since 6.4.0 * * @param WP_Post $item Post revision object. * @param WP_REST_Request $request Request object. * @return WP_REST_Response Response object. */ public function prepare_item_for_response( $item, $request ) { $template = _build_block_template_result_from_post( $item ); $response = $this->parent_controller->prepare_item_for_response( $template, $request ); // Don't prepare the response body for HEAD requests. if ( $request->is_method( 'HEAD' ) ) { return $response; } $fields = $this->get_fields_for_response( $request ); $data = $response->get_data(); if ( in_array( 'parent', $fields, true ) ) { $data['parent'] = (int) $item->post_parent; } $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; $data = $this->filter_response_by_context( $data, $context ); // Wrap the data in a response object. $response = new WP_REST_Response( $data ); if ( rest_is_field_included( '_links', $fields ) || rest_is_field_included( '_embedded', $fields ) ) { $links = $this->prepare_links( $template ); $response->add_links( $links ); } return $response; } /** * Checks if a given request has access to delete a revision. * * @since 6.4.0 * * @param WP_REST_Request $request Full details about the request. * @return true|WP_Error True if the request has access to delete the item, WP_Error object otherwise. */ public function delete_item_permissions_check( $request ) { $parent = $this->get_parent( $request['parent'] ); if ( is_wp_error( $parent ) ) { return $parent; } if ( ! current_user_can( 'delete_post', $parent->ID ) ) { return new WP_Error( 'rest_cannot_delete', __( 'Sorry, you are not allowed to delete revisions of this post.' ), array( 'status' => rest_authorization_required_code() ) ); } $revision = $this->get_revision( $request['id'] ); if ( is_wp_error( $revision ) ) { return $revision; } if ( ! current_user_can( 'edit_theme_options' ) ) { return new WP_Error( 'rest_cannot_delete', __( 'Sorry, you are not allowed to delete this revision.' ), array( 'status' => rest_authorization_required_code() ) ); } return true; } /** * Prepares links for the request. * * @since 6.4.0 * * @param WP_Block_Template $template Template. * @return array Links for the given post. */ protected function prepare_links( $template ) { $links = array( 'self' => array( 'href' => rest_url( sprintf( '/%s/%s/%s/%s/%d', $this->namespace, $this->parent_base, $template->id, $this->rest_base, $template->wp_id ) ), ), 'parent' => array( 'href' => rest_url( sprintf( '/%s/%s/%s', $this->namespace, $this->parent_base, $template->id ) ), ), ); return $links; } /** * Retrieves the item's schema, conforming to JSON Schema. * * @since 6.4.0 * * @return array Item schema data. */ public function get_item_schema() { if ( $this->schema ) { return $this->add_additional_fields_schema( $this->schema ); } $schema = $this->parent_controller->get_item_schema(); $schema['properties']['parent'] = array( 'description' => __( 'The ID for the parent of the revision.' ), 'type' => 'integer', 'context' => array( 'view', 'edit', 'embed' ), ); $this->schema = $schema; return $this->add_additional_fields_schema( $this->schema ); } } n preload_settings( array $settings ): array { // We only preload settings in the WP admin. if ( ! is_admin() ) { return $settings; } // Add the business location country to the settings. if ( ! isset( $settings[ Payments::PAYMENTS_NOX_PROFILE_KEY ] ) ) { $settings[ Payments::PAYMENTS_NOX_PROFILE_KEY ] = array(); } $settings[ Payments::PAYMENTS_NOX_PROFILE_KEY ]['business_country_code'] = $this->payments->get_country(); return $settings; } /** * Adds promo note IDs to the list of allowed ones. * * @param array $promo_notes Allowed promo note IDs. * * @return array The updated list of allowed promo note IDs. */ public function add_allowed_promo_notes( array $promo_notes = array() ): array { try { $providers = $this->payments->get_payment_providers( $this->payments->get_country() ); } catch ( Exception $e ) { // In case of an error, bail. return $promo_notes; } // Add all incentive promo IDs to the allowed promo notes list. foreach ( $providers as $provider ) { if ( ! empty( $provider['_incentive']['promo_id'] ) ) { $promo_notes[] = $provider['_incentive']['promo_id']; } } return $promo_notes; } /** * Alter the Payments tab sections under certain conditions. * * @param array $sections The payments/checkout tab sections. * * @return array The filtered sections. */ public function handle_sections( array $sections ): array { global $current_section; // For WooPayments and offline payment methods settings pages, we don't want any section navigation. if ( in_array( $current_section, array( 'woocommerce_payments', WC_Gateway_BACS::ID, WC_Gateway_Cheque::ID, WC_Gateway_COD::ID ), true ) ) { return array(); } return $sections; } /** * Check if the store has any enabled gateways (including offline payment methods). * * @return bool True if the store has any enabled gateways, false otherwise. */ private function store_has_enabled_gateways(): bool { $gateways = WC()->payment_gateways->get_available_payment_gateways(); $enabled_gateways = array_filter( $gateways, function ( $gateway ) { return 'yes' === $gateway->enabled; } ); return ! empty( $enabled_gateways ); } /** * Check if the store has any payment providers that have an active incentive. * * @return bool True if the store has providers with an active incentive. */ private function store_has_providers_with_incentive(): bool { try { $providers = $this->payments->get_payment_providers( $this->payments->get_country() ); } catch ( Exception $e ) { // In case of an error, just return false. return false; } // Go through the providers and check if any of them have a "prominently" visible incentive (i.e., modal or banner). foreach ( $providers as $provider ) { if ( empty( $provider['_incentive'] ) ) { continue; } $dismissals = $provider['_incentive']['_dismissals'] ?? array(); // If there are no dismissals at all, the incentive is prominently visible. if ( empty( $dismissals ) ) { return true; } // First, we check to see if the incentive was dismissed in the banner context. // The banner context has the lowest priority, so if it was dismissed, we don't need to check the modal context. // If the banner is dismissed, there is no prominent incentive. $is_dismissed_banner = ! empty( array_filter( $dismissals, function ( $dismissal ) { return isset( $dismissal['context'] ) && 'wc_settings_payments__banner' === $dismissal['context']; } ) ); if ( $is_dismissed_banner ) { continue; } // In case an incentive uses the modal surface also (like the WooPayments Switch incentive), // we rely on the fact that the modal falls back to the banner, once dismissed, after 30 days. // @see here's its frontend "brother" in client/admin/client/settings-payments/settings-payments-main.tsx. $is_dismissed_modal = ! empty( array_filter( $dismissals, function ( $dismissal ) { return isset( $dismissal['context'] ) && 'wc_settings_payments__modal' === $dismissal['context']; } ) ); // If there are no modal dismissals, the incentive is still visible. if ( ! $is_dismissed_modal ) { return true; } $is_dismissed_modal_more_than_30_days_ago = ! empty( array_filter( $dismissals, function ( $dismissal ) { return isset( $dismissal['context'], $dismissal['timestamp'] ) && 'wc_settings_payments__modal' === $dismissal['context'] && $dismissal['timestamp'] < strtotime( '-30 days' ); } ) ); // If the modal was dismissed less than 30 days ago, there is no prominent incentive (aka the banner is not shown). if ( ! $is_dismissed_modal_more_than_30_days_ago ) { continue; } // The modal was dismissed more than 30 days ago, so the banner is visible. return true; } return false; } /** * Check if the WooPayments account is onboarded. * * @return boolean */ private function is_woopayments_account_onboarded(): bool { // If WooPayments is active right now, we will not get to this point since the plugin is active check is done first. if ( ! class_exists( '\WC_Payments' ) ) { return false; } $account_data = get_option( 'wcpay_account_data', array() ); if ( empty( $account_data['data']['account_id'] ) ) { return false; } if ( empty( $account_data['data']['details_submitted'] ) ) { return false; } // We consider the store to have WooPayments account connected if account data in the WooPayments account cache // contains details_submitted = true entry. This implies that WooPayments was connected. return $account_data['data']['details_submitted']; } }