ChangesList: Expose basic properties of lines as data attributes
We have several types of change lists (old RC/watchlist/related changes, enhanced RC/watchlist, history) with slightly different HTML, each with their own idiosyncracies. JavaScript code trying to identify lines by log ID / revision ID has to jump through all kinds of hoops to work with that. To simplify the lives of frontend / gadget maintainers and provide something approaching an API for these pages, we now expose the basic attributes of each change line (revision ID for edits, log type/action and ID for log events) as data attributes. The OldChangesListRecentChangesLine, EnhancedChangesListModifyLineData, EnhancedChangesListModifyBlockLineData, PageHistoryLine, ContributionsLineEnding and DeletedContributionsLineEnding hooks are updated accordingly. New hooks (LogEventsListLineEnding and NewPagesLineEnding) are added for the change list pages which did not yet have them. Change-Id: I6dd006d0b1b0fd35c0020f0f9eea9113eca30b35
This commit is contained in:
parent
c721668afa
commit
016452cd09
17 changed files with 262 additions and 32 deletions
|
|
@ -99,6 +99,13 @@ changes to languages because of Phabricator reports.
|
|||
or wikilinks.
|
||||
* (T163966) Page moves are now counted as edits for the purposes of
|
||||
autopromotion, i.e., they increment the user_editcount field in the database.
|
||||
* Two new hooks, LogEventsListLineEnding and NewPagesLineEnding were added for
|
||||
manipulating Special:Log and Special:NewPages lines.
|
||||
* The OldChangesListRecentChangesLine, EnhancedChangesListModifyLineData,
|
||||
PageHistoryLineEnding, ContributionsLineEnding and DeletedContributionsLineEnding
|
||||
hooks have an additional parameter, for manipulating HTML data attributes of
|
||||
RC/history lines. EnhancedChangesListModifyBlockLineData can do that via the
|
||||
$data['attribs'] subarray.
|
||||
|
||||
== Compatibility ==
|
||||
MediaWiki 1.30 requires PHP 5.5.9 or later. There is experimental support for
|
||||
|
|
|
|||
|
|
@ -1155,6 +1155,9 @@ $page: SpecialPage object for contributions
|
|||
&$ret: the HTML line
|
||||
$row: the DB row for this line
|
||||
&$classes: the classes to add to the surrounding <li>
|
||||
&$attribs: associative array of other HTML attributes for the <li> element.
|
||||
Currently only data attributes reserved to MediaWiki are allowed
|
||||
(see Sanitizer::isReservedDataAttribute).
|
||||
|
||||
'ContributionsToolLinks': Change tool links above Special:Contributions
|
||||
$id: User identifier
|
||||
|
|
@ -1200,6 +1203,9 @@ $page: SpecialPage object for DeletedContributions
|
|||
&$ret: the HTML line
|
||||
$row: the DB row for this line
|
||||
&$classes: the classes to add to the surrounding <li>
|
||||
&$attribs: associative array of other HTML attributes for the <li> element.
|
||||
Currently only data attributes reserved to MediaWiki are allowed
|
||||
(see Sanitizer::isReservedDataAttribute).
|
||||
|
||||
'DifferenceEngineAfterLoadNewText': called in DifferenceEngine::loadNewText()
|
||||
after the new revision's content has been loaded into the class member variable
|
||||
|
|
@ -1512,6 +1518,9 @@ $changesList: EnhancedChangesList object
|
|||
$block: An array of RecentChange objects in that block
|
||||
$rc: The RecentChange object for this line
|
||||
&$classes: An array of classes to change
|
||||
&$attribs: associative array of other HTML attributes for the <tr> element.
|
||||
Currently only data attributes reserved to MediaWiki are allowed
|
||||
(see Sanitizer::isReservedDataAttribute).
|
||||
|
||||
'EnhancedChangesListModifyBlockLineData': to alter data used to build
|
||||
a non-grouped recent change line in EnhancedChangesList.
|
||||
|
|
@ -1999,6 +2008,16 @@ $file: the File object or false if broken link
|
|||
&$attribs: the attributes to be applied
|
||||
&$ret: the value to return if your hook returns false
|
||||
|
||||
'LogEventsListLineEnding': Called before a Special:Log line is finished
|
||||
$page: the LogEventsList object
|
||||
&$ret: the HTML line
|
||||
$entry: the DatabaseLogEntry object for this row
|
||||
&$classes: the classes to add to the surrounding <li>
|
||||
&$attribs: associative array of other HTML attributes for the <li> element.
|
||||
Currently only data attributes reserved to MediaWiki are allowed
|
||||
(see Sanitizer::isReservedDataAttribute).
|
||||
|
||||
|
||||
'HtmlPageLinkRendererBegin':
|
||||
Used when generating internal and interwiki links in
|
||||
LinkRenderer, before processing starts. Return false to skip default
|
||||
|
|
@ -2284,6 +2303,16 @@ $title: the diff page title (nullable)
|
|||
$old: the ?old= param value from the url
|
||||
$new: the ?new= param value from the url
|
||||
|
||||
'NewPagesLineEnding': Called before a NewPages line is finished.
|
||||
$page: the SpecialNewPages object
|
||||
&$ret: the HTML line
|
||||
$row: the database row for this page (the recentchanges record and a few extras - see
|
||||
NewPagesPager::getQueryInfo)
|
||||
&$classes: the classes to add to the surrounding <li>
|
||||
&$attribs: associative array of other HTML attributes for the <li> element.
|
||||
Currently only data attributes reserved to MediaWiki are allowed
|
||||
(see Sanitizer::isReservedDataAttribute).
|
||||
|
||||
'NewRevisionFromEditComplete': Called when a revision was inserted due to an
|
||||
edit.
|
||||
$wikiPage: the WikiPage edited
|
||||
|
|
@ -2296,7 +2325,10 @@ return false to omit the line from RecentChanges and Watchlist special pages.
|
|||
&$changeslist: The OldChangesList instance.
|
||||
&$s: HTML of the form "<li>...</li>" containing one RC entry.
|
||||
$rc: The RecentChange object.
|
||||
&$classes: array of css classes for the <li> element
|
||||
&$classes: array of css classes for the <li> element.
|
||||
&$attribs: associative array of other HTML attributes for the <li> element.
|
||||
Currently only data attributes reserved to MediaWiki are allowed
|
||||
(see Sanitizer::isReservedDataAttribute).
|
||||
|
||||
'OpenSearchUrls': Called when constructing the OpenSearch description XML. Hooks
|
||||
can alter or append to the array of URLs for search & suggestion formats.
|
||||
|
|
@ -2404,6 +2436,9 @@ $historyAction: the action object
|
|||
&$row: the revision row for this line
|
||||
&$s: the string representing this parsed line
|
||||
&$classes: array containing the <li> element classes
|
||||
&$attribs: associative array of other HTML attributes for the <li> element.
|
||||
Currently only data attributes reserved to MediaWiki are allowed
|
||||
(see Sanitizer::isReservedDataAttribute).
|
||||
|
||||
'PageHistoryPager::doBatchLookups': Called after the pager query was run, before
|
||||
any output is generated, to allow batch lookups for prefetching information
|
||||
|
|
|
|||
|
|
@ -203,6 +203,38 @@ function wfArrayDiff2_cmp( $a, $b ) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Like array_filter with ARRAY_FILTER_USE_BOTH, but works pre-5.6.
|
||||
*
|
||||
* @param array $arr
|
||||
* @param callable $callback Will be called with the array value and key (in that order) and
|
||||
* should return a bool which will determine whether the array element is kept.
|
||||
* @return array
|
||||
*/
|
||||
function wfArrayFilter( array $arr, callable $callback ) {
|
||||
if ( defined( 'ARRAY_FILTER_USE_BOTH' ) ) {
|
||||
return array_filter( $arr, $callback, ARRAY_FILTER_USE_BOTH );
|
||||
}
|
||||
$filteredKeys = array_filter( array_keys( $arr ), function ( $key ) use ( $arr, $callback ) {
|
||||
return call_user_func( $callback, $arr[$key], $key );
|
||||
} );
|
||||
return array_intersect_key( $arr, array_fill_keys( $filteredKeys, true ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Like array_filter with ARRAY_FILTER_USE_KEY, but works pre-5.6.
|
||||
*
|
||||
* @param array $arr
|
||||
* @param callable $callback Will be called with the array key and should return a bool which
|
||||
* will determine whether the array element is kept.
|
||||
* @return array
|
||||
*/
|
||||
function wfArrayFilterByKey( array $arr, callable $callback ) {
|
||||
return wfArrayFilter( $arr, function ( $val, $key ) use ( $callback ) {
|
||||
return call_user_func( $callback, $key );
|
||||
} );
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends to second array if $value differs from that in $default
|
||||
*
|
||||
|
|
|
|||
|
|
@ -782,15 +782,12 @@ class Sanitizer {
|
|||
|
||||
# Allow any attribute beginning with "data-"
|
||||
# However:
|
||||
# * data-ooui is reserved for ooui
|
||||
# * data-mw and data-parsoid are reserved for parsoid
|
||||
# * data-mw-<name here> is reserved for extensions (or core) if
|
||||
# they need to communicate some data to the client and want to be
|
||||
# sure that it isn't coming from an untrusted user.
|
||||
# * Disallow data attributes used by MediaWiki code
|
||||
# * Ensure that the attribute is not namespaced by banning
|
||||
# colons.
|
||||
if ( !preg_match( '/^data-(?!ooui|mw|parsoid)[^:]*$/i', $attribute )
|
||||
if ( !preg_match( '/^data-[^:]*$/i', $attribute )
|
||||
&& !isset( $whitelist[$attribute] )
|
||||
|| self::isReservedDataAttribute( $attribute )
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -858,6 +855,24 @@ class Sanitizer {
|
|||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an attribute name, checks whether it is a reserved data attribute
|
||||
* (such as data-mw-foo) which is unavailable to user-generated HTML so MediaWiki
|
||||
* core and extension code can safely use it to communicate with frontend code.
|
||||
* @param string $attr Attribute name.
|
||||
* @return bool
|
||||
*/
|
||||
public static function isReservedDataAttribute( $attr ) {
|
||||
// data-ooui is reserved for ooui.
|
||||
// data-mw and data-parsoid are reserved for parsoid.
|
||||
// data-mw-<name here> is reserved for extensions (or core) if
|
||||
// they need to communicate some data to the client and want to be
|
||||
// sure that it isn't coming from an untrusted user.
|
||||
// We ignore the possibility of namespaces since user-generated HTML
|
||||
// can't use them anymore.
|
||||
return (bool)preg_match( '/^data-(ooui|mw|parsoid)/i', $attr );
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two sets of HTML attributes. Conflicting items in the second set
|
||||
* will override those in the first, except for 'class' attributes which
|
||||
|
|
|
|||
|
|
@ -780,9 +780,11 @@ class HistoryPager extends ReverseChronologicalPager {
|
|||
$s .= ' <span class="mw-changeslist-separator">. .</span> ' . $s2;
|
||||
}
|
||||
|
||||
Hooks::run( 'PageHistoryLineEnding', [ $this, &$row, &$s, &$classes ] );
|
||||
$attribs = [ 'data-mw-revid' => $rev->getId() ];
|
||||
|
||||
Hooks::run( 'PageHistoryLineEnding', [ $this, &$row, &$s, &$classes, &$attribs ] );
|
||||
$attribs = wfArrayFilterByKey( $attribs, [ Sanitizer::class, 'isReservedDataAttribute' ] );
|
||||
|
||||
$attribs = [];
|
||||
if ( $classes ) {
|
||||
$attribs['class'] = implode( ' ', $classes );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -739,4 +739,26 @@ class ChangesList extends ContextSource {
|
|||
&& intval( $rcObj->getAttribute( 'rc_this_oldid' ) ) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get recommended data attributes for a change line.
|
||||
* @param RecentChange $rc
|
||||
* @return string[] attribute name => value
|
||||
*/
|
||||
protected function getDataAttributes( RecentChange $rc ) {
|
||||
$type = $rc->getAttribute( 'rc_source' );
|
||||
switch ( $type ) {
|
||||
case RecentChange::SRC_EDIT:
|
||||
case RecentChange::SRC_NEW:
|
||||
return [
|
||||
'data-mw-revid' => $rc->mAttribs['rc_this_oldid'],
|
||||
];
|
||||
case RecentChange::SRC_LOG:
|
||||
return [
|
||||
'data-mw-logid' => $rc->mAttribs['rc_logid'],
|
||||
'data-mw-logaction' => $rc->mAttribs['rc_log_type'] . '/' . $rc->mAttribs['rc_log_action'],
|
||||
];
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -447,13 +447,16 @@ class EnhancedChangesList extends ChangesList {
|
|||
# Tags
|
||||
$data['tags'] = $this->getTags( $rcObj, $classes );
|
||||
|
||||
$attribs = $this->getDataAttributes( $rcObj );
|
||||
|
||||
// give the hook a chance to modify the data
|
||||
$success = Hooks::run( 'EnhancedChangesListModifyLineData',
|
||||
[ $this, &$data, $block, $rcObj, &$classes ] );
|
||||
[ $this, &$data, $block, $rcObj, &$classes, &$attribs ] );
|
||||
if ( !$success ) {
|
||||
// skip entry if hook aborted it
|
||||
return [];
|
||||
}
|
||||
$attribs = wfArrayFilterByKey( $attribs, [ Sanitizer::class, 'isReservedDataAttribute' ] );
|
||||
|
||||
$lineParams['recentChangesFlagsRaw'] = [];
|
||||
if ( isset( $data['recentChangesFlags'] ) ) {
|
||||
|
|
@ -469,6 +472,7 @@ class EnhancedChangesList extends ChangesList {
|
|||
}
|
||||
|
||||
$lineParams['classes'] = array_values( $classes );
|
||||
$lineParams['attribs'] = Html::expandAttributes( $attribs );
|
||||
|
||||
// everything else: makes it easier for extensions to add or remove data
|
||||
$lineParams['data'] = array_values( $data );
|
||||
|
|
@ -671,6 +675,8 @@ class EnhancedChangesList extends ChangesList {
|
|||
# Show how many people are watching this if enabled
|
||||
$data['watchingUsers'] = $this->numberofWatchingusers( $rcObj->numberofWatchingusers );
|
||||
|
||||
$data['attribs'] = array_merge( $this->getDataAttributes( $rcObj ), [ 'class' => $classes ] );
|
||||
|
||||
// give the hook a chance to modify the data
|
||||
$success = Hooks::run( 'EnhancedChangesListModifyBlockLineData',
|
||||
[ $this, &$data, $rcObj ] );
|
||||
|
|
@ -678,9 +684,11 @@ class EnhancedChangesList extends ChangesList {
|
|||
// skip entry if hook aborted it
|
||||
return '';
|
||||
}
|
||||
$attribs = $data['attribs'];
|
||||
unset( $data['attribs'] );
|
||||
$attribs = wfArrayFilterByKey( $attribs, [ Sanitizer::class, 'isReservedDataAttribute' ] );
|
||||
|
||||
$line = Html::openElement( 'table', [ 'class' => $classes ] ) .
|
||||
Html::openElement( 'tr' );
|
||||
$line = Html::openElement( 'table', $attribs ) . Html::openElement( 'tr' );
|
||||
$line .= '<td class="mw-enhanced-rc"><span class="mw-enhancedchanges-arrow-space"></span>';
|
||||
|
||||
if ( isset( $data['recentChangesFlags'] ) ) {
|
||||
|
|
|
|||
|
|
@ -50,16 +50,23 @@ class OldChangesList extends ChangesList {
|
|||
$rc->mAttribs['rc_namespace'] . '-' . $rc->mAttribs['rc_title'] );
|
||||
}
|
||||
|
||||
$attribs = $this->getDataAttributes( $rc );
|
||||
|
||||
// Avoid PHP 7.1 warning from passing $this by reference
|
||||
$list = $this;
|
||||
if ( !Hooks::run( 'OldChangesListRecentChangesLine', [ &$list, &$html, $rc, &$classes ] ) ) {
|
||||
if ( !Hooks::run( 'OldChangesListRecentChangesLine',
|
||||
[ &$list, &$html, $rc, &$classes, &$attribs ] )
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
$attribs = wfArrayFilterByKey( $attribs, [ Sanitizer::class, 'isReservedDataAttribute' ] );
|
||||
|
||||
$dateheader = ''; // $html now contains only <li>...</li>, for hooks' convenience.
|
||||
$this->insertDateHeader( $dateheader, $rc->mAttribs['rc_timestamp'] );
|
||||
|
||||
return "$dateheader<li class=\"" . implode( ' ', $classes ) . "\">" . $html . "</li>\n";
|
||||
$attribs['class'] = implode( ' ', $classes );
|
||||
|
||||
return $dateheader . Html::rawElement( 'li', $attribs, $html ) . "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -390,9 +390,18 @@ class LogEventsList extends ContextSource {
|
|||
[ 'mw-logline-' . $entry->getType() ],
|
||||
$newClasses
|
||||
);
|
||||
$attribs = [
|
||||
'data-mw-logid' => $entry->getId(),
|
||||
'data-mw-logaction' => $entry->getFullType(),
|
||||
];
|
||||
$ret = "$del $time $action $comment $revert $tagDisplay";
|
||||
|
||||
return Html::rawElement( 'li', [ 'class' => $classes ],
|
||||
"$del $time $action $comment $revert $tagDisplay" ) . "\n";
|
||||
// Let extensions add data
|
||||
Hooks::run( 'LogEventsListLineEnding', [ $this, &$ret, $entry, &$classes, &$attribs ] );
|
||||
$attribs = wfArrayFilterByKey( $attribs, [ Sanitizer::class, 'isReservedDataAttribute' ] );
|
||||
$attribs['class'] = implode( ' ', $classes );
|
||||
|
||||
return Html::rawElement( 'li', $attribs, $ret ) . "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -314,6 +314,7 @@ class SpecialNewpages extends IncludableSpecialPage {
|
|||
$rev->setTitle( $title );
|
||||
|
||||
$classes = [];
|
||||
$attribs = [ 'data-mw-revid' => $result->rev_id ];
|
||||
|
||||
$lang = $this->getLanguage();
|
||||
$dm = $lang->getDirMark();
|
||||
|
|
@ -378,11 +379,19 @@ class SpecialNewpages extends IncludableSpecialPage {
|
|||
$tagDisplay = '';
|
||||
}
|
||||
|
||||
$css = count( $classes ) ? ' class="' . implode( ' ', $classes ) . '"' : '';
|
||||
|
||||
# Display the old title if the namespace/title has been changed
|
||||
$oldTitleText = '';
|
||||
$oldTitle = Title::makeTitle( $result->rc_namespace, $result->rc_title );
|
||||
$ret = "{$time} {$dm}{$plink} {$hist} {$dm}{$length} {$dm}{$ulink} {$comment} "
|
||||
. "{$tagDisplay} {$oldTitleText}";
|
||||
|
||||
// Let extensions add data
|
||||
Hooks::run( 'NewPagesLineEnding', [ $this, &$ret, $result, &$classes, &$attribs ] );
|
||||
$attribs = wfArrayFilterByKey( $attribs, [ Sanitizer::class, 'isReservedDataAttribute' ] );
|
||||
|
||||
if ( count( $classes ) ) {
|
||||
$attribs['class'] = implode( ' ', $classes );
|
||||
}
|
||||
|
||||
if ( !$title->equals( $oldTitle ) ) {
|
||||
$oldTitleText = $oldTitle->getPrefixedText();
|
||||
|
|
@ -393,8 +402,7 @@ class SpecialNewpages extends IncludableSpecialPage {
|
|||
);
|
||||
}
|
||||
|
||||
return "<li{$css}>{$time} {$dm}{$plink} {$hist} {$dm}{$length} "
|
||||
. "{$dm}{$ulink} {$comment} {$tagDisplay} {$oldTitleText}</li>\n";
|
||||
return Html::rawElement( 'li', $attribs, $ret ) . "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -365,9 +365,9 @@ class ContribsPager extends RangeChronologicalPager {
|
|||
* @return string
|
||||
*/
|
||||
function formatRow( $row ) {
|
||||
|
||||
$ret = '';
|
||||
$classes = [];
|
||||
$attribs = [];
|
||||
|
||||
$linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
|
||||
|
||||
|
|
@ -388,7 +388,7 @@ class ContribsPager extends RangeChronologicalPager {
|
|||
MediaWiki\restoreWarnings();
|
||||
|
||||
if ( $validRevision ) {
|
||||
$classes = [];
|
||||
$attribs['data-mw-revid'] = $rev->getId();
|
||||
|
||||
$page = Title::newFromRow( $row );
|
||||
$link = $linkRenderer->makeLink(
|
||||
|
|
@ -535,19 +535,21 @@ class ContribsPager extends RangeChronologicalPager {
|
|||
}
|
||||
|
||||
// Let extensions add data
|
||||
Hooks::run( 'ContributionsLineEnding', [ $this, &$ret, $row, &$classes ] );
|
||||
Hooks::run( 'ContributionsLineEnding', [ $this, &$ret, $row, &$classes, &$attribs ] );
|
||||
$attribs = wfArrayFilterByKey( $attribs, [ Sanitizer::class, 'isReservedDataAttribute' ] );
|
||||
|
||||
// TODO: Handle exceptions in the catch block above. Do any extensions rely on
|
||||
// receiving empty rows?
|
||||
|
||||
if ( $classes === [] && $ret === '' ) {
|
||||
if ( $classes === [] && $attribs === [] && $ret === '' ) {
|
||||
wfDebug( "Dropping Special:Contribution row that could not be formatted\n" );
|
||||
return "<!-- Could not format Special:Contribution row. -->\n";
|
||||
}
|
||||
$attribs['class'] = $classes;
|
||||
|
||||
// FIXME: The signature of the ContributionsLineEnding hook makes it
|
||||
// very awkward to move this LI wrapper into the template.
|
||||
return Html::rawElement( 'li', [ 'class' => $classes ], $ret ) . "\n";
|
||||
return Html::rawElement( 'li', $attribs, $ret ) . "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -195,6 +195,7 @@ class DeletedContribsPager extends IndexPager {
|
|||
function formatRow( $row ) {
|
||||
$ret = '';
|
||||
$classes = [];
|
||||
$attribs = [];
|
||||
|
||||
/*
|
||||
* There may be more than just revision rows. To make sure that we'll only be processing
|
||||
|
|
@ -213,17 +214,20 @@ class DeletedContribsPager extends IndexPager {
|
|||
MediaWiki\restoreWarnings();
|
||||
|
||||
if ( $validRevision ) {
|
||||
$attribs['data-mw-revid'] = $rev->getId();
|
||||
$ret = $this->formatRevisionRow( $row );
|
||||
}
|
||||
|
||||
// Let extensions add data
|
||||
Hooks::run( 'DeletedContributionsLineEnding', [ $this, &$ret, $row, &$classes ] );
|
||||
Hooks::run( 'DeletedContributionsLineEnding', [ $this, &$ret, $row, &$classes, &$attribs ] );
|
||||
$attribs = wfArrayFilterByKey( $attribs, [ Sanitizer::class, 'isReservedDataAttribute' ] );
|
||||
|
||||
if ( $classes === [] && $ret === '' ) {
|
||||
if ( $classes === [] && $attribs === [] && $ret === '' ) {
|
||||
wfDebug( "Dropping Special:DeletedContribution row that could not be formatted\n" );
|
||||
$ret = "<!-- Could not format Special:DeletedContribution row. -->\n";
|
||||
} else {
|
||||
$ret = Html::rawElement( 'li', [ 'class' => $classes ], $ret ) . "\n";
|
||||
$attribs['class'] = $classes;
|
||||
$ret = Html::rawElement( 'li', $attribs, $ret ) . "\n";
|
||||
}
|
||||
|
||||
return $ret;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
</td>
|
||||
</tr>
|
||||
{{# lines }}
|
||||
<tr class="{{# classes }}{{ . }} {{/ classes }}">
|
||||
<tr class="{{# classes }}{{ . }} {{/ classes }}"{{{ attribs }}}>
|
||||
<td></td>
|
||||
<td class="mw-enhanced-rc">{{{ recentChangesFlags }}} </td>
|
||||
<td class="mw-enhanced-rc-nested">
|
||||
|
|
|
|||
37
tests/phpunit/includes/GlobalFunctions/wfArrayFilterTest.php
Normal file
37
tests/phpunit/includes/GlobalFunctions/wfArrayFilterTest.php
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
class WfArrayFilterTest extends \PHPUnit_Framework_TestCase {
|
||||
public function testWfArrayFilter() {
|
||||
$arr = [ 'a' => 1, 'b' => 2, 'c' => 3 ];
|
||||
$filtered = wfArrayFilter( $arr, function( $val, $key ) {
|
||||
return $key !== 'b';
|
||||
} );
|
||||
$this->assertSame( [ 'a' => 1, 'c' => 3 ], $filtered );
|
||||
|
||||
$arr = [ 'a' => 1, 'b' => 2, 'c' => 3 ];
|
||||
$filtered = wfArrayFilter( $arr, function( $val, $key ) {
|
||||
return $val !== 2;
|
||||
} );
|
||||
$this->assertSame( [ 'a' => 1, 'c' => 3 ], $filtered );
|
||||
|
||||
$arr = [ 'a', 'b', 'c' ];
|
||||
$filtered = wfArrayFilter( $arr, function( $val, $key ) {
|
||||
return $key !== 0;
|
||||
} );
|
||||
$this->assertSame( [ 1 => 'b', 2 => 'c' ], $filtered );
|
||||
}
|
||||
|
||||
public function testWfArrayFilterByKey() {
|
||||
$arr = [ 'a' => 1, 'b' => 2, 'c' => 3 ];
|
||||
$filtered = wfArrayFilterByKey( $arr, function( $key ) {
|
||||
return $key !== 'b';
|
||||
} );
|
||||
$this->assertSame( [ 'a' => 1, 'c' => 3 ], $filtered );
|
||||
|
||||
$arr = [ 'a', 'b', 'c' ];
|
||||
$filtered = wfArrayFilterByKey( $arr, function( $key ) {
|
||||
return $key !== 0;
|
||||
} );
|
||||
$this->assertSame( [ 1 => 'b', 2 => 'c' ], $filtered );
|
||||
}
|
||||
}
|
||||
|
|
@ -362,4 +362,25 @@ class SanitizerTest extends MediaWikiTestCase {
|
|||
[ '+1 +2', '+1', '+2' ],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideIsReservedDataAttribute
|
||||
*/
|
||||
public function testIsReservedDataAttribute( $attr, $expected ) {
|
||||
$this->assertSame( $expected, Sanitizer::isReservedDataAttribute( $attr ) );
|
||||
}
|
||||
|
||||
public static function provideIsReservedDataAttribute() {
|
||||
return [
|
||||
[ 'foo', false ],
|
||||
[ 'data', false ],
|
||||
[ 'data-foo', false ],
|
||||
[ 'data-mw', true ],
|
||||
[ 'data-ooui', true ],
|
||||
[ 'data-parsoid', true ],
|
||||
[ 'data-mw-foo', true ],
|
||||
[ 'data-ooui-foo', true ],
|
||||
[ 'data-mwfoo', true ], // could be false but this is how it's implemented currently
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,6 +98,9 @@ class EnhancedChangesListTest extends MediaWikiLangTestCase {
|
|||
$recentChange = $this->getEditChange( '20131103092153' );
|
||||
$enhancedChangesList->recentChangesLine( $recentChange, false );
|
||||
|
||||
$html = $enhancedChangesList->endRecentChangesList();
|
||||
$this->assertContains( 'data-mw-revid="5"', $html );
|
||||
|
||||
$recentChange2 = $this->getEditChange( '20131103092253' );
|
||||
$enhancedChangesList->recentChangesLine( $recentChange2, false );
|
||||
|
||||
|
|
@ -105,6 +108,13 @@ class EnhancedChangesListTest extends MediaWikiLangTestCase {
|
|||
|
||||
preg_match_all( '/td class="mw-enhanced-rc-nested"/', $html, $matches );
|
||||
$this->assertCount( 2, $matches[0] );
|
||||
|
||||
$recentChange3 = $this->getLogChange();
|
||||
$enhancedChangesList->recentChangesLine( $recentChange3, false );
|
||||
|
||||
$html = $enhancedChangesList->endRecentChangesList();
|
||||
$this->assertContains( 'data-mw-logaction="foo/bar"', $html );
|
||||
$this->assertContains( 'data-mw-logid="25"', $html );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -129,6 +139,15 @@ class EnhancedChangesListTest extends MediaWikiLangTestCase {
|
|||
return $recentChange;
|
||||
}
|
||||
|
||||
private function getLogChange() {
|
||||
$user = $this->getMutableTestUser()->getUser();
|
||||
$recentChange = $this->testRecentChangesHelper->makeLogRecentChange( 'foo', 'bar', $user,
|
||||
'Title', '20131103092153', 0, 0
|
||||
);
|
||||
|
||||
return $recentChange;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RecentChange
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -119,15 +119,17 @@ class OldChangesListTest extends MediaWikiLangTestCase {
|
|||
);
|
||||
}
|
||||
|
||||
public function testRecentChangesLine_Tags() {
|
||||
public function testRecentChangesLine_Attribs() {
|
||||
$recentChange = $this->getEditChange();
|
||||
$recentChange->mAttribs['ts_tags'] = 'vandalism,newbie';
|
||||
|
||||
$oldChangesList = $this->getOldChangesList();
|
||||
$line = $oldChangesList->recentChangesLine( $recentChange, false, 1 );
|
||||
|
||||
$this->assertRegExp( '/<li class="[\w\s-]*mw-tag-vandalism[\w\s-]*">/', $line );
|
||||
$this->assertRegExp( '/<li class="[\w\s-]*mw-tag-newbie[\w\s-]*">/', $line );
|
||||
$this->assertRegExp( '/<li data-mw-revid="\d+" class="[\w\s-]*mw-tag-vandalism[\w\s-]*">/',
|
||||
$line );
|
||||
$this->assertRegExp( '/<li data-mw-revid="\d+" class="[\w\s-]*mw-tag-newbie[\w\s-]*">/',
|
||||
$line );
|
||||
}
|
||||
|
||||
public function testRecentChangesLine_numberOfWatchingUsers() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue