MediaWiki  master
SpecialPagesWithProp.php
Go to the documentation of this file.
1 <?php
25 namespace MediaWiki\Specials;
26 
27 use HTMLForm;
31 use Skin;
32 use 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 
294 class_alias( SpecialPagesWithProp::class, 'SpecialPagesWithProp' );
Object handling generic submission, CSRF protection, layout and other logic for UI forms in a reusabl...
Definition: HTMLForm.php:158
static factory( $displayFormat, $descriptor, IContextSource $context, $messagePrefix='')
Construct a HTMLForm object for given display type.
Definition: HTMLForm.php:360
This class is a collection of static functions that serve two purposes:
Definition: Html.php:57
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:264
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)
Definition: QueryPage.php:985
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:76
static newFromRow( $row)
Make a Title object from a DB row.
Definition: Title.php:559
The base class for all skins.
Definition: Skin.php:60
Provide primary and replica IDatabase connections.