'string', 'description' => __( 'Last reviewed GMT datetime for ContentCortex – Outdated Content Alerts', 'contentcortex' ), 'single' => true, 'sanitize_callback' => array( $this, 'sanitize_datetime' ), 'show_in_rest' => false, 'auth_callback' => function () { return current_user_can( 'edit_posts' ); }, ) ); register_post_meta( '', '_ocap_excluded', array( 'type' => 'integer', 'description' => __( 'Whether post is excluded from ContentCortex – Outdated Content Alerts', 'contentcortex' ), 'single' => true, 'sanitize_callback' => 'absint', 'show_in_rest' => false, 'auth_callback' => function () { return current_user_can( 'edit_posts' ); }, ) ); } /** * Sanitize GMT datetime string. * * @param mixed $value Input value. * @return string Sanitized datetime or empty string. */ public function sanitize_datetime( $value ) { if ( ! is_string( $value ) ) { return ''; } $timestamp = strtotime( $value ); if ( false === $timestamp ) { return ''; } return gmdate( 'Y-m-d H:i:s', $timestamp ); } /** * Handle single "Mark as reviewed" action. */ public function handle_single_mark_reviewed() { $post_id = isset( $_GET['post_id'] ) ? absint( $_GET['post_id'] ) : 0; if ( ! check_admin_referer( 'ocap_mark_reviewed_' . $post_id, '_wpnonce', false ) && ! check_admin_referer( 'sct_mark_reviewed_' . $post_id, '_wpnonce', false ) ) { wp_die( esc_html__( 'Security check failed.', 'contentcortex' ) ); } if ( ! current_user_can( 'edit_post', $post_id ) ) { wp_die( esc_html__( 'You do not have permission to review this item.', 'contentcortex' ) ); } $this->mark_as_reviewed( $post_id ); ocap_clear_stale_cache(); $redirect = isset( $_GET['redirect_to'] ) ? esc_url_raw( wp_unslash( $_GET['redirect_to'] ) ) : ocap_get_report_url(); $redirect = add_query_arg( 'ocap_notice', 'reviewed', $redirect ); wp_safe_redirect( $redirect ); exit; } /** * Handle single "Exclude from report" action. */ public function handle_single_exclude() { $post_id = isset( $_GET['post_id'] ) ? absint( $_GET['post_id'] ) : 0; if ( ! check_admin_referer( 'ocap_exclude_post_' . $post_id, '_wpnonce', false ) && ! check_admin_referer( 'sct_exclude_post_' . $post_id, '_wpnonce', false ) ) { wp_die( esc_html__( 'Security check failed.', 'contentcortex' ) ); } if ( ! current_user_can( 'edit_post', $post_id ) ) { wp_die( esc_html__( 'You do not have permission to exclude this item.', 'contentcortex' ) ); } $this->exclude_post( $post_id ); ocap_clear_stale_cache(); $redirect = isset( $_GET['redirect_to'] ) ? esc_url_raw( wp_unslash( $_GET['redirect_to'] ) ) : ocap_get_report_url(); $redirect = add_query_arg( 'ocap_notice', 'excluded', $redirect ); wp_safe_redirect( $redirect ); exit; } /** * Handle single "Include again" action. */ public function handle_single_include() { $post_id = isset( $_GET['post_id'] ) ? absint( $_GET['post_id'] ) : 0; if ( ! check_admin_referer( 'ocap_include_post_' . $post_id, '_wpnonce', false ) && ! check_admin_referer( 'sct_include_post_' . $post_id, '_wpnonce', false ) ) { wp_die( esc_html__( 'Security check failed.', 'contentcortex' ) ); } if ( ! current_user_can( 'edit_post', $post_id ) ) { wp_die( esc_html__( 'You do not have permission to restore this item.', 'contentcortex' ) ); } $this->include_post( $post_id ); ocap_clear_stale_cache(); $redirect = isset( $_GET['redirect_to'] ) ? esc_url_raw( wp_unslash( $_GET['redirect_to'] ) ) : ocap_get_report_url( array( 'view' => 'excluded' ) ); $redirect = add_query_arg( 'ocap_notice', 'included', $redirect ); wp_safe_redirect( $redirect ); exit; } /** * Handle bulk actions from the report table. */ public function handle_bulk_action() { check_admin_referer( 'bulk-posts' ); $action = ''; if ( ! empty( $_REQUEST['action'] ) && '-1' !== $_REQUEST['action'] ) { $action = sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ); } elseif ( ! empty( $_REQUEST['action2'] ) && '-1' !== $_REQUEST['action2'] ) { $action = sanitize_text_field( wp_unslash( $_REQUEST['action2'] ) ); } $post_ids = isset( $_REQUEST['post'] ) ? array_map( 'absint', (array) $_REQUEST['post'] ) : array(); if ( empty( $action ) || empty( $post_ids ) ) { wp_safe_redirect( ocap_get_report_url() ); exit; } $processed = 0; foreach ( $post_ids as $post_id ) { if ( ! current_user_can( 'edit_post', $post_id ) ) { continue; } if ( 'mark_reviewed' === $action ) { $this->mark_as_reviewed( $post_id ); ++$processed; } elseif ( 'exclude' === $action ) { $this->exclude_post( $post_id ); ++$processed; } elseif ( 'include' === $action ) { $this->include_post( $post_id ); ++$processed; } } if ( $processed > 0 ) { ocap_clear_stale_cache(); } $view = isset( $_REQUEST['view'] ) ? sanitize_key( wp_unslash( $_REQUEST['view'] ) ) : 'stale'; $redirect = ocap_get_report_url( array( 'view' => $view ) ); $redirect = add_query_arg( array( 'ocap_notice' => 'bulk_' . $action, 'count' => $processed, ), $redirect ); wp_safe_redirect( $redirect ); exit; } /** * Mark a single post as reviewed. * * @param int $post_id Post ID. */ public function mark_as_reviewed( $post_id ) { $gmt_now = gmdate( 'Y-m-d H:i:s' ); update_post_meta( $post_id, '_ocap_last_reviewed', $gmt_now ); update_post_meta( $post_id, '_sct_last_reviewed', $gmt_now ); do_action( 'ocap_post_marked_reviewed', $post_id, $gmt_now ); } /** * Exclude a post from stale reports. * * @param int $post_id Post ID. */ public function exclude_post( $post_id ) { update_post_meta( $post_id, '_ocap_excluded', 1 ); update_post_meta( $post_id, '_sct_excluded', 1 ); do_action( 'ocap_post_excluded', $post_id ); } /** * Include an excluded post back into stale reports. * * @param int $post_id Post ID. */ public function include_post( $post_id ) { delete_post_meta( $post_id, '_ocap_excluded' ); delete_post_meta( $post_id, '_sct_excluded' ); do_action( 'ocap_post_included', $post_id ); } /** * On post save, invalidate count cache. * * @param int $post_id Post ID. * @param WP_Post $post Post object. */ public function on_post_change( $post_id, $post ) { unset( $post ); if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } if ( wp_is_post_revision( $post_id ) ) { return; } ocap_clear_stale_cache(); } /** * On post delete/trash, invalidate count cache. * * @param int $post_id Post ID. */ public function on_post_delete( $post_id ) { unset( $post_id ); ocap_clear_stale_cache(); } }