1879 lines
57 KiB
PHP
1879 lines
57 KiB
PHP
<?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;
|
||
}
|
||
}
|
||
}
|