Skip to content

Commit

Permalink
Use post_updated hook for post updates, add separate term log
Browse files Browse the repository at this point in the history
Trying to catch everything that generated a log before but teasing them apart to get more data.
  • Loading branch information
tharsheblows committed Aug 8, 2024
1 parent e6a1ae7 commit e3d529a
Show file tree
Hide file tree
Showing 2 changed files with 245 additions and 36 deletions.
274 changes: 245 additions & 29 deletions connectors/class-connector-posts.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

namespace WP_Stream;

use WP_Post;

/**
* Class - Connector_Posts
*/
Expand All @@ -24,9 +26,9 @@ class Connector_Posts extends Connector {
* @var array
*/
public $actions = array(
'transition_post_status',
'deleted_post',
'post_updated'
'post_updated',
'set_object_terms',
);

/**
Expand Down Expand Up @@ -151,16 +153,136 @@ public function registered_post_type( $post_type, $args ) {
wp_stream_get_instance()->connectors->term_labels['stream_context'][ $post_type ] = $label;
}

/**
* Log when post object terms are set.
*
* @param int $object_id Object ID.
* @param array $terms An array of object term IDs or slugs.
* @param array $tt_ids An array of term taxonomy IDs.
* @param string $tax_slug Taxonomy slug.
* @param bool $append Whether to append new terms to the old terms.
* @param array $old_tt_ids Old array of term taxonomy IDs.
* @return void
*/
public function callback_set_object_terms( $object_id, $terms, $tt_ids, $tax_slug, $append, $old_tt_ids ) {

$object = get_post( $object_id );

if (
! is_a( $object, 'WP_Post' )
||
in_array( $object->post_type, $this->get_excluded_post_types(), true )
) {
return;
}

// Only post_tags and categories are included by default.
if ( ! in_array( $tax_slug, $this->get_included_taxonomies( $object_id ), true ) ) {
return;
}

$taxonomy = get_taxonomy( $tax_slug );
$tax_name = is_a( $taxonomy, 'WP_Taxonomy' ) ? $taxonomy->labels->singular_name : $tax_slug;

$removed = array_diff( $old_tt_ids, $tt_ids );
$added = array_diff( $tt_ids, $old_tt_ids );

if ( empty( $removed ) && empty( $added ) ) {
return;
}

$terms_lists = '';

if ( ! empty( $added ) ) {
$added_terms = sprintf(
/* Translators: %s is a linked list of the added terms. */
__( 'added: %s', 'stream' ),
$this->make_term_list( $added, $tax_slug )
);

$terms_lists .= sprintf( '<li>%s</li>', $added_terms );
}

if ( ! empty( $removed ) ) {
$removed_terms = sprintf(
/* Translators: %s is a linked list of the removed terms. */
__( 'removed: %s', 'stream' ),
$this->make_term_list( $removed, $tax_slug )
);

$terms_lists .= sprintf( '<li>%s</li>', $removed_terms );
}

/* translators: %1$s: a post title, %2$s: a post type singular name (e.g."HelloWorld", "Post") */
$summary = _x(
'"%1$s" %2$s %3$s terms updated',
'1: Post title, 2: Post type singular name, 3: Taxonomy name',
'stream'
);

$summary = sprintf( '%s<ul>%s</ul>', $summary, $terms_lists );

$this->log(
$summary,
array(
'post_title' => get_the_title( $object_id ),
'singular_name' => $this->get_post_type_name( $object->post_type ),
'taxonomy_name' => $tax_name,
'taxonomy' => $tax_slug,
'terms_updated' => array(
'added' => $added,
'removed' => $removed,
),
'post_date' => $object->post_date,
'post_date_gmt' => $object->post_date_gmt,
'revision_id' => $this->get_revision_id( $object ),
),
$object->ID,
$object->post_type,
__( 'Updated Terms' )
);
}

/**
* Generates a list of terms based on the provided term IDs and taxonomy.
*
* @param array $updated The array of term IDs to generate the list from.
* @param string $taxonomy The taxonomy to which the terms belong.
*
* @return string The generated list of terms as HTML links.
*/
private function make_term_list( $updated, $taxonomy ) {
$list = array_reduce(
$updated,
function ( $acc, $id ) use ( $taxonomy ) {
$term = get_term( $id, $taxonomy );

if ( empty( $term ) ) {
return $acc;
}

return $acc .= sprintf(
'<a href="%s">%s</a>, ',
get_term_link( $term, $taxonomy ),
$term->name
);
},
''
);

return rtrim( $list, ', ' );
}

/**
* Log all post status changes ( creating / updating / trashing )
*
* @action transition_post_status
*
* @param mixed $new_status New status.
* @param mixed $old_status Old status.
* @param \WP_Post $post Post object.
* @param WP_Post $post Post object.
*/
public function callback_transition_post_status( $new_status, $old_status, $post ) {
public function log_transition_post_status( $new_status, $old_status, $post ) {

if ( in_array( $post->post_type, $this->get_excluded_post_types(), true ) ) {
return;
Expand Down Expand Up @@ -269,8 +391,6 @@ public function callback_transition_post_status( $new_status, $old_status, $post

$post_type_name = strtolower( $this->get_post_type_name( $post->post_type ) );

add_filter( 'wp_stream_has_tracked_post_updated', '__return_true' );

$this->log(
$summary,
array(
Expand All @@ -288,8 +408,21 @@ public function callback_transition_post_status( $new_status, $old_status, $post
);
}

/**
* This currently only looks at the posts table.
*
* @param int|string $post_id The post id.
* @param WP_Post $post_after The post object of the final post.
* @param WP_Post $post_before The post object before it was updated.
* @return void
*/
public function callback_post_updated( $post_id, $post_after, $post_before ) {

// Don't log the non-included post types.
if ( in_array( $post_after->post_type, $this->get_excluded_post_types(), true ) ) {
return;
}

// If we have already tracked this change, bail.
if ( apply_filters( 'wp_stream_has_tracked_post_updated', false ) ) {
return;
Expand All @@ -303,8 +436,7 @@ public function callback_post_updated( $post_id, $post_after, $post_before ) {
$action = 'updated';
$post_type_name = $this->get_post_type_name( $post_after->post_type );

$message = '';
$fields_updated = '';
$fields_updated = array();

// Find out what was updated.
foreach ( $post_after as $field => $value ) {
Expand All @@ -314,50 +446,108 @@ public function callback_post_updated( $post_id, $post_after, $post_before ) {

switch ( $field ) {
case 'post_author':
$fields_updated .= sprintf(
__( "<a href="/">post_author</a> updated from %s to %s", 'stream' ),
'hey',
'there'
$fields_updated['post_author'] = sprintf(
/* Translators: %1$s is the previous post author, %2$s is the current post author */
__( '%1$s to %2$s', 'stream' ),
$this->get_author_maybe_link( $post_before->post_author ),
$this->get_author_maybe_link( $post_after->post_author )
);
break;
case 'post_modified':
case 'post_modified_gmt':
// Not including these for now.
break;
case 'post_content':
$fields_updated['post_content'] = __( 'updated', 'stream' );
break;
case 'post_status':
// This is mainly for back compat, post status transitions will be logged in a separate entry.
// However they are all triggered here to account for the block editor flow.
$this->log_transition_post_status( $post_after->post_status, $post_before->post_status, $post_after );
break;
default:
$fields_updated .= sprintf(
__( "<a href='https://google.com'>%s updated</a> from %s to %s", 'stream' ),
esc_html( $field ),
'hey',
'there'
$fields_updated[ $field ] = sprintf(
/* Translators: %1$s is the previous value, %2$s is the current value */
__( '"%1$s" to "%2$s"', 'stream' ),
esc_html( $post_before->$field ),
esc_html( $value )
);
break;
}
}

// If none of the post fields were updated, there should be a log somewhere else.
if ( empty( $fields_updated ) ) {
return;
}

// If it's not a post author, fall back to the default.
// Creating a string for the summary. The array will be stored in the meta.
$fields_updated_list_items = array_reduce(
array_keys( $fields_updated ),
function ( $acc, $key ) use ( $fields_updated ) {
return $acc .= sprintf( '<li>%s: %s</li>', $key, $fields_updated[ $key ] );
},
''
);

/* translators: %1$s: a post title, %2$s: a post type singular name (e.g. "HelloWorld", "Post") */
$summary = _x(
'"%1$s" %2$s updated:<br /> %3$s',
'1: Post title, 2: Post type singular name',
'"%1$s" %2$s updated',
'1: Post title, 2: Post type singular name, 3: Fields updated list',
'stream'
);

$this->log(
$summary_with_fields = sprintf(
'%s<br /><ul>%s</ul>',
$summary,
$fields_updated_list_items
);

$this->log(
$summary_with_fields,
array(
'post_title' => $post_after->post_title,
'singular_name' => $post_type_name,
'post_title' => $post_after->post_title,
'singular_name' => $post_type_name,
'fields_updated' => $fields_updated,
'post_date' => $post_after->post_date,
'post_date_gmt' => $post_after->post_date_gmt,
'status' => $post_after->post_status,
'revision_id' => $this->get_revision_id( $post_after ),
'post_date' => $post_after->post_date,
'post_date_gmt' => $post_after->post_date_gmt,
'revision_id' => $this->get_revision_id( $post_after ),
),
$post_after->ID,
$post_after->post_type,
$action
);
}

/**
* Retrieves the author name with an optional link to the author's profile.
*
* @param int $author_id The ID of the author.
* @return string The author name with an optional link to the author's profile.
*/
private function get_author_maybe_link( $author_id ) {
$author = get_userdata( $author_id );

if ( empty( $author ) || is_wp_error( $author ) ) {
/* Translators: %d is the user id. */
return sprintf( __( 'Unknown user %d', 'stream' ), $author_id );
}

$author_name = $author->display_name;

// This is the same cap check as in `get_edit_user_link()` so we'll use it
// here to return just the name if the link won't work for the current user.
if ( ! current_user_can( 'edit_user', $author_id ) ) {
return $author_name;
}

return sprintf(
'<a href="%s">%s</a>',
esc_url( get_edit_user_link( $author_id ) ),
esc_html( $author_name )
);
}

/**
* Log post deletion
*
Expand All @@ -369,7 +559,7 @@ public function callback_deleted_post( $post_id ) {
$post = get_post( $post_id );

// We check if post is an instance of WP_Post as it doesn't always resolve in unit testing.
if ( ! ( $post instanceof \WP_Post ) || in_array( $post->post_type, $this->get_excluded_post_types(), true ) ) {
if ( ! ( $post instanceof WP_Post ) || in_array( $post->post_type, $this->get_excluded_post_types(), true ) ) {
return;
}

Expand Down Expand Up @@ -413,6 +603,32 @@ public function get_excluded_post_types() {
);
}

/**
* Retrieves the list of taxonomies to include when logging term changes.
*
* By default, it includes the 'post_tag' and 'category' taxonomies.
*
* @param int|string $post_id The post id.
*
* @return array The list of taxonomies to log.
*/
public function get_included_taxonomies( $post_id ) {
/**
* Filter the taxonomies for which term changes should be logged.
*
* @param array An array of the taxonomies.
* @param int|string The post id.
*/
return apply_filters(
'wp_stream_posts_include_taxonomies',
array(
'post_tag',
'category',
),
$post_id
);
}

/**
* Gets the singular post type label
*
Expand Down Expand Up @@ -482,7 +698,7 @@ public function get_adjacent_post_revision( $revision_id, $previous = true ) {
* @param WP_Post $post The post object.
* @return int|null The ID of the latest revision, or null if revisions are not enabled for the post.
*/
public function get_revision_id( \WP_Post $post ) {
public function get_revision_id( WP_Post $post ) {
$revision_id = null;

if ( wp_revisions_enabled( $post ) ) {
Expand Down
7 changes: 0 additions & 7 deletions connectors/class-connector-taxonomies.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,6 @@ class Connector_Taxonomies extends Connector {
*/
public $context_labels;

/**
* Register connector in the WP Frontend
*
* @var bool
*/
public $register_frontend = false;

/**
* Return translated connector label
*
Expand Down

0 comments on commit e3d529a

Please sign in to comment.