Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
43.69% covered (danger)
43.69%
45 / 103
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiManageMentorList
43.69% covered (danger)
43.69%
45 / 103
0.00% covered (danger)
0.00%
0 / 5
126.85
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
 execute
68.18% covered (warning)
68.18%
45 / 66
0.00% covered (danger)
0.00%
0 / 1
32.89
 isWriteMode
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 needsToken
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getAllowedParams
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace GrowthExperiments\Api;
4
5use ApiBase;
6use ApiMain;
7use GrowthExperiments\MentorDashboard\MentorTools\IMentorWeights;
8use GrowthExperiments\MentorDashboard\MentorTools\MentorStatusManager;
9use GrowthExperiments\Mentorship\Provider\IMentorWriter;
10use GrowthExperiments\Mentorship\Provider\MentorProvider;
11use IDBAccessObject;
12use LogicException;
13use MediaWiki\ParamValidator\TypeDef\UserDef;
14use Wikimedia\ParamValidator\ParamValidator;
15
16class ApiManageMentorList extends ApiBase {
17
18    private MentorProvider $mentorProvider;
19    private IMentorWriter $mentorWriter;
20    private MentorStatusManager $mentorStatusManager;
21
22    /**
23     * @param ApiMain $mainModule
24     * @param string $moduleName
25     * @param MentorProvider $mentorProvider
26     * @param IMentorWriter $mentorWriter
27     * @param MentorStatusManager $mentorStatusManager
28     */
29    public function __construct(
30        ApiMain $mainModule,
31        $moduleName,
32        MentorProvider $mentorProvider,
33        IMentorWriter $mentorWriter,
34        MentorStatusManager $mentorStatusManager
35    ) {
36        parent::__construct( $mainModule, $moduleName );
37
38        $this->mentorProvider = $mentorProvider;
39        $this->mentorWriter = $mentorWriter;
40        $this->mentorStatusManager = $mentorStatusManager;
41    }
42
43    /**
44     * @inheritDoc
45     */
46    public function execute() {
47        $block = $this->getUser()->getBlock( IDBAccessObject::READ_LATEST );
48        if ( $block && $block->isSitewide() ) {
49            $this->dieBlocked( $block );
50        }
51
52        // if the user is not a mentor, require enrollasmentor or managementors; the if is here to
53        // allow users to remove themselves, even after they lost the ability to enroll themselves.
54        if ( !$this->mentorProvider->isMentor( $this->getUser() ) ) {
55            $this->checkUserRightsAny( [ 'enrollasmentor', 'managementors' ] );
56        }
57
58        $params = $this->extractRequestParams();
59
60        if ( $params['username'] !== null ) {
61            $this->checkUserRightsAny( 'managementors' );
62            // despite the name, $params['username'] is converted to an UserIdentity
63            // via UserDef::PARAM_RETURN_OBJECT in getAllowedParams().
64            $mentorUser = $params['username'];
65        } else {
66            $mentorUser = $this->getUser();
67        }
68
69        $mentor = $this->mentorProvider->newMentorFromUserIdentity( $mentorUser );
70
71        if ( $params['message'] !== null ) {
72            $mentor->setIntroText( $params['message'] !== '' ? $params['message'] : null );
73        }
74        if ( $params['weight'] !== null ) {
75            $mentor->setWeight( (int)$params['weight'] );
76        }
77
78        // ensure awaytimestamp is provided when isaway=true
79        if ( $params['isaway'] && $params['awaytimestamp'] === null ) {
80            $this->dieWithError(
81                'growthexperiments-api-managementors-error-no-away-timestamp',
82                'away-timestamp'
83            );
84        }
85
86        switch ( $params['geaction'] ) {
87            case 'add':
88                $statusValue = $this->mentorWriter->addMentor(
89                    $mentor,
90                    $this->getUser(),
91                    $params['summary']
92                );
93                break;
94            case 'change':
95                $statusValue = $this->mentorWriter->changeMentor(
96                    $mentor,
97                    $this->getUser(),
98                    $params['summary']
99                );
100                break;
101            case 'remove':
102                $statusValue = $this->mentorWriter->removeMentor(
103                    $mentor,
104                    $this->getUser(),
105                    $params['summary']
106                );
107                break;
108            default:
109                // this should never happen, unless getAllowedParams is wrong
110                throw new LogicException( 'Invalid geaction passed validation' );
111        }
112
113        if ( !$statusValue->isOK() ) {
114            $this->dieStatus( $statusValue );
115        }
116
117        if ( $params['geaction'] !== 'remove' ) {
118            if ( $params['isaway'] ) {
119                $result = $this->mentorStatusManager->markMentorAsAwayTimestamp(
120                    $mentorUser,
121                    $params['awaytimestamp']
122                );
123                if ( !$result->isOK() ) {
124                    $this->dieStatus( $result );
125                }
126            } else {
127                $this->mentorStatusManager->markMentorAsActive( $mentorUser );
128            }
129        }
130
131        $rawBackTs = $this->mentorStatusManager->getMentorBackTimestamp( $mentorUser );
132        $this->getResult()->addValue( null, $this->getModuleName(), [
133            'status' => 'ok',
134            'mentor' => [
135                'message' => $mentor->hasCustomIntroText() ? $mentor->getIntroText() : null,
136                'weight' => $mentor->getWeight(),
137                'awayTimestamp' => $rawBackTs,
138                'awayTimestampHuman' => $rawBackTs !== null ? $this->getContext()
139                    ->getLanguage()->date( $rawBackTs, true ) : null,
140
141                // NOTE: Legacy attribute, weight provides the same info.
142                'automaticallyAssigned' => $mentor->getWeight() !== IMentorWeights::WEIGHT_NONE,
143            ]
144        ] );
145    }
146
147    /**
148     * @inheritDoc
149     */
150    public function isWriteMode() {
151        return true;
152    }
153
154    /**
155     * @inheritDoc
156     */
157    public function needsToken() {
158        return 'csrf';
159    }
160
161    /**
162     * @inheritDoc
163     */
164    protected function getAllowedParams() {
165        return [
166            'geaction' => [
167                ParamValidator::PARAM_REQUIRED => true,
168                ParamValidator::PARAM_TYPE => [
169                    'add',
170                    'change',
171                    'remove',
172                ]
173            ],
174            'message' => [
175                ParamValidator::PARAM_TYPE => 'string'
176            ],
177            'weight' => [
178                ParamValidator::PARAM_TYPE => array_map( 'strval', IMentorWeights::WEIGHTS )
179            ],
180            'isaway' => [
181                ParamValidator::PARAM_TYPE => 'boolean',
182            ],
183            'awaytimestamp' => [
184                ParamValidator::PARAM_TYPE => 'timestamp',
185            ],
186            'summary' => [
187                ParamValidator::PARAM_TYPE => 'string',
188                ParamValidator::PARAM_DEFAULT => '',
189            ],
190            'username' => [
191                ParamValidator::PARAM_TYPE => 'user',
192                UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name' ],
193                UserDef::PARAM_RETURN_OBJECT => true,
194            ],
195        ];
196    }
197}