workflow = $workflow; $this->audit = $audit; } /** * Register hooks. * * @return void */ public function register_hooks() { add_action( 'add_meta_boxes', array( $this, 'add_meta_box' ), 10, 2 ); add_action( 'admin_menu', array( $this, 'add_submenu' ), 11 ); } /** * The admin screen's URL, optionally for one post. * * @param int $post_id Post ID, or 0 for the site-wide view. * @return string */ public static function screen_url( $post_id = 0 ) { $args = array( 'page' => self::MENU_SLUG ); if ( $post_id ) { $args['post'] = (int) $post_id; } return add_query_arg( $args, admin_url( 'admin.php' ) ); } /** * Add the History screen under the Editorial Workflow menu. * * @return void */ public function add_submenu() { add_submenu_page( Settings::MENU_SLUG, __( 'Editorial History', 'ost-editorial-workflow' ), __( 'History', 'ost-editorial-workflow' ), 'edit_others_posts', self::MENU_SLUG, array( $this, 'render_screen' ) ); } /** * Render the History screen: one post's trail, or the latest site-wide. * * @return void */ public function render_screen() { if ( ! current_user_can( 'edit_others_posts' ) ) { return; } // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only screen. $post_id = isset( $_GET['post'] ) ? absint( wp_unslash( $_GET['post'] ) ) : 0; echo '
' . esc_html__( 'That item does not exist, or you cannot view it.', 'ost-editorial-workflow' ) . '
' . esc_html__( 'The most recent workflow activity across the site. Open an item to see its own trail.', 'ost-editorial-workflow' ) . '
'; $this->render_table( $this->audit->recent( 200 ), null, true ); echo '' . esc_html__( 'Export the full trail as CSV', 'ost-editorial-workflow' ) . '
'; echo ''; } /** * Add the history meta box on managed post types. * * @param string $post_type Post type. * @param \WP_Post $post Post. * @return void */ public function add_meta_box( $post_type, $post ) { if ( ! $post instanceof \WP_Post || ! $this->workflow->for_post( $post ) ) { return; } if ( ! current_user_can( 'edit_post', $post->ID ) ) { return; } add_meta_box( 'ostew_history', __( 'Editorial history', 'ost-editorial-workflow' ), array( $this, 'render' ), $post_type, 'normal', 'low' ); } /** * Render the trail. * * @param \WP_Post $post Post. * @return void */ public function render( $post ) { $rows = $this->audit->for_post( $post->ID, 100 ); $this->render_table( $rows, $post, false ); if ( ! empty( $rows ) ) { printf( '', esc_url( self::screen_url( $post->ID ) ), esc_html__( 'Open the full history for this item', 'ost-editorial-workflow' ) ); } } /** * Render a set of audit rows as a table. * * @param array $rows Audit rows. * @param \WP_Post|null $post The post these rows belong to, when they all do. * @param bool $with_post Whether to include an item column. * @return void */ private function render_table( $rows, $post = null, $with_post = false ) { if ( empty( $rows ) ) { echo '' . esc_html__( 'Nothing recorded yet. Entries appear here as items move through the workflow.', 'ost-editorial-workflow' ) . '
'; return; } echo '| ' . esc_html__( 'When', 'ost-editorial-workflow' ) . ' | '; if ( $with_post ) { echo '' . esc_html__( 'Item', 'ost-editorial-workflow' ) . ' | '; } echo '' . esc_html__( 'Who', 'ost-editorial-workflow' ) . ' | '; echo '' . esc_html__( 'Action', 'ost-editorial-workflow' ) . ' | '; echo '' . esc_html__( 'Moved', 'ost-editorial-workflow' ) . ' | '; echo '' . esc_html__( 'Note', 'ost-editorial-workflow' ) . ' | '; echo '|
|---|---|---|---|---|---|---|
| ' . esc_html( mysql2date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $row->created_at ) ) . ' | '; if ( $with_post ) { if ( $row_post ) { printf( '%2$s | ', esc_url( self::screen_url( (int) $row->post_id ) ), esc_html( get_the_title( $row_post ) ) ); } else { echo '' . esc_html__( 'deleted', 'ost-editorial-workflow' ) . ' | '; } } $to_label = $this->status_label( $workflow, $row->to_status ); $from_label = $this->status_label( $workflow, $row->from_status ); // A post's first entry has no meaningful "from" — showing the entry status // on both sides reads as "Draft → Draft", which tells the reader nothing. $is_creation = in_array( (string) $row->from_status, array( '', 'new', 'auto-draft' ), true ); echo '' . esc_html( $user ? $user->display_name : __( 'System', 'ost-editorial-workflow' ) ) . ' | '; echo '' . esc_html( $is_creation ? __( 'Created', 'ost-editorial-workflow' ) : $this->action_label( isset( $row->action ) ? (string) $row->action : '' ) ) . ' | '; echo '' . esc_html( $is_creation ? $to_label : sprintf( /* translators: 1: previous status, 2: new status. */ __( '%1$s → %2$s', 'ost-editorial-workflow' ), $from_label, $to_label ) ) . ' | '; echo '' . esc_html( (string) $row->note ) . ' | '; echo '