Files

1879 lines
57 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* Sensor: Content
*
* Content sensor class file.
*
* @since 4.6.0
* @package wsal
* @subpackage sensors
*/
declare(strict_types=1);
namespace WSAL\WP_Sensors;
use WSAL\Helpers\WP_Helper;
use WSAL\Controllers\Alert_Manager;
// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( '\WSAL\WP_Sensors\WP_Content_Sensor' ) ) {
/**
* WordPress contents (posts, pages and custom posts).
*
* Apart from some other events, the following were migrated from plugins & themes sensor:
* 5019 A plugin created a post
* 5025 A plugin deleted a post
*
* @package wsal
* @subpackage sensors
*
* @since 4.5.0
*/
class WP_Content_Sensor {
/**
* Holds the name of the meta used to setting the lock status of the post
*/
const LOCK_META_NAME = '_edit_lock';
/**
* Old post.
*
* @var stdClass
*
* @since 4.5.0
*/
private static $old_post = null;
/**
* Old permalink.
*
* @var string
*
* @since 4.5.0
*/
private static $old_link = null;
/**
* Old categories.
*
* @var array
*
* @since 4.5.0
*/
private static $old_cats = null;
/**
* Old tags.
*
* @var array
*
* @since 4.5.0
*/
private static $old_tags = null;
/**
* Old path to file.
*
* @var string
*
* @since 4.5.0
*/
private static $old_tmpl = null;
/**
* Old post is marked as sticky.
*
* @var boolean
*
* @since 4.5.0
*/
private static $old_sticky = null;
/**
* Old Post Status.
*
* @var string
*
* @since 4.5.0
*/
private static $old_status = null;
/**
* Old Post Meta.
*
* @var string
*
* @since 4.5.0
*/
private static $old_meta = null;
/**
* Inits the main hooks
*
* @return void
*
* @since 4.5.0
*/
public static function init() {
\add_action( 'pre_post_update', array( __CLASS__, 'get_before_post_edit_data' ), 10, 2 );
\add_action( 'save_post', array( __CLASS__, 'post_changed' ), 10, 3 );
\add_action( 'set_object_terms', array( __CLASS__, 'post_terms_changed' ), 10, 4 );
\add_action( 'post_stuck', array( __CLASS__, 'post_stuck_event' ), 10, 1 );
\add_action( 'post_unstuck', array( __CLASS__, 'post_unstuck_event' ), 10, 1 );
\add_action( 'delete_post', array( __CLASS__, 'event_post_deleted' ), 10, 1 );
\add_action( 'wp_trash_post', array( __CLASS__, 'event_post_trashed' ), 10, 1 );
\add_action( 'untrash_post', array( __CLASS__, 'event_post_untrashed' ) );
\add_action( 'future_to_publish', array( __CLASS__, 'event_publish_future' ), 10, 1 );
\add_action( 'admin_action_edit', array( __CLASS__, 'edit_post_in_gutenberg' ), 10 );
\add_filter( 'post_edit_form_tag', array( __CLASS__, 'edit_post_in_classic' ), 10, 1 );
\add_action( 'wp_head', array( __CLASS__, 'viewing_post' ), 10 );
\add_action( 'create_category', array( __CLASS__, 'event_category_creation' ), 10, 1 );
\add_action( 'create_post_tag', array( __CLASS__, 'event_tag_creation' ), 10, 1 );
\add_action( 'pre_delete_term', array( __CLASS__, 'check_taxonomy_term_deletion' ), 10, 2 );
\add_filter( 'wp_update_term_data', array( __CLASS__, 'event_update_term_data' ), 10, 4 );
\add_filter( 'add_post_metadata', array( __CLASS__, 'check_added_meta' ), 10, 5 );
\add_filter( 'delete_post_metadata', array( __CLASS__, 'check_deleted_meta' ), 10, 5 );
\add_action( 'update_post_meta', array( __CLASS__, 'before_changing_meta' ), 10, 4 );
\add_action( 'updated_post_meta', array( __CLASS__, 'check_changed_meta' ), 10, 4 );
// Check if MainWP Child Plugin exists.
if ( \WpSecurityAuditLog::is_mainwp_active() ) {
add_action( 'mainwp_before_post_update', array( __CLASS__, 'event_mainwp_init' ), 10, 2 );
}
}
/**
* Get Post Data.
*
* Collect old post data before post update event.
*
* @param int $post_id - Post ID.
*
* @since 5.0.0
*/
public static function get_before_post_edit_data( $post_id ) {
$post_id = (int) $post_id; // Making sure that the post id is integer.
$post = \get_post( $post_id ); // Get post.
// If post exists.
if ( ! empty( $post ) && $post instanceof \WP_Post ) {
self::$old_post = $post;
self::$old_link = get_permalink( $post_id );
self::$old_tmpl = self::get_post_template( self::$old_post );
self::$old_cats = self::get_post_categories( self::$old_post );
self::$old_tags = self::get_post_tags( self::$old_post );
self::$old_sticky = in_array( $post_id, get_option( 'sticky_posts' ), true );
self::$old_status = $post->post_status;
self::$old_meta = get_post_meta( $post_id );
}
}
/**
* Check all the post changes.
*
* @param integer $post_id - Post ID.
* @param WP_Post $post - WP Post object.
* @param boolean $update - True if post update, false if post is new.
*
* @since 4.5.0
*/
public static function post_changed( $post_id, $post, $update ) {
// Ignore if post type is empty, revision or trash.
if ( empty( $post->post_type ) || 'revision' === $post->post_type || 'trash' === $post->post_status ) {
return;
}
if ( null === self::$old_post ) {
return;
}
// Ignorable states.
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
// Check post creation event.
if ( self::$old_post && 'auto-draft' === self::$old_post->post_status && 'draft' === $post->post_status ) {
self::check_post_creation( self::$old_post, $post );
}
return;
}
/**
* Post Changed.
*
* Don't let the second request for meta update from Gutenberg pass this checkpoint.
*
* Only pass these requests:
* 1. Rest request from Gutenberg.
* 2. Classic editor request.
* 3. Quick edit ajax request.
*
* @since 3.4
*/
if ( ! defined( 'REST_REQUEST' ) && ! defined( 'DOING_AJAX' ) ) {
// Either Gutenberg's second post request or classic editor's request.
if ( ! isset( $_REQUEST['classic-editor'] ) ) {
$editor_replace = get_option( 'classic-editor-replace', 'classic' );
$allow_users = get_option( 'classic-editor-allow-users', 'disallow' );
// If block editor is selected and users are not allowed to switch editors then it is Gutenberg's second request.
if ( 'block' === $editor_replace && 'disallow' === $allow_users ) {
return;
}
if ( 'allow' === $allow_users ) { // if users are allowed to switch then it is Gutenberg's second request.
return;
}
}
}
if ( $update ) {
$status_event = self::check_status_change( self::$old_post, $post );
if ( 2001 !== $status_event && 'auto-draft' !== self::$old_post->post_status ) {
// Handle update post events.
$changes = 0;
$changes = self::check_author_change( self::$old_post, $post )
+ self::check_parent_change( self::$old_post, $post )
+ self::check_visibility_change( self::$old_post, $post, self::$old_status, $post->post_status )
+ self::check_date_change( self::$old_post, $post )
+ self::check_permalink_change( self::$old_link, get_permalink( $post->ID ), $post )
+ self::check_comments_pings( self::$old_post, $post );
// If a status change event has occurred, then don't log event 2002 (post modified).
$changes = $status_event ? true : $changes;
if ( '1' === $changes ) {
remove_action( 'save_post', array( __CLASS__, 'post_changed' ), 10, 3 );
}
self::check_modification_change( $post->ID, self::$old_post, $post, $changes );
}
} else {
// If not update then check post creation.
self::check_post_creation( self::$old_post, $post );
}
}
/**
* Check if post terms changed via Gutenberg.
*
* @param int $post_id - Post ID.
* @param array $terms - Array of terms.
* @param array $tt_ids - Array of taxonomy term ids.
* @param string $taxonomy - Taxonomy slug.
*
* @since 4.5.0
*/
public static function post_terms_changed( $post_id, $terms, $tt_ids, $taxonomy ) {
$post = get_post( $post_id );
if ( is_wp_error( $post ) ) {
return;
}
if ( null === $post ) {
return;
}
if ( 'auto-draft' === $post->post_status ) {
return;
}
// Support for Admin Columns Pro plugin and its add-on.
if ( isset( $_POST['_ajax_nonce'] ) && ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_ajax_nonce'] ) ), 'ac-ajax' ) ) {
return;
}
if ( isset( $_POST['action'] ) && 'acp_editing_single_request' === sanitize_text_field( wp_unslash( $_POST['action'] ) ) ) {
return;
}
if ( 'post_tag' === $taxonomy ) {
// Check tags change event.
self::check_tags_change( self::$old_tags, self::get_post_tags( $post ), $post );
} else {
// Check categories change event.
self::check_categories_change( self::$old_cats, self::get_post_categories( $post ), $post );
}
}
/**
* Post Stuck Event.
*
* @param integer $post_id - Post ID.
*
* @since 4.5.0
*/
public static function post_stuck_event( $post_id ) {
self::log_sticky_post_event( $post_id, 2049 );
}
/**
* Post Unstuck Event.
*
* @param integer $post_id - Post ID.
*
* @since 4.5.0
*/
public static function post_unstuck_event( $post_id ) {
self::log_sticky_post_event( $post_id, 2050 );
}
/**
* Post permanently deleted.
*
* @param integer $post_id - Post ID.
*
* @since 4.5.0
*/
public static function event_post_deleted( $post_id ) {
// Exclude CPTs from external plugins.
$post = get_post( $post_id );
$event = 2008;
// Check WordPress backend operations.
if ( self::check_auto_draft( $event, $post->post_title ) ) {
return;
}
$event_data = self::get_post_event_data( $post ); // Get event data.
// Check if this was initiated by a plugin.
$request_params = \WSAL\Helpers\PHP_Helper::get_filtered_request_data();
if ( empty( $request_params['action'] ) && isset( $request_params['page'] ) ) {
$event = 5025;
$event_data = array(
'PostID' => $post->ID,
'PostType' => $post->post_type,
'PostTitle' => $post->post_title,
'PostStatus' => $post->post_status,
'Username' => 'Plugins',
);
}
Alert_Manager::trigger_event( $event, $event_data ); // Log event.
}
/**
* Post moved to the trash.
*
* @param integer $post_id - Post ID.
*
* @since 4.5.0
*/
public static function event_post_trashed( $post_id ) {
$post = get_post( $post_id );
$editor_link = self::get_editor_link( $post_id );
Alert_Manager::trigger_event(
2012,
array(
'PostID' => $post->ID,
'PostType' => $post->post_type,
'PostTitle' => $post->post_title,
'PostStatus' => $post->post_status,
'PostDate' => $post->post_date,
'PostUrl' => get_permalink( $post->ID ),
$editor_link['name'] => $editor_link['value'],
)
);
}
/**
* Post restored from trash.
*
* @param integer $post_id - Post ID.
*
* @since 4.5.0
*/
public static function event_post_untrashed( $post_id ) {
$post = get_post( $post_id );
$editor_link = self::get_editor_link( $post_id );
Alert_Manager::trigger_event(
2014,
array(
'PostID' => $post->ID,
'PostType' => $post->post_type,
'PostTitle' => $post->post_title,
'PostStatus' => $post->post_status,
'PostDate' => $post->post_date,
'PostUrl' => get_permalink( $post->ID ),
$editor_link['name'] => $editor_link['value'],
)
);
remove_action( 'save_post', array( __CLASS__, 'post_changed' ), 10, 3 );
}
/**
* Post future publishing.
*
* @param integer $post_id - Post ID.
*
* @since 4.5.0
*/
public static function event_publish_future( $post_id ) {
$post = get_post( $post_id );
$editor_link = self::get_editor_link( $post_id );
Alert_Manager::trigger_event(
2001,
array(
'PostID' => $post->ID,
'PostType' => $post->post_type,
'PostTitle' => $post->post_title,
'PostStatus' => $post->post_status,
'PostDate' => $post->post_date,
'PostUrl' => get_permalink( $post->ID ),
$editor_link['name'] => $editor_link['value'],
)
);
remove_action( 'save_post', array( __CLASS__, 'post_changed' ), 10, 3 );
}
/**
* Alert for Editing of Posts and Custom Post Types in Gutenberg.
*
* @since 4.5.0
*/
public static function edit_post_in_gutenberg() {
global $pagenow;
if ( 'post.php' !== $pagenow ) {
return;
}
$post_id = isset( $_GET['post'] ) ? (int) sanitize_text_field( wp_unslash( $_GET['post'] ) ) : false;
// Check post id.
if ( empty( $post_id ) ) {
return;
}
if ( is_user_logged_in() && is_admin() ) {
// Get post.
$post = get_post( $post_id );
// Log event.
self::post_opened_in_editor( $post );
}
}
/**
* Alerts for Editing of Posts, Pages and Custom Post Types.
*
* @param WP_Post $post - Post.
*
* @since 4.5.0
*/
public static function edit_post_in_classic( $post ) {
if ( is_user_logged_in() && is_admin() ) {
// Log event.
self::post_opened_in_editor( $post );
}
return $post;
}
/**
* Post View Event.
*
* Alerts for Viewing of Posts and Custom Post Types.
*
* @since 4.5.0
*/
public static function viewing_post() {
// Retrieve the current post object.
$post = get_queried_object();
if ( is_user_logged_in() && ! is_admin() ) {
$current_path = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : false;
if (
! empty( $_SERVER['HTTP_REFERER'] )
&& ! empty( $current_path )
&& false !== strpos( sanitize_text_field( wp_unslash( $_SERVER['HTTP_REFERER'] ) ), $current_path )
) {
// Ignore this if we were on the same page so we avoid double audit entries.
return;
}
if ( ! empty( $post->post_title ) ) {
$edit_link = self::get_editor_link( $post->ID ); // Get editor link.
$post_data = self::get_post_event_data( $post ); // Get event post data.
// Update post URL based on current actual path.
if ( WP_Helper::is_multisite() && ! is_subdomain_install() ) {
// For multisite using subfolders, remove the subfolder.
$subdir_path = parse_url( home_url(), PHP_URL_PATH ); // phpcs:ignore
if ( ! is_null( $subdir_path ) ) {
$escaped = str_replace( '/', '\/', preg_quote( $subdir_path ) ); // phpcs:ignore
$current_path = preg_replace( '/' . $escaped . '/', '', $current_path );
}
}
// Bail if this don't have this, as it's probably an archive.
if ( ! isset( $post_data['PostUrl'] ) ) {
return;
}
$full_current_path = home_url( $current_path );
if ( $full_current_path !== $post_data['PostUrl'] ) {
$post_data['PostUrl'] = esc_url( $full_current_path );
}
// Set editor link.
$post_data[ $edit_link['name'] ] = $edit_link['value'];
Alert_Manager::trigger_event( 2101, $post_data );
}
}
}
/**
* New category created.
*
* @param integer $category_id - Category ID.
*
* @since 4.5.0
*/
public static function event_category_creation( $category_id ) {
$category = get_category( $category_id );
$category_link = self::get_taxonomy_edit_link( $category_id, 'category' );
Alert_Manager::trigger_event(
2023,
array(
'CategoryName' => $category->name,
'Slug' => $category->slug,
'CategoryLink' => $category_link,
)
);
}
/**
* New tag created.
*
* @param int $tag_id - Tag ID.
*
* @since 4.5.0
*/
public static function event_tag_creation( $tag_id ) {
$tag = get_tag( $tag_id );
$tag_link = self::get_taxonomy_edit_link( $tag_id );
Alert_Manager::trigger_event(
2121,
array(
'TagName' => $tag->name,
'Slug' => $tag->slug,
'TagLink' => $tag_link,
)
);
}
/**
* Taxonomy Terms Deleted Events.
*
* @param integer $term_id - Term ID.
* @param string $taxonomy - Taxonomy Name.
*
* @since 4.5.0
*/
public static function check_taxonomy_term_deletion( $term_id, $taxonomy ) {
if ( 'post_tag' === $taxonomy ) {
$tag = get_tag( $term_id );
Alert_Manager::trigger_event(
2122,
array(
'TagID' => $term_id,
'TagName' => $tag->name,
'Slug' => $tag->slug,
)
);
} elseif ( 'category' === $taxonomy ) {
$category = get_category( $term_id );
$category_link = self::get_taxonomy_edit_link( $term_id, $taxonomy );
Alert_Manager::trigger_event(
2024,
array(
'CategoryID' => $term_id,
'CategoryName' => $category->cat_name,
'Slug' => $category->slug,
'CategoryLink' => $category_link,
)
);
}
}
/**
* Triggered when term data is updated.
*
* @param array $data Term data to be updated.
* @param int $term_id Term ID.
* @param string $taxonomy Taxonomy slug.
* @param array $args Arguments passed to wp_update_term().
*
* @since 4.5.0
*/
public static function event_update_term_data( $data, $term_id, $taxonomy, $args ) {
// Get new data.
$new_name = isset( $data['name'] ) ? $data['name'] : false;
$new_slug = isset( $data['slug'] ) ? $data['slug'] : false;
$new_desc = isset( $args['description'] ) ? $args['description'] : false;
$new_parent = isset( $args['parent'] ) ? $args['parent'] : false;
// Get old data.
$term = get_term( $term_id, $taxonomy );
$old_name = $term->name;
$old_slug = $term->slug;
$old_desc = $term->description;
$old_parent = $term->parent;
$term_link = self::get_taxonomy_edit_link( $term_id, $taxonomy );
// Check if the taxonomy is `post tag`.
if ( 'post_tag' === $taxonomy ) {
// Update if both names are not same.
if ( $old_name !== $new_name ) {
Alert_Manager::trigger_event(
2123,
array(
'old_name' => $old_name,
'new_name' => $new_name,
'TagLink' => $term_link,
'Slug' => $new_slug,
)
);
}
// Update if both slugs are not same.
if ( $old_slug !== $new_slug ) {
Alert_Manager::trigger_event(
2124,
array(
'tag' => $new_name,
'old_slug' => $old_slug,
'new_slug' => $new_slug,
'TagLink' => $term_link,
)
);
}
// Update if both descriptions are not same.
if ( $old_desc !== $new_desc ) {
Alert_Manager::trigger_event(
2125,
array(
'tag' => $new_name,
'TagLink' => $term_link,
'old_desc' => $old_desc,
'new_desc' => $new_desc,
)
);
}
} elseif ( 'category' === $taxonomy ) { // Check if the taxonomy is `category`.
// Log event if both names are not same.
if ( $old_name !== $new_name ) {
Alert_Manager::trigger_event(
2127,
array(
'old_name' => $old_name,
'new_name' => $new_name,
'slug' => $new_slug,
'cat_link' => $term_link,
)
);
}
// Log event if both slugs are not same.
if ( $old_slug !== $new_slug ) {
Alert_Manager::trigger_event(
2128,
array(
'CategoryName' => $new_name,
'old_slug' => $old_slug,
'new_slug' => $new_slug,
'cat_link' => $term_link,
)
);
}
if ( 0 !== $old_parent ) {
$old_parent_obj = get_category( $old_parent );
$old_parent_name = empty( $old_parent_obj ) ? 'no parent' : $old_parent_obj->name;
} else {
$old_parent_name = 'no parent';
}
if ( 0 !== $new_parent ) {
$new_parent_obj = get_category( $new_parent );
$new_parent_name = empty( $new_parent_obj ) ? 'no parent' : $new_parent_obj->name;
} else {
$new_parent_name = 'no parent';
}
if ( $old_parent_name !== $new_parent_name ) {
Alert_Manager::trigger_event(
2052,
array(
'CategoryName' => $new_name,
'OldParent' => $old_parent_name,
'NewParent' => $new_parent_name,
'CategoryLink' => $term_link,
'Slug' => $new_slug,
)
);
}
}
return $data; // Return data for the filter.
}
/**
* Checks if selected metadata items have changed. Function is called from different contexts.
*
* @param int $post_id Post ID.
* @param string $meta_key Meta key.
* @param mixed $meta_value Meta value.
* @param mixed $default_result Default result. The name is misleading - it holds the meta ID value.
*
* @return int - The meta ID.
*
* @since 4.5.0
*/
private static function check_selected_meta_change( $post_id, $meta_key, $meta_value, $default_result ) {
if ( ! $post_id ) {
return $default_result;
}
switch ( $meta_key ) {
case '_wp_page_template':
self::check_template_change( $post_id, $meta_value );
break;
case '_thumbnail_id':
self::check_featured_image_change( $post_id, $meta_value );
break;
default:
return $default_result;
}
return $default_result;
}
/**
* Check Page Template Update.
*
* @param int $meta_id ID of updated metadata entry.
* @param int $post_id Post ID.
* @param string $meta_key Meta key.
* @param mixed $meta_value Meta value.
*
* @return int ID of updated metadata entry.
*
* @since 4.5.0
*/
public static function check_changed_meta( $meta_id, $post_id, $meta_key, $meta_value ) {
return self::check_selected_meta_change( $post_id, $meta_key, $meta_value, $meta_id );
}
/**
* Check Page Template Update for deletions.
*
* @param null|bool $delete Whether to allow metadata deletion of the given type.
* @param int $object_id ID of the object metadata is for.
* @param string $meta_key Metadata key.
* @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
* @param bool $delete_all Whether to delete the matching metadata entries
* for all objects, ignoring the specified $object_id.
* Default false.
*
* @return bool|null Whether to allow metadata deletion of the given type.
*
* @since 4.5.0
*/
public static function check_deleted_meta( $delete, $object_id, $meta_key, $meta_value, $delete_all ) {
return self::check_selected_meta_change( $object_id, $meta_key, $meta_value, $delete );
}
/**
* Check Page Template Update for additions.
*
* @param null|bool $check Whether to allow adding metadata for the given type.
* @param int $object_id ID of the object metadata is for.
* @param string $meta_key Metadata key.
* @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
* @param bool $unique Whether the specified meta key should be unique for the object.
*
* @return bool|null Whether to allow metadata addition of the given type.
*
* @since 4.5.0
*/
public static function check_added_meta( $check, $object_id, $meta_key, $meta_value, $unique ) {
return self::check_selected_meta_change( $object_id, $meta_key, $meta_value, $check );
}
/**
* Check Page Template Update.
*
* @param int $post_id Post ID.
* @param mixed $meta_value Meta value.
*
* @since 4.5.0
*/
public static function check_template_change( $post_id, $meta_value ) {
$post = get_post( $post_id );
$old_tmpl = ( self::$old_tmpl && 'page' !== basename( self::$old_tmpl, '.php' ) ) ? ucwords( str_replace( array( '-', '_' ), ' ', basename( self::$old_tmpl, '.php' ) ) ) : __( 'Default template', 'wp-security-audit-log' );
$new_tmpl = ( $meta_value ) ? ucwords( str_replace( array( '-', '_' ), ' ', basename( $meta_value ) ) ) : __( 'Default', 'wp-security-audit-log' );
if ( $old_tmpl !== $new_tmpl ) {
$editor_link = self::get_editor_link( $post_id );
Alert_Manager::trigger_event(
2048,
array(
'PostID' => $post->ID,
'PostType' => $post->post_type,
'PostTitle' => $post->post_title,
'PostStatus' => $post->post_status,
'PostDate' => $post->post_date,
'OldTemplate' => $old_tmpl,
'NewTemplate' => $new_tmpl,
$editor_link['name'] => $editor_link['value'],
)
);
}
}
/**
* Fires immediately before updating a post's metadata.
*
* @param int $meta_id ID of metadata entry to update.
* @param int $object_id Post ID.
* @param string $meta_key Metadata key.
* @param mixed $meta_value Metadata value. This will be a PHP-serialized string representation of the value
* if the value is an array, an object, or itself a PHP-serialized string.
*
* @since 4.5.0
*/
public static function before_changing_meta( $meta_id, $object_id, $meta_key, $meta_value ) {
if ( self::LOCK_META_NAME === $meta_key ) {
self::fire_lock_change( (int) $object_id, (string) $meta_value );
}
}
/**
* Fires lock owner changing.
*
* @param integer $post_id - The ID of the post.
* @param string $meta_value - The meta value - that is in format "1649934679:2"
* where the "1649934679" is the timestamp of the locking
* and "2" is the ID of the \WP_User which currently editing the post.
*
* @return void
*
* @since 4.5.0
*/
public static function fire_lock_change( int $post_id, string $meta_value ) {
if ( $meta_value ) {
if ( ! function_exists( 'wp_check_post_lock' ) ) {
require_once ABSPATH . 'wp-admin/includes/post.php';
}
$user_id = \wp_check_post_lock( $post_id ); // The id of the user currently editing the post.
/**
* The post is currently edited by someone - proceed with extracting the new user editor
*/
if ( $user_id ) {
$lock = explode( ':', $meta_value );
$new_user_id = $lock[1];
if ( $new_user_id ) {
if ( $new_editor_user = get_userdata( $new_user_id ) ) { //phpcs:ignore
$post = get_post( $post_id );
$editor_link = self::get_editor_link( $post_id );
Alert_Manager::trigger_event(
2133,
array(
'PostID' => $post->ID,
'PostType' => $post->post_type,
'PostTitle' => $post->post_title,
'PostStatus' => $post->post_status,
$editor_link['name'] => $editor_link['value'],
'user' => get_userdata( $user_id )->display_name,
)
);
}
}
}
}
}
/**
* Check Post Featured Image Update.
*
* @param int $post_id Post ID.
* @param mixed $meta_value Meta value.
*
* @since 4.5.0
*/
public static function check_featured_image_change( $post_id, $meta_value ) {
$previous_featured_image = ( isset( self::$old_meta['_thumbnail_id'][0] ) ) ? wp_get_attachment_metadata( self::$old_meta['_thumbnail_id'][0] ) : false;
$new_featured_image = wp_get_attachment_metadata( $meta_value );
if ( empty( $new_featured_image['file'] ) && empty( $previous_featured_image['file'] ) ) {
return;
}
$event_type = 'modified';
if ( empty( $previous_featured_image['file'] ) && ! empty( $new_featured_image['file'] ) ) {
$event_type = 'added';
} elseif ( ! empty( $previous_featured_image['file'] ) && empty( $new_featured_image['file'] ) ) {
$event_type = 'removed';
}
$previous_image = is_array( $previous_featured_image ) && array_key_exists( 'file', $previous_featured_image ) ? $previous_featured_image['file'] : __( 'No previous image', 'wp-security-audit-log' );
$new_image = is_array( $new_featured_image ) && array_key_exists( 'file', $new_featured_image ) ? $new_featured_image['file'] : __( 'No image', 'wp-security-audit-log' );
$post = get_post( $post_id );
$editor_link = self::get_editor_link( $post_id );
Alert_Manager::trigger_event(
2130,
array(
'PostID' => $post->ID,
'PostType' => $post->post_type,
'PostTitle' => $post->post_title,
'PostStatus' => $post->post_status,
'PostDate' => $post->post_date,
'previous_image' => $previous_image,
'new_image' => $new_image,
$editor_link['name'] => $editor_link['value'],
'EventType' => $event_type,
)
);
}
/**
* Collect old post data before MainWP Post update event.
*
* @param array $new_post - Array of new post data.
* @param array $post_custom - Array of data related to MainWP.
*
* @since 4.5.0
*/
public static function event_mainwp_init( $new_post, $post_custom ) {
// Get post id.
$post_id = isset( $post_custom['_mainwp_edit_post_id'][0] ) ? $post_custom['_mainwp_edit_post_id'][0] : false;
$post_id = (int) $post_id;
// Check if ID exists.
if ( $post_id ) {
// Get post.
$post = get_post( $post_id );
// If post exists.
if ( ! empty( $post ) ) {
self::$old_post = $post;
self::$old_link = get_permalink( $post_id );
self::$old_tmpl = self::get_post_template( self::$old_post );
self::$old_cats = self::get_post_categories( self::$old_post );
self::$old_tags = self::get_post_tags( self::$old_post );
self::$old_sticky = in_array( $post_id, get_option( 'sticky_posts' ), true );
}
}
}
/**
* Get the template path.
*
* @param WP_Post $post - The post.
* @return string - Full path to file.
*
* @since 4.5.0
*/
private static function get_post_template( $post ) {
if ( ! isset( $post->ID ) ) {
return '';
}
$id = $post->ID;
$template = get_page_template_slug( $id );
$pagename = $post->post_name;
$templates = array();
if ( $template && 0 === validate_file( $template ) ) {
$templates[] = $template;
}
if ( $pagename ) {
$templates[] = "page-$pagename.php";
}
if ( $id ) {
$templates[] = "page-$id.php";
}
$templates[] = 'page.php';
return get_query_template( 'page', $templates );
}
/**
* Get post categories (array of category names).
*
* @param stdClass $post - The post.
* @return array - List of categories.
*
* @since 4.5.0
*/
private static function get_post_categories( $post ) {
return ! isset( $post->ID ) ? array() : wp_get_post_categories( $post->ID, array( 'fields' => 'names' ) );
}
/**
* Get post tags (array of tag names).
*
* @param stdClass $post - The post.
* @return array - List of tags.
*
* @since 4.5.0
*/
private static function get_post_tags( $post ) {
return ! isset( $post->ID ) ? array() : wp_get_post_tags( $post->ID, array( 'fields' => 'names' ) );
}
/**
* Check post creation.
*
* @global array $_POST
*
* @param WP_Post $old_post - Old post.
* @param WP_Post $new_post - New post.
*
* @since 4.5.0
*/
private static function check_post_creation( $old_post, $new_post ) {
if ( ! empty( $new_post ) && $new_post instanceof \WP_Post ) {
$event = 0;
$is_scheduled = false;
switch ( $new_post->post_status ) {
case 'publish':
$event = 2001;
break;
case 'draft':
$event = 2000;
break;
case 'future':
$event = 2074;
$is_scheduled = true;
break;
case 'pending':
$event = 2073;
break;
default:
break;
}
if ( $event ) {
$editor_link = self::get_editor_link( $new_post->ID ); // Editor link.
$event_data = self::get_post_event_data( $new_post ); // Post event data.
// Set editor link in the event data.
$event_data[ $editor_link['name'] ] = $editor_link['value'];
if ( $is_scheduled ) {
$event_data['PublishingDate'] = $new_post->post_date;
Alert_Manager::trigger_event( $event, $event_data );
} else {
// So far we assume that the action is initiated by a user, let's check if it was actually initiated
// by a plugin.
$request_params = \WSAL\Helpers\PHP_Helper::get_filtered_request_data();
if ( array_key_exists( 'plugin', $request_params ) && ! empty( $request_params['plugin'] ) ) {
// Event initiated by a plugin.
$plugin_name = $request_params['plugin'];
$plugin_data = get_plugin_data( trailingslashit( WP_PLUGIN_DIR ) . $plugin_name );
$event_data = array(
'PluginName' => ( $plugin_data && isset( $plugin_data['Name'] ) ) ? $plugin_data['Name'] : false,
'PostID' => $new_post->ID,
'PostType' => $new_post->post_type,
'PostTitle' => $new_post->post_title,
'PostStatus' => $new_post->post_status,
'Username' => 'Plugins',
$editor_link['name'] => $editor_link['value'],
);
}
Alert_Manager::trigger_event( $event, $event_data );
}
}
}
}
/**
* Get editor link.
*
* @param int $post_id - The \WP_Post post ID.
*
* @return array $editor_link - Name and value link.
*
* @since 4.5.0
*/
public static function get_editor_link( $post_id ): array {
$post_id = \is_object( $post_id ) ? $post_id->ID : (int) $post_id;
return array(
'name' => 'EditorLinkPost',
'value' => \get_edit_post_link( $post_id ),
);
}
/**
* Return Post Event Data.
*
* @param WP_Post $post - WP Post object.
* @return array
*
* @since 4.5.0
*/
public static function get_post_event_data( $post ) {
if ( ! empty( $post ) && $post instanceof \WP_Post ) {
$event_data = array(
'PostID' => $post->ID,
'PostType' => $post->post_type,
'PostTitle' => $post->post_title,
'PostStatus' => $post->post_status,
'PostDate' => $post->post_date,
'PostUrl' => get_permalink( $post->ID ),
);
return $event_data;
}
return array();
}
/**
* Author changed.
*
* @param stdClass $oldpost - Old post.
* @param stdClass $newpost - New post.
*
* @since 4.5.0
*/
private static function check_author_change( $oldpost, $newpost ) {
if ( $oldpost->post_author !== $newpost->post_author ) {
$editor_link = self::get_editor_link( $oldpost->ID );
$old_author = get_userdata( $oldpost->post_author );
$old_author = ( is_object( $old_author ) ) ? $old_author->user_login : 'N/A';
$new_author = get_userdata( $newpost->post_author );
$new_author = ( is_object( $new_author ) ) ? $new_author->user_login : 'N/A';
Alert_Manager::trigger_event(
2019,
array(
'PostID' => $oldpost->ID,
'PostType' => $oldpost->post_type,
'PostTitle' => $oldpost->post_title,
'PostStatus' => $oldpost->post_status,
'PostDate' => $oldpost->post_date,
'PostUrl' => get_permalink( $oldpost->ID ),
'OldAuthor' => $old_author,
'NewAuthor' => $new_author,
$editor_link['name'] => $editor_link['value'],
)
);
return 1;
}
}
/**
* Status changed.
*
* @param stdClass $oldpost - Old post.
* @param stdClass $newpost - New post.
*
* @return integer
*
* @since 4.5.0
*/
private static function check_status_change( $oldpost, $newpost ) {
if ( $oldpost->post_status !== $newpost->post_status ) {
$event = 0;
$is_scheduled = false;
if ( 'auto-draft' === $oldpost->post_status && 'draft' === $newpost->post_status ) {
$event = 2000;
} elseif ( 'publish' === $newpost->post_status ) {
$event = 2001;
} elseif ( 'pending' === $newpost->post_status ) {
$event = 2073;
} elseif ( 'future' === $newpost->post_status ) {
$event = 2074;
$is_scheduled = true;
} else {
$event = 2021;
}
if ( $event ) {
$editor_link = self::get_editor_link( $newpost->ID ); // Editor link.
$event_data = self::get_post_event_data( $newpost ); // Post event data.
// Set editor link in the event data.
$event_data[ $editor_link['name'] ] = $editor_link['value'];
if ( $is_scheduled ) {
$event_data['PublishingDate'] = $newpost->post_date;
Alert_Manager::trigger_event( $event, $event_data );
} elseif ( 2021 === $event ) {
$event_data['OldStatus'] = $oldpost->post_status;
$event_data['NewStatus'] = $newpost->post_status;
Alert_Manager::trigger_event( $event, $event_data );
} else {
// NOTE: this triggers if NOT firing event 5019.
Alert_Manager::trigger_event_if( $event, $event_data, array( __CLASS__, 'plugin_not_created_post' ) );
}
}
return $event;
}
}
/**
* Post parent changed.
*
* @param stdClass $oldpost - Old post.
* @param stdClass $newpost - New post.
*
* @since 4.5.0
*/
private static function check_parent_change( $oldpost, $newpost ) {
if ( $oldpost->post_parent !== $newpost->post_parent && 'page' === $newpost->post_type ) {
$editor_link = self::get_editor_link( $oldpost->ID );
Alert_Manager::trigger_event(
2047,
array(
'PostID' => $oldpost->ID,
'PostType' => $oldpost->post_type,
'PostTitle' => $oldpost->post_title,
'PostStatus' => $oldpost->post_status,
'PostDate' => $oldpost->post_date,
'OldParent' => $oldpost->post_parent,
'NewParent' => $newpost->post_parent,
'OldParentName' => $oldpost->post_parent ? get_the_title( $oldpost->post_parent ) : 'no parent',
'NewParentName' => $newpost->post_parent ? get_the_title( $newpost->post_parent ) : 'no parent',
$editor_link['name'] => $editor_link['value'],
)
);
return 1;
}
}
/**
* Permalink changed.
*
* @param string $old_link - Old permalink.
* @param string $new_link - New permalink.
*
* @param stdClass $post - The post.
*
* @since 4.5.0
*/
private static function check_permalink_change( $old_link, $new_link, $post ) {
if ( in_array( $post->post_status, array( 'draft', 'pending' ), true ) ) {
$old_link = self::$old_post->post_name;
$new_link = $post->post_name;
}
if ( $old_link !== $new_link ) {
$editor_link = self::get_editor_link( $post->ID );
Alert_Manager::trigger_event(
2017,
array(
'PostID' => $post->ID,
'PostType' => $post->post_type,
'PostTitle' => $post->post_title,
'PostStatus' => $post->post_status,
'PostDate' => $post->post_date,
'OldUrl' => $old_link,
'NewUrl' => $new_link,
$editor_link['name'] => $editor_link['value'],
)
);
return 1;
}
return 0;
}
/**
* Post visibility changed.
*
* @param WP_Post $oldpost - Old post.
* @param WP_Post $newpost - New post.
* @param string $old_status - Old status.
* @param string $new_status - New status.
*
* @since 4.5.0
*/
private static function check_visibility_change( $oldpost, $newpost, $old_status, $new_status ) {
$old_visibility = '';
$new_visibility = '';
if ( $oldpost->post_password ) {
$old_visibility = esc_html__( 'Password Protected', 'wp-security-audit-log' );
} elseif ( 'private' === $oldpost->post_status ) {
$old_visibility = esc_html__( 'Private', 'wp-security-audit-log' );
} else {
$old_visibility = esc_html__( 'Public', 'wp-security-audit-log' );
}
if ( $newpost->post_password ) {
$new_visibility = esc_html__( 'Password Protected', 'wp-security-audit-log' );
} elseif ( 'private' === $newpost->post_status ) {
$new_visibility = esc_html__( 'Private', 'wp-security-audit-log' );
} else {
$new_visibility = esc_html__( 'Public', 'wp-security-audit-log' );
}
if ( $old_visibility && $new_visibility && ( $old_visibility !== $new_visibility ) ) {
$editor_link = self::get_editor_link( $oldpost->ID );
Alert_Manager::trigger_event(
2025,
array(
'PostID' => $oldpost->ID,
'PostType' => $oldpost->post_type,
'PostTitle' => $oldpost->post_title,
'PostStatus' => $newpost->post_status,
'PostDate' => $oldpost->post_date,
'PostUrl' => get_permalink( $oldpost->ID ),
'OldVisibility' => $old_visibility,
'NewVisibility' => $new_visibility,
$editor_link['name'] => $editor_link['value'],
)
);
return 1;
}
}
/**
* Post date changed.
*
* @param \WP_Post $oldpost - Old post.
* @param \WP_Post $newpost - New post.
*
* @since 4.5.0
*/
private static function check_date_change( $oldpost, $newpost ) {
$from = 0;
$to = 0;
if ( isset( $oldpost->post_date ) && null !== $oldpost->post_date ) {
$from = strtotime( $oldpost->post_date );
}
if ( isset( $newpost->post_date ) && null !== $newpost->post_date ) {
$to = strtotime( $newpost->post_date );
}
if ( 'pending' === $oldpost->post_status ) {
return 0;
}
/*
* Return early if this looks like a re-save on a draft.
*/
if ( self::is_draft_resave( $oldpost, $newpost ) ) {
return 0;
}
if ( $from !== $to ) {
$editor_link = self::get_editor_link( $oldpost->ID );
Alert_Manager::trigger_event(
2027,
array(
'PostID' => $oldpost->ID,
'PostType' => $oldpost->post_type,
'PostTitle' => $oldpost->post_title,
'PostStatus' => $oldpost->post_status,
'PostDate' => $newpost->post_date,
'PostUrl' => get_permalink( $oldpost->ID ),
'OldDate' => $oldpost->post_date,
'NewDate' => $newpost->post_date,
$editor_link['name'] => $editor_link['value'],
)
);
return 1;
}
return 0;
}
/**
* Comments/Trackbacks and Pingbacks check.
*
* @param stdClass $oldpost - Old post.
* @param stdClass $newpost - New post.
*
* @since 4.5.0
*/
private static function check_comments_pings( $oldpost, $newpost ) {
$result = 0;
$editor_link = self::get_editor_link( $newpost->ID );
// Comments.
if ( $oldpost->comment_status !== $newpost->comment_status ) {
Alert_Manager::trigger_event(
2111,
array(
'PostID' => $newpost->ID,
'PostType' => $newpost->post_type,
'PostStatus' => $newpost->post_status,
'PostDate' => $newpost->post_date,
'PostTitle' => $newpost->post_title,
'PostUrl' => get_permalink( $newpost->ID ),
$editor_link['name'] => $editor_link['value'],
'EventType' => 'open' === $newpost->comment_status ? 'enabled' : 'disabled',
)
);
$result = 1;
}
// Trackbacks and Pingbacks.
if ( $oldpost->ping_status !== $newpost->ping_status ) {
Alert_Manager::trigger_event(
2112,
array(
'PostID' => $newpost->ID,
'PostType' => $newpost->post_type,
'PostTitle' => $newpost->post_title,
'PostStatus' => $newpost->post_status,
'PostDate' => $newpost->post_date,
'PostUrl' => get_permalink( $newpost->ID ),
$editor_link['name'] => $editor_link['value'],
'EventType' => 'open' === $newpost->ping_status ? 'enabled' : 'disabled',
)
);
$result = 1;
}
return $result;
}
/**
* Categories changed.
*
* @param array $old_cats - Old categories.
* @param array $new_cats - New categories.
* @param WP_Post $post - The post.
*
* @since 4.5.0
*/
private static function check_categories_change( $old_cats, $new_cats, $post ) {
$old_cats = implode( ', ', (array) $old_cats );
$new_cats = implode( ', ', (array) $new_cats );
if ( $old_cats !== $new_cats && 'page' !== $post->post_type ) {
$editor_link = self::get_editor_link( $post->ID );
$alert_data = array(
'PostID' => $post->ID,
'PostType' => $post->post_type,
'PostTitle' => $post->post_title,
'PostStatus' => $post->post_status,
'PostDate' => $post->post_date,
'PostUrl' => get_permalink( $post->ID ),
'OldCategories' => $old_cats ? $old_cats : 'no categories',
'NewCategories' => $new_cats ? $new_cats : 'no categories',
$editor_link['name'] => $editor_link['value'],
);
Alert_Manager::trigger_event( 2016, $alert_data );
}
}
/**
* Reports tags change event. This could be tags addition, removal and possibly other in the future.
*
* @param int $event_code Event code.
* @param WP_Post $post WordPress post object.
* @param string[] $tags_changed Changed tags.
*
* @since 4.5.0
*/
private static function report_tags_change_event( $event_code, $post, $tags_changed ) {
$editor_link = self::get_editor_link( $post->ID );
$post_status = ( 'publish' === $post->post_status ) ? 'published' : $post->post_status;
Alert_Manager::trigger_event(
$event_code,
array(
'PostID' => $post->ID,
'PostType' => $post->post_type,
'PostStatus' => $post_status,
'PostTitle' => $post->post_title,
'PostDate' => $post->post_date,
'PostUrl' => get_permalink( $post->ID ),
'tag' => ! empty( $tags_changed ) ? implode( ', ', $tags_changed ) : esc_html__( 'no tags', 'wp-security-audit-log' ),
$editor_link['name'] => $editor_link['value'],
)
);
}
/**
* Tags changed.
*
* @param array $old_tags - Old tags.
* @param array $new_tags - New tags.
* @param WP_Post $post - The post.
*
* @since 4.5.0
*/
private static function check_tags_change( $old_tags, $new_tags, $post ) {
// Ensure old_tags is not null.
if ( ! $old_tags ) {
$old_tags = array();
}
$intersection = array_intersect( $old_tags, $new_tags );
if ( count( $intersection ) === count( $old_tags ) && count( $old_tags ) === count( $new_tags ) ) {
// No change, let's leave.
return;
}
// Check for added tags.
$added_tags = array_diff( (array) $new_tags, (array) $old_tags );
if ( ! empty( $added_tags ) ) {
self::report_tags_change_event( 2119, $post, $added_tags );
}
// Check for removed tags.
$removed_tags = array_diff( (array) $old_tags, (array) $new_tags );
if ( ! empty( $removed_tags ) ) {
self::report_tags_change_event( 2120, $post, $removed_tags );
}
}
/**
* Post modified content.
*
* @param integer $post_id Post ID.
* @param stdClass $oldpost Old post.
* @param stdClass $newpost New post.
* @param int $modified Set to 0 if no changes done to the post.
*
* @return int|void
*
* @since 4.5.0
*/
public static function check_modification_change( $post_id, $oldpost, $newpost, $modified ) {
self::check_title_change( $oldpost, $newpost );
$content_changed = $oldpost->post_content !== $newpost->post_content;
/*
* If the content hasn't changed and this looks to be a draft resave
* then we won't track anything for this modification.
*/
if ( ! $content_changed && self::is_draft_resave( $oldpost, $newpost ) ) {
return;
}
if ( $oldpost->post_modified !== $newpost->post_modified ) {
$event = 0;
if ( $content_changed ) { // Check if content changed.
$event = 2065;
} elseif ( ! $modified ) {
$event = 2002;
}
if ( $event ) {
if ( 2002 === $event ) {
// Get Yoast alerts.
$yoast_alerts = Alert_Manager::get_alerts_by_category( 'Yoast SEO' );
$yoast_metabox_alerts = Alert_Manager::get_alerts_by_category( 'Yoast SEO Meta Box' );
$yoast_alerts = $yoast_alerts + $yoast_metabox_alerts;
// Check all alerts.
foreach ( $yoast_alerts as $alert_code => $alert ) {
if ( Alert_Manager::will_or_has_triggered( $alert_code ) ) {
return 0; // Return if any Yoast alert has or will trigger.
}
}
}
$event_data = self::get_post_event_data( $oldpost );
$editor_link = self::get_editor_link( $oldpost->ID );
$event_data[ $editor_link['name'] ] = $editor_link['value'];
$event_data['RevisionLink'] = self::get_post_revision( $post_id, $oldpost );
// Check excerpt change.
$old_post_excerpt = $oldpost->post_excerpt;
$post_excerpt = get_post_field( 'post_excerpt', $post_id );
if ( empty( $old_post_excerpt ) && ! empty( $post_excerpt ) ) {
$event_data['EventType'] = 'added';
} elseif ( ! empty( $old_post_excerpt ) && empty( $post_excerpt ) ) {
$event_data['EventType'] = 'removed';
} elseif ( $old_post_excerpt !== $post_excerpt ) {
$event_data['EventType'] = 'modified';
}
if ( $old_post_excerpt !== $post_excerpt ) {
$event = 2129;
// We are purposefully showing an empty, not NULL value.
$event_data['old_post_excerpt'] = ( $old_post_excerpt ) ? $old_post_excerpt : ' ';
$event_data['post_excerpt'] = ( $post_excerpt ) ? $post_excerpt : ' ';
}
if ( 2002 === $event ) {
// If we reach this point, we no longer need to check if the content has changed as we already have
// an event to handle it. So trigger 2002 regardless and "something" has changed in the post, we
// just don't detect it elsewhere.
Alert_Manager::trigger_event_if( $event, $event_data, array( __CLASS__, 'ignore_other_post_events' ) );
} else {
Alert_Manager::trigger_event( $event, $event_data );
}
}
}
}
/**
* Method: Ensure no other post-related events are being fired, or have recently been fired.
*
* @return bool
*
* @since 4.5.0
*/
public static function ignore_other_post_events() {
$post_events = array_keys( Alert_Manager::get_alerts_by_sub_category( 'Content' ) );
$post_events = array_merge( $post_events, array_keys( Alert_Manager::get_alerts_by_sub_category( 'Coupons' ) ) );
$post_events = array_merge( $post_events, array_keys( Alert_Manager::get_alerts_by_sub_category( 'Tags' ) ) );
$post_events = array_merge( $post_events, array_keys( Alert_Manager::get_alerts_by_sub_category( 'Categories' ) ) );
$post_events = array_merge( $post_events, array_keys( Alert_Manager::get_alerts_by_sub_category( 'Custom Fields' ) ) );
$post_events = array_merge( $post_events, array_keys( Alert_Manager::get_alerts_by_sub_category( 'Custom Fields (ACF)' ) ) );
$post_events = array_merge( $post_events, array_keys( Alert_Manager::get_alerts_by_sub_category( 'Comments' ) ) );
$post_events = array_merge( $post_events, array_keys( Alert_Manager::get_alerts_by_sub_category( 'Custom Post Types' ) ) );
$post_events = array_merge( $post_events, array_keys( Alert_Manager::get_alerts_by_sub_category( 'Pages' ) ) );
foreach ( $post_events as $event ) {
if ( Alert_Manager::will_or_has_triggered( $event ) || Alert_Manager::was_triggered_recently( $event ) ) {
return false;
}
}
return true;
}
/**
* Changed title of a post.
*
* @param stdClass $oldpost - Old post.
* @param stdClass $newpost - New post.
*
* @since 4.5.0
*/
private static function check_title_change( $oldpost, $newpost ) {
if ( $oldpost->post_title !== $newpost->post_title ) {
$editor_link = self::get_editor_link( $oldpost->ID );
Alert_Manager::trigger_event(
2086,
array(
'PostID' => $newpost->ID,
'PostType' => $newpost->post_type,
'PostTitle' => $newpost->post_title,
'PostStatus' => $newpost->post_status,
'PostDate' => $newpost->post_date,
'PostUrl' => get_permalink( $newpost->ID ),
'OldTitle' => $oldpost->post_title,
'NewTitle' => $newpost->post_title,
$editor_link['name'] => $editor_link['value'],
)
);
return 1;
}
return 0;
}
/**
* Return post revision link.
*
* @param integer $post_id - Post ID.
* @param WP_Post $post - WP Post object.
*
* @return string
*
* @since 4.5.0
*/
private static function get_post_revision( $post_id, $post ) {
$revisions = wp_get_post_revisions( $post_id );
if ( ! empty( $revisions ) ) {
$revision = array_shift( $revisions );
return self::get_revision_link( $revision->ID );
}
}
/**
* Builds revision link.
*
* @param integer $revision_id - Revision ID.
*
* @return string|null - Link.
*
* @since 4.5.0
*/
private static function get_revision_link( $revision_id ) {
return ! empty( $revision_id ) ? add_query_arg( 'revision', $revision_id, \network_admin_url( 'revision.php' ) ) : null;
}
/**
* Log post stuck/unstuck events.
*
* @param integer $post_id - Post ID.
* @param integer $event - Event ID.
*
* @since 4.5.0
*/
private static function log_sticky_post_event( $post_id, $event ) {
// Get post.
$post = get_post( $post_id );
if ( is_wp_error( $post ) ) {
return;
}
$editor_link = self::get_editor_link( $post_id ); // Editor link.
$event_data = self::get_post_event_data( $post ); // Event data.
$event_data[ $editor_link['name'] ] = $editor_link['value'];
Alert_Manager::trigger_event( $event, $event_data );
}
/**
* Check auto draft and the setting: Hide Plugin in Plugins Page
*
* @param integer $code - Alert code.
* @param string $title - Title.
*
* @return boolean
*
* @since 4.5.0
*/
private static function check_auto_draft( $code, $title ) {
$ignore = 0;
if ( 2008 === $code && ( 'auto-draft' === $title || 'Auto Draft' === $title ) ) {
$ignore = ! \WSAL\Helpers\Settings_Helper::get_boolean_option_value( 'wp-backend' );
}
return $ignore;
}
/**
* Post Opened for Editing in WP Editors.
*
* @param WP_Post $post Post object.
*
* @since 4.5.0
*/
public static function post_opened_in_editor( $post ) {
if ( empty( $post ) || ! $post instanceof \WP_Post ) {
return;
}
$current_path = isset( $_SERVER['SCRIPT_NAME'] ) ? esc_url_raw( wp_unslash( $_SERVER['SCRIPT_NAME'] ) ) . '?post=' . $post->ID : false;
$referrer = isset( $_SERVER['HTTP_REFERER'] ) ? esc_url_raw( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) : false;
// Check referrer URL.
if ( ! empty( $referrer ) ) {
// Parse the referrer.
$parsed_url = wp_parse_url( $referrer );
// If the referrer is post-new then we can ignore this one.
if ( isset( $parsed_url['path'] ) && 'post-new' === basename( $parsed_url['path'], '.php' ) ) {
return $post;
}
}
if ( ! empty( $referrer ) && strpos( $referrer, $current_path ) !== false ) {
// Ignore this if we were on the same page so we avoid double audit entries.
return $post;
}
if ( ! empty( $post->post_title ) ) {
$event = 2100;
if ( ! Alert_Manager::was_triggered( $event ) ) {
$editor_link = self::get_editor_link( $post->ID );
Alert_Manager::trigger_event(
$event,
array(
'PostID' => $post->ID,
'PostType' => $post->post_type,
'PostTitle' => $post->post_title,
'PostStatus' => $post->post_status,
'PostDate' => $post->post_date,
'PostUrl' => get_permalink( $post->ID ),
$editor_link['name'] => $editor_link['value'],
)
);
}
}
}
/**
* Builds category link.
*
* @param integer $tag_id - Tag ID.
* @param string $taxonomy - Taxonomy.
*
* @return string|null - Link.
*
* @since 4.5.0
*/
private static function get_taxonomy_edit_link( $tag_id, $taxonomy = 'post_tag' ) {
$tag_args = array(
'taxonomy' => $taxonomy,
'tag_ID' => $tag_id,
);
return ! empty( $tag_id ) ? add_query_arg( $tag_args, \network_admin_url( 'term.php' ) ) : null;
}
/**
* Returns true if this looks like a re-save on a draft.
*
* @method is_draft_resave
*
* @param \WP_Post $oldpost The old post object if one exists.
* @param \WP_Post $newpost The new post object.
*
* @return boolean
*
* @since 4.5.0
*/
private static function is_draft_resave( $oldpost, $newpost ) {
/*
* If this is a 'draft' and used to be a 'draft' plus the gmt dates
* match and it contains only characters that would appear in the
* a default unpublished post then...
*/
if ( 'draft' === $oldpost->post_status
&& $oldpost->post_status === $newpost->post_status
&& $oldpost->post_date_gmt === $newpost->post_date_gmt
&& preg_match( '/^[0\-\ \:]+$/', $oldpost->post_date_gmt ) ) {
// Don't track this as a date change.
return true;
}
}
/**
* Callback to test if a post was just made by a plugin.
*
* NOTE: the return is flipped to handle a double NOT in _CommitItem().
*
* @method plugin_not_created_post
*
* @return boolean
*
* @since 4.5.0
*/
public static function plugin_not_created_post() {
// Ensure no event for a WC coupon.
if ( Alert_Manager::will_or_has_triggered( 9063 ) ) {
return false;
}
$triggered = Alert_Manager::will_or_has_triggered( 5019 );
// inverting value here to account for the double NOT in _CommitItem().
return ! $triggered;
}
}
}