Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
76.58% covered (warning)
76.58%
85 / 111
44.44% covered (danger)
44.44%
4 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
ProtectLogFormatter
77.27% covered (warning)
77.27%
85 / 110
44.44% covered (danger)
44.44%
4 / 9
56.86
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getPreloadTitles
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 getMessageKey
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 getMessageParameters
93.33% covered (success)
93.33%
14 / 15
0.00% covered (danger)
0.00%
0 / 1
7.01
 getActionLinks
67.86% covered (warning)
67.86%
19 / 28
0.00% covered (danger)
0.00%
0 / 1
7.20
 getParametersForApi
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
1 / 1
8
 formatParametersForApi
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
5
 createProtectDescription
92.86% covered (success)
92.86%
13 / 14
0.00% covered (danger)
0.00%
0 / 1
4.01
 formatExpiry
20.00% covered (danger)
20.00%
2 / 10
0.00% covered (danger)
0.00%
0 / 1
4.05
1<?php
2/**
3 * Formatter for protect log entries.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 * @file
21 * @license GPL-2.0-or-later
22 * @since 1.26
23 */
24
25namespace MediaWiki\Logging;
26
27use MediaWiki\Message\Message;
28use MediaWiki\Title\MalformedTitleException;
29use MediaWiki\Title\Title;
30use MediaWiki\Title\TitleParser;
31
32/**
33 * This class formats protect log entries.
34 *
35 * @since 1.26
36 */
37class ProtectLogFormatter extends LogFormatter {
38    private TitleParser $titleParser;
39
40    public function __construct(
41        LogEntry $entry,
42        TitleParser $titleParser
43    ) {
44        parent::__construct( $entry );
45        $this->titleParser = $titleParser;
46    }
47
48    public function getPreloadTitles() {
49        $subtype = $this->entry->getSubtype();
50        if ( $subtype === 'move_prot' ) {
51            $params = $this->extractParameters();
52            try {
53                return [ $this->titleParser->parseTitle( $params[3] ) ];
54            } catch ( MalformedTitleException $_ ) {
55            }
56        }
57        return [];
58    }
59
60    protected function getMessageKey() {
61        $key = parent::getMessageKey();
62        $params = $this->extractParameters();
63        if ( isset( $params[4] ) && $params[4] ) {
64            // Messages: logentry-protect-protect-cascade, logentry-protect-modify-cascade
65            $key .= '-cascade';
66        }
67
68        return $key;
69    }
70
71    protected function getMessageParameters() {
72        $params = parent::getMessageParameters();
73
74        $subtype = $this->entry->getSubtype();
75        if ( $subtype === 'protect' || $subtype === 'modify' ) {
76            $rawParams = $this->entry->getParameters();
77            if ( isset( $rawParams['details'] ) ) {
78                $params[3] = $this->createProtectDescription( $rawParams['details'] );
79            } elseif ( isset( $params[3] ) ) {
80                // Old way of Restrictions and expiries
81                $params[3] = $this->context->getLanguage()->getDirMark() . $params[3];
82            } else {
83                // Very old way (nothing set)
84                $params[3] = '';
85            }
86            // Cascading flag
87            if ( isset( $params[4] ) ) {
88                // handled in getMessageKey
89                unset( $params[4] );
90            }
91        } elseif ( $subtype === 'move_prot' ) {
92            $oldname = $this->makePageLink( Title::newFromText( $params[3] ), [ 'redirect' => 'no' ] );
93            $params[3] = Message::rawParam( $oldname );
94        }
95
96        return $params;
97    }
98
99    public function getActionLinks() {
100        $linkRenderer = $this->getLinkRenderer();
101        $subtype = $this->entry->getSubtype();
102        if ( $this->entry->isDeleted( LogPage::DELETED_ACTION ) // Action is hidden
103            || $subtype === 'move_prot' // the move log entry has the right action link
104        ) {
105            return '';
106        }
107
108        // Show history link for pages that exist otherwise show nothing
109        $title = $this->entry->getTarget();
110        $links = [];
111        if ( $title->exists() ) {
112            $links[] = $linkRenderer->makeLink( $title,
113                $this->msg( 'hist' )->text(),
114                [],
115                [
116                    'action' => 'history',
117                    'offset' => $this->entry->getTimestamp(),
118                ]
119            );
120        }
121
122        // Show change protection link
123        if ( $this->context->getAuthority()->isAllowed( 'protect' ) ) {
124            $links[] = $linkRenderer->makeKnownLink(
125                $title,
126                $this->msg( 'protect_change' )->text(),
127                [],
128                [ 'action' => 'protect' ]
129            );
130        }
131
132        if ( !$links ) {
133            return '';
134        } else {
135            return $this->msg( 'parentheses' )->rawParams(
136                $this->context->getLanguage()->pipeList( $links )
137            )->escaped();
138        }
139    }
140
141    protected function getParametersForApi() {
142        $entry = $this->entry;
143        $subtype = $this->entry->getSubtype();
144        $params = $entry->getParameters();
145
146        $map = [];
147        if ( $subtype === 'protect' || $subtype === 'modify' ) {
148            $map = [
149                '4::description',
150                '5:bool:cascade',
151                'details' => ':array:details',
152            ];
153        } elseif ( $subtype === 'move_prot' ) {
154            $map = [
155                '4:title:oldtitle',
156                '4::oldtitle' => '4:title:oldtitle',
157            ];
158        }
159        foreach ( $map as $index => $key ) {
160            if ( isset( $params[$index] ) ) {
161                $params[$key] = $params[$index];
162                unset( $params[$index] );
163            }
164        }
165
166        // Change string to explicit boolean
167        if ( isset( $params['5:bool:cascade'] ) && is_string( $params['5:bool:cascade'] ) ) {
168            $params['5:bool:cascade'] = $params['5:bool:cascade'] === 'cascade';
169        }
170
171        return $params;
172    }
173
174    public function formatParametersForApi() {
175        $ret = parent::formatParametersForApi();
176        if ( isset( $ret['details'] ) && is_array( $ret['details'] ) ) {
177            $contLang = $this->getContentLanguage();
178            foreach ( $ret['details'] as &$detail ) {
179                if ( isset( $detail['expiry'] ) ) {
180                    $detail['expiry'] = $contLang->
181                        formatExpiry( $detail['expiry'], TS_ISO_8601, 'infinite' );
182                }
183            }
184        }
185
186        return $ret;
187    }
188
189    /**
190     * Create the protect description to show in the log formatter
191     *
192     * @param array[] $details
193     * @return string
194     */
195    public function createProtectDescription( array $details ) {
196        $protectDescription = '';
197
198        foreach ( $details as $param ) {
199            $expiryText = $this->formatExpiry( $param['expiry'] );
200
201            // Messages: restriction-edit, restriction-move, restriction-create,
202            // restriction-upload
203            $action = $this->context->msg( 'restriction-' . $param['type'] )->escaped();
204
205            $protectionLevel = $param['level'];
206            // Messages: protect-level-autoconfirmed, protect-level-sysop
207            $message = $this->context->msg( 'protect-level-' . $protectionLevel );
208            if ( $message->isDisabled() ) {
209                // Require "$1" permission
210                $restrictions = $this->context->msg( "protect-fallback", $protectionLevel )->parse();
211            } else {
212                $restrictions = $message->escaped();
213            }
214
215            if ( $protectDescription !== '' ) {
216                $protectDescription .= $this->context->msg( 'word-separator' )->escaped();
217            }
218
219            $protectDescription .= $this->context->msg( 'protect-summary-desc' )
220                ->params( $action, $restrictions, $expiryText )->escaped();
221        }
222
223        return $protectDescription;
224    }
225
226    private function formatExpiry( $expiry ) {
227        if ( wfIsInfinity( $expiry ) ) {
228            return $this->context->msg( 'protect-expiry-indefinite' )->text();
229        }
230        $lang = $this->context->getLanguage();
231        $user = $this->context->getUser();
232        return $this->context->msg(
233            'protect-expiring-local',
234            $lang->userTimeAndDate( $expiry, $user ),
235            $lang->userDate( $expiry, $user ),
236            $lang->userTime( $expiry, $user )
237        )->text();
238    }
239
240}
241
242/** @deprecated class alias since 1.44 */
243class_alias( ProtectLogFormatter::class, 'ProtectLogFormatter' );