array( 'title' => $area['label'], 'slug' => $area['area'], 'view' => array( 'filters' => array( array( 'field' => 'area', 'operator' => 'is', 'value' => $area['area'], 'isLocked' => true, ), ), ), ); } $form = array( 'layout' => array( 'type' => 'panel' ), 'fields' => array( array( 'id' => 'last_edited_date', 'layout' => array( 'type' => 'panel', 'labelPosition' => 'none', ), ), 'revisions', ), ); $data->set( array( 'default_view' => $default_view, 'default_layouts' => $default_layouts, 'view_list' => $view_list, 'form' => $form, ), 1 ); return $data; } /** * Provides the view configuration for the `wp_template` post type. * * @param Gutenberg_View_Config_Data $data The view configuration container for the entity. * @return Gutenberg_View_Config_Data The updated view configuration container. */ function _gutenberg_get_entity_view_config_posttype_wp_template( $data ) { $default_view = array( 'type' => 'grid', 'perPage' => 20, 'sort' => array( 'field' => 'title', 'direction' => 'asc', ), 'titleField' => 'title', 'descriptionField' => 'description', 'mediaField' => 'preview', 'fields' => array( 'author', 'active', 'slug', 'theme' ), 'filters' => array(), 'showMedia' => true, ); $default_layouts = array( 'table' => array( 'showMedia' => false ), 'grid' => array( 'showMedia' => true ), 'list' => array( 'showMedia' => false ), ); $view_list = array( array( 'title' => __( 'All templates', 'gutenberg' ), 'slug' => 'all', ), ); $templates = get_block_templates( array(), 'wp_template' ); // Collect unique authors, tracking whether they come from a registered // source (theme, plugin, site) so we can sort those before user ones. $seen_authors = array(); $registered_authors = array(); $user_authors = array(); foreach ( $templates as $template ) { /* * Determine the original source of the template ('theme', 'plugin', * 'site', or 'user'). */ $original_source = 'user'; if ( 'wp_template' === $template->type || 'wp_template_part' === $template->type ) { if ( $template->has_theme_file && ( 'theme' === $template->origin || ( empty( $template->origin ) && in_array( $template->source, array( 'theme', 'custom', ), true ) ) ) ) { /* * Added by theme. * Template originally provided by a theme, but customized by a user. * Templates originally didn't have the 'origin' field so identify * older customized templates by checking for no origin and a 'theme' * or 'custom' source. */ $original_source = 'theme'; } elseif ( 'plugin' === $template->origin ) { // Added by plugin. $original_source = 'plugin'; } elseif ( empty( $template->has_theme_file ) && 'custom' === $template->source && empty( $template->author ) ) { /* * Added by site. * Template was created from scratch, but has no author. Author support * was only added to templates in WordPress 5.9. Fallback to showing the * site logo and title. */ $original_source = 'site'; } } // Determine a human readable text for the author of the template. $author_text = ''; switch ( $original_source ) { case 'theme': $theme_name = wp_get_theme( $template->theme )->get( 'Name' ); $author_text = empty( $theme_name ) ? $template->theme : $theme_name; break; case 'plugin': if ( ! function_exists( 'get_plugins' ) ) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } $plugin_name = ''; if ( isset( $template->plugin ) ) { $plugins = wp_get_active_and_valid_plugins(); foreach ( $plugins as $plugin_file ) { $plugin_basename = plugin_basename( $plugin_file ); list( $plugin_slug, ) = explode( '/', $plugin_basename ); if ( $plugin_slug === $template->plugin ) { $plugin_data = get_plugin_data( $plugin_file ); if ( ! empty( $plugin_data['Name'] ) ) { $plugin_name = $plugin_data['Name']; } break; } } } /* * Fall back to the theme name if the plugin is not defined. That's needed to keep backwards * compatibility with templates that were registered before the plugin attribute was added. */ if ( '' === $plugin_name ) { $plugins = get_plugins(); $plugin_basename = plugin_basename( sanitize_text_field( $template->theme . '.php' ) ); if ( isset( $plugins[ $plugin_basename ] ) && isset( $plugins[ $plugin_basename ]['Name'] ) ) { $plugin_name = $plugins[ $plugin_basename ]['Name']; } else { $plugin_name = $template->plugin ?? $template->theme; } } $author_text = $plugin_name; break; case 'site': $author_text = get_bloginfo( 'name' ); break; case 'user': $author = get_user_by( 'id', $template->author ); if ( ! $author ) { $author_text = __( 'Unknown author', 'gutenberg' ); } else { $author_text = $author->get( 'display_name' ); } break; } if ( ! empty( $author_text ) && ! isset( $seen_authors[ $author_text ] ) ) { $seen_authors[ $author_text ] = true; $entry = array( 'title' => $author_text, 'slug' => $author_text, 'view' => array( 'filters' => array( array( 'field' => 'author', 'operator' => 'is', 'value' => $author_text, 'isLocked' => true, ), ), ), ); if ( 'user' === $original_source ) { $user_authors[] = $entry; } else { $registered_authors[] = $entry; } } } $form = array( 'layout' => array( 'type' => 'panel' ), 'fields' => array( array( 'id' => 'description', 'layout' => array( 'type' => 'panel', 'labelPosition' => 'top', ), ), array( 'id' => 'description_readonly', 'layout' => array( 'type' => 'regular', 'labelPosition' => 'none', ), ), array( 'id' => 'last_edited_date', 'layout' => array( 'type' => 'panel', 'labelPosition' => 'none', ), ), 'revisions', // The following fields are only meaningful in the `home`/`index` // template summary. They edit other entities (`root/site` and the // posts page); the editor merges those records into the form data // under a namespace and controls when the fields are shown. 'posts_page_title', 'posts_per_page', 'default_comment_status', ), ); $data->set( array( 'default_view' => $default_view, 'default_layouts' => $default_layouts, 'view_list' => array_merge( $view_list, $registered_authors, $user_authors ), 'form' => $form, ), 1 ); return $data; } /** * Registers the Gutenberg entity view configuration filters, overriding any * defaults that WordPress core may have already registered. * * Core registers its own `_wp_get_entity_view_config_posttype_*` callbacks on * the shared `get_entity_view_config_{$kind}_{$name}` hooks. The Gutenberg * plugin always ships the newest configuration, so it removes the core defaults * and installs its own `_gutenberg_*` callbacks instead. * * Post types without a dedicated `_gutenberg_*` callback (e.g. `post`) still have * the core callback removed so they fall back to the default configuration * built in gutenberg_get_entity_view_config(). * * This runs on `init` rather than at file include time so that the core * defaults are guaranteed to be registered first, regardless of whether core * registers them at include time or lazily on a hook. */ function gutenberg_register_entity_view_config_filters() { $post_types = array( 'page', 'post', 'wp_block', 'wp_template_part', 'wp_template' ); foreach ( $post_types as $post_type ) { $hook = gutenberg_get_entity_view_config_hook_name( 'postType', $post_type ); $wp_callback = "_wp_get_entity_view_config_posttype_{$post_type}"; $gb_callback = "_gutenberg_get_entity_view_config_posttype_{$post_type}"; // has_filter() returns the priority the callback was registered at. $wp_priority = has_filter( $hook, $wp_callback ); if ( false !== $wp_priority ) { remove_filter( $hook, $wp_callback, $wp_priority ); } if ( function_exists( $gb_callback ) ) { // Base definitions run before the default priority, so third-party // callbacks registered at the default compose on top of them // regardless of registration order. add_filter( $hook, $gb_callback, 5, 1 ); } } } add_action( 'init', 'gutenberg_register_entity_view_config_filters' ); ent->type; $this->response = 'WPForms Square: ' . $this->event_type . ' event received.'; $processed = $this->process_event( $event ); $this->response_code = $processed ? 200 : 202; $this->respond(); } catch ( Exception $e ) { $this->response = $e->getMessage(); $this->response_code = $e instanceof BadMethodCallException ? 501 : 500; $this->respond(); } } /** * Get webhook signature. * * @since 1.9.5 * * @throws RuntimeException When Square signature is not set. * * @return string */ private function get_webhook_signature(): string { if ( ! isset( $_SERVER['HTTP_X_SQUARE_HMACSHA256_SIGNATURE'] ) ) { throw new RuntimeException( 'Square signature is not set.' ); } return sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_SQUARE_HMACSHA256_SIGNATURE'] ) ); } /** * Get webhook signing secret. * * @since 1.9.5 * * @throws RuntimeException When webhook signing secret is not set. * * @return string */ private function get_webhook_signing_secret(): string { $secret = wpforms_setting( 'square-webhooks-secret-' . Helpers::get_mode() ); if ( empty( $secret ) ) { throw new RuntimeException( 'Webhook signing secret is not set.' ); } return $secret; } /** * Process Square event. * * @since 1.9.5 * * @param object $event Square event. * * @return bool True if event has handling class, false otherwise. */ private function process_event( $event ): bool { $webhooks = self::get_event_whitelist(); // Event can't be handled. if ( ! isset( $webhooks[ $event->type ] ) || ! class_exists( $webhooks[ $event->type ] ) ) { return false; } $handler = new $webhooks[ $event->type ](); $handler->setup( $event ); return $handler->handle(); } /** * Get event allowlist. * * @since 1.9.5 * * @return array */ private static function get_event_whitelist(): array { return [ 'refund.updated' => Webhooks\RefundUpdated::class, 'payment.updated' => Webhooks\PaymentUpdated::class, 'payment.created' => Webhooks\PaymentCreated::class, 'subscription.created' => Webhooks\SubscriptionCreated::class, 'subscription.updated' => Webhooks\SubscriptionUpdated::class, ]; } /** * Check if rest verification is requested. * * @since 1.9.5 * * @return bool */ private function is_rest_verification(): bool { // phpcs:ignore WordPress.Security.NonceVerification.Recommended return isset( $_GET['verify'] ) && $_GET['verify'] === '1'; } /** * Respond to the request. * * @since 1.9.5 */ private function respond() { $this->log_webhook(); wp_die( esc_html( $this->response ), '', (int) $this->response_code ); } /** * Log webhook request. * * @since 1.9.5 */ private function log_webhook() { // log only if WP_DEBUG_LOG and WPFORMS_WEBHOOKS_DEBUG are set to true. if ( ! defined( 'WPFORMS_WEBHOOKS_DEBUG' ) || ! WPFORMS_WEBHOOKS_DEBUG || ! defined( 'WP_DEBUG_LOG' ) || ! WP_DEBUG_LOG ) { return; } // If it is set to explicitly display logs on output, return: this would make response to Square malformed. if ( defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG_DISPLAY ) { return; } $webhook_log = maybe_serialize( [ 'event_type' => $this->event_type, 'response_code' => $this->response_code, 'response' => $this->response, 'payload' => $this->payload, ] ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log error_log( $webhook_log ); } /** * Get a webhook events list. * * @since 1.9.5 * * @return array */ public static function get_webhooks_events_list(): array { return array_keys( self::get_event_whitelist() ); } }