mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Linter
synced 2024-11-27 17:20:12 +00:00
6aa4cdeba9
* Tag and Template search is enabled using config variable 'LinterUserInterfaceTagAndTemplateStage' and also checks for the linter table column 'linter_tag' to exist to protect the report code from error if the column is absent. As the linter table alter maintenance added both the linter_tag and linter_template at the same time, there is no reason to check both. The user interface code does not check for the field presence only the config variable. * This code depends on the recordLintJob code writing the tag and template data which is enabled by the config variable 'LinterWriteTagAndTemplateColumnsStage' and also assumes the data migration maintenance script migrateTagTemplate.php has been run to populate linter error records created prior to the table alter and the write code being enabled. Bug: T175177 Change-Id: I2f951dfcd34e3dc6ca17e8754cfaeba8baa3e835
345 lines
10 KiB
PHP
345 lines
10 KiB
PHP
<?php
|
|
/**
|
|
* 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
|
|
*
|
|
* @file
|
|
*/
|
|
|
|
namespace MediaWiki\Linter;
|
|
|
|
use ExtensionRegistry;
|
|
use Html;
|
|
use IContextSource;
|
|
use InvalidArgumentException;
|
|
use LinkCache;
|
|
use MediaWiki\Linker\LinkRenderer;
|
|
use MediaWiki\MediaWikiServices;
|
|
use TablePager;
|
|
use Title;
|
|
use TitleValue;
|
|
|
|
class LintErrorsPager extends TablePager {
|
|
|
|
/**
|
|
* @var CategoryManager
|
|
*/
|
|
private $categoryManager;
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
private $category;
|
|
|
|
/**
|
|
* @var int|null
|
|
*/
|
|
private $categoryId;
|
|
|
|
/**
|
|
* @var LinkRenderer
|
|
*/
|
|
private $linkRenderer;
|
|
|
|
/**
|
|
* @var bool
|
|
*/
|
|
private $haveParserMigrationExt;
|
|
|
|
/**
|
|
* @var int|null
|
|
*/
|
|
private $namespace;
|
|
|
|
/**
|
|
* @var bool
|
|
*/
|
|
private $invertNamespace;
|
|
|
|
/**
|
|
* @var bool
|
|
*/
|
|
private $exactMatch;
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
private $title;
|
|
|
|
/**
|
|
* Allowed values are keys 'all', 'with' or 'without'
|
|
* @var string
|
|
*/
|
|
private $throughTemplate;
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
private $tag;
|
|
|
|
/**
|
|
* @param IContextSource $context
|
|
* @param string|null $category
|
|
* @param LinkRenderer $linkRenderer
|
|
* @param CategoryManager $catManager
|
|
* @param int|null $namespace
|
|
* @param bool $invertNamespace
|
|
* @param bool $exactMatch
|
|
* @param string $title
|
|
* @param string $throughTemplate
|
|
* @param string $tag
|
|
*/
|
|
public function __construct( IContextSource $context, $category, LinkRenderer $linkRenderer,
|
|
CategoryManager $catManager, $namespace, $invertNamespace, $exactMatch, $title, $throughTemplate, $tag
|
|
) {
|
|
$this->category = $category;
|
|
$this->categoryManager = $catManager;
|
|
if ( $category !== null ) {
|
|
$this->categoryId = $catManager->getCategoryId( $this->category );
|
|
} else {
|
|
$this->categoryId = null;
|
|
}
|
|
$this->linkRenderer = $linkRenderer;
|
|
$this->namespace = $namespace;
|
|
$this->invertNamespace = $invertNamespace;
|
|
$this->exactMatch = $exactMatch;
|
|
$this->title = $title;
|
|
$this->throughTemplate = $throughTemplate;
|
|
$this->tag = $tag;
|
|
$this->haveParserMigrationExt = ExtensionRegistry::getInstance()->isLoaded( 'ParserMigration' );
|
|
parent::__construct( $context );
|
|
}
|
|
|
|
/** @inheritDoc */
|
|
public function getQueryInfo() {
|
|
$conds = [];
|
|
if ( $this->categoryId !== null ) {
|
|
$conds[ 'linter_cat' ] = $this->categoryId;
|
|
}
|
|
if ( $this->namespace !== null ) {
|
|
$comp_op = $this->invertNamespace ? '!=' : '=';
|
|
$mwServices = MediaWikiServices::getInstance();
|
|
$config = $mwServices->getMainConfig();
|
|
$enableUseNamespaceColumnStage = $config->get( 'LinterUseNamespaceColumnStage' );
|
|
$fieldExists = $this->mDb->fieldExists( 'linter', 'linter_namespace', __METHOD__ );
|
|
if ( !$enableUseNamespaceColumnStage || !$fieldExists ) {
|
|
$conds[] = "page_namespace $comp_op " . $this->mDb->addQuotes( $this->namespace );
|
|
} else {
|
|
$conds[] = "linter_namespace $comp_op " . $this->mDb->addQuotes( $this->namespace );
|
|
}
|
|
}
|
|
if ( $this->exactMatch ) {
|
|
if ( $this->title !== '' ) {
|
|
$conds[] = "page_title = " . $this->mDb->addQuotes( $this->title );
|
|
}
|
|
} else {
|
|
$conds[] = 'page_title' . $this->mDb->buildLike( $this->title, $this->mDb->anyString() );
|
|
}
|
|
|
|
$mwServices = MediaWikiServices::getInstance();
|
|
$config = $mwServices->getMainConfig();
|
|
$enableUserInterfaceTagAndTemplateStage = $config->get( 'LinterUserInterfaceTagAndTemplateStage' );
|
|
$fieldTagExists = $this->mDb->fieldExists( 'linter', 'linter_tag', __METHOD__ );
|
|
if ( $enableUserInterfaceTagAndTemplateStage && $fieldTagExists ) {
|
|
switch ( $this->throughTemplate ) {
|
|
case 'with':
|
|
$conds[] = "linter_template != ''";
|
|
break;
|
|
case 'without':
|
|
$conds[] = "linter_template = ''";
|
|
break;
|
|
case 'all':
|
|
default:
|
|
break;
|
|
}
|
|
switch ( $this->tag ) {
|
|
case 'all':
|
|
break;
|
|
default:
|
|
$htmlTags = new HtmlTags( $this );
|
|
if ( $htmlTags->checkAllowedHTMLTags( $this->tag ) ) {
|
|
$conds[] = 'linter_tag = ' . $this->mDb->addQuotes( $this->tag );
|
|
}
|
|
}
|
|
}
|
|
|
|
return [
|
|
'tables' => [ 'page', 'linter' ],
|
|
'fields' => array_merge(
|
|
LinkCache::getSelectFields(),
|
|
[
|
|
'page_namespace', 'page_title',
|
|
'linter_id', 'linter_params',
|
|
'linter_start', 'linter_end',
|
|
'linter_cat'
|
|
]
|
|
),
|
|
'conds' => $conds,
|
|
'join_conds' => [ 'page' => [ 'INNER JOIN', 'page_id=linter_page' ] ]
|
|
];
|
|
}
|
|
|
|
protected function doBatchLookups() {
|
|
$linkCache = MediaWikiServices::getInstance()->getLinkCache();
|
|
foreach ( $this->mResult as $row ) {
|
|
$titleValue = new TitleValue( (int)$row->page_namespace, $row->page_title );
|
|
$linkCache->addGoodLinkObjFromRow( $titleValue, $row );
|
|
}
|
|
}
|
|
|
|
/** @inheritDoc */
|
|
public function isFieldSortable( $field ) {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @param string $name
|
|
* @param string $value
|
|
* @return string
|
|
* @throws InvalidArgumentException
|
|
*/
|
|
public function formatValue( $name, $value ) {
|
|
$row = $this->mCurrentRow;
|
|
|
|
// To support multiple lint errors of varying types for a single page, the
|
|
// category is set each time based on the category set in the lint error $row
|
|
// not by the class when lints are being reported by type for many pages
|
|
if ( $this->category === null && $row->linter_cat !== null ) {
|
|
$category = $this->categoryManager->getCategoryName( $row->linter_cat );
|
|
} else {
|
|
$category = $this->category;
|
|
$row->linter_cat = $this->categoryId;
|
|
}
|
|
$lintError = Database::makeLintError( $row );
|
|
|
|
if ( !$lintError ) {
|
|
return '';
|
|
}
|
|
if ( $this->haveParserMigrationExt &&
|
|
$this->categoryManager->needsParserMigrationEdit( $category )
|
|
) {
|
|
$editAction = 'parsermigration-edit';
|
|
} else {
|
|
$editAction = 'edit';
|
|
}
|
|
|
|
switch ( $name ) {
|
|
case 'title':
|
|
$title = Title::makeTitle( $row->page_namespace, $row->page_title );
|
|
$viewLink = $this->linkRenderer->makeLink( $title );
|
|
$permManager = MediaWikiServices::getInstance()->getPermissionManager();
|
|
$editMsgKey = $permManager->quickUserCan( 'edit', $this->getUser(), $title ) ?
|
|
'linter-page-edit' : 'linter-page-viewsource';
|
|
$editLink = $this->linkRenderer->makeLink(
|
|
$title,
|
|
$this->msg( $editMsgKey )->text(),
|
|
[],
|
|
[ 'action' => $editAction, 'lintid' => $lintError->lintId, ]
|
|
);
|
|
|
|
$historyLink = $this->linkRenderer->makeLink(
|
|
$title,
|
|
$this->msg( 'linter-page-history' )->text(),
|
|
[],
|
|
[ 'action' => 'history' ]
|
|
);
|
|
|
|
$editHistLinks = $this->getLanguage()->pipeList( [ $editLink, $historyLink ] );
|
|
return $this->msg( 'linter-page-title-edit' )
|
|
->rawParams( $viewLink, $editHistLinks )
|
|
->escaped();
|
|
case 'details':
|
|
if ( $this->categoryManager->hasNameParam( $category ) &&
|
|
isset( $lintError->params['name'] ) ) {
|
|
return Html::element( 'code', [], $lintError->params['name'] );
|
|
} elseif ( $category === 'bogus-image-options' && isset( $lintError->params['items'] ) ) {
|
|
$list = array_map( static function ( $in ) {
|
|
return Html::element( 'code', [], $in );
|
|
}, $lintError->params['items'] );
|
|
return $this->getLanguage()->commaList( $list );
|
|
} elseif ( $category === 'pwrap-bug-workaround' &&
|
|
isset( $lintError->params['root'] ) &&
|
|
isset( $lintError->params['child'] ) ) {
|
|
return Html::element( 'code', [],
|
|
$lintError->params['root'] . " > " . $lintError->params['child'] );
|
|
} elseif ( $category === 'tidy-whitespace-bug' &&
|
|
isset( $lintError->params['node'] ) &&
|
|
isset( $lintError->params['sibling'] ) ) {
|
|
return Html::element( 'code', [],
|
|
$lintError->params['node'] . " + " . $lintError->params['sibling'] );
|
|
} elseif ( $category === 'multi-colon-escape' &&
|
|
isset( $lintError->params['href'] ) ) {
|
|
return Html::element( 'code', [], $lintError->params['href'] );
|
|
} elseif ( $category === 'multiline-html-table-in-list' ) {
|
|
/* ancestor and name will be set */
|
|
return Html::element( 'code', [],
|
|
$lintError->params['ancestorName'] . " > " . $lintError->params['name'] );
|
|
} elseif ( $category === 'misc-tidy-replacement-issues' ) {
|
|
/* There will be a 'subtype' param to disambiguate */
|
|
return Html::element( 'code', [], $lintError->params['subtype'] );
|
|
}
|
|
return '';
|
|
case 'template':
|
|
if ( !$lintError->templateInfo ) {
|
|
return '—';
|
|
}
|
|
|
|
if ( isset( $lintError->templateInfo['multiPartTemplateBlock'] ) ) {
|
|
return $this->msg( 'multi-part-template-block' )->escaped();
|
|
} else {
|
|
// @phan-suppress-next-line PhanTypeArraySuspiciousNullable Null checked above
|
|
$templateName = $lintError->templateInfo['name'];
|
|
// Parsoid provides us with fully qualified template title
|
|
// So, fallback to the default main namespace
|
|
$templateTitle = Title::newFromText( $templateName );
|
|
if ( !$templateTitle ) {
|
|
// Shouldn't be possible...???
|
|
return '—';
|
|
}
|
|
}
|
|
|
|
return $this->linkRenderer->makeLink(
|
|
$templateTitle
|
|
);
|
|
case 'category':
|
|
return Html::element( 'code', [], $category );
|
|
default:
|
|
throw new InvalidArgumentException( "Unexpected name: $name" );
|
|
}
|
|
}
|
|
|
|
/** @inheritDoc */
|
|
public function getDefaultSort() {
|
|
return 'linter_id';
|
|
}
|
|
|
|
/**
|
|
* @return string[]
|
|
*/
|
|
public function getFieldNames() {
|
|
$names = [
|
|
'title' => $this->msg( 'linter-pager-title-header' )->text(),
|
|
];
|
|
if ( !$this->category ) {
|
|
$names['category'] = $this->msg( 'linter-pager-category-header' )->text();
|
|
$names['details'] = $this->msg( "linter-pager-details-header" )->text();
|
|
} elseif ( !$this->categoryManager->hasNoParams( $this->category ) ) {
|
|
$names['details'] = $this->msg( "linter-pager-{$this->category}-details" )->text();
|
|
}
|
|
$names['template'] = $this->msg( "linter-pager-template-header" )->text();
|
|
return $names;
|
|
}
|
|
}
|