Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 128
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpecialNewFiles
0.00% covered (danger)
0.00%
0 / 128
0.00% covered (danger)
0.00%
0 / 5
210
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
 execute
0.00% covered (danger)
0.00%
0 / 53
0.00% covered (danger)
0.00%
0 / 1
72
 buildForm
0.00% covered (danger)
0.00%
0 / 60
0.00% covered (danger)
0.00%
0 / 1
6
 getGroupName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setTopText
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2/**
3 * @license GPL-2.0-or-later
4 * @file
5 */
6
7namespace MediaWiki\Specials;
8
9use MediaWiki\Context\DerivativeContext;
10use MediaWiki\Context\IContextSource;
11use MediaWiki\Html\FormOptions;
12use MediaWiki\Html\Html;
13use MediaWiki\HTMLForm\Field\HTMLUserTextField;
14use MediaWiki\HTMLForm\HTMLForm;
15use MediaWiki\Page\LinkBatchFactory;
16use MediaWiki\Permissions\GroupPermissionsLookup;
17use MediaWiki\Request\DerivativeRequest;
18use MediaWiki\SpecialPage\IncludableSpecialPage;
19use MediaWiki\Specials\Pager\NewFilesPager;
20use Wikimedia\Mime\MimeAnalyzer;
21use Wikimedia\Rdbms\IConnectionProvider;
22
23/**
24 * Implements Special:Newimages
25 *
26 * @ingroup SpecialPage
27 */
28class SpecialNewFiles extends IncludableSpecialPage {
29    /** @var FormOptions */
30    protected $opts;
31
32    /** @var string[] */
33    protected $mediaTypes;
34
35    public function __construct(
36        MimeAnalyzer $mimeAnalyzer,
37        private readonly GroupPermissionsLookup $groupPermissionsLookup,
38        private readonly IConnectionProvider $dbProvider,
39        private readonly LinkBatchFactory $linkBatchFactory,
40    ) {
41        parent::__construct( 'Newimages' );
42        $this->mediaTypes = $mimeAnalyzer->getMediaTypes();
43    }
44
45    /** @inheritDoc */
46    public function execute( $par ) {
47        $context = new DerivativeContext( $this->getContext() );
48
49        $this->setHeaders();
50        $this->outputHeader();
51
52        $out = $this->getOutput();
53        $this->addHelpLink( 'Help:New images' );
54
55        $opts = new FormOptions();
56
57        $opts->add( 'user', '' );
58        $opts->add( 'showbots', false );
59        $opts->add( 'hidepatrolled', false );
60        $opts->add( 'mediatype', $this->mediaTypes );
61        $opts->add( 'limit', 50 );
62        $opts->add( 'offset', '' );
63        $opts->add( 'start', '' );
64        $opts->add( 'end', '' );
65
66        $opts->fetchValuesFromRequest( $this->getRequest() );
67
68        if ( $par !== null ) {
69            $opts->setValue( 'limit', $par );
70        }
71
72        // If start date comes after end date chronologically, swap them.
73        // They are swapped in the interface by JS.
74        $start = $opts->getValue( 'start' );
75        $end = $opts->getValue( 'end' );
76        if ( $start !== '' && $end !== '' && $start > $end ) {
77            $temp = $end;
78            $end = $start;
79            $start = $temp;
80
81            $opts->setValue( 'start', $start, true );
82            $opts->setValue( 'end', $end, true );
83
84            // also swap values in request object, which is used by HTMLForm
85            // to pre-populate the fields with the previous input
86            $request = $context->getRequest();
87            $context->setRequest( new DerivativeRequest(
88                $request,
89                [ 'start' => $start, 'end' => $end ] + $request->getValues(),
90                $request->wasPosted()
91            ) );
92        }
93
94        // Avoid unexpected query or query errors to assoc array input, or nested arrays via
95        // URL query params. Keep only string values (T321133).
96        $mediaTypes = $opts->getValue( 'mediatype' );
97        $mediaTypes = array_filter( $mediaTypes, 'is_string' );
98        // Avoid unbounded query size with bogus values. Keep only known types.
99        $mediaTypes = array_values( array_intersect( $this->mediaTypes, $mediaTypes ) );
100        // Optimization: Remove redundant IN() query condition if all types are checked.
101        if ( !array_diff( $this->mediaTypes, $mediaTypes ) ) {
102            $mediaTypes = [];
103        }
104        $opts->setValue( 'mediatype', $mediaTypes );
105
106        $opts->validateIntBounds( 'limit', 0, 500 );
107
108        $this->opts = $opts;
109
110        if ( !$this->including() ) {
111            $this->setTopText();
112            $this->buildForm( $context );
113        }
114
115        $pager = new NewFilesPager(
116            $context,
117            $this->groupPermissionsLookup,
118            $this->linkBatchFactory,
119            $this->getLinkRenderer(),
120            $this->dbProvider,
121            $opts
122        );
123
124        $out->addHTML( $pager->getBody() );
125        if ( !$this->including() ) {
126            $out->addHTML( $pager->getNavigationBar() );
127        }
128    }
129
130    protected function buildForm( IContextSource $context ) {
131        $mediaTypesText = array_map( function ( $type ) {
132            // mediastatistics-header-unknown, mediastatistics-header-bitmap,
133            // mediastatistics-header-drawing, mediastatistics-header-audio,
134            // mediastatistics-header-video, mediastatistics-header-multimedia,
135            // mediastatistics-header-office, mediastatistics-header-text,
136            // mediastatistics-header-executable, mediastatistics-header-archive,
137            // mediastatistics-header-3d,
138            return $this->msg( 'mediastatistics-header-' . strtolower( $type ) )->escaped();
139        }, $this->mediaTypes );
140        $mediaTypesOptions = array_combine( $mediaTypesText, $this->mediaTypes );
141        ksort( $mediaTypesOptions );
142
143        $formDescriptor = [
144            'user' => [
145                'class' => HTMLUserTextField::class,
146                'label-message' => 'newimages-user',
147                'name' => 'user',
148            ],
149
150            'showbots' => [
151                'type' => 'check',
152                'label-message' => 'newimages-showbots',
153                'name' => 'showbots',
154            ],
155
156            'hidepatrolled' => [
157                'type' => 'check',
158                'label-message' => 'newimages-hidepatrolled',
159                'name' => 'hidepatrolled',
160            ],
161
162            'mediatype' => [
163                'type' => 'multiselect',
164                'flatlist' => true,
165                'name' => 'mediatype',
166                'label-message' => 'newimages-mediatype',
167                'options' => $mediaTypesOptions,
168                'default' => $this->mediaTypes,
169            ],
170
171            'limit' => [
172                'type' => 'hidden',
173                'default' => $this->opts->getValue( 'limit' ),
174                'name' => 'limit',
175            ],
176
177            'offset' => [
178                'type' => 'hidden',
179                'default' => $this->opts->getValue( 'offset' ),
180                'name' => 'offset',
181            ],
182
183            'start' => [
184                'type' => 'date',
185                'label-message' => 'date-range-from',
186                'name' => 'start',
187            ],
188
189            'end' => [
190                'type' => 'date',
191                'label-message' => 'date-range-to',
192                'name' => 'end',
193            ],
194        ];
195
196        if ( !$this->getUser()->useFilePatrol() ) {
197            unset( $formDescriptor['hidepatrolled'] );
198        }
199
200        HTMLForm::factory( 'ooui', $formDescriptor, $context )
201            // For the 'multiselect' field values to be preserved on submit
202            ->setFormIdentifier( 'specialnewimages' )
203            ->setWrapperLegendMsg( 'newimages-legend' )
204            ->setSubmitTextMsg( 'ilsubmit' )
205            ->setMethod( 'get' )
206            ->prepareForm()
207            ->displayForm( false );
208    }
209
210    /** @inheritDoc */
211    protected function getGroupName() {
212        return 'changes';
213    }
214
215    /**
216     * Send the text to be displayed above the options
217     */
218    public function setTopText() {
219        $message = $this->msg( 'newimagestext' )->inContentLanguage();
220        if ( !$message->isDisabled() ) {
221            $contLang = $this->getContentLanguage();
222            $this->getOutput()->addWikiTextAsContent(
223                Html::rawElement( 'div',
224                    [
225                        'lang' => $contLang->getHtmlCode(),
226                        'dir' => $contLang->getDir()
227                    ],
228                    "\n" . $message->plain() . "\n"
229                )
230            );
231        }
232    }
233}
234
235// @codeCoverageIgnoreStart
236/**
237 * Retain the old class name for backwards compatibility.
238 * @deprecated since 1.41
239 */
240class_alias( SpecialNewFiles::class, 'SpecialNewFiles' );
241// @codeCoverageIgnoreEnd