tbd @

MW 1.43.0

maintained by us

(Gareth, Phex)
Extension:MultiCategorySearch
Code
Extension:Progressbar
Code
Feature by Stip, includes/SpecialContact.php
(restricts legal characters in field "your name")
search replace
			if ( !Sanitizer::validateEmail( $fromAddress ) ) {
				return [ 'invalidemailaddress' ];
			}
			if ( !Sanitizer::validateEmail( $fromAddress ) ) {
				return [ 'invalidemailaddress' ];
			}
			if ( preg_match('/[^A-Za-z ÄäÜüÖöß]/', $fromName) ) {
				return [ 'contactpage-invalidusername' ];
			}
Security Fix by Stip, includes/SpecialContact.php
(removes option of cc-mails; if this extension is activated for anonymous users, this option could be used by anyone to send mails with arbitrary content to arbitrary people, thus sending spam from your email address.)
search replace
			'CCme' => [
				'label-message' => $this->getFormSpecificMessageKey( 'emailccme' ),
				'type' => 'check',
				'default' => $this->userOptionsLookup->getBoolOption( $user, 'ccmeonemails' ),
			],
		if ( $formData['CCme'] && $fromUserAddress ) {
			$cc_subject = $this->msg( 'emailccsubject', $ccName, $subject )->text();
			if ( $hookRunner->onContactForm(
				$fromUserAddress, $senderAddress, $cc_subject, $text, $this->formType, $formData )
			) {
				wfDebug( __METHOD__ . ': sending cc mail from ' . $senderAddress->toString() .
					' to ' . $fromUserAddress->toString() . "\n"
				);
				// @phan-suppress-next-line SecurityCheck-XSS UserMailer::send defaults to text/plain if passed a string
				$ccResult = UserMailer::send(
					$fromUserAddress,
					$senderAddress,
					$cc_subject,
					$text,
				);
				if ( !$ccResult->isOK() ) {
					// At this stage, the user's CC mail has failed, but their
					// original mail has succeeded. It's unlikely, but still, what to do?
					// We can either show them an error, or we can say everything was fine,
					// or we can say we sort of failed AND sort of succeeded. Of these options,
					// simply saying there was an error is probably best.
					return [ $ccResult->getMessage( 'contactpage-usermailererror', false, $language ) ];
				}
			}
		}
Bugfix by Stip, includes/Lister/Lister.php
(replaced deprecated system message from Extension:HitCounters)
search replace
				'(' . wfMessage( 'hitcounters-nviews', $lang->formatNum( $article->mCounter ) )->escaped() . ')'
				'(' . wfMessage( 'hitcounters-pop-page-line', $lang->formatNum( $article->mCounter ) )->escaped() . ')'
