Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 46
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
StarredMenteesStore
0.00% covered (danger)
0.00%
0 / 46
0.00% covered (danger)
0.00%
0 / 7
110
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 encodeMenteeIds
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 decodeMenteeIds
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getStarredMentees
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 getStarredMenteeIds
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 starMentee
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
6
 unstarMentee
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace GrowthExperiments\MentorDashboard\MenteeOverview;
4
5use MediaWiki\User\Options\UserOptionsManager;
6use MediaWiki\User\UserIdentity;
7use MediaWiki\User\UserIdentityLookup;
8use Wikimedia\Rdbms\IDBAccessObject;
9
10class StarredMenteesStore {
11    public const STARRED_MENTEES_PREFERENCE = 'growthexperiments-starred-mentees';
12
13    private const SEPARATOR = '|';
14
15    /** @var UserIdentityLookup */
16    private $userIdentityLookup;
17
18    /** @var UserOptionsManager */
19    private $userOptionsManager;
20
21    /**
22     * @param UserIdentityLookup $userIdentityLookup
23     * @param UserOptionsManager $userOptionsManager
24     */
25    public function __construct(
26        UserIdentityLookup $userIdentityLookup,
27        UserOptionsManager $userOptionsManager
28    ) {
29        $this->userIdentityLookup = $userIdentityLookup;
30        $this->userOptionsManager = $userOptionsManager;
31    }
32
33    private function encodeMenteeIds( array $ids ): string {
34        return implode( self::SEPARATOR, $ids );
35    }
36
37    private function decodeMenteeIds( string $encodedIds ): array {
38        $res = explode( self::SEPARATOR, $encodedIds );
39        return array_map( 'intval', array_filter( $res, 'is_numeric' ) );
40    }
41
42    /**
43     * @param UserIdentity $user
44     * @param int $flags Bitarray, one of IDBAccessObject::READ_*
45     * @return UserIdentity[]
46     */
47    public function getStarredMentees( UserIdentity $user, int $flags = 0 ): array {
48        $ids = $this->getStarredMenteeIds( $user, $flags );
49        if ( $ids === [] ) {
50            // UserIdentityLookup will throw if $ids is empty
51            return [];
52        }
53
54        return iterator_to_array( $this->userIdentityLookup
55            ->newSelectQueryBuilder()
56            ->whereUserIds( $ids )
57            ->caller( __METHOD__ )
58            ->fetchUserIdentities() );
59    }
60
61    /**
62     * @param UserIdentity $user
63     * @param int $flags Bitarray, one of IDBAccessObject::READ_* constants
64     * @return int[]
65     */
66    private function getStarredMenteeIds( UserIdentity $user, int $flags = 0 ): array {
67        return $this->decodeMenteeIds(
68            $this->userOptionsManager
69                ->getOption(
70                    $user,
71                    self::STARRED_MENTEES_PREFERENCE,
72                    null,
73                    false,
74                    $flags
75                )
76        );
77    }
78
79    /**
80     * @param UserIdentity $mentor
81     * @param UserIdentity $mentee
82     */
83    public function starMentee( UserIdentity $mentor, UserIdentity $mentee ): void {
84        $starredMentees = $this->getStarredMenteeIds( $mentor, IDBAccessObject::READ_LOCKING );
85        $menteeId = $mentee->getId();
86
87        if ( in_array( $menteeId, $starredMentees ) ) {
88            // $mentee is already starred
89            return;
90        }
91
92        // Update the user option
93        $starredMentees[] = $mentee->getId();
94        $this->userOptionsManager->setOption(
95            $mentor,
96            self::STARRED_MENTEES_PREFERENCE,
97            $this->encodeMenteeIds( $starredMentees )
98        );
99        $this->userOptionsManager->saveOptions( $mentor );
100    }
101
102    /**
103     * @param UserIdentity $mentor
104     * @param UserIdentity $mentee
105     */
106    public function unstarMentee( UserIdentity $mentor, UserIdentity $mentee ): void {
107        $starredMentees = $this->getStarredMenteeIds( $mentor, IDBAccessObject::READ_LOCKING );
108        $menteeId = $mentee->getId();
109
110        // Delete $menteeId from $starredMentees, if it is there
111        $key = array_search( $menteeId, $starredMentees );
112        if ( $key !== false ) {
113            unset( $starredMentees[$key] );
114            $starredMentees = array_values( $starredMentees );
115
116            // $starredMentees was changed, update option
117            $this->userOptionsManager->setOption(
118                $mentor,
119                self::STARRED_MENTEES_PREFERENCE,
120                $this->encodeMenteeIds( $starredMentees )
121            );
122            $this->userOptionsManager->saveOptions( $mentor );
123        }
124    }
125}