PluginProbe
Gutenberg / trunk
Gutenberg vtrunk
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.6.2 → trunk 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' );