Use PageContentSave rather than ArticleSave hook.

This commit also contains some refactoring which should lend itself to
increased performance.
This commit is contained in:
Daniel Kraus
2014-06-08 19:08:19 +02:00
parent 5745621366
commit adbc1ea5cd
3 changed files with 38 additions and 40 deletions

View File

@ -28,7 +28,6 @@
/// Central class of the extension. Sets up parser hooks. /// Central class of the extension. Sets up parser hooks.
/// This class contains only static functions; do not instantiate. /// This class contains only static functions; do not instantiate.
/// @todo Use the current PageContentSave hook rather than the deprecated ArticleSave hook.
class LinkTitles { class LinkTitles {
/// Setup function, hooks the extension's functions to MediaWiki events. /// Setup function, hooks the extension's functions to MediaWiki events.
public static function setup() { public static function setup() {
@ -36,7 +35,7 @@
global $wgLinkTitlesParseOnRender; global $wgLinkTitlesParseOnRender;
global $wgHooks; global $wgHooks;
if ( $wgLinkTitlesParseOnEdit ) { if ( $wgLinkTitlesParseOnEdit ) {
$wgHooks['ArticleSave'][] = 'LinkTitles::onArticleSave'; $wgHooks['PageContentSave'][] = 'LinkTitles::onPageContentSave';
}; };
if ( $wgLinkTitlesParseOnRender ) { if ( $wgLinkTitlesParseOnRender ) {
$wgHooks['ArticleAfterFetchContent'][] = 'LinkTitles::onArticleAfterFetchContent'; $wgHooks['ArticleAfterFetchContent'][] = 'LinkTitles::onArticleAfterFetchContent';
@ -45,18 +44,18 @@
} }
/// Event handler that is hooked to the ArticleSave event. /// Event handler that is hooked to the ArticleSave event.
public static function onArticleSave( &$article, &$user, &$text, &$summary, public static function onPageContentSave( &$wikiPage, &$user, &$content, &$summary,
$minor, $watchthis, $sectionanchor, &$flags, &$status ) { $isMinor, $isWatch, $section, &$flags, &$status ) {
// To prevent time-consuming parsing of the page whenever // To prevent time-consuming parsing of the page whenever
// it is edited and saved, we only parse it if the flag // it is edited and saved, we only parse it if the flag
// 'minor edits' is not set. // 'minor edits' is not set.
return $minor or self::parseContent( $article, $text ); return $isMinor or self::parseContent( $wikiPage, $content );
} }
/// Event handler that is hooked to the ArticleAfterFetchContent event. /// Event handler that is hooked to the ArticleAfterFetchContent event.
/// @param $article Article object /// @param $article Article object
/// @param $content String that holds the article content /// @param $content Content object that holds the article content
public static function onArticleAfterFetchContent( &$article, &$content ) { public static function onArticleAfterFetchContent( &$article, &$content ) {
// The ArticleAfterFetchContent event is triggered whenever page content // The ArticleAfterFetchContent event is triggered whenever page content
// is retrieved from the database, i.e. also for editing etc. // is retrieved from the database, i.e. also for editing etc.
@ -71,17 +70,16 @@
/// Core function of the extension, performs the actual parsing of the content. /// Core function of the extension, performs the actual parsing of the content.
/// @param $article Article object /// @param $article Article object
/// @param $text String that holds the article content /// @param $content Content object that holds the article content
/// @returns true /// @returns true
static function parseContent( &$article, &$text ) { static function parseContent( &$article, &$content ) {
// If the page contains the magic word '__NOAUTOLINKS__', do not parse // If the page contains the magic word '__NOAUTOLINKS__', do not parse
// the content. // the content.
$noAutoLinks = MagicWord::get('MAG_LINKTITLES_NOAUTOLINKS'); if ( $content->matchMagicWord(
if ( $noAutoLinks -> match( $text ) ) { MagicWord::get('MAG_LINKTITLES_NOAUTOLINKS') ) ) {
return true; return true;
} }
$noAutoLinkTarget = MagicWord::get('MAG_LINKTITLES_NOTARGET');
// Configuration variables need to be defined here as globals. // Configuration variables need to be defined here as globals.
global $wgLinkTitlesPreferShortTitles; global $wgLinkTitlesPreferShortTitles;
@ -138,8 +136,11 @@
'(?<=\b)\S+\@(?:\S+\.)+\S+(?=\b)' . // email addresses '(?<=\b)\S+\@(?:\S+\.)+\S+(?=\b)' . // email addresses
')/i'; ')/i';
// Build a blacklist of pages that are not supposed to be link
// targets. This includes the current page.
$black_list = str_replace( '_', ' ', $black_list = str_replace( '_', ' ',
'("' . implode( '", "',$wgLinkTitlesBlackList ) . '")' ); '("' . implode( '", "',$wgLinkTitlesBlackList ) .
$myTitle->getDbKey() . '")' );
// Build an SQL query and fetch all page titles ordered by length from // Build an SQL query and fetch all page titles ordered by length from
@ -175,6 +176,8 @@
); );
} }
$text = $content->getContentHandler()->serializeContent($content);
// Iterate through the page titles // Iterate through the page titles
foreach( $res as $row ) { foreach( $res as $row ) {
// Obtain an instance of a Title class for the current database // Obtain an instance of a Title class for the current database
@ -190,41 +193,33 @@
// To prevent linking to pages that redirect to the current page, // To prevent linking to pages that redirect to the current page,
// obtain the title that the target page redirects to. Will be null // obtain the title that the target page redirects to. Will be null
// if there is no redirect. // if there is no redirect.
$redirectTitle = $targetPage->getRedirectTarget(); $redirectTitle = $targetPage->getContent()->getUltimateRedirectTarget();
if ( $redirectTitle ) {
// If the target page redirects to the current page, exit the
// function.
if ( $redirectTitle->equals($myTitle) ) {
continue;
}
}
if ( $noAutoLinkTarget->match($targetText) ) { // Proceed only if the currently examined page does not redirect to
continue; // our page and does not contain the no-target magic word
} if (
!( $redirectTitle && $redirectTitle->equals($myTitle) ) &&
!( $targetPage->getContent()->matchMagicWord(
MagicWord::get('MAG_LINKTITLES_NOTARGET') ) ) ) {
// Only proceed if we're not operating on the very same page, if the // split the page content by [[...]] groups
// target page does not have the __NOAUTOLINKTARGET__ magic word in
// it, and if the target page does not redirect to the current page.
if ( ! $myTitle->equals($targetTitle) ) {
// split the string by [[...]] groups
// credits to inhan @ StackOverflow for suggesting preg_split // credits to inhan @ StackOverflow for suggesting preg_split
// see http://stackoverflow.com/questions/10672286 // see http://stackoverflow.com/questions/10672286
$arr = preg_split( $delimiter, $text, -1, PREG_SPLIT_DELIM_CAPTURE ); $arr = preg_split( $delimiter, $text, -1, PREG_SPLIT_DELIM_CAPTURE );
// Escape certain special characters in the page title to prevent // Escape certain special characters in the page title to prevent
// regexp compilation errors // regexp compilation errors
$escapedTitle = preg_quote($targetTitleText, '/'); $quotedTitle = preg_quote($targetTitleText, '/');
// Depending on the global configuration setting $wgCapitalLinks, // Depending on the global configuration setting $wgCapitalLinks,
// the title has to be searched for either in a strictly case-sensitive // the title has to be searched for either in a strictly case-sensitive
// way, or in a 'fuzzy' way where the first letter of the title may // way, or in a 'fuzzy' way where the first letter of the title may
// be either case. // be either case.
if ( $wgCapitalLinks && ( $escapedTitle[0] != '\\' )) { if ( $wgCapitalLinks && ( $quotedTitle[0] != '\\' )) {
$searchTerm = '((?i)' . $escapedTitle[0] . '(?-i)' . $searchTerm = '((?i)' . $quotedTitle[0] . '(?-i)' .
substr($escapedTitle, 1) . ')'; substr($quotedTitle, 1) . ')';
} else { } else {
$searchTerm = '(' . $escapedTitle . ')'; $searchTerm = '(' . $quotedTitle . ')';
} }
for ( $i = 0; $i < count( $arr ); $i+=2 ) { for ( $i = 0; $i < count( $arr ); $i+=2 ) {
@ -286,15 +281,17 @@
for ( $i = 0; $i < count( $arr ); $i+=2 ) { for ( $i = 0; $i < count( $arr ); $i+=2 ) {
// even indexes will point to text that is not enclosed by brackets // even indexes will point to text that is not enclosed by brackets
$arr[$i] = preg_replace_callback( '/(?<![\:\.\@\/\?\&])' . $arr[$i] = preg_replace_callback( '/(?<![\:\.\@\/\?\&])' .
$wordStartDelim . '(' . $escapedTitle . ')' . $wordStartDelim . '(' . $quotedTitle . ')' .
$wordEndDelim . '/i', $callback, $arr[$i], $limit, $count ); $wordEndDelim . '/i', $callback, $arr[$i], $limit, $count );
if (( $limit >= 0 ) && ( $count > 0 )) { if (( $limit >= 0 ) && ( $count > 0 )) {
break; break;
}; };
}; };
$text = implode( '', $arr ); $text = implode( '', $arr );
} // @todo check if text was changed
}; // if ! $myTitle.equals($targetTitle) $content = $content->getContentHandler()->unserializeContent( $text );
} // $wgLinkTitlesSmartMode
}
}; // foreach $res as $row }; // foreach $res as $row
return true; return true;
} }
@ -311,10 +308,9 @@
// TODO: make this namespace-aware // TODO: make this namespace-aware
$titleObj = Title::makeTitle(0, $title); $titleObj = Title::makeTitle(0, $title);
$page = WikiPage::factory($titleObj); $page = WikiPage::factory($titleObj);
$content = $page->getContent();
$article = Article::newFromWikiPage($page, $context); $article = Article::newFromWikiPage($page, $context);
$text = $article->getContent(); LinkTitles::parseContent($article, $content);
LinkTitles::parseContent($article, $text);
$content = new WikitextContent($text);
$page->doEditContent($content, $page->doEditContent($content,
"Links to existing pages added by LinkTitles bot.", "Links to existing pages added by LinkTitles bot.",
EDIT_MINOR | EDIT_FORCE_BOT, EDIT_MINOR | EDIT_FORCE_BOT,

View File

@ -155,7 +155,7 @@
'name' => 'LinkTitles', 'name' => 'LinkTitles',
'author' => '[https://www.mediawiki.org/wiki/User:Bovender Daniel Kraus]', 'author' => '[https://www.mediawiki.org/wiki/User:Bovender Daniel Kraus]',
'url' => 'https://www.mediawiki.org/wiki/Extension:LinkTitles', 'url' => 'https://www.mediawiki.org/wiki/Extension:LinkTitles',
'version' => '2.4.0', 'version' => '2.4.2',
'descriptionmsg' => 'linktitles-desc' 'descriptionmsg' => 'linktitles-desc'
); );

View File

@ -5,6 +5,8 @@ MediaWiki extension that automatically adds links to words that match titles of
For more information, see http://www.mediawiki.org/wiki/Extension:LinkTitles For more information, see http://www.mediawiki.org/wiki/Extension:LinkTitles
Minimum requirements: MediaWiki 1.21+, PHP 5.3
Source code documentation can be found at the [Github project Source code documentation can be found at the [Github project
pages](http://bovender.github.io/LinkTitles). pages](http://bovender.github.io/LinkTitles).