Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 62
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
PersonalizedPraise
0.00% covered (danger)
0.00%
0 / 62
0.00% covered (danger)
0.00%
0 / 9
156
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 getHeaderText
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSubheaderTag
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getBody
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
2
 getPraiseworthyMentees
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 getFlowEnrollmentStatuses
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
12
 getMenteeGenders
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 getJsConfigVars
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
2
 getMobileSummaryBody
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace GrowthExperiments\MentorDashboard\Modules;
4
5use GrowthExperiments\MentorDashboard\PersonalizedPraise\PersonalizedPraiseSettings;
6use GrowthExperiments\MentorDashboard\PersonalizedPraise\PraiseworthyConditionsLookup;
7use GrowthExperiments\MentorDashboard\PersonalizedPraise\PraiseworthyMenteeSuggester;
8use GrowthExperiments\UserImpact\UserImpact;
9use MediaWiki\Cache\GenderCache;
10use MediaWiki\Context\IContextSource;
11use MediaWiki\Html\Html;
12use MediaWiki\Json\FormatJson;
13use MediaWiki\Registration\ExtensionRegistry;
14use MediaWiki\Title\Title;
15
16class PersonalizedPraise extends BaseModule {
17
18    private PraiseworthyMenteeSuggester $praiseworthyMenteeSuggester;
19    private PersonalizedPraiseSettings $personalizedPraiseSettings;
20    private GenderCache $genderCache;
21
22    /**
23     * @param string $name
24     * @param IContextSource $ctx
25     * @param PraiseworthyMenteeSuggester $praiseworthyMenteeSuggester
26     * @param PersonalizedPraiseSettings $personalizedPraiseSettings
27     * @param GenderCache $genderCache
28     */
29    public function __construct(
30        $name,
31        IContextSource $ctx,
32        PraiseworthyMenteeSuggester $praiseworthyMenteeSuggester,
33        PersonalizedPraiseSettings $personalizedPraiseSettings,
34        GenderCache $genderCache
35    ) {
36        parent::__construct( $name, $ctx );
37
38        $this->praiseworthyMenteeSuggester = $praiseworthyMenteeSuggester;
39        $this->personalizedPraiseSettings = $personalizedPraiseSettings;
40        $this->genderCache = $genderCache;
41    }
42
43    /** @inheritDoc */
44    protected function getHeaderText() {
45        return $this->msg( 'growthexperiments-mentor-dashboard-personalized-praise-title' )->text();
46    }
47
48    /** @inheritDoc */
49    protected function getSubheaderTag() {
50        return 'div';
51    }
52
53    /** @inheritDoc */
54    protected function getBody() {
55        return Html::rawElement(
56            'div',
57            [
58                'id' => 'vue-root-personalizedpraise',
59                'class' => 'growthexperiments-mentor-dashboard-module-mentee-overview-content'
60            ],
61            Html::element(
62                'p',
63                [ 'class' => 'growthexperiments-mentor-dashboard-no-js-fallback' ],
64                $this->msg( 'growthexperiments-mentor-dashboard-mentee-overview-no-js-fallback' )->text()
65            )
66        );
67    }
68
69    /**
70     * List of user impacts for praiseworthy mentees
71     *
72     * @return UserImpact[]
73     */
74    private function getPraiseworthyMentees(): array {
75        return array_values(
76            $this->praiseworthyMenteeSuggester->getPraiseworthyMenteesForMentor(
77                $this->getUser()
78            )
79        );
80    }
81
82    /**
83     * Check which users have Flow on their talk pages
84     *
85     * This method checks which praiseworthy mentees make use of Flow in their user talk pages,
86     * so UserCard.vue can calculate the number of topics accordingly. The method can be called
87     * even when Flow is not installed (in that case, it short-circuits and always returns false).
88     *
89     * @return array Dictionary of username => bool
90     */
91    private function getFlowEnrollmentStatuses(): array {
92        $usernames = array_map( static function ( UserImpact $mentee ) {
93            return $mentee->getUser()->getName();
94        }, $this->getPraiseworthyMentees() );
95
96        if ( !ExtensionRegistry::getInstance()->isLoaded( 'Flow' ) ) {
97            return array_fill_keys( $usernames, false );
98        }
99
100        $result = [];
101        foreach ( $usernames as $username ) {
102            $result[$username] = Title::newFromText( $username, NS_USER_TALK )->getContentModel()
103                === CONTENT_MODEL_FLOW_BOARD;
104        }
105        return $result;
106    }
107
108    /**
109     * Get an array of mentee objects with gender property.
110     *
111     * This function clones the mentee objects from getPraiseworthyMentees()
112     * and adds a gender property, based on the genderCache service.
113     * The gender property can be 'male', 'female', 'unknown' or null.
114     *
115     * @return array An array of mentee objects with gender property
116     */
117    protected function getMenteeGenders() {
118        $praiseworthyMentees = $this->getPraiseworthyMentees();
119        $menteeGenders = [];
120
121        foreach ( $praiseworthyMentees as $mentee ) {
122            $userId = $mentee->getUser()->getId();
123            $menteeGenders[$userId] = $this->genderCache->getGenderOf( $mentee->getUser()->getName(), __METHOD__ );
124        }
125
126        return $menteeGenders;
127    }
128
129    /** @inheritDoc */
130    protected function getJsConfigVars() {
131        return [
132            'GEPraiseworthyMentees' => $this->getPraiseworthyMentees(),
133            'GEPraiseworthyMenteesByFlowStatus' => $this->getFlowEnrollmentStatuses(),
134            'GEPersonalizedPraiseSettings' => FormatJson::encode( $this->personalizedPraiseSettings->toArray(
135                $this->getUser()
136            ) ),
137            'GEPraiseworthyMessageSubject' => $this->personalizedPraiseSettings->getPraisingMessageDefaultSubject(
138                $this->getUser()
139            ),
140            'GEPraiseworthyMessageUserTitle' => $this->personalizedPraiseSettings->getPraisingMessageUserTitle(
141                $this->getUser()
142            )->getPrefixedText(),
143            'GEPraiseworthyMessageTitle' => $this->personalizedPraiseSettings->getPraisingMessageTitle(
144                $this->getUser()
145            )->getPrefixedText(),
146            'GEPersonalizedPraiseNotificationsEnabled' => $this->getConfig()->get(
147                'GEPersonalizedPraiseNotificationsEnabled'
148            ),
149            'GEPersonalizedPraiseSkipMenteesForDays' =>
150                PraiseworthyConditionsLookup::SKIP_MENTEES_FOR_DAYS,
151            'GEMenteeGenders' => $this->getMenteeGenders(),
152        ];
153    }
154
155    /** @inheritDoc */
156    protected function getMobileSummaryBody() {
157        return $this->getBody();
158    }
159}