PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 28.2
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v28.2
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
wordpress-seo / src / bulk-editor / user-interface / bulk-editor-integration.php

bulk-editor-integration.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 28.2, at src/bulk-editor/user-interface/bulk-editor-integration.php

246 lines 7.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 // phpcs:disable Yoast.NamingConventions.NamespaceName.TooLong -- Needed in the folder structure.
4 namespace Yoast\WP\SEO\Bulk_Editor\User_Interface;
5
6 use WPSEO_Admin_Asset_Manager;
7 use Yoast\WP\SEO\Bulk_Editor\Application\Content_Types\Content_Types_Repository;
8 use Yoast\WP\SEO\Bulk_Editor\Application\Endpoints\Endpoints_Repository;
9 use Yoast\WP\SEO\Bulk_Editor\Infrastructure\Nonces\Nonce_Repository;
10 use Yoast\WP\SEO\Conditionals\Admin_Conditional;
11 use Yoast\WP\SEO\General\User_Interface\General_Page_Integration;
12 use Yoast\WP\SEO\Helpers\Current_Page_Helper;
13 use Yoast\WP\SEO\Helpers\Options_Helper;
14 use Yoast\WP\SEO\Helpers\Product_Helper;
15 use Yoast\WP\SEO\Helpers\Short_Link_Helper;
16 use Yoast\WP\SEO\Integrations\Integration_Interface;
17
18 /**
19 * Adds the bulk editor page to the Yoast admin menu.
20 */
21 class Bulk_Editor_Integration implements Integration_Interface {
22
23 /**
24 * The page name.
25 */
26 public const PAGE = 'wpseo_page_bulk_edit';
27
28 /**
29 * The assets name.
30 */
31 public const ASSETS_NAME = 'bulk-editor-page';
32
33 /**
34 * Holds the WPSEO_Admin_Asset_Manager.
35 *
36 * @var WPSEO_Admin_Asset_Manager
37 */
38 private $asset_manager;
39
40 /**
41 * Holds the Current_Page_Helper.
42 *
43 * @var Current_Page_Helper
44 */
45 private $current_page_helper;
46
47 /**
48 * Holds the Product_Helper.
49 *
50 * @var Product_Helper
51 */
52 private $product_helper;
53
54 /**
55 * Holds the Short_Link_Helper.
56 *
57 * @var Short_Link_Helper
58 */
59 private $short_link_helper;
60
61 /**
62 * Holds the Content_Types_Repository.
63 *
64 * @var Content_Types_Repository
65 */
66 private $content_types_repository;
67
68 /**
69 * Holds the Nonce_Repository.
70 *
71 * @var Nonce_Repository
72 */
73 private $nonce_repository;
74
75 /**
76 * Holds the Endpoints_Repository.
77 *
78 * @var Endpoints_Repository
79 */
80 private $endpoints_repository;
81
82 /**
83 * Holds the Options_Helper.
84 *
85 * @var Options_Helper
86 */
87 private $options_helper;
88
89 /**
90 * Constructs the instance.
91 *
92 * @param WPSEO_Admin_Asset_Manager $asset_manager The WPSEO_Admin_Asset_Manager.
93 * @param Current_Page_Helper $current_page_helper The Current_Page_Helper.
94 * @param Product_Helper $product_helper The Product_Helper.
95 * @param Short_Link_Helper $short_link_helper The Short_Link_Helper.
96 * @param Content_Types_Repository $content_types_repository The Content_Types_Repository.
97 * @param Nonce_Repository $nonce_repository The Nonce_Repository.
98 * @param Endpoints_Repository $endpoints_repository The Endpoints_Repository.
99 * @param Options_Helper $options_helper The Options_Helper.
100 */
101 public function __construct(
102 WPSEO_Admin_Asset_Manager $asset_manager,
103 Current_Page_Helper $current_page_helper,
104 Product_Helper $product_helper,
105 Short_Link_Helper $short_link_helper,
106 Content_Types_Repository $content_types_repository,
107 Nonce_Repository $nonce_repository,
108 Endpoints_Repository $endpoints_repository,
109 Options_Helper $options_helper
110 ) {
111 $this->asset_manager = $asset_manager;
112 $this->current_page_helper = $current_page_helper;
113 $this->product_helper = $product_helper;
114 $this->short_link_helper = $short_link_helper;
115 $this->content_types_repository = $content_types_repository;
116 $this->nonce_repository = $nonce_repository;
117 $this->endpoints_repository = $endpoints_repository;
118 $this->options_helper = $options_helper;
119 }
120
121 /**
122 * Returns the conditionals based on which this loadable should be active.
123 *
124 * @return array<string> The conditionals.
125 */
126 public static function get_conditionals() {
127 return [ Admin_Conditional::class ];
128 }
129
130 /**
131 * Initializes the integration.
132 *
133 * This is the place to register hooks and filters.
134 *
135 * @return void
136 */
137 public function register_hooks() {
138 \add_filter( 'wpseo_submenu_pages', [ $this, 'add_page' ] );
139
140 // Hide the menu item without losing the page. See remove_menu_item() for why this runs on admin_head.
141 \add_action( 'admin_head', [ $this, 'remove_menu_item' ] );
142
143 // Are we on our page?
144 if ( $this->current_page_helper->get_current_yoast_seo_page() === self::PAGE ) {
145 \add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
146 \add_action( 'in_admin_header', [ $this, 'remove_notices' ], \PHP_INT_MAX );
147 }
148 }
149
150 /**
151 * Removes the bulk editor's submenu item from the Yoast SEO menu while keeping the page reachable by URL.
152 *
153 * Runs on admin_head rather than admin_menu on purpose: by then WordPress has already resolved the page's
154 * parent and capability (so the page stays accessible and keeps its `seo_page_wpseo_page_bulk_edit` body
155 * class, which its styles depend on), but the menu HTML has not been rendered yet, so the item is hidden.
156 * The page is opened from the Tools page instead of its own menu item.
157 *
158 * @return void
159 */
160 public function remove_menu_item() {
161 \remove_submenu_page( 'wpseo_dashboard', self::PAGE );
162 }
163
164 /**
165 * Adds the page to the (currently) last position in the array.
166 *
167 * @param array<array<string|callable|null>> $pages The pages.
168 *
169 * @return array<array<string|callable|null>> The pages.
170 */
171 public function add_page( $pages ) {
172 $pages[] = [
173 'wpseo_dashboard',
174 '',
175 \__( 'Bulk editor', 'wordpress-seo' ),
176 'wpseo_manage_options',
177 self::PAGE,
178 [ $this, 'display_page' ],
179 ];
180
181 return $pages;
182 }
183
184 /**
185 * Displays the page.
186 *
187 * @return void
188 */
189 public function display_page() {
190 echo '<div id="yoast-seo-bulk-editor"></div>';
191 }
192
193 /**
194 * Enqueues the assets.
195 *
196 * @return void
197 */
198 public function enqueue_assets() {
199 // Remove the emoji script as it is incompatible with both React and any contenteditable fields.
200 \remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
201 $this->asset_manager->enqueue_script( self::ASSETS_NAME );
202 $this->asset_manager->enqueue_style( self::ASSETS_NAME );
203 $this->asset_manager->localize_script( self::ASSETS_NAME, 'wpseoBulkEditorData', $this->get_script_data() );
204 }
205
206 /**
207 * Creates the script data.
208 *
209 * @return array<string, string|array<string, string|bool|array<string, string>>> The script data.
210 */
211 public function get_script_data() {
212 return [
213 'contentTypes' => $this->content_types_repository->get_content_types(),
214 'endpoints' => $this->endpoints_repository->get_all_endpoints()->to_array(),
215 // These must stay server-generated URLs: the bulk editor assigns them to window.location.href for its
216 // "Back to Tools" / logo navigation. If a link ever derives from request input, validate it with
217 // wp_validate_redirect() here before exposing it, to avoid an open redirect on the front-end.
218 'links' => [
219 'dashboard' => \admin_url( 'admin.php?page=' . General_Page_Integration::PAGE ),
220 'tools' => \admin_url( 'admin.php?page=wpseo_tools' ),
221 ],
222 'nonce' => $this->nonce_repository->get_rest_nonce(),
223 'restRoot' => \esc_url_raw( \rest_url() ),
224 'preferences' => [
225 'isPremium' => $this->product_helper->is_premium(),
226 'isAiEnabled' => $this->options_helper->get( 'enable_ai_generator' ) === true,
227 'isRtl' => \is_rtl(),
228 'pluginUrl' => \plugins_url( '', \WPSEO_FILE ),
229 ],
230 'linkParams' => $this->short_link_helper->get_query_params(),
231 ];
232 }
233
234 /**
235 * Removes all current WP notices.
236 *
237 * @return void
238 */
239 public function remove_notices() {
240 \remove_all_actions( 'admin_notices' );
241 \remove_all_actions( 'user_admin_notices' );
242 \remove_all_actions( 'network_admin_notices' );
243 \remove_all_actions( 'all_admin_notices' );
244 }
245 }
246