tbd @

MW 1.45.3

maintained by us

(Gareth, Phex)
Extension:MultiCategorySearch
Code
Extension:Progressbar
Code
Feature by Stip, /resources/renderUserAgreement.js
(adds a "decline" button, which when pressed will logout the account)
search replace
					useragreementHtml =
					logoutButton = new OO.ui.ButtonInputWidget( {
						id: 'uaLogout',
						label: mw.msg( 'useragreement-logout-message' ),
						icon: 'cancel'
					} ),
					useragreementHtml =
							console.error( '[UserAgreement] Failed to accept user agreement for the current user.' );
							console.error( '[UserAgreement] Failed to accept user agreement for the current user.' );
						} );
					} );
					$( '#uaAcceptInput' ).append( logoutButton.$element );
					logoutButton.$input.on( 'click', () => {
						const api2 = new mw.Api();
						api2.post( {
							action: 'logout',
							token: mw.user.tokens.get( 'csrfToken' )
						} ).done( ( /* data */ ) => {
							location.reload( true );
						} ).fail( ( /* data */ ) => {
							// eslint-disable-next-line no-console
							console.error( '[UserAgreement] Failed to logout for the current user.' );
Feature by Stip, extension.json
(declares new system message)
search replace
				"useragreement-dialog-message"
				"useragreement-dialog-message",
				"useragreement-logout-message"
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
@color-success #0000ff
@border-color-success #0000ff
Bugfix, /includes/specials/SpecialPatroller.php
(new MediaWiki code structure)
search replace
use MediaWiki\Revision\SlotRecord;
use MediaWiki\Revision\SlotRecord;
use MediaWiki\Html\Html;
doMarkPatrolled(line 66 and 403) MarkPatrolled
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;
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, /pygments/pygmentize
(enables extension, see MediaWiki)

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

Bugfix by Stip
(restored functionality for h2 headings after Heading HTML changes)

/modules/CollapsibleSections.js

search replace
		$headings = $( $mwContentsBlock ).find( mw.config.get( 'wgCollapsibleSectionsTag' ) );
		$headings = $( $mwContentsBlock ).find( '.mw-heading2' );
		if ( $( element ).find( 'span.mw-headline' ).length ) {
			new mw.CollapsibleSection( element );
		}
			new mw.CollapsibleSection( element );

/modules/CollapsibleSections.Section.js

search replace
		const currentHeading = parseInt( mw.config.get( 'wgCollapsibleSectionsTag' ).charAt( 1 ) );
		// Find and wrap all contents until next section (any heading!)
		// eslint-disable-next-line no-jquery/variable-pattern
		elements = this.$element.nextUntil( function () {
			return parseInt( this.tagName.charAt( 1 ) ) <= currentHeading;
		} );
		// Find and wrap all contents until next section (any heading!)
		// eslint-disable-next-line no-jquery/variable-pattern
		elements = this.$element.nextUntil( '.mw-heading2' );
Bugfix, /src/NamespaceRelations.php
(new MediaWiki code structure, see Gerrit)
search replace
use MediaWiki\MediaWikiServices;
use MediaWiki\MediaWikiServices;
use MediaWiki\Title\Title;
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, /src/MediaFunctions.php
(new MediaWiki code structure, see Gerrit)
search replace
use MediaWiki\MediaWikiServices;
use MediaWiki\MediaWikiServices;
use MediaWiki\Title\Title;
Bugfix, /includes/specials/SpecialFavoritelist.php
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. See gerrit and github
search replace
	private function buildRemoveList( $user ) {
		$list = '';
		$toc = Linker::tocIndent();
		$tocLength = 0;

		$favorites = FavoriteListInfo::getForUser( $user, [ 'ignoreTalkNS' => true ] );

		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' => true ] );

		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;
	}
	
	/**
	 * Add another level to the Table of Contents
	 *
	 * Copied from HandleTOCMarkers::tocIndent(), which is unfortunately private.
	 *
	 * @return string
	 */
	private static function tocIndent() {
		return "\n<ul>\n";
	}
	
	/**
	 * parameter level defines if we are on an indentation level
	 *
	 * Copied from HandleTOCMarkers::tocLine(), which is unfortunately private.
	 *
	 * @param string $linkAnchor Identifier
	 * @param string $tocline Properly escaped HTML
	 * @param string $tocnumber Unescaped text
	 * @param int $level
	 * @param string|false $sectionIndex
	 * @return string
	 */
	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.
	 *
	 * Copied from HandleTOCMarkers::tocLineEnd(), which is unfortunately private.
	 *
	 * @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.
	 *
	 * Copied from HandleTOCMarkers::tocList(), which is unfortunately private.
	 *
	 * @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";
	}
    
	/**
	 * Linker::makeHeadLine was deprecated in 1.43
	 * Code restored from from 1.42.5, https://github.com/wikimedia/mediawiki/blob/REL1_42/includes/linker/Linker.php :: 1956)
	 */

	/**
	 * 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>";
	}
Bugfix, /includes/FavoritelistEditor.php
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. See gerrit and github
search replace
	private function buildRemoveList( $user ) {
		$list = '';
		$toc = Linker::tocIndent();
		$tocLength = 0;

		$favorites = FavoriteListInfo::getForUser( $user );

		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 );

		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;
	}


	/**
	 * Add another level to the Table of Contents
	 *
	 * Copied from HandleTOCMarkers::tocIndent(), which is unfortunately private.
	 *
	 * @return string
	 */
	private static function tocIndent() {
		return "\n<ul>\n";
	}

	/**
	 * parameter level defines if we are on an indentation level
	 *
	 * Copied from HandleTOCMarkers::tocLine(), which is unfortunately private.
	 *
	 * @param string $linkAnchor Identifier
	 * @param string $tocline Properly escaped HTML
	 * @param string $tocnumber Unescaped text
	 * @param int $level
	 * @param string|false $sectionIndex
	 * @return string
	 */
	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.
	 *
	 * Copied from HandleTOCMarkers::tocLineEnd(), which is unfortunately private.
	 *
	 * @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.
	 *
	 * Copied from HandleTOCMarkers::tocList(), which is unfortunately private.
	 *
	 * @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";
	}

	/**
	 * Linker::makeHeadLine was deprecated in 1.43
	 * Code restored from from 1.42.5, https://github.com/wikimedia/mediawiki/blob/REL1_42/includes/linker/Linker.php :: 1956)
	 */

	/**
	 * 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>";
	}