Bugfix by Stip, includes/LST.php
(defines variable before testing it, see stackoverflow.com)
search replace
			if ( !$end_off ) {
			$isset = array_key_exists('end_off', get_defined_vars());

			if ( !$isset ) {
			if ( $end_off ) {
			$isset = array_key_exists('end_off', get_defined_vars());

			if ( $isset ) {
Bugfix by Laskyr, includes/Query.php
(simultaneous usage of "uses", "linksfrom" or "linksto" no longer results in empty result list; see this diff)
search replace
$this->addTable( 'linktarget', 'lt' );
$this->addTable( 'linktarget', 'ltf' );
				$this->tableNames['page'] . '.page_namespace = lt.lt_namespace',
				$this->tableNames['page'] . '.page_title = lt.lt_title',
				'lt.lt_id = plf.pl_target_id',
				$this->tableNames['page'] . '.page_namespace = ltf.lt_namespace',
				$this->tableNames['page'] . '.page_title = ltf.lt_title',
				'ltf.lt_id = plf.pl_target_id',
			$this->addTable( 'pagelinks', 'pl' );
			$this->addTable( 'linktarget', 'lt' );

			if ( $this->isPageselFormatUsed() ) {
				$this->addSelect( [ 'sel_title' => 'lt.lt_title', 'sel_ns' => 'lt.lt_namespace' ] );
			}

			$this->addWhere( 'pl.pl_target_id = lt.lt_id' );

			foreach ( $option as $index => $linkGroup ) {
				if ( $index == 0 ) {
					$where = $this->tableNames['page'] . '.page_id=pl.pl_from AND ';
					$ors = [];

					foreach ( $linkGroup as $link ) {
						$_or = '(lt.lt_namespace=' . (int)$link->getNamespace();
			$this->addTable( 'pagelinks', 'plt' );
			$this->addTable( 'linktarget', 'ltt' );

			if ( $this->isPageselFormatUsed() ) {
				$this->addSelect( [ 'sel_title' => 'ltt.lt_title', 'sel_ns' => 'ltt.lt_namespace' ] );
			}

			$this->addWhere( 'plt.pl_target_id = ltt.lt_id' );

			foreach ( $option as $index => $linkGroup ) {
				if ( $index == 0 ) {
					$where = $this->tableNames['page'] . '.page_id=plt.pl_from AND ';
					$ors = [];

					foreach ( $linkGroup as $link ) {
						$_or = '(ltt.lt_namespace=' . (int)$link->getNamespace();
							$_or .= ' AND LOWER(CAST(lt.lt_title AS char)) ' .
								$operator . ' LOWER(' . $this->dbr->addQuotes( $link->getDBkey() ) . ')';
						} else {
							$_or .= ' AND lt.lt_title ' . $operator . ' ' . $this->dbr->addQuotes( $link->getDBkey() );
							$_or .= ' AND LOWER(CAST(ltt.lt_title AS char)) ' .
								$operator . ' LOWER(' . $this->dbr->addQuotes( $link->getDBkey() ) . ')';
						} else {
							$_or .= ' AND ltt.lt_title ' . $operator . ' ' . $this->dbr->addQuotes( $link->getDBkey() );
			'linktarget' => 'lt',
			'templatelinks' => 'tl',
		] );

		$where = $this->tableNames['page'] . '.page_id=tl.tl_from AND lt.lt_id = tl.tl_target_id AND (';
			'linktarget' => 'ltu',
			'templatelinks' => 'tlu',
		] );

		$where = $this->tableNames['page'] . '.page_id=tlu.tl_from AND ltu.lt_id = tlu.tl_target_id AND (';
				$_or = '(lt.' . $nsField . '=' . (int)$link->getNamespace();

				if ( $this->parameters->getParameter( 'ignorecase' ) ) {
					$_or .= ' AND LOWER(CAST(lt.' . $titleField . ' AS char)) = LOWER(' .
						$this->dbr->addQuotes( $link->getDBkey() ) . '))';
				} else {
					$_or .= ' AND ' . $titleField . ' = ' . $this->dbr->addQuotes( $link->getDBkey() ) . ')';
				$_or = '(ltu.' . $nsField . '=' . (int)$link->getNamespace();

				if ( $this->parameters->getParameter( 'ignorecase' ) ) {
					$_or .= ' AND LOWER(CAST(ltu.' . $titleField . ' AS char)) = LOWER(' .
						$this->dbr->addQuotes( $link->getDBkey() ) . '))';
				} else {
					$_or .= ' AND ltu.' . $titleField . ' = ' . $this->dbr->addQuotes( $link->getDBkey() ) . ')';

Extension:RSS (2.25.1)

Bugfix by Stip, RSSParser.php
(self-closing img tags are filtered)
search replace
			$ret = Sanitizer::removeSomeTags( $text, [
				'extraTags' => $extraInclude,
				'removeTags' => $extraExclude,
			] );
			$text = preg_replace( '#<img (.*?) />#i', '', $text );
			$ret = Sanitizer::removeSomeTags( $text, [
				'extraTags' => $extraInclude,
				'removeTags' => $extraExclude,
			] );
Bugfix, NamespaceRelations.php
fixes variable initialization, see MediaWiki
search replace
$namespaceInfo = MediaWikiServices::getInstance()->getNamespaceInfo();
move to
$namespaceInfo = MediaWikiServices::getInstance()->getNamespaceInfo();
if ( array_key_exists( $subjectNS, $this->namespacesToNamespace ) ) {
Bugfix, /pygments/pygmentize
(enables extension, see MediaWiki)

change file permissions from 644 to 744 (file needs to be executable by owner)

Adjustment, /modules/ext.quiz.css
(correct/incorrect should not be colored green/red, because of Congenital red–green color blindness; ideally this should be a configuration variable)
search replace
#14866d #0000ff
Adjustment, /includes/specials/SpecialPatroller.php
(in our wiki there are not many edits we have to patrol, so edits which were taken but not marked as patrolled should be available faster; ideally this should be a configuration variable)
search replace
		if ( mt_rand( 0, 499 ) == 0 ) {
		if ( mt_rand( 0, 9 ) == 0 ) {
Adjustment, /includes/specials/SpecialPatroller.php
(we do want to patrol all changes to one page, not only the last one; ideally this should be a configuration variable)
search replace
		'rc_this_oldid = page_latest',
Feature by Orci, includes/Hooks.php
(we only want content between pre and nowiki)
search replace
	$editPage->textbox1 = $content;
    $content = preg_replace( '#<pre>(.*?)</pre>#ims', '$1', $content );
    $content = preg_replace( '#<nowiki>(.*?)</nowiki>#ims', '$1', $content );
	$editPage->textbox1 = $content;

First of all, apply this patch.

Multiple MediaWiki functions which were in use by this extensions were declared "private", and thus cannot be used by this extension anymore; quickfix: Copy the code.

Bugfix, /includes/FavoritelistEditor.php
search replace
	private function buildRemoveList( $user ) {
		$list = '';
		$toc = Linker::tocIndent();
		$tocLength = 0;

		$favorites = FavoriteListInfo::getForUser( $user, 'ignoreTalkNS' );

		foreach ( $favorites as $namespace => $pages ) {
			$tocLength++;
			$heading = htmlspecialchars( $this->getNamespaceHeading( $namespace ) );
			$anchor = 'editfavoritelist-ns' . $namespace;

			$list .= Linker::makeHeadLine( 2, '>', $anchor, $heading, '' );
			$toc .= Linker::tocLine( $anchor, $heading, $tocLength, 1 ) . Linker::tocLineEnd();

			$list .= "\n";
			foreach ( $pages as $dbkey => $redirect ) {
				$title = Title::makeTitleSafe( $namespace, $dbkey );
				$list .= $this->buildRemoveLine( $title, $redirect );
			}
			$list .= "\n";
		}

		// ISSUE: omit the TOC if the total number of titles is low?
		if ( $tocLength > 1 ) {
			$list = Linker::tocList( $toc ) . $list;
		}

		return $list;
	}
	private function buildRemoveList( $user ) {
		$list = '';
		$toc = self::tocIndent();
		$tocLength = 0;

		$favorites = FavoriteListInfo::getForUser( $user, 'ignoreTalkNS' );

		foreach ( $favorites as $namespace => $pages ) {
			$tocLength++;
			$heading = htmlspecialchars( $this->getNamespaceHeading( $namespace ) );
			$anchor = 'editfavoritelist-ns' . $namespace;

			$list .= self::makeHeadLine( 2, '>', $anchor, $heading, '' );
			$toc .= self::tocLine( $anchor, $heading, $tocLength, 1 ) . self::tocLineEnd();

			$list .= "\n";
			foreach ( $pages as $dbkey => $redirect ) {
				$title = Title::makeTitleSafe( $namespace, $dbkey );
				$list .= $this->buildRemoveLine( $title, $redirect );
			}
			$list .= "\n";
		}

		// ISSUE: omit the TOC if the total number of titles is low?
		if ( $tocLength > 1 ) {
			$list = self::tocList( $toc ) . $list;
		}

		return $list;
	}


	/* The following was copied from HandleTOCMarkers, better solution should probably be found */

	/**
	 * Add another level to the Table of Contents
	 *
	 * @return string
	 */
	private static function tocIndent() {
		return "\n<ul>\n";
	}

	private static function tocLine( $linkAnchor, $tocline, $tocnumber, $level, $sectionIndex = false ) {
		$classes = "toclevel-$level";

		// Parser.php used to suppress tocLine by setting $sectionindex to false.
		// In those circumstances, we can now encounter '' or a "T-" prefixed index
		// for when the section comes from templates.
		if ( $sectionIndex !== false && $sectionIndex !== '' && !str_starts_with( $sectionIndex, "T-" ) ) {
			$classes .= " tocsection-$sectionIndex";
		}

		// <li class="$classes"><a href="#$linkAnchor"><span class="tocnumber">
		// $tocnumber</span> <span class="toctext">$tocline</span></a>
		return Html::openElement( 'li', [ 'class' => $classes ] )
		. Html::rawElement( 'a',
							[ 'href' => "#$linkAnchor" ],
					  Html::element( 'span', [ 'class' => 'tocnumber' ], $tocnumber )
					  . ' '
					  . Html::rawElement( 'span', [ 'class' => 'toctext' ], $tocline )
		);
	}

	/**
	 * End a Table Of Contents line.
	 * tocUnindent() will be used instead if we're ending a line below
	 * the new level.
	 * @return string
	 */
	private static function tocLineEnd() {
		return "</li>\n";
	}

	/**
	 * Wraps the TOC in a div with ARIA navigation role and provides the hide/collapse JavaScript.
	 *
	 * @param string $toc Html of the Table Of Contents
	 * @param Language|null $lang Language for the toc title, defaults to user language
	 * @return string Full html of the TOC
	 */
	private static function tocList( $toc, ?Language $lang = null ) {
		$lang ??= RequestContext::getMain()->getLanguage();

		$title = wfMessage( 'toc' )->inLanguage( $lang )->escaped();

		return '<div id="toc" class="toc" role="navigation" aria-labelledby="mw-toc-heading">'
		. Html::element( 'input', [
			'type' => 'checkbox',
			'role' => 'button',
			'id' => 'toctogglecheckbox',
			'class' => 'toctogglecheckbox',
			'style' => 'display:none',
		] )
		. Html::openElement( 'div', [
			'class' => 'toctitle',
			'lang' => $lang->getHtmlCode(),
							 'dir' => $lang->getDir(),
		] )
		. '<h2 id="mw-toc-heading">' . $title . '</h2>'
		. '<span class="toctogglespan">'
		. Html::label( '', 'toctogglecheckbox', [
			'class' => 'toctogglelabel',
		] )
		. '</span>'
		. '</div>'
		. $toc
		. "</ul>\n</div>\n";
	}

	/* The following was copied from an old online copy of Linker.php */

	/**
	 * Create a headline for content
	 *
	 * @since 1.16.3
	 * @param int $level The level of the headline (1-6)
	 * @param string $attribs Any attributes for the headline, starting with
	 *   a space and ending with '>'
	 *   This *must* be at least '>' for no attribs
	 * @param string $anchor The anchor to give the headline (the bit after the #)
	 * @param string $html HTML for the text of the header
	 * @param string $link HTML to add for the section edit link
	 * @param string|false $fallbackAnchor A second, optional anchor to give for
	 *   backward compatibility (false to omit)
	 *
	 * @return string HTML headline
	 */
	private static function makeHeadline( $level, $attribs, $anchor, $html,
										  $link, $fallbackAnchor = false
	) {
		$anchorEscaped = htmlspecialchars( $anchor, ENT_COMPAT );
		$fallback = '';
		if ( $fallbackAnchor !== false && $fallbackAnchor !== $anchor ) {
			$fallbackAnchor = htmlspecialchars( $fallbackAnchor, ENT_COMPAT );
			$fallback = "<span id=\"$fallbackAnchor\"></span>";
		}
		return "<h$level$attribs"
		. "$fallback<span class=\"mw-headline\" id=\"$anchorEscaped\">$html</span>"
		. $link
		. "</h$level>";
	}

	/* End of copy */
Bugfix, /includes/Specials/SpecialFavoritelist.php
search replace
	private function buildRemoveList( $user ) {
		$list = '';
		$toc = Linker::tocIndent();
		$tocLength = 0;

		$favorites = FavoriteListInfo::getForUser( $user, 'ignoreTalkNS' );

		foreach ( $favorites as $namespace => $pages ) {
			$tocLength++;
			$heading = htmlspecialchars( $this->getNamespaceHeading( $namespace ) );
			$anchor = 'editfavoritelist-ns' . $namespace;

			$list .= Linker::makeHeadLine( 2, '>', $anchor, $heading, '' );
			$toc .= Linker::tocLine( $anchor, $heading, $tocLength, 1 ) . Linker::tocLineEnd();

			$list .= "<ul>\n";
			foreach ( $pages as $dbkey => $redirect ) {
				$title = Title::makeTitleSafe( $namespace, $dbkey );
				$list .= $this->buildRemoveLine( $title, $redirect );
			}
			$list .= "</ul>\n";
		}

		// ISSUE: omit the TOC if the total number of titles is low?
		if ( $tocLength > 10 ) {
			$list = Linker::tocList( $toc ) . $list;
		}

		return $list;
	}
	private function buildRemoveList( $user ) {
		$list = '';
		$toc = self::tocIndent();
		$tocLength = 0;

		$favorites = FavoriteListInfo::getForUser( $user, 'ignoreTalkNS' );

		foreach ( $favorites as $namespace => $pages ) {
			$tocLength++;
			$heading = htmlspecialchars( $this->getNamespaceHeading( $namespace ) );
			$anchor = 'editfavoritelist-ns' . $namespace;

			$list .= self::makeHeadLine( 2, '>', $anchor, $heading, '' );
			$toc .= self::tocLine( $anchor, $heading, $tocLength, 1 ) . self::tocLineEnd();

			$list .= "<ul>\n";
			foreach ( $pages as $dbkey => $redirect ) {
				$title = Title::makeTitleSafe( $namespace, $dbkey );
				$list .= $this->buildRemoveLine( $title, $redirect );
			}
			$list .= "</ul>\n";
		}

		// ISSUE: omit the TOC if the total number of titles is low?
		if ( $tocLength > 10 ) {
			$list = self::tocList( $toc ) . $list;
		}

		return $list;
	}
	/* The following was copied from HandleTOCMarkers, better solution should probably be found */

	/**
	 * Add another level to the Table of Contents
	 *
	 * @return string
	 */
	private static function tocIndent() {
		return "\n<ul>\n";
	}

	private static function tocLine( $linkAnchor, $tocline, $tocnumber, $level, $sectionIndex = false ) {
		$classes = "toclevel-$level";

		// Parser.php used to suppress tocLine by setting $sectionindex to false.
		// In those circumstances, we can now encounter '' or a "T-" prefixed index
		// for when the section comes from templates.
		if ( $sectionIndex !== false && $sectionIndex !== '' && !str_starts_with( $sectionIndex, "T-" ) ) {
			$classes .= " tocsection-$sectionIndex";
		}

		// <li class="$classes"><a href="#$linkAnchor"><span class="tocnumber">
		// $tocnumber</span> <span class="toctext">$tocline</span></a>
		return Html::openElement( 'li', [ 'class' => $classes ] )
		. Html::rawElement( 'a',
							[ 'href' => "#$linkAnchor" ],
					  Html::element( 'span', [ 'class' => 'tocnumber' ], $tocnumber )
					  . ' '
					  . Html::rawElement( 'span', [ 'class' => 'toctext' ], $tocline )
		);
	}

	/**
	 * End a Table Of Contents line.
	 * tocUnindent() will be used instead if we're ending a line below
	 * the new level.
	 * @return string
	 */
	private static function tocLineEnd() {
		return "</li>\n";
	}

	/**
	 * Wraps the TOC in a div with ARIA navigation role and provides the hide/collapse JavaScript.
	 *
	 * @param string $toc Html of the Table Of Contents
	 * @param Language|null $lang Language for the toc title, defaults to user language
	 * @return string Full html of the TOC
	 */
	private static function tocList( $toc, ?Language $lang = null ) {
		$lang ??= RequestContext::getMain()->getLanguage();

		$title = wfMessage( 'toc' )->inLanguage( $lang )->escaped();

		return '<div id="toc" class="toc" role="navigation" aria-labelledby="mw-toc-heading">'
		. Html::element( 'input', [
			'type' => 'checkbox',
			'role' => 'button',
			'id' => 'toctogglecheckbox',
			'class' => 'toctogglecheckbox',
			'style' => 'display:none',
		] )
		. Html::openElement( 'div', [
			'class' => 'toctitle',
			'lang' => $lang->getHtmlCode(),
							 'dir' => $lang->getDir(),
		] )
		. '<h2 id="mw-toc-heading">' . $title . '</h2>'
		. '<span class="toctogglespan">'
		. Html::label( '', 'toctogglecheckbox', [
			'class' => 'toctogglelabel',
		] )
		. '</span>'
		. '</div>'
		. $toc
		. "</ul>\n</div>\n";
	}

	/* The following was copied from an old online copy of Linker.php */

	/**
	 * Create a headline for content
	 *
	 * @since 1.16.3
	 * @param int $level The level of the headline (1-6)
	 * @param string $attribs Any attributes for the headline, starting with
	 *   a space and ending with '>'
	 *   This *must* be at least '>' for no attribs
	 * @param string $anchor The anchor to give the headline (the bit after the #)
	 * @param string $html HTML for the text of the header
	 * @param string $link HTML to add for the section edit link
	 * @param string|false $fallbackAnchor A second, optional anchor to give for
	 *   backward compatibility (false to omit)
	 *
	 * @return string HTML headline
	 */
	private static function makeHeadline( $level, $attribs, $anchor, $html,
										 $link, $fallbackAnchor = false
	) {
		$anchorEscaped = htmlspecialchars( $anchor, ENT_COMPAT );
		$fallback = '';
		if ( $fallbackAnchor !== false && $fallbackAnchor !== $anchor ) {
			$fallbackAnchor = htmlspecialchars( $fallbackAnchor, ENT_COMPAT );
			$fallback = "<span id=\"$fallbackAnchor\"></span>";
		}
		return "<h$level$attribs"
		. "$fallback<span class=\"mw-headline\" id=\"$anchorEscaped\">$html</span>"
		. $link
		. "</h$level>";
	}

	/* End of copy */