mirror of
https://github.com/WordPress/five-for-the-future.git
synced 2025-04-22 11:03:43 +03:00
Plugin: Add a system for logging events related to pledges and contributors (#54)
Uses action hooks to capture relevant events as log entries on a per-pledge basis. This provides a running history of a pledge and can be used as an audit log if questions arise about changes to a pledge or there are weird bugs. Fixes #39
This commit is contained in:
parent
6209060eb2
commit
5c5ae83287
|
@ -123,22 +123,40 @@ function populate_list_table_columns( $column, $post_id ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a contributor post as a child of a pledge post.
|
* Add one or more contributors to a pledge.
|
||||||
*
|
*
|
||||||
* @param string $wporg_username
|
* Note that this does not validate whether a contributor's wporg username exists in the system.
|
||||||
* @param int $pledge_id
|
|
||||||
*
|
*
|
||||||
* @return int|WP_Error Post ID on success. Otherwise WP_Error.
|
* @param int $pledge_id The post ID of the pledge.
|
||||||
|
* @param array $contributors Array of contributor wporg usernames.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
*/
|
*/
|
||||||
function create_new_contributor( $wporg_username, $pledge_id ) {
|
function add_pledge_contributors( $pledge_id, $contributors ) {
|
||||||
$args = array(
|
$results = array();
|
||||||
'post_type' => CPT_ID,
|
|
||||||
'post_title' => sanitize_user( $wporg_username ),
|
|
||||||
'post_parent' => $pledge_id,
|
|
||||||
'post_status' => 'pending',
|
|
||||||
);
|
|
||||||
|
|
||||||
return wp_insert_post( $args, true );
|
foreach ( $contributors as $wporg_username ) {
|
||||||
|
$args = array(
|
||||||
|
'post_type' => CPT_ID,
|
||||||
|
'post_title' => sanitize_user( $wporg_username ),
|
||||||
|
'post_parent' => $pledge_id,
|
||||||
|
'post_status' => 'pending',
|
||||||
|
);
|
||||||
|
|
||||||
|
$result = wp_insert_post( $args, true );
|
||||||
|
|
||||||
|
$results[ $wporg_username ] = ( is_wp_error( $result ) ) ? $result->get_error_code() : $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action: Fires when one or more contributors are added to a pledge.
|
||||||
|
*
|
||||||
|
* @param int $pledge_id The post ID of the pledge.
|
||||||
|
* @param array $contributors Array of contributor wporg usernames.
|
||||||
|
* @param array $results Associative array, key is wporg username, value is post ID on success,
|
||||||
|
* or an error code on failure.
|
||||||
|
*/
|
||||||
|
do_action( FiveForTheFuture\PREFIX . '_add_pledge_contributors', $pledge_id, $contributors, $results );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -152,7 +170,19 @@ function create_new_contributor( $wporg_username, $pledge_id ) {
|
||||||
* @return false|WP_Post|null
|
* @return false|WP_Post|null
|
||||||
*/
|
*/
|
||||||
function remove_contributor( $contributor_post_id ) {
|
function remove_contributor( $contributor_post_id ) {
|
||||||
return wp_trash_post( $contributor_post_id );
|
$pledge_id = get_post( $contributor_post_id )->post_parent;
|
||||||
|
$result = wp_trash_post( $contributor_post_id );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action: Fires when a contributor is removed from a pledge.
|
||||||
|
*
|
||||||
|
* @param int $pledge_id
|
||||||
|
* @param int $contributor_post_id
|
||||||
|
* @param WP_Post|false|null $result
|
||||||
|
*/
|
||||||
|
do_action( FiveForTheFuture\PREFIX . '_remove_contributor', $pledge_id, $contributor_post_id, $result );
|
||||||
|
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -271,7 +301,7 @@ function process_my_pledges_form() {
|
||||||
'post_status' => $status,
|
'post_status' => $status,
|
||||||
) );
|
) );
|
||||||
} elseif ( 'trash' === $status ) {
|
} elseif ( 'trash' === $status ) {
|
||||||
wp_delete_post( $contributor_post_id );
|
remove_contributor( $contributor_post_id );
|
||||||
}
|
}
|
||||||
|
|
||||||
return $message;
|
return $message;
|
||||||
|
|
|
@ -90,9 +90,7 @@ function process_form_new() {
|
||||||
return $new_pledge_id;
|
return $new_pledge_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ( $contributors as $wporg_username ) {
|
Contributor\add_pledge_contributors( $new_pledge_id, $contributors );
|
||||||
Contributor\create_new_contributor( $wporg_username, $new_pledge_id );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attach logo to the pledge.
|
// Attach logo to the pledge.
|
||||||
wp_update_post( array(
|
wp_update_post( array(
|
||||||
|
|
319
plugins/wporg-5ftf/includes/pledge-log.php
Normal file
319
plugins/wporg-5ftf/includes/pledge-log.php
Normal file
|
@ -0,0 +1,319 @@
|
||||||
|
<?php
|
||||||
|
namespace WordPressDotOrg\FiveForTheFuture\PledgeLog;
|
||||||
|
|
||||||
|
use WordPressDotOrg\FiveForTheFuture;
|
||||||
|
use WordPressDotOrg\FiveForTheFuture\{ Contributor, Pledge, PledgeForm, PledgeMeta };
|
||||||
|
use WP_Post;
|
||||||
|
|
||||||
|
defined( 'WPINC' ) || die();
|
||||||
|
|
||||||
|
const LOG_META_KEY = FiveForTheFuture\PREFIX . '_activity-log';
|
||||||
|
|
||||||
|
// Log display.
|
||||||
|
add_action( 'admin_init', __NAMESPACE__ . '\add_log_meta_box' );
|
||||||
|
|
||||||
|
// Log capture.
|
||||||
|
add_action( 'save_post_' . Pledge\CPT_ID, __NAMESPACE__ . '\capture_save_post', 99, 3 );
|
||||||
|
add_action( 'updated_postmeta', __NAMESPACE__ . '\capture_updated_postmeta', 99, 4 );
|
||||||
|
add_action( 'added_post_meta', __NAMESPACE__ . '\capture_added_post_meta', 99, 4 );
|
||||||
|
add_action( 'transition_post_status', __NAMESPACE__ . '\capture_transition_post_status', 99, 3 );
|
||||||
|
add_action( FiveForTheFuture\PREFIX . '_add_pledge_contributors', __NAMESPACE__ . '\capture_add_pledge_contributors', 99, 3 );
|
||||||
|
add_action( FiveForTheFuture\PREFIX . '_remove_contributor', __NAMESPACE__ . '\capture_remove_contributor', 99, 3 );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a meta box for the log on the custom post type.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function add_log_meta_box() {
|
||||||
|
add_meta_box(
|
||||||
|
'activity-log',
|
||||||
|
__( 'Log', 'wordpressorg' ),
|
||||||
|
__NAMESPACE__ . '\render_log_meta_box',
|
||||||
|
Pledge\CPT_ID,
|
||||||
|
'advanced',
|
||||||
|
'low'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output the log in the meta box.
|
||||||
|
*
|
||||||
|
* @param WP_Post $pledge
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function render_log_meta_box( $pledge ) {
|
||||||
|
$log = get_pledge_log( $pledge->ID );
|
||||||
|
|
||||||
|
require FiveForTheFuture\get_views_path() . 'log.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defaults for a log entry.
|
||||||
|
*
|
||||||
|
* @return array {
|
||||||
|
* @type int $timestamp Time of the event.
|
||||||
|
* @type string $type The type of event. A snake_case or kebab-case string.
|
||||||
|
* @type string $message Description of the event.
|
||||||
|
* @type array $data Details and data related to the event.
|
||||||
|
* @type int $user_id The ID of the logged in user who triggered the event, if applicable.
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
function get_log_entry_template() {
|
||||||
|
return array(
|
||||||
|
'timestamp' => time(),
|
||||||
|
'type' => '',
|
||||||
|
'message' => '',
|
||||||
|
'data' => array(),
|
||||||
|
'user_id' => get_current_user_id(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a time-sorted array of log entries for a particular pledge.
|
||||||
|
*
|
||||||
|
* @param int $pledge_id
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
function get_pledge_log( $pledge_id ) {
|
||||||
|
$log = get_post_meta( $pledge_id, LOG_META_KEY, false );
|
||||||
|
|
||||||
|
if ( ! $log ) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
usort( $log, function( $a, $b ) {
|
||||||
|
if ( $a['timestamp'] === $b['timestamp'] ) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ( $a['timestamp'] < $b['timestamp'] ) ? -1 : 1;
|
||||||
|
} );
|
||||||
|
|
||||||
|
return $log;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new log entry for a particular pledge.
|
||||||
|
*
|
||||||
|
* @param int $pledge_id
|
||||||
|
* @param string $type
|
||||||
|
* @param string $message
|
||||||
|
* @param array $data
|
||||||
|
* @param int $user_id
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function add_log_entry( $pledge_id, $type, $message, array $data = array(), $user_id = 0 ) {
|
||||||
|
$entry = get_log_entry_template();
|
||||||
|
|
||||||
|
$entry['type'] = $type;
|
||||||
|
$entry['message'] = $message;
|
||||||
|
$entry['data'] = $data;
|
||||||
|
|
||||||
|
if ( $user_id ) {
|
||||||
|
// The template defaults to the current user, so this function parameter shouldn't override unless it's different.
|
||||||
|
$entry['user_id'] = $user_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
add_post_meta( $pledge_id, LOG_META_KEY, $entry, false );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record logs for events when saving a post.
|
||||||
|
*
|
||||||
|
* Hooked to "save_post_{$post->post_type}".
|
||||||
|
*
|
||||||
|
* @param int $post_ID Post ID.
|
||||||
|
* @param WP_Post $post Unused. Post object.
|
||||||
|
* @param bool $update Whether this is an existing post being updated or not.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function capture_save_post( $post_ID, $post, $update ) {
|
||||||
|
if ( false === $update ) {
|
||||||
|
add_log_entry(
|
||||||
|
$post_ID,
|
||||||
|
'pledge_created',
|
||||||
|
sprintf(
|
||||||
|
'Pledge created. Status set to <code>%s</code>.',
|
||||||
|
esc_html( get_post_status( $post_ID ) )
|
||||||
|
),
|
||||||
|
PledgeForm\get_form_submission()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record logs for events when postmeta values change.
|
||||||
|
*
|
||||||
|
* @param int $meta_id Unused. ID of updated metadata entry.
|
||||||
|
* @param int $object_id Post ID.
|
||||||
|
* @param string $meta_key Meta key.
|
||||||
|
* @param mixed $meta_value Meta value.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function capture_updated_postmeta( $meta_id, $object_id, $meta_key, $meta_value ) {
|
||||||
|
$post_type = get_post_type( $object_id );
|
||||||
|
|
||||||
|
if ( Pledge\CPT_ID !== $post_type ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$valid_keys = array_keys( PledgeMeta\get_pledge_meta_config( 'user_input' ) );
|
||||||
|
$trimmed_meta_key = str_replace( PledgeMeta\META_PREFIX, '', $meta_key );
|
||||||
|
|
||||||
|
if ( in_array( $trimmed_meta_key, $valid_keys, true ) ) {
|
||||||
|
add_log_entry(
|
||||||
|
$object_id,
|
||||||
|
'pledge_data_changed',
|
||||||
|
sprintf(
|
||||||
|
'Changed <code>%1$s</code> to <code>%2$s</code>.',
|
||||||
|
esc_html( $trimmed_meta_key ),
|
||||||
|
esc_html( $meta_value )
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
$meta_key => $meta_value,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record logs for events when new postmeta values are added (not changed).
|
||||||
|
*
|
||||||
|
* @param int $meta_id Unused. ID of updated metadata entry.
|
||||||
|
* @param int $object_id Post ID.
|
||||||
|
* @param string $meta_key Meta key.
|
||||||
|
* @param mixed $meta_value Meta value.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function capture_added_post_meta( $meta_id, $object_id, $meta_key, $meta_value ) {
|
||||||
|
$post_type = get_post_type( $object_id );
|
||||||
|
|
||||||
|
if ( Pledge\CPT_ID !== $post_type ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ( $meta_key ) {
|
||||||
|
case PledgeMeta\META_PREFIX . 'pledge-email-confirmed':
|
||||||
|
if ( true === $meta_value ) {
|
||||||
|
add_log_entry(
|
||||||
|
$object_id,
|
||||||
|
'pledge_email_confirmed',
|
||||||
|
'Pledge email address confirmed.',
|
||||||
|
array(
|
||||||
|
'email' => get_post_meta( $object_id, PledgeMeta\META_PREFIX . 'org-pledge-email', true ),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record logs for events when a pledge post's status changes.
|
||||||
|
*
|
||||||
|
* @param string $new_status
|
||||||
|
* @param string $old_status
|
||||||
|
* @param WP_Post $post
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function capture_transition_post_status( $new_status, $old_status, WP_Post $post ) {
|
||||||
|
$cpts = array( Pledge\CPT_ID, Contributor\CPT_ID );
|
||||||
|
$post_type = get_post_type( $post );
|
||||||
|
|
||||||
|
if ( ! in_array( $post_type, $cpts, true ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $new_status === $old_status ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ( $post_type ) {
|
||||||
|
case Pledge\CPT_ID:
|
||||||
|
add_log_entry(
|
||||||
|
$post->ID,
|
||||||
|
'pledge_status_changed',
|
||||||
|
sprintf(
|
||||||
|
'Pledge status changed from <code>%1$s</code> to <code>%2$s</code>.',
|
||||||
|
esc_html( $old_status ),
|
||||||
|
esc_html( $new_status )
|
||||||
|
),
|
||||||
|
array()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Contributor\CPT_ID:
|
||||||
|
$pledge = get_post( $post->post_parent );
|
||||||
|
|
||||||
|
add_log_entry(
|
||||||
|
$pledge->ID,
|
||||||
|
'contributor_status_changed',
|
||||||
|
sprintf(
|
||||||
|
'Contributor <code>%1$s</code> status changed from <code>%2$s</code> to <code>%3$s</code>.',
|
||||||
|
esc_html( $post->post_title ),
|
||||||
|
esc_html( $old_status ),
|
||||||
|
esc_html( $new_status )
|
||||||
|
),
|
||||||
|
array()
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record a log for the event of contributors being added to a pledge.
|
||||||
|
*
|
||||||
|
* @param int $pledge_id The post ID of the pledge.
|
||||||
|
* @param array $contributors Array of contributor wporg usernames.
|
||||||
|
* @param array $results Associative array, key is wporg username, value is post ID on success,
|
||||||
|
* or an error code on failure.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function capture_add_pledge_contributors( $pledge_id, $contributors, $results ) {
|
||||||
|
add_log_entry(
|
||||||
|
$pledge_id,
|
||||||
|
'contributors_added',
|
||||||
|
sprintf(
|
||||||
|
'Contributors added: <code>%s</code>',
|
||||||
|
implode( '</code>, <code>', $contributors )
|
||||||
|
),
|
||||||
|
$results
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record a log for the event when a contributor is removed from a pledge.
|
||||||
|
*
|
||||||
|
* @param int $pledge_id The post ID of the pledge.
|
||||||
|
* @param int $contributor_post_id The post ID of the pledge.
|
||||||
|
* @param WP_Post|false|null $result The result of the attempt to trash the post.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function capture_remove_contributor( $pledge_id, $contributor_post_id, $result ) {
|
||||||
|
// If the result isn't a post object, then it was already trashed, or didn't exist.
|
||||||
|
if ( $result instanceof WP_Post ) {
|
||||||
|
$contributor_post = get_post( $contributor_post_id );
|
||||||
|
|
||||||
|
add_log_entry(
|
||||||
|
$pledge_id,
|
||||||
|
'contributor_removed',
|
||||||
|
sprintf(
|
||||||
|
'Contributor removed: <code>%s</code>',
|
||||||
|
esc_html( $contributor_post->post_title )
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'previous_status' => $contributor_post->_wp_trash_meta_status,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,9 +28,11 @@ add_action( 'added_post_meta', __NAMESPACE__ . '\update_generated_meta', 10, 4
|
||||||
/**
|
/**
|
||||||
* Define pledge meta fields and their properties.
|
* Define pledge meta fields and their properties.
|
||||||
*
|
*
|
||||||
|
* @param string $context Optional. The part of the config to return. 'user_input', 'generated', or 'all'.
|
||||||
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
function get_pledge_meta_config( $context = '' ) {
|
function get_pledge_meta_config( $context = 'all' ) {
|
||||||
$user_input = array(
|
$user_input = array(
|
||||||
'org-description' => array(
|
'org-description' => array(
|
||||||
'single' => true,
|
'single' => true,
|
||||||
|
@ -283,6 +285,7 @@ function update_generated_meta( $meta_id, $object_id, $meta_key, $_meta_value )
|
||||||
case META_PREFIX . 'org-name':
|
case META_PREFIX . 'org-name':
|
||||||
if ( 'updated_postmeta' === current_action() ) {
|
if ( 'updated_postmeta' === current_action() ) {
|
||||||
wp_update_post( array(
|
wp_update_post( array(
|
||||||
|
'ID' => $object_id,
|
||||||
'post_title' => $_meta_value,
|
'post_title' => $_meta_value,
|
||||||
) );
|
) );
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ function load() {
|
||||||
require_once get_includes_path() . 'pledge-form.php';
|
require_once get_includes_path() . 'pledge-form.php';
|
||||||
require_once get_includes_path() . 'directory.php';
|
require_once get_includes_path() . 'directory.php';
|
||||||
require_once get_includes_path() . 'xprofile.php';
|
require_once get_includes_path() . 'xprofile.php';
|
||||||
|
require_once get_includes_path() . 'pledge-log.php';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
55
plugins/wporg-5ftf/views/log.php
Normal file
55
plugins/wporg-5ftf/views/log.php
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
<?php
|
||||||
|
namespace WordPressDotOrg\FiveForTheFuture\View;
|
||||||
|
|
||||||
|
/** @var array $log */
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="5ftf-activity-log">
|
||||||
|
<?php if ( ! empty( $log ) ) : ?>
|
||||||
|
|
||||||
|
<table class="striped widefat">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td>Date</td>
|
||||||
|
<td>Entry</td>
|
||||||
|
<td>User</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ( $log as $entry ) : ?>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<?php echo esc_html( date( 'Y-m-d H:i:s', $entry['timestamp'] ) ); ?>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<?php if ( ! empty( $entry['data'] ) ) : ?>
|
||||||
|
<details>
|
||||||
|
<summary>
|
||||||
|
<?php echo wp_kses_data( $entry['message'] ); ?>
|
||||||
|
</summary>
|
||||||
|
<pre><?php echo esc_html( print_r( $entry['data'], true ) ); ?></pre>
|
||||||
|
</details>
|
||||||
|
<?php else : ?>
|
||||||
|
<?php echo wp_kses_data( $entry['message'] ); ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<?php
|
||||||
|
$user = get_user_by( 'id', $entry['user_id'] );
|
||||||
|
if ( $user ) : ?>
|
||||||
|
<?php echo sanitize_user( $user->user_login ); ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<?php else : ?>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
There are no log entries.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
Loading…
Reference in a new issue