$type, 'post' => (int) $post_id, 'user' => $user, 'time' => $now, ) + $extra; } update_option( self::OPTION, array_slice( $list, -self::LIMIT ), false ); } /** * Publish, trash and restore. * * @param string $new New status. * @param string $old Old status. * @param \WP_Post $post Post. */ public function status( $new, $old, $post ) { if ( PostTypes::ARTICLE !== $post->post_type || $new === $old ) { return; } if ( 'publish' === $new ) { self::add( 'trash' === $old ? 'restored' : 'published', $post->ID ); } elseif ( 'trash' === $new ) { self::add( 'trashed', $post->ID ); } } /** * Edits of published articles, and moves (new parent). * * @param int $post_id Post. * @param \WP_Post $after After. * @param \WP_Post $before Before. */ public function updated( $post_id, $after, $before ) { if ( PostTypes::ARTICLE !== $after->post_type || 'publish' !== $after->post_status || 'publish' !== $before->post_status ) { return; } if ( (int) $after->post_parent !== (int) $before->post_parent || (int) $after->menu_order !== (int) $before->menu_order ) { self::add( 'moved', $post_id ); } elseif ( $after->post_content !== $before->post_content || $after->post_title !== $before->post_title ) { self::add( 'updated', $post_id ); } } /** * Reader feedback (votes only count when a reason or message was given). * * @param int $post_id Article. * @param string $vote yes|no. * @param string $message Message. */ public function feedback( $post_id, $vote, $message ) { if ( '' !== trim( (string) $message ) ) { self::add( 'feedback', $post_id, array( 'vote' => $vote ) ); } } /** * GET /activity. * * @param string $ns Namespace. */ public function routes( $ns ) { register_rest_route( $ns, '/activity', array( 'methods' => 'GET', 'callback' => static fn( $request ) => rest_ensure_response( self::feed( (int) ( $request['limit'] ?? 20 ) ) ), 'args' => array( 'limit' => array( 'type' => 'integer', 'default' => 20, 'minimum' => 1, 'maximum' => 100, ), ), 'permission_callback' => array( Routes::class, 'can_edit' ), ) ); } /** * Newest events with names, avatars and links. * * @param int $limit Events. * @return array[] */ public static function feed( $limit = 20 ) { $list = get_option( self::OPTION, array() ); $out = array(); foreach ( array_reverse( is_array( $list ) ? $list : array() ) as $event ) { $post = get_post( (int) $event['post'] ); if ( ! $post || ! current_user_can( 'edit_post', $post->ID ) ) { continue; } $user = $event['user'] ? get_userdata( (int) $event['user'] ) : null; $out[] = array( 'type' => $event['type'], 'time' => (int) $event['time'], 'vote' => $event['vote'] ?? '', 'user' => $user ? array( 'name' => $user->display_name, 'avatar' => get_avatar_url( $user->ID, array( 'size' => 48 ) ), ) : null, 'post' => array( 'id' => $post->ID, 'title' => html_entity_decode( get_the_title( $post ), ENT_QUOTES, 'UTF-8' ), 'editLink' => get_edit_post_link( $post->ID, 'raw' ), ), ); if ( count( $out ) >= $limit ) { break; } } return $out; } }