PluginProbe
Blocks – Reusable Content, Shortcodes & Site Variables / trunk
Blocks – Reusable Content, Shortcodes & Site Variables vtrunk
26.09.03.15 26.08.31.21 26.08.22.20 26.08.22.22 26.08.22.17 26.08.22.13 26.08.21.19 26.08.07.23 26.07.19.14 26.07.13.21 26.07.13.17 26.07.12.13 026.07.07.21 026.07.05.18 026.06.26.20 026.06.26.21 026.06.08.20 026.05.13.14 026.04.29.10 trunk 026.02.22.22 026.03.16.23 026.04.23.13
blocks / src / Settings / SettingsPage.php

SettingsPage.php in Blocks – Reusable Content, Shortcodes & Site Variables trunk, at src/Settings/SettingsPage.php

229 lines 7.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 namespace RenzoJohnson\Blocks\Settings;
6
7 \defined( 'ABSPATH' ) || exit;
8
9 final class SettingsPage {
10 public const OPTION_GROUP_PREFIX = 'blocks_settings_';
11 public const PAGE_SLUG = 'blocks-settings';
12
13 private ?SettingsRegistry $registry = null;
14
15 public function __construct(
16 private readonly SettingsRegistryFactory $registry_factory,
17 private readonly SettingsRegistrar $registrar,
18 private readonly SettingsSchemaBuilder $schema_builder,
19 ) {
20 }
21
22 public function register_hooks(): void {
23 \add_action( 'admin_menu', $this->add_page( ... ), 20 );
24 \add_action( 'admin_init', $this->register_settings( ... ) );
25 \add_action( 'admin_enqueue_scripts', $this->enqueue_assets( ... ) );
26 \add_filter( 'wp_redirect', $this->preserve_tab( ... ) );
27 }
28
29 public function add_page(): void {
30 \add_submenu_page(
31 'block',
32 \__( 'Settings', 'blocks' ),
33 \__( 'Settings', 'blocks' ),
34 'manage_options',
35 self::PAGE_SLUG,
36 $this->render( ... )
37 );
38 }
39
40 public function register_settings(): void {
41 $this->registrar->register( $this->registry() );
42 }
43
44 public function enqueue_assets( string $hook ): void {
45 if ( 'blocks_page_' . self::PAGE_SLUG !== $hook ) {
46 return;
47 }
48
49 $has_images = $this->current_tab_has_field_type( FieldType::Image );
50 $dependencies = array( 'wp-components', 'wp-compose', 'wp-element', 'wp-i18n' );
51 if ( $this->current_tab_has_field_type( FieldType::Product ) ) {
52 $dependencies[] = 'wp-api-fetch';
53 }
54
55 if ( $has_images ) {
56 \wp_enqueue_media();
57 }
58 \wp_enqueue_style( 'wp-components' );
59 if ( $has_images ) {
60 \wp_enqueue_style( 'dashicons' );
61 }
62 \wp_enqueue_style(
63 'blocks-settings-page',
64 \blocks_plugin_url( 'assets/css/settings-page.css' ),
65 $has_images ? array( 'wp-components', 'dashicons' ) : array( 'wp-components' ),
66 BLOCKS_VERSION
67 );
68 \wp_enqueue_script(
69 'blocks-settings-page',
70 \blocks_plugin_url( 'assets/js/settings-page.js' ),
71 $dependencies,
72 BLOCKS_VERSION,
73 true
74 );
75 \wp_set_script_translations( 'blocks-settings-page', 'blocks', BLOCKS_PLUGIN_DIR . '/languages' );
76 \wp_localize_script( 'blocks-settings-page', 'blocksSettingsPage', $this->runtime_data() );
77
78 if ( 'woocommerce' === $this->current_tab() && Requirement::WooCommerce->satisfied() ) {
79 \wp_enqueue_style( 'woocommerce_admin_styles' );
80 }
81 }
82
83 public function preserve_tab( string $location ): string {
84 if ( ! isset( $_POST['blocks_active_tab'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Core Settings API verifies the options.php nonce.
85 return $location;
86 }
87
88 $tab = \sanitize_key( \wp_unslash( $_POST['blocks_active_tab'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Core Settings API verifies the options.php nonce.
89 $location = \add_query_arg( 'settings-updated', 'true', $location );
90 if ( 'general' !== $tab && isset( $this->tab_map()[ $tab ] ) ) {
91 return \add_query_arg( 'tab', $tab, $location );
92 }
93
94 return $location;
95 }
96
97 public function render(): void {
98 if ( ! \current_user_can( 'manage_options' ) ) {
99 return;
100 }
101
102 $current = $this->current_tab();
103 ?>
104 <div class="wrap blocks-settings-wrap">
105 <h1 class="screen-reader-text"><?php echo \esc_html( \get_admin_page_title() ); ?></h1>
106 <?php \settings_errors(); ?>
107 <div id="blocks-settings-root">
108 <p><span class="spinner is-active"></span><?php \esc_html_e( 'Loading Blocks settings…', 'blocks' ); ?></p>
109 </div>
110 <noscript><?php $this->render_fallback( $current ); ?></noscript>
111 </div>
112 <?php
113 }
114
115 /** @return array<string, TabDefinition> */
116 private function tab_map(): array {
117 $tabs = array();
118 foreach ( $this->registry()->tabs() as $tab ) {
119 $tabs[ $tab->slug ] = $tab;
120 }
121
122 return $tabs;
123 }
124
125 private function current_tab(): string {
126 $tabs = $this->tab_map();
127 $tab = isset( $_GET['tab'] ) ? \sanitize_key( \wp_unslash( $_GET['tab'] ) ) : 'general'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only tab navigation.
128
129 return isset( $tabs[ $tab ] ) ? $tab : 'general';
130 }
131
132 private function current_tab_has_field_type( FieldType $type ): bool {
133 $tab = $this->tab_map()[ $this->current_tab() ];
134 foreach ( $tab->sections as $section ) {
135 foreach ( $section->fields as $field ) {
136 if ( $type === $field->type ) {
137 return true;
138 }
139 }
140 }
141
142 return false;
143 }
144
145 private function registry(): SettingsRegistry {
146 if ( null === $this->registry ) {
147 $this->registry = $this->registry_factory->create();
148 }
149
150 return $this->registry;
151 }
152
153 /** @return array<string, mixed> */
154 private function runtime_data(): array {
155 $current = $this->current_tab();
156 $schema = $this->schema_builder->build( $this->registry() );
157
158 foreach ( $schema as &$tab ) {
159 $slug = isset( $tab['slug'] ) && \is_string( $tab['slug'] ) ? $tab['slug'] : 'general';
160 $tab['url'] = $this->tab_url( $slug );
161 }
162 unset( $tab );
163
164 $option_group = self::OPTION_GROUP_PREFIX . $current;
165
166 return array(
167 'schema' => $schema,
168 'currentTab' => $current,
169 'pluginsUrl' => \admin_url( 'plugins.php' ),
170 'saved' => $this->save_succeeded(),
171 'form' => array(
172 'action' => \admin_url( 'options.php' ),
173 'optionGroup' => $option_group,
174 'nonce' => \wp_create_nonce( $option_group . '-options' ),
175 'referer' => \wp_make_link_relative( $this->tab_url( $current ) ),
176 ),
177 );
178 }
179
180 private function tab_url( string $slug ): string {
181 $url = \menu_page_url( self::PAGE_SLUG, false );
182
183 return 'general' === $slug ? $url : \add_query_arg( 'tab', $slug, $url );
184 }
185
186 private function save_succeeded(): bool {
187 $updated = isset( $_GET['settings-updated'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only redirect state from the Settings API.
188 && 'true' === \sanitize_text_field( \wp_unslash( $_GET['settings-updated'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only redirect state from the Settings API.
189 if ( ! $updated ) {
190 return false;
191 }
192
193 foreach ( \get_settings_errors() as $error ) {
194 if ( isset( $error['type'] ) && 'error' === $error['type'] ) {
195 return false;
196 }
197 }
198
199 return true;
200 }
201
202 private function render_fallback( string $current ): void {
203 $tab = $this->tab_map()[ $current ];
204 ?>
205 <nav class="nav-tab-wrapper" aria-label="<?php \esc_attr_e( 'Settings sections', 'blocks' ); ?>">
206 <?php foreach ( $this->tab_map() as $slug => $candidate ) : ?>
207 <a href="<?php echo \esc_url( $this->tab_url( $slug ) ); ?>" class="nav-tab <?php echo $current === $slug ? 'nav-tab-active' : ''; ?>"><?php echo \esc_html( $candidate->label ); ?></a>
208 <?php endforeach; ?>
209 </nav>
210 <?php
211 if ( ! $tab->requirement->satisfied() ) {
212 ?>
213 <div class="notice notice-warning inline"><p><?php \esc_html_e( 'The required plugin is not active. These settings are available after the prerequisite is installed and activated.', 'blocks' ); ?></p></div>
214 <?php
215 return;
216 }
217 ?>
218 <form action="options.php" method="post">
219 <input type="hidden" name="blocks_active_tab" value="<?php echo \esc_attr( $current ); ?>" />
220 <?php
221 \settings_fields( self::OPTION_GROUP_PREFIX . $current );
222 \do_settings_sections( self::PAGE_SLUG . '-' . $current );
223 \submit_button( \__( 'Save Changes', 'blocks' ) );
224 ?>
225 </form>
226 <?php
227 }
228 }
229