Merge branch 'release-4.0.9'

This commit is contained in:
Daniel Kraus
2017-03-21 22:19:01 +01:00
7 changed files with 77 additions and 68 deletions

View File

@ -1,6 +1,6 @@
<?php <?php
/* /*
* Copyright 2012-2016 Daniel Kraus <bovender@bovender.de> @bovender * Copyright 2012-2017 Daniel Kraus <bovender@bovender.de> @bovender
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -49,7 +49,7 @@ require_once( __DIR__ . "/includes/LinkTitles_Extension.php" );
/// Core class of the maintanance script. /// Core class of the maintanance script.
/// @note Note that the execution of maintenance scripts is prohibited for /// @note Note that the execution of maintenance scripts is prohibited for
/// an Apache web server due to a `.htaccess` file that declares `deny from /// an Apache web server due to a `.htaccess` file that declares `deny from
/// all`. Other webservers may exhibit different behavior. Be aware that /// all`. Other webservers may exhibit different behavior. Be aware that
/// anybody who is able to execute this script may place a high load on the /// anybody who is able to execute this script may place a high load on the
/// server. /// server.
/// @ingroup batch /// @ingroup batch
@ -116,7 +116,7 @@ class Cli extends \Maintenance {
$this->allPages( $startIndex); $this->allPages( $startIndex);
} }
} }
private function singlePage() { private function singlePage() {
$pageName = strval( $this->getOption( 'page' ) ); $pageName = strval( $this->getOption( 'page' ) );
$this->output( "Processing single page: '$pageName'\n" ); $this->output( "Processing single page: '$pageName'\n" );
@ -130,7 +130,7 @@ class Cli extends \Maintenance {
} }
return $success; return $success;
} }
private function allPages( $index = 0 ) { private function allPages( $index = 0 ) {
global $wgLinkTitlesNamespaces; global $wgLinkTitlesNamespaces;

8
NEWS
View File

@ -1,3 +1,11 @@
Version 4.0.9 (2017-03-21)
------------------------------------------------------------------------
- Fix: __NOAUTOLINKS__ was not respected during rendering.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Version 4.0.8 (2017-02-16) Version 4.0.8 (2017-02-16)
------------------------------------------------------------------------ ------------------------------------------------------------------------

View File

@ -1,6 +1,6 @@
@mainpage LinkTitles @mainpage LinkTitles
@author [Daniel Kraus (bovender)](http://www.mediawiki.org/wiki/User:Bovender) @author [Daniel Kraus (bovender)](http://www.mediawiki.org/wiki/User:Bovender)
@date 2012-2016 @date 2012-2017
@copyright [GNU GPL v2+](http://www.gnu.org/licenses/gpl-2.0.html) @copyright [GNU GPL v2+](http://www.gnu.org/licenses/gpl-2.0.html)
%LinkTitles source code documentation %LinkTitles source code documentation

View File

@ -1,13 +1,13 @@
{ {
"name": "LinkTitles", "name": "LinkTitles",
"author": [ "author": [
"[https://www.mediawiki.org/wiki/User:Bovender Daniel Kraus (bovender)]", "[https://www.mediawiki.org/wiki/User:Bovender Daniel Kraus (bovender)]",
"Ulrich Strauss (c0nnex)", "Ulrich Strauss (c0nnex)",
"Brent Laabs (labster)" "Brent Laabs (labster)"
], ],
"type": "parserhook", "type": "parserhook",
"url": "https://www.mediawiki.org/wiki/Extension:LinkTitles", "url": "https://www.mediawiki.org/wiki/Extension:LinkTitles",
"version": "4.0.8", "version": "4.0.9",
"license-name": "GPL-2.0+", "license-name": "GPL-2.0+",
"descriptionmsg": "linktitles-desc", "descriptionmsg": "linktitles-desc",
"requires": { "requires": {

View File

@ -1,17 +1,17 @@
<?php <?php
/* /*
* Copyright 2012-2016 Daniel Kraus <bovender@bovender.de> ('bovender') * Copyright 2012-2017 Daniel Kraus <bovender@bovender.de> ('bovender')
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or * the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
@ -49,7 +49,7 @@ class Extension {
private static $targetTitleValue; private static $targetTitleValue;
/// The content object for the currently processed target page. /// The content object for the currently processed target page.
/// This variable is necessary to be able to prevent loading the target /// This variable is necessary to be able to prevent loading the target
/// content twice. /// content twice.
private static $targetContent; private static $targetContent;
@ -84,7 +84,7 @@ class Extension {
$title = $wikiPage->getTitle(); $title = $wikiPage->getTitle();
// Only process if page is in one of our namespaces we want to link // Only process if page is in one of our namespaces we want to link
// Fixes ugly autolinking of sidebar pages // Fixes ugly autolinking of sidebar pages
if ( in_array( $title->getNamespace(), $wgLinkTitlesNamespaces )) { if ( in_array( $title->getNamespace(), $wgLinkTitlesNamespaces )) {
$text = $content->getContentHandler()->serializeContent( $content ); $text = $content->getContentHandler()->serializeContent( $content );
if ( !\MagicWord::get( 'MAG_LINKTITLES_NOAUTOLINKS' )->match( $text ) ) { if ( !\MagicWord::get( 'MAG_LINKTITLES_NOAUTOLINKS' )->match( $text ) ) {
@ -109,7 +109,8 @@ class Extension {
// If the page contains the magic word '__NOAUTOLINKS__', do not parse it. // If the page contains the magic word '__NOAUTOLINKS__', do not parse it.
// Only process if page is in one of our namespaces we want to link // Only process if page is in one of our namespaces we want to link
if ( !isset( $parser->mDoubleUnderScores[$text] ) && in_array( $title->getNamespace(), $wgLinkTitlesNamespaces ) ) { if ( !\MagicWord::get( 'MAG_LINKTITLES_NOAUTOLINKS' )->match( $text ) &&
in_array( $title->getNamespace(), $wgLinkTitlesNamespaces ) ) {
$text = self::parseContent( $title, $text ); $text = self::parseContent( $title, $text );
} }
return true; return true;
@ -162,13 +163,13 @@ class Extension {
// 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 && ( $quotedTitle[0] != '\\' )) { if ( $wgCapitalLinks && ( $quotedTitle[0] != '\\' )) {
$searchTerm = '((?i)' . $quotedTitle[0] . '(?-i)' . $searchTerm = '((?i)' . $quotedTitle[0] . '(?-i)' .
substr($quotedTitle, 1) . ')'; substr($quotedTitle, 1) . ')';
} else { } else {
$searchTerm = '(' . $quotedTitle . ')'; $searchTerm = '(' . $quotedTitle . ')';
} }
$regex = '/(?<![\:\.\@\/\?\&])' . self::$wordStartDelim . $regex = '/(?<![\:\.\@\/\?\&])' . self::$wordStartDelim .
$searchTerm . self::$wordEndDelim . '/S'; $searchTerm . self::$wordEndDelim . '/S';
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
@ -176,7 +177,7 @@ class Extension {
'LinkTitles\Extension::simpleModeCallback', $arr[$i], $limit, $count ); 'LinkTitles\Extension::simpleModeCallback', $arr[$i], $limit, $count );
if ( $wgLinkTitlesFirstOnly && ( $count > 0 ) ) { if ( $wgLinkTitlesFirstOnly && ( $count > 0 ) ) {
$limitReached = true; $limitReached = true;
break; break;
}; };
}; };
$newText = implode( '', $arr ); $newText = implode( '', $arr );
@ -190,11 +191,11 @@ class Extension {
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( '/(?<![\:\.\@\/\?\&])' .
self::$wordStartDelim . '(' . $quotedTitle . ')' . self::$wordStartDelim . '(' . $quotedTitle . ')' .
self::$wordEndDelim . '/iS', 'LinkTitles\Extension::smartModeCallback', self::$wordEndDelim . '/iS', 'LinkTitles\Extension::smartModeCallback',
$arr[$i], $limit, $count ); $arr[$i], $limit, $count );
if ( $wgLinkTitlesFirstOnly && ( $count > 0 )) { if ( $wgLinkTitlesFirstOnly && ( $count > 0 )) {
break; break;
}; };
}; };
$newText = implode( '', $arr ); $newText = implode( '', $arr );
@ -204,10 +205,10 @@ class Extension {
} }
/// Automatically processes a single page, given a $title Title object. /// Automatically processes a single page, given a $title Title object.
/// This function is called by the SpecialLinkTitles class and the /// This function is called by the SpecialLinkTitles class and the
/// LinkTitlesJob class. /// LinkTitlesJob class.
/// @param Title $title Title object. /// @param Title $title Title object.
/// @param RequestContext $context Current request context. /// @param RequestContext $context Current request context.
/// If in doubt, call MediaWiki's `RequestContext::getMain()` /// If in doubt, call MediaWiki's `RequestContext::getMain()`
/// to obtain such an object. /// to obtain such an object.
/// @returns boolean True if the page exists, false if the page does not exist /// @returns boolean True if the page exists, false if the page does not exist
@ -235,8 +236,8 @@ class Extension {
} }
} }
/// Adds the two magic words defined by this extension to the list of /// Adds the two magic words defined by this extension to the list of
/// 'double-underscore' terms that are automatically removed before a /// 'double-underscore' terms that are automatically removed before a
/// page is displayed. /// page is displayed.
/// @param $doubleUnderscoreIDs Array of magic word IDs. /// @param $doubleUnderscoreIDs Array of magic word IDs.
/// @return true /// @return true
@ -255,7 +256,7 @@ class Extension {
global $wgLinkTitlesNamespaces; global $wgLinkTitlesNamespaces;
( $wgLinkTitlesPreferShortTitles ) ? $sort_order = 'ASC' : $sort_order = 'DESC'; ( $wgLinkTitlesPreferShortTitles ) ? $sort_order = 'ASC' : $sort_order = 'DESC';
// Build a blacklist of pages that are not supposed to be link // Build a blacklist of pages that are not supposed to be link
// targets. This includes the current page. // targets. This includes the current page.
$blackList = str_replace( ' ', '_', '("' . implode( '","',$wgLinkTitlesBlackList ) . '")' ); $blackList = str_replace( ' ', '_', '("' . implode( '","',$wgLinkTitlesBlackList ) . '")' );
@ -273,33 +274,33 @@ class Extension {
$weightSelect = $weightSelect . " END "; $weightSelect = $weightSelect . " END ";
$namespacesClause = '(' . implode( ', ', $namespaces ) . ')'; $namespacesClause = '(' . implode( ', ', $namespaces ) . ')';
// 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
// shortest to longest. Only titles from 'normal' pages (namespace uid // shortest to longest. Only titles from 'normal' pages (namespace uid
// = 0) are returned. Since the db may be sqlite, we need a try..catch // = 0) are returned. Since the db may be sqlite, we need a try..catch
// structure because sqlite does not support the CHAR_LENGTH function. // structure because sqlite does not support the CHAR_LENGTH function.
$dbr = wfGetDB( DB_SLAVE ); $dbr = wfGetDB( DB_SLAVE );
try { try {
$res = $dbr->select( $res = $dbr->select(
'page', 'page',
array( 'page_title', 'page_namespace' , "weight" => $weightSelect), array( 'page_title', 'page_namespace' , "weight" => $weightSelect),
array( array(
'page_namespace IN ' . $namespacesClause, 'page_namespace IN ' . $namespacesClause,
'CHAR_LENGTH(page_title) >= ' . $wgLinkTitlesMinimumTitleLength, 'CHAR_LENGTH(page_title) >= ' . $wgLinkTitlesMinimumTitleLength,
'page_title NOT IN ' . $blackList, 'page_title NOT IN ' . $blackList,
), ),
__METHOD__, __METHOD__,
array( 'ORDER BY' => 'weight ASC, CHAR_LENGTH(page_title) ' . $sort_order ) array( 'ORDER BY' => 'weight ASC, CHAR_LENGTH(page_title) ' . $sort_order )
); );
} catch (Exception $e) { } catch (Exception $e) {
$res = $dbr->select( $res = $dbr->select(
'page', 'page',
array( 'page_title', 'page_namespace' , "weight" => $weightSelect ), array( 'page_title', 'page_namespace' , "weight" => $weightSelect ),
array( array(
'page_namespace IN ' . $namespacesClause, 'page_namespace IN ' . $namespacesClause,
'LENGTH(page_title) >= ' . $wgLinkTitlesMinimumTitleLength, 'LENGTH(page_title) >= ' . $wgLinkTitlesMinimumTitleLength,
'page_title NOT IN ' . $blackList, 'page_title NOT IN ' . $blackList,
), ),
__METHOD__, __METHOD__,
array( 'ORDER BY' => 'weight ASC, LENGTH(page_title) ' . $sort_order ) array( 'ORDER BY' => 'weight ASC, LENGTH(page_title) ' . $sort_order )
); );
} }
@ -320,19 +321,19 @@ class Extension {
} }
// Callback function for use with preg_replace_callback. // Callback function for use with preg_replace_callback.
// This essentially performs a case-sensitive comparison of the // This essentially performs a case-sensitive comparison of the
// current page title and the occurrence found on the page; if // current page title and the occurrence found on the page; if
// the cases do not match, it builds an aliased (piped) link. // the cases do not match, it builds an aliased (piped) link.
// If $wgCapitalLinks is set to true, the case of the first // If $wgCapitalLinks is set to true, the case of the first
// letter is ignored by MediaWiki and we don't need to build a // letter is ignored by MediaWiki and we don't need to build a
// piped link if only the case of the first letter is different. // piped link if only the case of the first letter is different.
private static function smartModeCallback( array $matches ) { private static function smartModeCallback( array $matches ) {
global $wgCapitalLinks; global $wgCapitalLinks;
if ( $wgCapitalLinks ) { if ( $wgCapitalLinks ) {
// With $wgCapitalLinks set to true we have a slightly more // With $wgCapitalLinks set to true we have a slightly more
// complicated version of the callback than if it were false; // complicated version of the callback than if it were false;
// we need to ignore the first letter of the page titles, as // we need to ignore the first letter of the page titles, as
// it does not matter for linking. // it does not matter for linking.
if ( self::checkTargetPage() ) { if ( self::checkTargetPage() ) {
self::ltLog( "Linking (smart) '$matches[0]' to '" . self::$targetTitle . "'" ); self::ltLog( "Linking (smart) '$matches[0]' to '" . self::$targetTitle . "'" );
@ -349,7 +350,7 @@ class Extension {
return $matches[0]; return $matches[0];
} }
} else { } else {
// If $wgCapitalLinks is false, we can use the simple variant // If $wgCapitalLinks is false, we can use the simple variant
// of the callback function. // of the callback function.
if ( self::checkTargetPage() ) { if ( self::checkTargetPage() ) {
self::ltLog( "Linking (smart) '$matches[0]' to '" . self::$targetTitle . "'" ); self::ltLog( "Linking (smart) '$matches[0]' to '" . self::$targetTitle . "'" );
@ -378,10 +379,10 @@ class Extension {
} }
/// Returns the content of the current target page. /// Returns the content of the current target page.
/// This function serves to be used in preg_replace_callback callback /// This function serves to be used in preg_replace_callback callback
/// functions, in order to load the target page content from the /// functions, in order to load the target page content from the
/// database only when needed. /// database only when needed.
/// @note It is absolutely necessary that the newTarget() /// @note It is absolutely necessary that the newTarget()
/// function is called for every new page. /// function is called for every new page.
private static function getTargetContent() { private static function getTargetContent() {
if ( ! isset( $targetContent ) ) { if ( ! isset( $targetContent ) ) {
@ -391,18 +392,18 @@ class Extension {
return self::$targetContent; return self::$targetContent;
} }
/// Examines the current target page. Returns true if it may be linked; /// Examines the current target page. Returns true if it may be linked;
/// false if not. This depends on the settings /// false if not. This depends on the settings
/// $wgLinkTitlesCheckRedirect and $wgLinkTitlesEnableNoTargetMagicWord /// $wgLinkTitlesCheckRedirect and $wgLinkTitlesEnableNoTargetMagicWord
/// and whether the target page is a redirect or contains the /// and whether the target page is a redirect or contains the
/// __NOAUTOLINKTARGET__ magic word. /// __NOAUTOLINKTARGET__ magic word.
/// @returns boolean /// @returns boolean
private static function checkTargetPage() { private static function checkTargetPage() {
global $wgLinkTitlesEnableNoTargetMagicWord; global $wgLinkTitlesEnableNoTargetMagicWord;
global $wgLinkTitlesCheckRedirect; global $wgLinkTitlesCheckRedirect;
// If checking for redirects is enabled and the target page does // If checking for redirects is enabled and the target page does
// indeed redirect to the current page, return the page title as-is // indeed redirect to the current page, return the page title as-is
// (unlinked). // (unlinked).
if ( $wgLinkTitlesCheckRedirect ) { if ( $wgLinkTitlesCheckRedirect ) {
$redirectTitle = self::getTargetContent()->getUltimateRedirectTarget(); $redirectTitle = self::getTargetContent()->getUltimateRedirectTarget();
@ -411,8 +412,8 @@ class Extension {
} }
}; };
// If the magic word __NOAUTOLINKTARGET__ is enabled and the target // If the magic word __NOAUTOLINKTARGET__ is enabled and the target
// page does indeed contain this magic word, return the page title // page does indeed contain this magic word, return the page title
// as-is (unlinked). // as-is (unlinked).
if ( $wgLinkTitlesEnableNoTargetMagicWord ) { if ( $wgLinkTitlesEnableNoTargetMagicWord ) {
if ( self::getTargetContent()->matchMagicWord( if ( self::getTargetContent()->matchMagicWord(
@ -435,7 +436,7 @@ private static function BuildDelimiters() {
// Use unicode character properties rather than \b escape sequences // Use unicode character properties rather than \b escape sequences
// to detect whole words containing non-ASCII characters as well. // to detect whole words containing non-ASCII characters as well.
// Note that this requires a PCRE library that was compiled with // Note that this requires a PCRE library that was compiled with
// --enable-unicode-properties // --enable-unicode-properties
( $wgLinkTitlesWordStartOnly ) ? self::$wordStartDelim = '(?<!\pL)' : self::$wordStartDelim = ''; ( $wgLinkTitlesWordStartOnly ) ? self::$wordStartDelim = '(?<!\pL)' : self::$wordStartDelim = '';
( $wgLinkTitlesWordEndOnly ) ? self::$wordEndDelim = '(?!\pL)' : self::$wordEndDelim = ''; ( $wgLinkTitlesWordEndOnly ) ? self::$wordEndDelim = '(?!\pL)' : self::$wordEndDelim = '';
@ -444,19 +445,19 @@ private static function BuildDelimiters() {
{ {
$templatesDelimiter = '{{[^}]+}}|'; $templatesDelimiter = '{{[^}]+}}|';
} else { } else {
// Match template names (ignoring any piped [[]] links in them) // Match template names (ignoring any piped [[]] links in them)
// along with the trailing pipe and parameter name or closing // along with the trailing pipe and parameter name or closing
// braces; also match sequences of '|wordcharacters=' (without // braces; also match sequences of '|wordcharacters=' (without
// spaces in them) that usually only occur as parameter names in // spaces in them) that usually only occur as parameter names in
// transclusions (but could also occur as wiki table cell contents). // transclusions (but could also occur as wiki table cell contents).
// TODO: Find a way to match parameter names in transclusions, but // TODO: Find a way to match parameter names in transclusions, but
// not in table cells or other sequences involving a pipe character // not in table cells or other sequences involving a pipe character
// and equal sign. // and equal sign.
$templatesDelimiter = '{{[^|]*?(?:(?:\[\[[^]]+]])?)[^|]*?(?:\|(?:\w+=)?|(?:}}))|\|\w+=|'; $templatesDelimiter = '{{[^|]*?(?:(?:\[\[[^]]+]])?)[^|]*?(?:\|(?:\w+=)?|(?:}}))|\|\w+=|';
} }
// Build a regular expression that will capture existing wiki links ("[[...]]"), // Build a regular expression that will capture existing wiki links ("[[...]]"),
// wiki headings ("= ... =", "== ... ==" etc.), // wiki headings ("= ... =", "== ... ==" etc.),
// urls ("http://example.com", "[http://example.com]", "[http://example.com Description]", // urls ("http://example.com", "[http://example.com]", "[http://example.com Description]",
// and email addresses ("mail@example.com"). // and email addresses ("mail@example.com").
// Since there is a user option to skip headings, we make this part of the expression // Since there is a user option to skip headings, we make this part of the expression

View File

@ -1,6 +1,6 @@
<?php <?php
/* /*
* Copyright 2012-2016 Daniel Kraus <bovender@bovender.de> ('bovender') * Copyright 2012-2017 Daniel Kraus <bovender@bovender.de> ('bovender')
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by

View File

@ -1,6 +1,6 @@
<?php <?php
/* /*
* Copyright 2012-2016 Daniel Kraus <bovender@bovender.de> ('bovender') * Copyright 2012-2017 Daniel Kraus <bovender@bovender.de> ('bovender')
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by