setup_hooks(); } /** * Set up hooks for various purposes. * * This method adds hooks for different purposes as needed. * * @since 1.0.0 * * @return void */ protected function setup_hooks(): void { add_action( 'admin_print_scripts', array( $this, 'add_global_object' ), PHP_INT_MIN ); add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) ); add_action( 'enqueue_block_assets', array( $this, 'block_enqueue_scripts' ) ); add_action( 'enqueue_block_editor_assets', array( $this, 'editor_enqueue_scripts' ) ); add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_variation_assets' ) ); add_action( 'init', array( $this, 'register_variation_assets' ) ); add_action( 'wp_head', array( $this, 'add_global_object' ), PHP_INT_MIN ); // Set priority to 11 to not conflict with media modal. add_action( 'admin_footer', array( $this, 'event_communication_modal' ), 11 ); add_filter( 'render_block', array( $this, 'maybe_enqueue_styles' ), 10, 2 ); } /** * Localize the global GatherPress JavaScript object for use in build scripts. * * This method generates JavaScript code to create a global 'GatherPress' object containing localized data. * This object is made available for use in JavaScript build scripts, enabling seamless integration of * server-side data with client-side functionality. * * @since 1.0.0 * * @return void */ public function add_global_object(): void { ?> get_asset_data( 'utility_style' ); wp_register_style( 'gatherpress-utility-style', $this->build . 'utility_style.css', $asset['dependencies'], $asset['version'] ); } /** * Conditionally enqueue utility styles if GatherPress blocks are rendered. * * @since 1.0.0 * * @param string $block_content The block content. * @param array $block The block settings. * @return string The block content. */ public function maybe_enqueue_styles( string $block_content, array $block ): string { if ( isset( $block['blockName'] ) && str_contains( $block['blockName'], 'gatherpress/' ) ) { wp_enqueue_style( 'gatherpress-utility-style' ); } return $block_content; } /** * Enqueue backend styles and scripts for the WordPress admin. * * This method is responsible for enqueuing backend styles and scripts necessary for the * proper functioning of the WordPress admin area. It conditionally loads assets based on * the admin page's context, such as post editing, settings pages, or general admin pages. * * @since 1.0.0 * * @param string $hook The name of the current admin page. * @return void */ public function admin_enqueue_scripts( string $hook ): void { $asset = $this->get_asset_data( 'admin_style' ); wp_enqueue_style( 'gatherpress-admin-style', $this->build . 'admin_style.css', $asset['dependencies'], $asset['version'] ); if ( 'post-new.php' === $hook || 'post.php' === $hook ) { $asset = $this->get_asset_data( 'panels' ); wp_enqueue_script( 'gatherpress-panels', $this->build . 'panels.js', $asset['dependencies'], $asset['version'], true ); wp_set_script_translations( 'gatherpress-panels', 'gatherpress' ); $asset = $this->get_asset_data( 'modals' ); wp_enqueue_script( 'gatherpress-modals', $this->build . 'modals.js', $asset['dependencies'], $asset['version'], true ); wp_set_script_translations( 'gatherpress-modals', 'gatherpress' ); } $settings = Settings::get_instance(); $setting_hooks = array_map( function ( $key ) { return sprintf( 'gatherpress_event_page_%s', Utility::prefix_key( sanitize_key( $key ) ) ); }, array_keys( $settings->get_sub_pages() ) ); if ( in_array( $hook, $setting_hooks, true ) ) { // Need to load block styling for some dynamic fields. wp_enqueue_style( 'wp-edit-blocks' ); $asset = $this->get_asset_data( 'settings_style' ); wp_enqueue_style( 'gatherpress-settings-style', $this->build . 'style-settings_style.css', $asset['dependencies'], $asset['version'] ); $asset = $this->get_asset_data( 'settings' ); wp_enqueue_script( 'gatherpress-settings', $this->build . 'settings.js', $asset['dependencies'], $asset['version'], true ); wp_set_script_translations( 'gatherpress-settings', 'gatherpress' ); } if ( 'profile.php' === $hook ) { $asset = $this->get_asset_data( 'profile' ); wp_enqueue_script( 'gatherpress-profile', $this->build . 'profile.js', $asset['dependencies'], $asset['version'], true ); wp_set_script_translations( 'gatherpress-profile', 'gatherpress' ); } } /** * Enqueue backend styles and scripts for the WordPress block editor. * * This method is responsible for enqueuing backend styles and scripts required for the proper functioning * of the WordPress block editor (Gutenberg). It ensures that the editor has access to necessary assets. * * @since 1.0.0 * * @return void */ public function editor_enqueue_scripts(): void { $asset = $this->get_asset_data( 'editor' ); wp_enqueue_script( 'gatherpress-editor', $this->build . 'editor.js', $asset['dependencies'], $asset['version'], true ); wp_enqueue_style( 'gatherpress-utility-style' ); wp_add_inline_script( 'gatherpress-editor', 'GatherPress.misc.timezoneChoices = ' . wp_json_encode( Utility::timezone_choices() ), 'before' ); wp_set_script_translations( 'gatherpress-editor', 'gatherpress' ); } /** * Adds markup to the event edit page for storing the communication modal. * * This method inserts HTML markup on the event edit page specifically for storing the communication modal. * It is responsible for creating a designated container for the modal's content. * * @since 1.0.0 * * @return void */ public function event_communication_modal(): void { if ( get_post_type() === Event::POST_TYPE ) { echo '
'; } } /** * Localize data for JavaScript usage. * * This method prepares and localizes data for use in JavaScript scripts. It collects various event-related * information and settings, making them available in the client-side context. The localized data includes * response details, current user information, time zone settings, event properties, and more. * * @since 1.0.0 * * @param int $post_id The Post ID for an event. * @return array An associative array containing localized data for JavaScript. */ protected function localize( int $post_id ): array { $event = new Event( $post_id ); $settings = Settings::get_instance(); $event_details = array(); $event_rest_api_slug = sprintf( '%s/event', GATHERPRESS_REST_NAMESPACE ); $user_identifier = Rsvp_Setup::get_instance()->get_user_identifier(); if ( ! empty( $event->event ) ) { $event_details = array( 'currentUser' => $event->rsvp->get( $user_identifier ), 'dateTime' => $event->get_datetime(), 'enableAnonymousRsvp' => (bool) get_post_meta( $post_id, 'gatherpress_enable_anonymous_rsvp', true ), 'maxAttendanceLimit' => (int) get_post_meta( $post_id, 'gatherpress_max_attendance_limit', true ), 'maxGuestLimit' => (int) get_post_meta( $post_id, 'gatherpress_max_guest_limit', true ), 'hasEventPast' => $event->has_event_past(), 'postId' => $post_id, 'responses' => $event->rsvp->responses(), ); } return array( 'eventDetails' => $event_details, 'misc' => array( 'isAdmin' => is_admin(), 'isUserLoggedIn' => is_user_logged_in(), 'nonce' => wp_create_nonce( 'wp_rest' ), 'unregisterBlocks' => $this->unregister_blocks(), ), 'settings' => array( 'dateFormat' => $settings->get_value( 'general', 'formatting', 'date_format' ), 'enableAnonymousRsvp' => ( 1 === (int) $settings->get_value( 'general', 'general', 'enable_anonymous_rsvp' ) ), 'mapPlatform' => $settings->get_value( 'general', 'general', 'map_platform' ), 'maxAttendanceLimit' => $settings->get_value( 'general', 'general', 'max_attendance_limit' ), 'maxGuestLimit' => $settings->get_value( 'general', 'general', 'max_guest_limit' ), 'showTimezone' => ( 1 === (int) $settings->get_value( 'general', 'formatting', 'show_timezone' ) ), 'timeFormat' => $settings->get_value( 'general', 'formatting', 'time_format' ), ), 'urls' => array( 'pluginUrl' => GATHERPRESS_CORE_URL, 'eventApiPath' => '/' . $event_rest_api_slug, 'eventApiUrl' => home_url( 'wp-json/' . $event_rest_api_slug ), 'loginUrl' => Utility::get_login_url( $post_id ), 'registrationUrl' => Utility::get_registration_url( $post_id ), 'homeUrl' => get_home_url(), ), ); } /** * Retrieve a list of blocks to unregister based on the current post type. * * This method determines which blocks should be unregistered on the current page * in the WordPress admin based on the post type. It returns an array of block names * that should be removed from the block editor for the given post type. * * @since 1.0.0 * * @return array An array of block names to unregister. */ protected function unregister_blocks(): array { $blocks = array(); if ( ! is_admin() || ! get_post_type() ) { return $blocks; } switch ( get_post_type() ) { case Event::POST_TYPE: break; case Venue::POST_TYPE: $blocks = array( 'gatherpress/online-event', ); break; default: $blocks = array( 'gatherpress/online-event', 'gatherpress/venue', ); } return $blocks; } /** * Retrieve asset data generated by the build script. * * This method fetches data related to a specific asset that has been generated by the build script. * The data is cached to ensure efficient retrieval, as `require_once` only loads the file contents * on the first request and returns `true` thereafter. * * @since 1.0.0 * * @param string $asset The file name of the asset. * @param ?string $path (Optional) The absolute path to the asset file * or null to use the path based on the default naming scheme. * @return array An array containing asset-related data. */ protected function get_asset_data( string $asset, ?string $path = null ): array { $path = $path ?? $this->path . sprintf( '%s.asset.php', $asset ); if ( empty( $this->asset_data[ $asset ] ) ) { // Loading WordPress asset metadata file that returns an array, not importing a class. $this->asset_data[ $asset ] = require_once $path; // NOSONAR. } return (array) $this->asset_data[ $asset ]; } /** * Register all assets. * * @since 1.0.0 * * @return void */ public function register_variation_assets(): void { $variations = Block::get_instance()->get_block_variations(); foreach ( $variations as $variation ) { $this->register_asset( $variation, 'variations/core/' ); } } /** * Enqueue all assets. * * @since 1.0.0 * * @return void */ public function enqueue_variation_assets(): void { array_map( array( $this, 'enqueue_asset' ), Block::get_instance()->get_block_variations() ); } /** * Register a new script and sets translated strings for the script. * @since 1.0.0 * * @param string $folder_name Slug of the block to register scripts and translations for. * @param string $build_dir Name of the folder to register assets from, relative to the plugins root directory. * * @return void */ protected function register_asset( string $folder_name, string $build_dir = '' ): void { $slug = sprintf( 'gatherpress-%s', $folder_name ); $folders = sprintf( '%1$s%2$s', $build_dir, $folder_name ); $dir = sprintf( '%1$s%2$s', $this->path, $folders ); $path_php = sprintf( '%1$s/index.asset.php', $dir ); $path_css = sprintf( '%1$s/index.css', $dir ); $url_js = sprintf( '%s/index.js', $this->build . $folders ); $url_css = sprintf( '%s/index.css', $this->build . $folders ); if ( ! $this->asset_exists( $path_php, $folder_name ) ) { return; } $asset = $this->get_asset_data( $folder_name, $path_php ); wp_register_script( $slug, $url_js, $asset['dependencies'], $asset['version'], true ); wp_set_script_translations( $slug, 'gatherpress' ); if ( $this->asset_exists( $path_css, $folder_name, false ) ) { wp_register_style( $slug, $url_css, array( 'global-styles' ), $asset['version'], 'screen' ); } } /** * Enqueue a script and a style with the same name, if registered. * * @since 1.0.0 * * @param string $folder_name Slug of the block to load the frontend scripts for. * * @return void */ protected function enqueue_asset( string $folder_name ): void { $slug = sprintf( 'gatherpress-%s', $folder_name ); wp_enqueue_script( $slug ); if ( wp_style_is( $slug, 'registered' ) ) { wp_enqueue_style( $slug ); } } /** * A better file_exists with built-in error handling. * * @since 1.0.0 * * @throws Error Throws error for non-existent file with given path, * if this is a development environment, * returns false for all other environments. * * @param string $path Absolute path to the file to check. * @param string $name Name of the asset, without file type. * @param bool $critical Whether file is mandatory for the plugin to work, defaults to true. * * @return bool */ protected function asset_exists( string $path, string $name, bool $critical = true ): bool { /** * Filters whether an asset file is considered critical. * * This filter allows modification of the critical flag for asset files, * which determines whether missing assets throw an Error in development * environments or silently return false. * * @since 1.0.0 * * @param bool $critical Whether file is mandatory for the plugin to work. * @param string $path Full file path to the asset file. * @param string $name Name of the asset being loaded. * * @return bool True if asset is critical, false otherwise. */ $critical = apply_filters( 'gatherpress_asset_critical', $critical, $path, $name ); if ( ! file_exists( $path ) ) { $error_message = sprintf( /* Translators: %s Name of a block-asset */ __( // phpcs:ignore Generic.Files.LineLength.TooLong 'You need to run `npm start` or `npm run build` for the "%1$s" block-asset first. %2$s does not exist.', 'gatherpress' ), $name, $path ); if ( in_array( wp_get_environment_type(), array( 'local', 'development' ), true ) && $critical ) { throw new Error( esc_html( $error_message ) ); } else { // Should write to the \error_log( $error_message ); if possible. return false; } } return true; } }