PluginProbe
Gutenberg / 24.1.0
Gutenberg v24.1.0
24.1.0 24.0.0 23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 All 404 releases
← All changes | lib/experimental/media-editor/load.php +124 -9 23.7.1 → 24.1.0 View file →
@@ -4,10 +4,8 @@
4 4 *
5 5 * @package gutenberg
6 6 */
7 7
8 -add_action( 'admin_menu', 'gutenberg_register_media_editor_admin_page' );
9 -
10 8 /**
11 9 * Registers a hidden wp-admin page for direct media editor deep links.
12 10 */
13 11 function gutenberg_register_media_editor_admin_page() {
@@ -22,20 +20,137 @@
22 20 'gutenberg_media_editor_wp_admin_render_page'
23 21 );
24 22
25 23 if ( $hook_suffix ) {
26 - // Hidden pages do not resolve a title from a visible menu item, so set
27 - // one before admin-header.php formats the page title.
28 - add_action( "load-$hook_suffix", 'gutenberg_media_editor_wp_admin_set_title' );
24 + add_action( "load-$hook_suffix", 'gutenberg_media_editor_wp_admin_prepare_screen' );
29 25 }
30 26 }
31 27
32 28 /**
33 - * Sets the admin page title before wp-admin/admin-header.php renders.
29 + * Prepares the admin chrome before wp-admin/admin-header.php renders.
34 30 *
35 - * @global string $title The admin page title.
31 + * @global string $title The admin page title.
32 + * @global string $parent_file The current top-level menu item.
33 + * @global string $submenu_file The current submenu item.
36 34 */
37 -function gutenberg_media_editor_wp_admin_set_title() {
38 - global $title;
35 +function gutenberg_media_editor_wp_admin_prepare_screen() {
36 + global $title, $parent_file, $submenu_file;
39 37
38 + // Hidden pages do not resolve a title from a visible menu item, so set one
39 + // before admin-header.php formats the page title.
40 40 $title = __( 'Edit media', 'gutenberg' );
41 +
42 + /*
43 + * Take the page out of the hidden `''` submenu bucket it was registered in.
44 + * Left in place, get_admin_page_parent() matches it there and resets
45 + * $parent_file to '' — and it does so *after* the `parent_file` filter runs,
46 + * so filtering cannot win. With no match it preserves a non-empty
47 + * $parent_file instead.
48 + *
49 + * Safe at this point: `load-` fires after the capability check in admin.php,
50 + * which needs the page registered, and before the menu is rendered.
51 + */
52 + remove_submenu_page( '', 'media-editor-wp-admin' );
53 +
54 + // Match the classic Edit Media screen, which the media editor stands in for:
55 + // Media expanded and current, Library the current submenu item.
56 + $parent_file = 'upload.php';
57 + $submenu_file = 'upload.php';
41 58 }
59 +
60 +add_action( 'admin_menu', 'gutenberg_register_media_editor_admin_page' );
61 +
62 +/**
63 + * Builds the media editor URL for an attachment.
64 + *
65 + * The router reads its internal path from the `p` query argument, so the
66 + * attachment id travels as `p=/media-editor/<id>` rather than as a query
67 + * argument of its own.
68 + *
69 + * @param int $post_id Attachment ID.
70 + * @param string $separator Argument separator. `&amp;` when the URL is
71 + * destined for HTML output, `&` otherwise.
72 + * @return string The media editor URL.
73 + */
74 +function gutenberg_media_editor_get_url( $post_id, $separator = '&' ) {
75 + return admin_url(
76 + 'admin.php?page=media-editor-wp-admin' . $separator . 'p=' . rawurlencode( '/media-editor/' . (int) $post_id )
77 + );
78 +}
79 +
80 +/**
81 + * Points every "edit this attachment" link at the media editor.
82 + *
83 + * Filtering here rather than redirecting means the Media Library list table,
84 + * the media modal's "Edit more details" and "Edit Image" links, and the admin
85 + * bar all navigate straight to the media editor with no redirect hop.
86 + *
87 + * @param string|null $link The edit link, or null when the user cannot edit the post.
88 + * @param int $post_id Post ID.
89 + * @param string $context The link context. `display` expects an HTML-escaped separator.
90 + * @return string|null The filtered edit link.
91 + */
92 +function gutenberg_media_editor_filter_edit_post_link( $link, $post_id, $context ) {
93 + // A null link means the user lacks the capability. Leave that alone.
94 + if ( ! $link || 'attachment' !== get_post_type( $post_id ) ) {
95 + return $link;
96 + }
97 +
98 + // `edit_post` is enough for the classic screen, but the media editor page
99 + // requires `upload_files`. A contributor can own an attachment and edit it
100 + // without being able to upload, so pointing them at the media editor would
101 + // send them to a screen they cannot open.
102 + if ( ! current_user_can( 'upload_files' ) ) {
103 + return $link;
104 + }
105 +
106 + return gutenberg_media_editor_get_url( $post_id, 'display' === $context ? '&amp;' : '&' );
107 +}
108 +
109 +add_filter( 'get_edit_post_link', 'gutenberg_media_editor_filter_edit_post_link', 10, 3 );
110 +
111 +/**
112 + * Redirects the classic Edit Media screen to the media editor.
113 + *
114 + * The `get_edit_post_link` filter above covers links rendered by WordPress,
115 + * but bookmarks, hand-typed URLs, and hard-coded links still reach post.php.
116 + * This catches those. It deliberately bails on the failure paths so that
117 + * WordPress keeps rendering its own error messages instead of sending people
118 + * to a screen that would only fail again.
119 + */
120 +function gutenberg_media_editor_redirect_classic_screen() {
121 + // GET renders the form. POST is the editattachment/editpost save handler,
122 + // which must run untouched.
123 + if ( ! isset( $_SERVER['REQUEST_METHOD'] ) || 'GET' !== strtoupper( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) ) ) {
124 + return;
125 + }
126 +
127 + // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Reading the same unauthenticated query args post.php itself reads to decide what to render.
128 + if ( ! isset( $_GET['action'] ) || 'edit' !== $_GET['action'] ) {
129 + return;
130 + }
131 +
132 + // Guard against `?post[]=1`, where an array casts to int 1 without warning.
133 + $post_id = isset( $_GET['post'] ) && is_scalar( $_GET['post'] ) ? (int) $_GET['post'] : 0;
134 + // phpcs:enable WordPress.Security.NonceVerification.Recommended
135 +
136 + if ( ! $post_id || 'attachment' !== get_post_type( $post_id ) ) {
137 + return;
138 + }
139 +
140 + // Let post.php own these: it already renders the appropriate wp_die().
141 + if ( ! current_user_can( 'edit_post', $post_id ) || 'trash' === get_post_status( $post_id ) ) {
142 + return;
143 + }
144 +
145 + // The media editor page requires `upload_files`, which `edit_post` does not
146 + // imply. Without this, a contributor who owns an attachment would be
147 + // redirected off a screen that works onto one that refuses them.
148 + if ( ! current_user_can( 'upload_files' ) ) {
149 + return;
150 + }
151 +
152 + wp_safe_redirect( gutenberg_media_editor_get_url( $post_id ) );
153 + exit;
154 +}
155 +
156 +add_action( 'load-post.php', 'gutenberg_media_editor_redirect_classic_screen' );