get_usage_data(); return $params; } ); } /** * Get the Notes feature usage data. * * @return array */ private function get_usage_data() { return [ 'threads' => $this->get_threads_usage(), 'replies' => $this->get_replies_usage(), 'mentions' => $this->get_mentions_usage(), 'first_interaction' => $this->get_first_interaction(), 'last_interaction' => $this->get_last_interaction(), ]; } /** * Get the Threads' usage data. * * @return array */ private function get_threads_usage() { return [ 'total' => Note::query() ->only_threads() ->count(), 'resolved' => Note::query() ->only_threads() ->where( 'is_resolved', '=', true ) ->count(), 'trashed' => Note::query() ->only_threads() ->only_trashed() ->count(), 'users_used' => $this->get_notes_users_used( static::THREADS ), 'document_type' => $this->get_notes_document_types( static::THREADS ), ]; } /** * Get the replies' usage data. * * @return array */ private function get_replies_usage() { return [ 'total' => Note::query() ->only_replies() ->count(), 'trashed' => Note::query() ->only_replies() ->only_trashed() ->count(), 'replied_threads' => (int) Note::query() ->disable_model_initiation() ->only_replies() ->select_raw( [ 'COUNT(DISTINCT `parent_id`) AS `count`' ] ) ->first()['count'], 'users_used' => $this->get_notes_users_used( static::REPLIES ), 'document_type' => $this->get_notes_document_types( static::REPLIES ), ]; } /** * Get the mentions' usage data. * * @return array */ private function get_mentions_usage() { return [ 'total' => ( new Query_Builder() ) ->table( Module::TABLE_NOTES_USERS_RELATIONS, 'e_notes_relations' ) ->join( function ( Join_Clause $j ) { $j->table( Module::TABLE_NOTES, 'e_notes' ) ->on_column( 'e_notes.id', '=', 'e_notes_relations.note_id' ); } ) ->where( 'type', '=', Note::USER_RELATION_MENTION ) ->where( 'e_notes.status', '!=', Note::STATUS_TRASH ) ->count(), 'users_used' => $this->get_mentions_users_used(), 'document_type' => ( new Query_Builder() ) ->select( [ 'type' => 'postmeta.meta_value' ] ) ->add_count_select( '*', 'count' ) ->from( Module::TABLE_NOTES_USERS_RELATIONS, 'e_notes_relations' ) ->join( function ( Join_Clause $j ) { $j->table( Module::TABLE_NOTES, 'e_notes' ) ->on_column( 'e_notes.id', '=', 'e_notes_relations.note_id' ); } ) ->join( function ( Join_Clause $j ) { $j->table( 'posts' ) ->on_column( 'posts.ID', '=', 'e_notes.post_id' ); } ) ->join( function ( Join_Clause $j ) { $j->table( 'postmeta' ) ->on_column( 'posts.ID', '=', 'postmeta.post_id' ) ->on( 'postmeta.meta_key', '=', Document::TYPE_META_KEY ); } ) ->where( 'e_notes.status', '!=', Note::STATUS_TRASH ) ->group_by( 'postmeta.meta_value' ) ->get() ->map_with_keys( function ( $row ) { return [ $row['type'] => (int) $row['count'] ]; } ) ->all(), ]; } /** * Get the first user interaction with the Notes feature. * * @return string|null */ private function get_first_interaction() { $note = Note::query() ->with_trashed() ->select( [ 'created_at' ] ) ->order_by( 'created_at' ) ->first(); return $note && $note->created_at ? $note->created_at->format( 'Y-m-d\TH:i:sO' ) : null; } /** * Get the last user interaction with the Notes feature. * * @return string|null */ private function get_last_interaction() { $note = Note::query() ->with_trashed() ->select( [ 'last_activity_at' ] ) ->order_by( 'last_activity_at', 'desc' ) ->first(); return $note && $note->last_activity_at ? $note->last_activity_at->format( 'Y-m-d\TH:i:sO' ) : null; } /** * Get the count of `$type` usages grouped by user role. * * e.g. for 10 replies it can be something like: `{ admin: 7, subscriber: 3 }` * * @param string $type - Threads or replies. * * @return array */ private function get_notes_users_used( $type ) { $query = Note::query() ->disable_model_initiation() ->select( [ 'user_used_id' => Module::TABLE_NOTES . '.author_id' ] ) ->add_count_select( Module::TABLE_NOTES . '.id', 'count' ) ->group_by( 'user_used_id' ); switch ( $type ) { case static::THREADS: $query->only_threads(); break; case static::REPLIES: $query->only_replies(); break; } return $this->normalize_users_used( $query->get() ); } /** * Get the count of mentions usages grouped by user role. * * e.g. for 10 mentions it can be something like: `{ admin: 7, subscriber: 3 }` * * @return array */ private function get_mentions_users_used() { $query = ( new Query_Builder() ) ->from( Module::TABLE_NOTES_USERS_RELATIONS, 'e_notes_relations' ) ->select( [ 'user_used_id' => 'e_notes.author_id' ] ) ->add_count_select( 'e_notes_relations.id', 'count' ) ->join( function ( Join_Clause $j ) { $j->table( Module::TABLE_NOTES, 'e_notes' ) ->on_column( 'e_notes_relations.note_id', '=', 'e_notes.id' ); } ) ->where( 'e_notes_relations.type', '=', Note::USER_RELATION_MENTION ) ->where( 'e_notes.status', '!=', Note::STATUS_TRASH ) ->group_by( 'e_notes.author_id' ); return $this->normalize_users_used( $query->get() ); } /** * Normalize `users_used` query results into counts array grouped by user role. * * @param Collection $query_results * * @return array */ private function normalize_users_used( Collection $query_results ) { if ( $query_results->is_empty() ) { return []; } $results = $query_results->map_with_keys( function ( $row ) { return [ $row['user_used_id'] => (int) $row['count'] ]; } ); /** * @type \WP_User[] $users */ $users = get_users( [ 'include' => $results->keys(), ] ); $counts = []; foreach ( $users as $user ) { /** * WordPress also uses the first role. * * @see https://github.com/WordPress/WordPress/blob/57039311720709d55e96e1a074414ebadba64e00/wp-admin/user-edit.php#L419-L435 */ $role = reset( $user->roles ); if ( ! isset( $counts[ $role ] ) ) { $counts[ $role ] = 0; } $counts[ $role ] += $results->get( $user->ID ); } return $counts; } /** * Get the count of `$type` usages grouped by document type. * * @param string $type - Threads or replies. * * @return array */ private function get_notes_document_types( $type ) { $query = Note::query() ->disable_model_initiation() ->select( [ 'type' => 'postmeta.meta_value' ] ) ->add_count_select( '*', 'count' ) ->join( function ( Join_Clause $j ) { $j->table( 'posts' ) ->on_column( 'posts.ID', '=', Module::TABLE_NOTES . '.post_id' ); } ) ->join( function ( Join_Clause $j ) { $j->table( 'postmeta' ) ->on_column( 'posts.ID', '=', 'postmeta.post_id' ) ->on( 'postmeta.meta_key', '=', Document::TYPE_META_KEY ); } ) ->group_by( 'postmeta.meta_value' ); switch ( $type ) { case static::THREADS: $query->only_threads(); break; case static::REPLIES: $query->only_replies(); break; } return $query->get() ->map_with_keys( function ( $row ) { return [ $row['type'] => (int) $row['count'] ]; } ) ->all(); } }