MediaWiki master
SpecialPagesWithProp.php
Go to the documentation of this file.
1<?php
25namespace MediaWiki\Specials;
26
31use Skin;
32use stdClass;
34
41
45 private $propName = null;
46
50 private $existingPropNames = null;
51
55 private $ns;
56
60 private $reverse = false;
61
65 private $sortByValue = false;
66
70 public function __construct( IConnectionProvider $dbProvider ) {
71 parent::__construct( 'PagesWithProp' );
72 $this->setDatabaseProvider( $dbProvider );
73 }
74
75 public function isCacheable() {
76 return false;
77 }
78
79 public function execute( $par ) {
80 $this->setHeaders();
81 $this->outputHeader();
82 $this->getOutput()->addModuleStyles( 'mediawiki.special' );
83
84 $request = $this->getRequest();
85 $propname = $request->getVal( 'propname', $par );
86 $this->ns = $request->getIntOrNull( 'namespace' );
87 $this->reverse = $request->getBool( 'reverse' );
88 $this->sortByValue = $request->getBool( 'sortbyvalue' );
89
90 $propnames = $this->getExistingPropNames();
91
92 $fields = [
93 'propname' => [
94 'type' => 'combobox',
95 'name' => 'propname',
96 'options' => $propnames,
97 'default' => $propname,
98 'label-message' => 'pageswithprop-prop',
99 'required' => true,
100 ],
101 'namespace' => [
102 'type' => 'namespaceselect',
103 'name' => 'namespace',
104 'label-message' => 'namespace',
105 'all' => '',
106 'default' => $this->ns,
107 ],
108 'reverse' => [
109 'type' => 'check',
110 'name' => 'reverse',
111 'default' => $this->reverse,
112 'label-message' => 'pageswithprop-reverse',
113 'required' => false,
114 ],
115 'sortbyvalue' => [
116 'type' => 'check',
117 'name' => 'sortbyvalue',
118 'default' => $this->sortByValue,
119 'label-message' => 'pageswithprop-sortbyvalue',
120 'required' => false,
121 ]
122 ];
123
124 $form = HTMLForm::factory( 'ooui', $fields, $this->getContext() )
125 ->setMethod( 'get' )
126 ->setTitle( $this->getPageTitle() ) // Remove subpage
127 ->setSubmitCallback( [ $this, 'onSubmit' ] )
128 ->setWrapperLegendMsg( 'pageswithprop-legend' )
129 ->addHeaderHtml( $this->msg( 'pageswithprop-text' )->parseAsBlock() )
130 ->setSubmitTextMsg( 'pageswithprop-submit' )
131 ->prepareForm();
132 $form->displayForm( false );
133 if ( $propname !== '' && $propname !== null ) {
134 $form->trySubmit();
135 }
136 }
137
138 public function onSubmit( $data, $form ) {
139 $this->propName = $data['propname'];
140 parent::execute( $data['propname'] );
141 }
142
151 public function prefixSearchSubpages( $search, $limit, $offset ) {
152 $subpages = array_keys( $this->queryExistingProps( $limit, $offset ) );
153 // We've already limited and offsetted, set to N and 0 respectively.
154 return self::prefixSearchArray( $search, count( $subpages ), $subpages, 0 );
155 }
156
161 public function isSyndicated() {
162 return false;
163 }
164
168 protected function linkParameters() {
169 $params = [
170 'reverse' => $this->reverse,
171 'sortbyvalue' => $this->sortByValue,
172 ];
173 if ( $this->ns !== null ) {
174 $params['namespace'] = $this->ns;
175 }
176 return $params;
177 }
178
179 public function getQueryInfo() {
180 $query = [
181 'tables' => [ 'page_props', 'page' ],
182 'fields' => [
183 'page_id' => 'pp_page',
184 'page_namespace',
185 'page_title',
186 'page_len',
187 'page_is_redirect',
188 'page_latest',
189 'pp_value',
190 ],
191 'conds' => [
192 'pp_propname' => $this->propName,
193 ],
194 'join_conds' => [
195 'page' => [ 'JOIN', 'page_id = pp_page' ]
196 ],
197 'options' => []
198 ];
199
200 if ( $this->ns !== null ) {
201 $query['conds']['page_namespace'] = $this->ns;
202 }
203
204 return $query;
205 }
206
207 protected function getOrderFields() {
208 $sort = [ 'page_id' ];
209 if ( $this->sortByValue ) {
210 array_unshift( $sort, 'pp_sortkey' );
211 }
212 return $sort;
213 }
214
218 public function sortDescending() {
219 return !$this->reverse;
220 }
221
227 public function formatResult( $skin, $result ) {
228 $title = Title::newFromRow( $result );
229 $ret = $this->getLinkRenderer()->makeKnownLink( $title );
230 if ( $result->pp_value !== '' ) {
231 // Do not show very long or binary values on the special page
232 $valueLength = strlen( $result->pp_value );
233 $isBinary = str_contains( $result->pp_value, "\0" );
234 $isTooLong = $valueLength > 1024;
235
236 if ( $isBinary || $isTooLong ) {
237 $message = $this
238 ->msg( $isBinary ? 'pageswithprop-prophidden-binary' : 'pageswithprop-prophidden-long' )
239 ->sizeParams( $valueLength );
240
241 $propValue = Html::element( 'span', [ 'class' => 'prop-value-hidden' ], $message->text() );
242 } else {
243 $propValue = Html::element( 'span', [ 'class' => 'prop-value' ], $result->pp_value );
244 }
245
246 $ret .= $this->msg( 'colon-separator' )->escaped() . $propValue;
247 }
248
249 return $ret;
250 }
251
252 public function getExistingPropNames() {
253 if ( $this->existingPropNames === null ) {
254 $this->existingPropNames = $this->queryExistingProps();
255 }
256 return $this->existingPropNames;
257 }
258
259 protected function queryExistingProps( $limit = null, $offset = 0 ) {
260 $queryBuilder =
261 $this->getDatabaseProvider()
262 ->getReplicaDatabase()
263 ->newSelectQueryBuilder()
264 ->select( 'pp_propname' )
265 ->distinct()
266 ->from( 'page_props' )
267 ->orderBy( 'pp_propname' );
268
269 if ( $limit ) {
270 $queryBuilder->limit( $limit );
271 }
272 if ( $offset ) {
273 $queryBuilder->offset( $offset );
274 }
275 $res = $queryBuilder->caller( __METHOD__ )->fetchResultSet();
276
277 $propnames = [];
278 foreach ( $res as $row ) {
279 $propnames[$row->pp_propname] = $row->pp_propname;
280 }
281
282 return $propnames;
283 }
284
285 protected function getGroupName() {
286 return 'pages';
287 }
288}
289
294class_alias( SpecialPagesWithProp::class, 'SpecialPagesWithProp' );
array $params
The job parameters.
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition HTMLForm.php:206
This class is a collection of static functions that serve two purposes:
Definition Html.php:56
This is a class for doing query pages; since they're almost all the same, we factor out some of the f...
Definition QueryPage.php:88
setDatabaseProvider(IConnectionProvider $databaseProvider)
int $offset
The offset and limit in use, as passed to the query() function.
Definition QueryPage.php:93
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
static prefixSearchArray( $search, $limit, array $subpages, $offset)
Helper function for implementations of prefixSearchSubpages() that filter the values in memory (as op...
getPageTitle( $subpage=false)
Get a self-referential title object.
getContext()
Gets the context this SpecialPage is executed in.
getRequest()
Get the WebRequest being used for this instance.
msg( $key,... $params)
Wrapper around wfMessage that sets the current context.
getOutput()
Get the OutputPage being used for this instance.
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages Per default the message key is the canonical name o...
Special:PagesWithProp to search the page_props table.
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
getQueryInfo()
Subclasses return an SQL query here, formatted as an array with the following keys: tables => Table(s...
__construct(IConnectionProvider $dbProvider)
getOrderFields()
Subclasses return an array of fields to order by here.
isCacheable()
Is the output of this query cacheable? Non-cacheable expensive pages will be disabled in miser mode a...
linkParameters()
If using extra form wheely-dealies, return a set of parameters here as an associative array....
prefixSearchSubpages( $search, $limit, $offset)
Return an array of subpages beginning with $search that this special page will accept.
execute( $par)
This is the actual workhorse.
Represents a title within MediaWiki.
Definition Title.php:78
The base class for all skins.
Definition Skin.php:58
Provide primary and replica IDatabase connections.
element(SerializerNode $parent, SerializerNode $node, $contents)
This program is free software; you can redistribute it and/or modify it under the terms of the GNU Ge...