wiki.techinc.nl/includes/specials/SpecialPagesWithProp.php

284 lines
6.8 KiB
PHP
Raw Normal View History

<?php
/**
* Implements Special:PagesWithProp
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @since 1.21
* @file
* @ingroup SpecialPage
*/
use Wikimedia\Rdbms\ILoadBalancer;
/**
* Special:PagesWithProp to search the page_props table
* @ingroup SpecialPage
* @since 1.21
*/
class SpecialPagesWithProp extends QueryPage {
/**
* @var string|null
*/
private $propName = null;
/**
* @var string[]|null
*/
private $existingPropNames = null;
/**
* @var int|null
*/
private $ns;
/**
* @var bool
*/
private $reverse = false;
/**
* @var bool
*/
private $sortByValue = false;
/**
* @param ILoadBalancer $loadBalancer
*/
public function __construct( ILoadBalancer $loadBalancer ) {
parent::__construct( 'PagesWithProp' );
$this->setDBLoadBalancer( $loadBalancer );
}
public function isCacheable() {
return false;
}
public function execute( $par ) {
$this->setHeaders();
$this->outputHeader();
mediawiki.special: Combine various tiny specialpage style modules These stylesheets are sufficiently tiny that it doesn't make sense to offer them the ability to be loaded separately from each other (saving bytes in double-digits) at the cost of 1) exporting a dedicated registry item with meta data shipped on every page view, 2) reduced cache re-use from increased fragmentation. Instead, move these to the 'mediawiki.special' style module. The entries retain their own files to keep them as easy to find and edit as before. Where not already, ensure addModuleStyles() is always placed above any addModules() call in the same method. The load order isn't affected by the call order, but given blocking style-modules load before async JS, it helps to order them in a way that visually matches the effective load order (from top to bottom). The following 7 modules were remove without deprecation: 1. "mediawiki.special.apisandbox.styles" (1 rule) 2. "mediawiki.special.edittags.styles" (3 rules) 3. "mediawiki.special.movePage.styles" (1 rule) 4. "mediawiki.special.pagesWithProp" (1 rule) 5. "mediawiki.special.upload.styles" (2 rules) 6. "mediawiki.special.watchlist.styles" (3 rules) 7. "mediawiki.special.comparepages.styles" (4 rules) These module names were only used on the core classes loading them, and aren't depended on outside core by module name, rather, extensions and gadgets depend on the styles styles being loaded in a blocking manner on these pages, which remains unaffected. Bug: T192623 Change-Id: I6e663dc3c80c7104c9b9abdde44c654543185373
2018-05-11 14:33:41 +00:00
$this->getOutput()->addModuleStyles( 'mediawiki.special' );
$request = $this->getRequest();
$propname = $request->getVal( 'propname', $par );
$this->ns = $request->getIntOrNull( 'namespace' );
$this->reverse = $request->getBool( 'reverse' );
$this->sortByValue = $request->getBool( 'sortbyvalue' );
$propnames = $this->getExistingPropNames();
$fields = [
'propname' => [
'type' => 'combobox',
'name' => 'propname',
'options' => $propnames,
'default' => $propname,
'label-message' => 'pageswithprop-prop',
'required' => true,
],
'namespace' => [
'type' => 'namespaceselect',
'name' => 'namespace',
'label-message' => 'namespace',
'all' => '',
'default' => $this->ns,
],
'reverse' => [
'type' => 'check',
'name' => 'reverse',
'default' => $this->reverse,
'label-message' => 'pageswithprop-reverse',
'required' => false,
],
'sortbyvalue' => [
'type' => 'check',
'name' => 'sortbyvalue',
'default' => $this->sortByValue,
'label-message' => 'pageswithprop-sortbyvalue',
'required' => false,
]
];
$context = new DerivativeContext( $this->getContext() );
$context->setTitle( $this->getPageTitle() ); // Remove subpage
$form = HTMLForm::factory( 'ooui', $fields, $context )
->setMethod( 'get' )
->setSubmitCallback( [ $this, 'onSubmit' ] )
->setWrapperLegendMsg( 'pageswithprop-legend' )
->addHeaderText( $this->msg( 'pageswithprop-text' )->parseAsBlock() )
->setSubmitTextMsg( 'pageswithprop-submit' )
->prepareForm();
$form->displayForm( false );
if ( $propname !== '' && $propname !== null ) {
$form->trySubmit();
}
}
public function onSubmit( $data, $form ) {
$this->propName = $data['propname'];
parent::execute( $data['propname'] );
}
/**
* Return an array of subpages beginning with $search that this special page will accept.
*
* @param string $search Prefix to search for
* @param int $limit Maximum number of results to return
* @param int $offset Number of pages to skip
* @return string[] Matching subpages
*/
public function prefixSearchSubpages( $search, $limit, $offset ) {
$subpages = array_keys( $this->queryExistingProps( $limit, $offset ) );
// We've already limited and offsetted, set to N and 0 respectively.
return self::prefixSearchArray( $search, count( $subpages ), $subpages, 0 );
}
/**
* Disable RSS/Atom feeds
* @return bool
*/
public function isSyndicated() {
return false;
}
/**
* @inheritDoc
*/
protected function linkParameters() {
$params = [
'reverse' => $this->reverse,
'sortbyvalue' => $this->sortByValue,
];
if ( $this->ns !== null ) {
$params['namespace'] = $this->ns;
}
return $params;
}
public function getQueryInfo() {
$query = [
'tables' => [ 'page_props', 'page' ],
'fields' => [
'page_id' => 'pp_page',
'page_namespace',
'page_title',
'page_len',
'page_is_redirect',
'page_latest',
'pp_value',
],
'conds' => [
'pp_propname' => $this->propName,
],
'join_conds' => [
'page' => [ 'JOIN', 'page_id = pp_page' ]
],
'options' => []
];
if ( $this->ns !== null ) {
$query['conds']['page_namespace'] = $this->ns;
}
return $query;
}
protected function getOrderFields() {
$sort = [ 'page_id' ];
if ( $this->sortByValue ) {
array_unshift( $sort, 'pp_sortkey' );
}
return $sort;
}
/**
* @return bool
*/
public function sortDescending() {
return !$this->reverse;
}
/**
* @param Skin $skin
* @param stdClass $result Result row
* @return string
*/
public function formatResult( $skin, $result ) {
$title = Title::newFromRow( $result );
$ret = $this->getLinkRenderer()->makeKnownLink( $title );
if ( $result->pp_value !== '' ) {
// Do not show very long or binary values on the special page
$valueLength = strlen( $result->pp_value );
$isBinary = strpos( $result->pp_value, "\0" ) !== false;
$isTooLong = $valueLength > 1024;
if ( $isBinary || $isTooLong ) {
$message = $this
->msg( $isBinary ? 'pageswithprop-prophidden-binary' : 'pageswithprop-prophidden-long' )
->params( $this->getLanguage()->formatSize( $valueLength ) );
$propValue = Html::element( 'span', [ 'class' => 'prop-value-hidden' ], $message->text() );
} else {
$propValue = Html::element( 'span', [ 'class' => 'prop-value' ], $result->pp_value );
}
$ret .= $this->msg( 'colon-separator' )->escaped() . $propValue;
}
return $ret;
}
public function getExistingPropNames() {
if ( $this->existingPropNames === null ) {
$this->existingPropNames = $this->queryExistingProps();
}
return $this->existingPropNames;
}
protected function queryExistingProps( $limit = null, $offset = 0 ) {
$opts = [
'DISTINCT', 'ORDER BY' => 'pp_propname'
];
if ( $limit ) {
$opts['LIMIT'] = $limit;
}
if ( $offset ) {
$opts['OFFSET'] = $offset;
}
$dbr = $this->getDBLoadBalancer()->getConnectionRef( ILoadBalancer::DB_REPLICA );
$res = $dbr->select(
'page_props',
'pp_propname',
'',
__METHOD__,
$opts
);
$propnames = [];
foreach ( $res as $row ) {
$propnames[$row->pp_propname] = $row->pp_propname;
}
return $propnames;
}
protected function getGroupName() {
return 'pages';
}
}