diff --git a/src/wp-admin/includes/class-wp-media-list-table.php b/src/wp-admin/includes/class-wp-media-list-table.php
index 7ac2ac3649d..292d6b5a520 100644
--- a/src/wp-admin/includes/class-wp-media-list-table.php
+++ b/src/wp-admin/includes/class-wp-media-list-table.php
@@ -182,16 +182,17 @@ protected function get_bulk_actions() {
if ( MEDIA_TRASH ) {
if ( $this->is_trash ) {
$actions['untrash'] = __( 'Restore' );
- $actions['delete'] = __( 'Delete permanently' );
+ $actions['delete'] = __( 'Delete permanently' );
} else {
$actions['trash'] = __( 'Move to Trash' );
}
} else {
+ $actions['edit'] = __( 'Edit' );
$actions['delete'] = __( 'Delete permanently' );
}
if ( $this->detached ) {
- $actions['attach'] = __( 'Attach' );
+ $actions['attach'] = __( 'Attach to post' );
}
return $actions;
@@ -356,6 +357,9 @@ public function get_columns() {
}
/* translators: Column name. */
+ $posts_columns['thumbnail'] = _x( 'Featured Image', 'column name' );
+ $posts_columns['used_in'] = _x( 'Used In', 'column name' );
+
if ( ! $this->detached ) {
$posts_columns['parent'] = _x( 'Uploaded to', 'column name' );
@@ -389,11 +393,13 @@ public function get_columns() {
*/
protected function get_sortable_columns() {
return array(
- 'title' => 'title',
- 'author' => 'author',
- 'parent' => 'parent',
- 'comments' => 'comment_count',
- 'date' => array( 'date', true ),
+ 'title' => 'title',
+ 'author' => 'author',
+ 'parent' => 'parent',
+ 'thumbnail' => 'thumbnail',
+ 'used_in' => 'used_in',
+ 'comments' => 'comment_count',
+ 'date' => array( 'date', true ),
);
}
@@ -552,6 +558,89 @@ public function column_date( $post ) {
echo apply_filters( 'media_date_column_time', $h_time, $post, 'date' );
}
+ /**
+ * Handles the thumbnail column output.
+ *
+ * @since CP-2.2.0
+ *
+ * @param WP_Post $post The current WP_Post object.
+ */
+ public function column_thumbnail( $post ) {
+ $user_can_edit = current_user_can( 'edit_post', $post->ID );
+
+ // Get all post types except attachments and revisions.
+ $parent_types = get_post_types();
+ unset( $parent_types['attachment'] );
+ unset( $parent_types['revision'] );
+
+ // Set output variable.
+ $output = '';
+
+ foreach ( $parent_types as $parent_type ) {
+
+ // Get all posts where this attachment is the featured image.
+ $relationship_ids = cp_get_object_relationship_ids( $post->ID, 'thumbnail', $parent_type );
+
+ if ( ! empty( $relationship_ids ) ) {
+ foreach ( $relationship_ids as $relationship_id ) {
+ if ( absint( $relationship_id ) !== 0 ) {
+ $ancestor = get_post( $relationship_id );
+ $ancestor_type_obj = get_post_type_object( $ancestor->post_type );
+ $title = _draft_or_post_title( $relationship_id );
+
+ if ( $ancestor_type_obj->show_ui && current_user_can( 'edit_post', $relationship_id ) ) {
+ $output .= '' . esc_html( $title ) . '
';
+ } else {
+ $output .= $title . '
';
+ }
+ }
+ }
+ }
+ }
+ return $output;
+ }
+
+ /**
+ * Handles the used-in column output.
+ *
+ * @since CP-2.2.0
+ *
+ * @param WP_Post $post The current WP_Post object.
+ */
+ public function column_used_in( $post ) {
+ $user_can_edit = current_user_can( 'edit_post', $post->ID );
+
+ // Get all post types except attachments and revisions.
+ $parent_types = get_post_types();
+ unset( $parent_types['attachment'] );
+ unset( $parent_types['revision'] );
+
+ // Set output variable.
+ $output = '';
+
+ foreach ( $parent_types as $parent_type ) {
+
+ // Get all posts where this attachment is used in the content.
+ $parent_ids = cp_get_object_relationship_ids( $post->ID, 'attachment', $parent_type );
+
+ if ( ! empty( $parent_ids ) ) {
+ foreach ( $parent_ids as $parent_id ) {
+ if ( absint( $parent_id ) !== 0 ) {
+ $parent_type_obj = get_post_type_object( $parent_type );
+ $title = _draft_or_post_title( $parent_id );
+
+ if ( $parent_type_obj->show_ui && current_user_can( 'edit_post', $parent_id ) ) {
+ $output .= '' . esc_html( $title ) . '
';
+ } else {
+ $output .= $title . '
';
+ }
+ }
+ }
+ }
+ }
+ return $output;
+ }
+
/**
* Handles the parent column output.
*
diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php
index 81f4eea6aab..32910c68040 100644
--- a/src/wp-includes/media.php
+++ b/src/wp-includes/media.php
@@ -5059,6 +5059,165 @@ function attachment_url_to_postid( $url ) {
return (int) apply_filters( 'attachment_url_to_postid', $post_id, $url );
}
+/**
+ * Gets an attachment ID from its URL.
+ *
+ * Based on https://stackoverflow.com/a/50335619
+ *
+ * @since CP-2.2.0
+ *
+ * @param string $url URL of media file.
+ *
+ * @return int $attachment_id on success, 0 on failure.
+ */
+function cp_get_attachment_id_from_url( $url ) {
+
+ $attachment_id = 0;
+ $dir = wp_upload_dir();
+
+ if ( 0 === strpos( $url, $dir['baseurl'] . '/' ) ) { // Is URL in uploads directory?
+
+ $file = basename( $url );
+
+ $args = array(
+ 'post_type' => 'attachment',
+ 'post_status' => 'inherit',
+ 'fields' => 'ids',
+ 'meta_query' => array(
+ array(
+ 'value' => $file,
+ 'compare' => 'LIKE',
+ 'key' => '_wp_attachment_metadata',
+ ),
+ ),
+ );
+ $post_ids = get_posts( $args );
+
+ if ( ! empty( $post_ids ) ) {
+ foreach ( $post_ids as $post_id ) {
+ $meta = wp_get_attachment_metadata( $post_id );
+
+ $original_file = basename( $meta['file'] );
+ $cropped_image_files = wp_list_pluck( $meta['sizes'], 'file' );
+
+ if ( $original_file === $file || in_array( $file, $cropped_image_files ) ) {
+ $attachment_id = $post_id;
+ break;
+ }
+ }
+ }
+ }
+ return $attachment_id;
+}
+
+/**
+ * Creates a relationship between a post, page, or custom post and an attachment.
+ *
+ * @since CP-2.2.0
+ *
+ * @param int $post_id Post ID.
+ * @param WP_Post $post Post object.
+ * @param bool $update Whether this is an existing post being updated.
+ */
+function cp_create_post_attachment_relationship( $post_id, $post, $update ) {
+
+ // Don't run for updates because we have another function for that.
+ if ( $update ) {
+ return;
+ }
+
+ // Create empty array of attachment IDs.
+ $attachment_ids = array();
+
+ // Get all hyperlinks included in a post.
+ $link_strings = wp_extract_urls( $post->post_content );
+
+ // Get the attachment ID (if it exists) for each URL.
+ if ( ! empty( $link_strings ) ) {
+ foreach ( $link_strings as $link_string ) {
+ $attachment_id = cp_get_attachment_id_from_url( $link_string );
+
+ // Filter out all hyperlinks that do not point to the uploads folder.
+ if ( absint( $attachment_id ) !== 0 ) {
+ $attachment_ids[] = $attachment_id;
+ }
+ }
+ }
+
+ // Create a relationship between the post and the attachment.
+ if ( ! empty( $attachment_ids ) ) {
+ foreach ( $attachment_ids as $attachment_id ) {
+ cp_add_object_relationship( $attachment_id, 'attachment', $post->post_type, $post_id );
+ }
+ }
+}
+
+/**
+ * Updates the relationship between a post, page, or custom post and an attachment.
+ *
+ * @since CP-2.2.0
+ *
+ * @param int $post_id Post ID.
+ * @param WP_Post $post_after Post object following the update.
+ * @param WP_Post $post_before Post object before the update.
+ * @param string $post_type Post type.
+ */
+function cp_update_post_attachment_relationship( $post_id, $post_after, $post_before, $post_type ) {
+
+ // Create empty array of attachment IDs after the post was updated.
+ $new_attachment_ids = array();
+
+ // Get all hyperlinks included in a post.
+ $link_strings = wp_extract_urls( $post_after->post_content );
+
+ // Get the attachment ID (if it exists) for each URL.
+ if ( ! empty( $link_strings ) ) {
+ foreach ( $link_strings as $link_string ) {
+ $new_attachment_id = cp_get_attachment_id_from_url( $link_string );
+
+ // Filter out all hyperlinks that do not point to the uploads folder.
+ if ( absint( $new_attachment_id ) !== 0 ) {
+ $new_attachment_ids[] = $new_attachment_id;
+ }
+ }
+ }
+
+ // Update a relationship between the post and the attachment.
+ if ( ! empty( $new_attachment_ids ) ) {
+ foreach ( $new_attachment_ids as $new_attachment_id ) {
+ cp_add_object_relationship( $new_attachment_id, 'attachment', $post_type, $post_id );
+ }
+ }
+
+ // Create empty array of attachment IDs before the post was updated.
+ $old_attachment_ids = array();
+
+ // Get previous hyperlinks included in the post before it was updated.
+ $old_link_strings = wp_extract_urls( $post_before->post_content );
+
+ // Get the attachment ID (if it exists) for each URL.
+ if ( ! empty( $old_link_strings ) ) {
+ foreach ( $old_link_strings as $old_link_string ) {
+ $old_attachment_id = cp_get_attachment_id_from_url( $old_link_string );
+
+ // Filter out all hyperlinks that do not point to the uploads folder.
+ if ( absint( $old_attachment_id ) !== 0 ) {
+ $old_attachment_ids[] = $old_attachment_id;
+ }
+ }
+ }
+
+ // Identify the hyperlinks from the old array that are not present in the updated array.
+ $removed_attachment_ids = array_diff( $old_attachment_ids, $new_attachment_ids );
+
+ // Remove the relationship between the post and the attachments that are no longer used in it.
+ if ( ! empty( $removed_attachment_ids ) ) {
+ foreach ( $removed_attachment_ids as $removed_attachment_id ) {
+ cp_delete_object_relationship( $removed_attachment_id, 'attachment', $post_type, $post_id );
+ }
+ }
+}
+
/**
* Returns the URLs for CSS files used in an iframe-sandbox'd TinyMCE media view.
*
@@ -5197,7 +5356,7 @@ function wp_show_heic_upload_error( $plupload_settings ) {
* @param array $image_info Optional. Extended image information (passed by reference).
* @return array|false Array of image information or false on failure.
*/
-function wp_getimagesize( $filename, ?array &$image_info = null ) {
+function wp_getimagesize( $filename, array &$image_info = null ) {
// Don't silence errors when in debug mode, unless running unit tests.
if ( defined( 'WP_DEBUG' ) && WP_DEBUG
&& ! defined( 'WP_RUN_CORE_TESTS' )
diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php
index 51e70f1e594..4a86ca6dc8c 100644
--- a/src/wp-includes/post.php
+++ b/src/wp-includes/post.php
@@ -4465,6 +4465,18 @@ function wp_insert_post( $postarr, $wp_error = false, $fire_after_hooks = true )
$post_after = get_post( $post_id );
+ /**
+ * Updates the relationship between the post and any attachment.
+ *
+ * @since CP-2.2.0
+ *
+ * @param int $post_id Post ID.
+ * @param WP_Post $post_after Post object following the update.
+ * @param WP_Post $post_before Post object before the update.
+ * @param string $post->post_type Post type.
+ */
+ cp_update_post_attachment_relationship( $post_id, $post_after, $post_before, $post->post_type );
+
/**
* Fires once an existing post has been updated.
*
@@ -4496,6 +4508,17 @@ function wp_insert_post( $postarr, $wp_error = false, $fire_after_hooks = true )
*/
do_action( "save_post_{$post->post_type}", $post_id, $post, $update );
+ /**
+ * Creates a relationship between the post and any attachment.
+ *
+ * @since CP-2.2.0
+ *
+ * @param int $post_id Post ID.
+ * @param WP_Post $post Post object.
+ * @param bool $update Whether this is an existing post being updated.
+ */
+ cp_create_post_attachment_relationship( $post_id, $post, $update );
+
/**
* Fires once a post has been saved.
*
@@ -7502,8 +7525,32 @@ function set_post_thumbnail( $post, $thumbnail_id ) {
$thumbnail_id = absint( $thumbnail_id );
if ( $post && $thumbnail_id && get_post( $thumbnail_id ) ) {
if ( wp_get_attachment_image( $thumbnail_id, 'thumbnail' ) ) {
+
+ /**
+ * Creates a relationship between a post, page, or custom post and its thumbnail.
+ *
+ * @since CP-2.2.0
+ *
+ * @param int $thumbnail_id Thumbnail ID.
+ * @param string $post->post_type Name of post type.
+ * @param int $post->ID Post ID.
+ */
+ cp_add_object_relationship( $thumbnail_id, 'thumbnail', $post->post_type, $post->ID );
+
return update_post_meta( $post->ID, '_thumbnail_id', $thumbnail_id );
} else {
+
+ /**
+ * Deletes a relationship between a post, page, or custom post and its thumbnail.
+ *
+ * @since CP-2.2.0
+ *
+ * @param int $thumbnail_id Thumbnail ID.
+ * @param string $post->post_type Name of post type.
+ * @param int $post->ID Post ID.
+ */
+ cp_delete_object_relationship( $thumbnail_id, 'thumbnail', $post->post_type, $post->ID );
+
return delete_post_meta( $post->ID, '_thumbnail_id' );
}
}
@@ -7521,6 +7568,19 @@ function set_post_thumbnail( $post, $thumbnail_id ) {
function delete_post_thumbnail( $post ) {
$post = get_post( $post );
if ( $post ) {
+
+ /**
+ * Deletes a relationship between a post, page, or custom post and its thumbnail.
+ *
+ * @since CP-2.2.0
+ *
+ * @param int $thumbnail_id Thumbnail ID.
+ * @param string $post->post_type Name of post type.
+ * @param int $post->ID Post ID.
+ */
+ $thumbnail_id = get_post_thumbnail_id( $post );
+ cp_delete_object_relationship( $thumbnail_id, 'thumbnail', $post->post_type, $post->ID );
+
return delete_post_meta( $post->ID, '_thumbnail_id' );
}
return false;
diff --git a/src/wp-includes/version.php b/src/wp-includes/version.php
index 6f86be8c341..26d869c53f0 100644
--- a/src/wp-includes/version.php
+++ b/src/wp-includes/version.php
@@ -54,7 +54,7 @@
*
* @global int $cp_db_version
*/
-$cp_db_version = 1438;
+$cp_db_version = 1446;
/**
* Holds the TinyMCE version.