Merge "Detect signature using Title class"

This commit is contained in:
jenkins-bot 2014-10-23 21:25:37 +00:00 committed by Gerrit Code Review
commit c5559da8cb
2 changed files with 130 additions and 66 deletions

View file

@ -669,74 +669,74 @@ abstract class EchoDiscussionParser {
static function getUserFromLine( $line, $timestampPos ) { static function getUserFromLine( $line, $timestampPos ) {
global $wgContLang; global $wgContLang;
// Later entries have a higher precedence // lifted from Parser::pstPass2
// @todo FIXME: handle optional whitespace in links $tc = '[' . Title::legalChars() . ']';
$languages = array( $wgContLang ); $nc = '[ _0-9A-Za-z\x80-\xff-]'; // Namespaces can use non-ascii
if ( $wgContLang->getCode() !== 'en' ) {
$languages[] = Language::factory( 'en' ); // [[ns:page]] with optional fragment(#foo) and/or pipe(|bar)
$regex = "/\[\[($nc+:$tc+)(?:#.*?)?(?:\\|.*?)?]]/";
$potentialContext = substr( $line, 0, $timestampPos );
// only look at the 300 chars preceding the timestamp
$startCheckAt = max( 0, $timestampPos - 300 );
$context = substr( $potentialContext, -$startCheckAt );
if ( !preg_match_all( $regex, $context, $matches, PREG_SET_ORDER ) ) {
return false;
} }
$possiblePrefixes = array(); // prefer the last match in the line
$winningUser = $winner = false;
foreach ( $languages as $language ) { foreach ( array_reverse( $matches ) as $match ) {
$nsNames = $language->getNamespaces(); $title = Title::newFromText( $match[1] );
$possiblePrefixes[] = '[[' . $nsNames[NS_USER] . ':'; if ( !$title ) {
$possiblePrefixes[] = '[[' . $nsNames[NS_USER_TALK] . ':'; continue;
}
$nsAliases = $language->getNamespaceAliases(); if ( $title->getNamespace() === NS_USER ) {
foreach ( $nsAliases as $text => $id ) { if ( $winningUser === false || $winningUser === $title->getText() ) {
if ( $id == NS_USER || $id == NS_USER_TALK ) { // registered user winner!!
$possiblePrefixes[] = '[[' . $text . ':'; $winningUser = $title->getText();
$winner = $match;
}
break;
}
// Only check Special:Contributions and NS_USER_TALK if NS_USER_TALK
// has not yet triggered.
if ( $winningUser === false ) {
if ( $title->isSpecial( 'Contributions' ) ) {
// anon user winner!!
$parts = explode( '/', $title->getText(), 2 );
$winningUser = end( $parts );
$winner = $match;
break;
}
if ( $title->getNamespace() === NS_USER_TALK ) {
// registered user winner!
// but keep looking for a matching NS_USER link to the same user so
// we return the correct starting position. often the signature is:
// NS_USER (NS_USER_TALK) <timestamp>
// Wiki's have complete control over their signatures via Mediawiki:Signature,
// so it's also possible there is no matching NS_USER link.
$winningUser = $title->getText();
$winner = $match;
continue;
} }
} }
} }
// @todo FIXME: Check aliases too if ( !$winningUser ) {
$possiblePrefixes[] = '[[' . SpecialPage::getTitleFor( 'Contributions' )->getPrefixedText() . '/';
foreach ( $possiblePrefixes as $prefix ) {
if ( strpos( $prefix, '_' ) !== false ) {
$possiblePrefixes[] = str_replace( '_', ' ', $prefix );
}
}
$winningUser = false;
$winningPos = false;
// Look for the leftmost link to the rightmost user
foreach ( $possiblePrefixes as $prefix ) {
$output = self::getLinkFromLine( $line, $prefix );
if ( $output === false ) {
continue;
} else {
list( $pos, $user ) = $output;
}
// Couldn't be a signature
if ( ( $timestampPos - $pos ) > 255 ) {
continue;
}
if (
$winningPos === false ||
( $pos > $winningPos && $user !== $winningUser ) ||
(
$pos < $winningPos &&
$user === $winningUser
)
) {
$winningPos = $pos;
$winningUser = ucfirst( trim( $user ) );
}
}
if ( $winningUser === false ) {
// print "E\tNo winning user\n";
return false; return false;
} }
return array( $winningPos, $winningUser ); $pos = strrpos( $potentialContext, $winner[0] );
if ( !$pos ) {
// shouldn't be possible, $winner[0] is the string match from preg_match_all above,
// but just in case.
wfDebugLog( 'Echo', __METHOD__ . 'Did not find user "' . $match[0] . '" in wikitext: ' . $line );
return false;
}
return array( $pos, $winningUser );
} }
/** /**

View file

@ -83,17 +83,66 @@ TEXT
$ts = self::getExemplarTimestamp(); $ts = self::getExemplarTimestamp();
return array( return array(
// Basic // Basic
array( "I like this. [[User:Werdna]] ([[User talk:Werdna|talk]]) $ts", 'Werdna' ), array(
"I like this. [[User:Werdna]] ([[User talk:Werdna|talk]]) $ts",
array(
13,
'Werdna'
),
),
// Confounding // Confounding
array( "[[User:Jorm]] is a meanie. --[[User:Werdna|Andrew]] $ts", "Werdna" ), array(
"[[User:Jorm]] is a meanie. --[[User:Werdna|Andrew]] $ts",
array(
29,
"Werdna"
),
),
// Talk page link only // Talk page link only
array( "[[User:Swalling|Steve]] is the best person I have ever met. --[[User talk:Werdna|Andrew]] $ts", 'Werdna' ), array(
"[[User:Swalling|Steve]] is the best person I have ever met. --[[User talk:Werdna|Andrew]] $ts",
array(
62,
'Werdna'
),
),
// Anonymous user // Anonymous user
array( "I am anonymous because I like my IP address. --[[Special:Contributions/127.0.0.1]] $ts", '127.0.0.1' ), array(
"I am anonymous because I like my IP address. --[[Special:Contributions/127.0.0.1]] $ts",
array(
47,
'127.0.0.1'
),
),
// No signature // No signature
array( "Well, I do think that [[User:Newyorkbrad]] is pretty cool, but what do I know?", false ), array(
"Well, \nI do think that [[User:Newyorkbrad]] is pretty cool, but what do I know?",
false
),
// Hash symbols in usernames // Hash symbols in usernames
array( "What do you think? [[User talk:We buried our secrets in the garden#top|wbositg]] $ts", 'We buried our secrets in the garden' ), array(
"What do you think? [[User talk:We buried our secrets in the garden#top|wbositg]] $ts",
array(
19,
'We buried our secrets in the garden'
),
),
// Title that gets normalized different than it is provided in the wikitext
array(
"Beep boop [[User:I_Heart_Spaces]] ([[User_talk:I_Heart_Spaces]]) $ts",
array(
10,
'I Heart Spaces'
),
),
// Accepts ] in the pipe
array(
"Shake n Bake --[[User:Werdna|wer]dna]] $ts",
array(
15,
'Werdna',
),
),
); );
} }
@ -423,7 +472,9 @@ TEXT
return array( return array(
array( array(
'Must detect first sub heading when inserting in the middle of two sub headings', 'Must detect first sub heading when inserting in the middle of two sub headings',
// expected header content
'Sub Heading 1', 'Sub Heading 1',
// test content format
" "
== Heading == == Heading ==
$comment $comment
@ -435,12 +486,15 @@ $comment
== Sub Heading 2 == == Sub Heading 2 ==
$comment $comment
", ",
// user signing new comment
$name $name
), ),
array( array(
'Must detect second sub heading when inserting in the end of two sub headings', 'Must detect second sub heading when inserting in the end of two sub headings',
// expected header content
'Sub Heading 2', 'Sub Heading 2',
// test content format
" "
== Heading == == Heading ==
$comment $comment
@ -452,12 +506,15 @@ $comment
$comment $comment
%s %s
", ",
// user signing new comment
$name $name
), ),
array( array(
'Commenting in multiple sub-headings must result in no section link', 'Commenting in multiple sub-headings must result in no section link',
// expected header content
'', '',
// test content format
" "
== Heading == == Heading ==
$comment $comment
@ -471,29 +528,36 @@ $comment
%s %s
", ",
// user signing new comment
$name $name
), ),
array( array(
'Must accept headings without a space between the = and the section name', 'Must accept headings without a space between the = and the section name',
// expected header content
'Heading', 'Heading',
// test content format
" "
==Heading== ==Heading==
$comment $comment
%s %s
", ",
// user signing new comment
$name $name
), ),
array( array(
'Must not accept invalid headings split with a return', 'Must not accept invalid headings split with a return',
// expected header content
'', '',
// test content format
" "
==Some ==Some
Heading== Heading==
$comment $comment
%s %s
", ",
// user signing new comment
$name $name
), ),
); );
@ -502,7 +566,7 @@ $comment
/** /**
* @dataProvider provider_detectSectionTitleAndText * @dataProvider provider_detectSectionTitleAndText
*/ */
public function testdetectSectionTitleAndText( $message, $expect, $format, $name ) { public function testDetectSectionTitleAndText( $message, $expect, $format, $name ) {
// str_replace because we want to replace multiple instances of '%s' with the same valueA // str_replace because we want to replace multiple instances of '%s' with the same valueA
$before = str_replace( '%s', '', $format ); $before = str_replace( '%s', '', $format );
$after = str_replace( '%s', self::signedMessage( $name ), $format ); $after = str_replace( '%s', self::signedMessage( $name ), $format );