Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
45.45% covered (danger)
45.45%
15 / 33
33.33% covered (danger)
33.33%
2 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
RemoteApiActionExecutor
45.45% covered (danger)
45.45%
15 / 33
33.33% covered (danger)
33.33%
2 / 6
30.64
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 executeTestEditActionQuery
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 executeEditAction
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 executeUserRightsQuery
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 executeDeleteAction
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 statusFromApiResponse
80.00% covered (warning)
80.00%
8 / 10
0.00% covered (danger)
0.00%
0 / 1
4.13
1<?php
2
3namespace FileImporter\Remote\MediaWiki;
4
5use FileImporter\Data\SourceUrl;
6use StatusValue;
7use User;
8
9/**
10 * @license GPL-2.0-or-later
11 */
12class RemoteApiActionExecutor {
13
14    public const CHANGE_TAG = 'fileimporter-remote';
15
16    /**
17     * @var RemoteApiRequestExecutor
18     */
19    private $remoteApiRequestExecutor;
20
21    /**
22     * @param RemoteApiRequestExecutor $remoteApiRequestExecutor
23     */
24    public function __construct( RemoteApiRequestExecutor $remoteApiRequestExecutor ) {
25        $this->remoteApiRequestExecutor = $remoteApiRequestExecutor;
26    }
27
28    /**
29     * @param SourceUrl $sourceUrl
30     * @param User $user
31     * @param string $title
32     *
33     * @return StatusValue ok if the user can edit the page
34     */
35    public function executeTestEditActionQuery( SourceUrl $sourceUrl, User $user, string $title ): StatusValue {
36        // Expected return values:
37        // { "query": { "pages": [ { "actions": { "edit": true }, …
38        // { "query": { "pages": [ { "actions": { "edit": false }, …
39        $result = $this->remoteApiRequestExecutor->execute(
40            $sourceUrl,
41            $user,
42            [
43                'action' => 'query',
44                'format' => 'json',
45                'formatversion' => 2,
46                'prop' => 'info',
47                'titles' => $title,
48                'intestactions' => 'edit',
49            ],
50            true
51        );
52
53        $status = $this->statusFromApiResponse( $result );
54        $canEdit = $result['query']['pages'][0]['actions']['edit'] ?? false;
55        $status->setOK( $canEdit );
56
57        return $status;
58    }
59
60    /**
61     * @param SourceUrl $sourceUrl
62     * @param User $user
63     * @param string $title
64     * @param array $params At least one of the parameters "text", "appendtext", "prependtext" and
65     *  "undo" is required.
66     * @param string $editSummary
67     *
68     * @return StatusValue
69     */
70    public function executeEditAction(
71        SourceUrl $sourceUrl,
72        User $user,
73        string $title,
74        array $params,
75        string $editSummary
76    ): StatusValue {
77        $token = $this->remoteApiRequestExecutor->getCsrfToken( $sourceUrl, $user );
78        if ( $token === null ) {
79            return $this->statusFromApiResponse();
80        }
81
82        $result = $this->remoteApiRequestExecutor->execute(
83            $sourceUrl,
84            $user,
85            array_merge(
86                [
87                    'action' => 'edit',
88                    'token' => $token,
89                    'format' => 'json',
90                    'title' => $title,
91                    'summary' => $editSummary,
92                    'tags' => self::CHANGE_TAG,
93                ],
94                $params
95            ),
96            true
97        );
98        return $this->statusFromApiResponse( $result );
99    }
100
101    /**
102     * @param SourceUrl $sourceUrl
103     * @param User $user
104     *
105     * @return StatusValue ok if the user is allowed to delete pages
106     */
107    public function executeUserRightsQuery( SourceUrl $sourceUrl, User $user ): StatusValue {
108        // Expected return values:
109        // { "query": { "userinfo": { "rights": [ "delete", …
110        // Same with formatversion=2
111        $result = $this->remoteApiRequestExecutor->execute(
112            $sourceUrl,
113            $user,
114            [
115                'action' => 'query',
116                'format' => 'json',
117                'meta' => 'userinfo',
118                'uiprop' => 'rights',
119            ]
120        );
121
122        $status = $this->statusFromApiResponse( $result );
123        $rights = $result['query']['userinfo']['rights'] ?? [];
124        $canDelete = in_array( 'delete', $rights );
125        $status->setOK( $canDelete );
126
127        return $status;
128    }
129
130    /**
131     * @param SourceUrl $sourceUrl
132     * @param User $user
133     * @param string $title
134     * @param string $deletionReason
135     *
136     * @return StatusValue
137     */
138    public function executeDeleteAction(
139        SourceUrl $sourceUrl,
140        User $user,
141        string $title,
142        string $deletionReason
143    ): StatusValue {
144        $token = $this->remoteApiRequestExecutor->getCsrfToken( $sourceUrl, $user );
145        if ( $token === null ) {
146            return $this->statusFromApiResponse();
147        }
148
149        $result = $this->remoteApiRequestExecutor->execute(
150            $sourceUrl,
151            $user,
152            [
153                'action' => 'delete',
154                'format' => 'json',
155                'title' => $title,
156                'reason' => $deletionReason,
157                'tags' => self::CHANGE_TAG,
158                'token' => $token,
159            ],
160            true
161        );
162        return $this->statusFromApiResponse( $result );
163    }
164
165    /**
166     * @param array|null $apiResponse
167     * @return StatusValue
168     */
169    private function statusFromApiResponse( array $apiResponse = null ): StatusValue {
170        $status = StatusValue::newGood();
171
172        if ( !$apiResponse ) {
173            $status->setOK( false );
174            return $status;
175        }
176
177        // It's an array of "errors" with errorformat=plaintext, but a single "error" without.
178        $errors = $apiResponse['errors'] ?? [];
179        if ( isset( $apiResponse['error'] ) ) {
180            $errors[] = $apiResponse['error'];
181        }
182        foreach ( $errors as $error ) {
183            // Errors contain "code" and "info" with formatversion=2, but "code" and "*" without.
184            $status->error( $error['code'] );
185        }
186
187        return $status;
188    }
189
190}