Manual:Upgrading

readOnlyPage; return; }

# Permission check if( !$wgUser->isAllowed( 'block' ) ) { $wgOut->permissionRequired( 'block' ); return; }

$ipb = new IPBlockForm( $par );

$action = $wgRequest->getVal( 'action' ); if ( 'success' == $action ) { $ipb->showSuccess; } else if ( $wgRequest->wasPosted && 'submit' == $action &&		$wgUser->matchEditToken( $wgRequest->getVal( 'wpEditToken' ) ) ) { $ipb->doSubmit; } else { $ipb->showForm( '' ); } }

/** * Form object for the Special:Blockip page. * * @ingroup SpecialPage */ class IPBlockForm { var $BlockAddress, $BlockExpiry, $BlockReason; // The maximum number of edits a user can have and still be hidden const HIDEUSER_CONTRIBLIMIT = 1000;
 * 1) 	var $BlockEmail;

function IPBlockForm( $par ) { global $wgRequest, $wgUser, $wgBlockAllowsUTEdit;

$this->BlockAddress = $wgRequest->getVal( 'wpBlockAddress', $wgRequest->getVal( 'ip', $par ) ); $this->BlockAddress = strtr( $this->BlockAddress, '_', ' ' ); $this->BlockReason = $wgRequest->getText( 'wpBlockReason' ); $this->BlockReasonList = $wgRequest->getText( 'wpBlockReasonList' ); $this->BlockExpiry = $wgRequest->getVal( 'wpBlockExpiry', wfMsg('ipbotheroption') ); $this->BlockOther = $wgRequest->getVal( 'wpBlockOther', '' );

# Unchecked checkboxes are not included in the form data at all, so having one # that is true by default is a bit tricky $byDefault = !$wgRequest->wasPosted; $this->BlockAnonOnly = $wgRequest->getBool( 'wpAnonOnly', $byDefault ); $this->BlockCreateAccount = $wgRequest->getBool( 'wpCreateAccount', $byDefault ); $this->BlockEnableAutoblock = $wgRequest->getBool( 'wpEnableAutoblock', $byDefault ); $this->BlockEmail = $wgRequest->getBool( 'wpEmailBan', false ); $this->BlockWatchUser = $wgRequest->getBool( 'wpWatchUser', false ); # Re-check user's rights to hide names, very serious, defaults to 0 $this->BlockHideName = ( $wgRequest->getBool( 'wpHideName', 0 ) && $wgUser->isAllowed( 'hideuser' ) ) ? 1 : 0;		$this->BlockAllowUsertalk = ( $wgRequest->getBool( 'wpAllowUsertalk', $byDefault ) && $wgBlockAllowsUTEdit ); $this->BlockReblock = $wgRequest->getBool( 'wpChangeBlock', false ); }

function showForm( $err ) { global $wgOut, $wgUser, $wgSysopUserBans;

$wgOut->setPagetitle( wfMsg( 'blockip' ) ); $wgOut->addWikiMsg( 'blockiptext' );

if($wgSysopUserBans) { $mIpaddress = Xml::label( wfMsg( 'ipadressorusername' ), 'mw-bi-target' ); } else { $mIpaddress = Xml::label( wfMsg( 'ipaddress' ), 'mw-bi-target' ); }		$mIpbexpiry = Xml::label( wfMsg( 'ipbexpiry' ), 'wpBlockExpiry' ); $mIpbother = Xml::label( wfMsg( 'ipbother' ), 'mw-bi-other' ); $mIpbreasonother = Xml::label( wfMsg( 'ipbreason' ), 'wpBlockReasonList' ); $mIpbreason = Xml::label( wfMsg( 'ipbotherreason' ), 'mw-bi-reason' );

$titleObj = SpecialPage::getTitleFor( 'Blockip' ); $user = User::newFromName( $this->BlockAddress ); $alreadyBlocked = false; if ( $err && $err[0] != 'ipb_already_blocked' ) { $key = array_shift($err); $msg = wfMsgReal($key, $err); $wgOut->setSubtitle( wfMsgHtml( 'formerror' ) ); $wgOut->addHTML( Xml::tags( 'p', array( 'class' => 'error' ), $msg ) ); } elseif ( $this->BlockAddress ) { $userId = 0; if ( is_object( $user ) ) $userId = $user->getId; $currentBlock = Block::newFromDB( $this->BlockAddress, $userId ); if ( !is_null($currentBlock) && !$currentBlock->mAuto && # The block exists and isn't an autoblock				( $currentBlock->mRangeStart == $currentBlock->mRangeEnd || # The block isn't a rangeblock # or if it is, the range is what we're about to block ( $currentBlock->mAddress == $this->BlockAddress ) ) ) { $wgOut->addWikiMsg( 'ipb-needreblock', $this->BlockAddress ); $alreadyBlocked = true; # Set the block form settings to the existing block $this->BlockAnonOnly = $currentBlock->mAnonOnly; $this->BlockCreateAccount = $currentBlock->mCreateAccount; $this->BlockEnableAutoblock = $currentBlock->mEnableAutoblock; $this->BlockEmail = $currentBlock->mBlockEmail; $this->BlockHideName = $currentBlock->mHideName; $this->BlockAllowUsertalk = $currentBlock->mAllowUsertalk; if( $currentBlock->mExpiry == 'infinity' ) { $this->BlockOther = 'indefinite'; } else { $this->BlockOther = wfTimestamp( TS_ISO_8601, $currentBlock->mExpiry ); }					$this->BlockReason = $currentBlock->mReason; }		}

$scBlockExpiryOptions = wfMsgForContent( 'ipboptions' );

$showblockoptions = $scBlockExpiryOptions != '-'; if (!$showblockoptions) $mIpbother = $mIpbexpiry;

$blockExpiryFormOptions = Xml::option( wfMsg( 'ipbotheroption' ), 'other' ); foreach (explode(',', $scBlockExpiryOptions) as $option) { if ( strpos($option, ":") === false ) $option = "$option:$option"; list($show, $value) = explode(":", $option); $show = htmlspecialchars($show); $value = htmlspecialchars($value); $blockExpiryFormOptions .= Xml::option( $show, $value, $this->BlockExpiry === $value ? true : false ). "\n"; }

$reasonDropDown = Xml::listDropDown( 'wpBlockReasonList',			wfMsgForContent( 'ipbreason-dropdown' ),			wfMsgForContent( 'ipbreasonotherlist' ), $this->BlockReasonList, 'wpBlockDropDown', 4 );

global $wgStylePath, $wgStyleVersion; $wgOut->addHTML(			Xml::tags( 'script', array( 'type' => 'text/javascript', 'src' => "$wgStylePath/common/block.js?$wgStyleVersion" ), '' ) .			Xml::openElement( 'form', array( 'method' => 'post', 'action' => $titleObj->getLocalURL( "action=submit" ), 'id' => 'blockip' ) ) .			Xml::openElement( 'fieldset' ) .			Xml::element( 'legend', null, wfMsg( 'blockip-legend' ) ) .			Xml::openElement( 'table', array ( 'border' => '0', 'id' => 'mw-blockip-table' ) ) .			" 									{$mIpaddress}				" .					Xml::input( 'wpBlockAddress', 45, $this->BlockAddress, array(							'tabindex' => '1',							'id' => 'mw-bi-target',							'onchange' => 'updateBlockOptions' ) ). "			 "		); if ( $showblockoptions ) { $wgOut->addHTML("									{$mIpbexpiry}				" .					Xml::tags( 'select', array(							'id' => 'wpBlockExpiry',							'name' => 'wpBlockExpiry',							'onchange' => 'considerChangingExpiryFocus',							'tabindex' => '2' ), $blockExpiryFormOptions ) .				" "			); }		$wgOut->addHTML("												{$mIpbother}				" .					Xml::input( 'wpBlockOther', 45, $this->BlockOther, array( 'tabindex' => '3', 'id' => 'mw-bi-other' ) ) . "									{$mIpbreasonother}									{$reasonDropDown}												{$mIpbreason}				" .					Xml::input( 'wpBlockReason', 45, $this->BlockReason, array( 'tabindex' => '5', 'id' => 'mw-bi-reason', 'maxlength'=> '200' ) ) . "							" .				Xml::checkLabel( wfMsg( 'ipbanononly' ), 'wpAnonOnly', 'wpAnonOnly', $this->BlockAnonOnly, array( 'tabindex' => '6' ) ) . "							<td class='mw-input'>" .					Xml::checkLabel( wfMsg( 'ipbcreateaccount' ), 'wpCreateAccount', 'wpCreateAccount', $this->BlockCreateAccount, array( 'tabindex' => '7' ) ) . "			<tr id='wpEnableAutoblockRow'>				<td class='mw-input'>" .					Xml::checkLabel( wfMsg( 'ipbenableautoblock' ), 'wpEnableAutoblock', 'wpEnableAutoblock', $this->BlockEnableAutoblock, array( 'tabindex' => '8' ) ) . "			 "		);

global $wgSysopEmailBans, $wgBlockAllowsUTEdit; if ( $wgSysopEmailBans && $wgUser->isAllowed( 'blockemail' ) ) { $wgOut->addHTML("				<tr id='wpEnableEmailBan'>					<td class='mw-input'>" .						Xml::checkLabel( wfMsg( 'ipbemailban' ), 'wpEmailBan', 'wpEmailBan', $this->BlockEmail, array( 'tabindex' => '9' )) . "				 "			); }

// Allow some users to hide name from block log, blocklist and listusers if ( $wgUser->isAllowed( 'hideuser' ) ) { $wgOut->addHTML("				<tr id='wpEnableHideUser'>					<td class='mw-input'> " .						Xml::checkLabel( wfMsg( 'ipbhidename' ), 'wpHideName', 'wpHideName', $this->BlockHideName, array( 'tabindex' => '10' ) ) . "				 "			); }		# Watchlist their user page? $wgOut->addHTML("			<tr id='wpEnableWatchUser'>				<td class='mw-input'>" .					Xml::checkLabel( wfMsg( 'ipbwatchuser' ), 'wpWatchUser', 'wpWatchUser', $this->BlockWatchUser, array( 'tabindex' => '11' ) ) . "			 "		); if( $wgBlockAllowsUTEdit ){ $wgOut->addHTML("				<tr id='wpAllowUsertalkRow'>					<td class='mw-input'>" .						Xml::checkLabel( wfMsg( 'ipballowusertalk' ), 'wpAllowUsertalk', 'wpAllowUsertalk', $this->BlockAllowUsertalk, array( 'tabindex' => '12' ) ) . "				 "			); }

$wgOut->addHTML("				<td style='padding-top: 1em'> 				<td class='mw-submit' style='padding-top: 1em'>" .					Xml::submitButton( wfMsg( $alreadyBlocked ? 'ipb-change-block' : 'ipbsubmit' ), array( 'name' => 'wpBlock', 'tabindex' => '13', 'accesskey' => 's' ) ) . "			 " .			Xml::closeElement( 'table' ) .			Xml::hidden( 'wpEditToken', $wgUser->editToken ) .			( $alreadyBlocked ? Xml::hidden( 'wpChangeBlock', 1 ) : "" ) .			Xml::closeElement( 'fieldset' ) .			Xml::closeElement( 'form' ) .			Xml::tags( 'script', array( 'type' => 'text/javascript' ), 'updateBlockOptions' ) . "\n"		);

$wgOut->addHTML( $this->getConvenienceLinks );

if( is_object( $user ) ) { $this->showLogFragment( $wgOut, $user->getUserPage ); } elseif( preg_match( '/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/', $this->BlockAddress ) ) { $this->showLogFragment( $wgOut, Title::makeTitle( NS_USER, $this->BlockAddress ) ); } elseif( preg_match( '/^\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}/', $this->BlockAddress ) ) { $this->showLogFragment( $wgOut, Title::makeTitle( NS_USER, $this->BlockAddress ) ); }	}

/**	 * Backend block code. * $userID and $expiry will be filled accordingly * @return array(message key, arguments) on failure, empty array on success */	function doBlock( &$userId = null, &$expiry = null ) { global $wgUser, $wgSysopUserBans, $wgSysopRangeBans, $wgBlockAllowsUTEdit;

$userId = 0; # Expand valid IPv6 addresses, usernames are left as is		$this->BlockAddress = IP::sanitizeIP( $this->BlockAddress ); # isIPv4 and IPv6 are used for final validation $rxIP4 = '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'; $rxIP6 = '\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}:\w{1,4}'; $rxIP = "($rxIP4|$rxIP6)";

# Check for invalid specifications if ( !preg_match( "/^$rxIP$/", $this->BlockAddress ) ) { $matches = array; if ( preg_match( "/^($rxIP4)\\/(\\d{1,2})$/", $this->BlockAddress, $matches ) ) { # IPv4 if ( $wgSysopRangeBans ) { if ( !IP::isIPv4( $this->BlockAddress ) || $matches[2] < 16 || $matches[2] > 32 ) { return array('ip_range_invalid'); }					$this->BlockAddress = Block::normaliseRange( $this->BlockAddress ); } else { # Range block illegal return array('range_block_disabled'); }			} else if ( preg_match( "/^($rxIP6)\\/(\\d{1,3})$/", $this->BlockAddress, $matches ) ) { # IPv6 if ( $wgSysopRangeBans ) { if ( !IP::isIPv6( $this->BlockAddress ) || $matches[2] < 64 || $matches[2] > 128 ) { return array('ip_range_invalid'); }					$this->BlockAddress = Block::normaliseRange( $this->BlockAddress ); } else { # Range block illegal return array('range_block_disabled'); }			} else { # Username block if ( $wgSysopUserBans ) { $user = User::newFromName( $this->BlockAddress ); if( !is_null( $user ) && $user->getId ) { # Use canonical name $userId = $user->getId; $this->BlockAddress = $user->getName; } else { return array('nosuchusershort', htmlspecialchars( $user ? $user->getName : $this->BlockAddress ) ); }				} else { return array('badipaddress'); }			}		}

if ( $wgUser->isBlocked && ( $wgUser->getId !== $userId ) ) { return array( 'cant-block-while-blocked' ); }

$reasonstr = $this->BlockReasonList; if ( $reasonstr != 'other' && $this->BlockReason != '' ) { // Entry from drop down menu + additional comment $reasonstr .= wfMsgForContent( 'colon-separator' ). $this->BlockReason; } elseif ( $reasonstr == 'other' ) { $reasonstr = $this->BlockReason; }

$expirestr = $this->BlockExpiry; if( $expirestr == 'other' ) $expirestr = $this->BlockOther;

if ( ( strlen( $expirestr ) == 0) || ( strlen( $expirestr ) > 50) ) { return array('ipb_expiry_invalid'); }		if ( false === ($expiry = Block::parseExpiryInput( $expirestr )) ) { // Bad expiry. return array('ipb_expiry_invalid'); }		if( $this->BlockHideName ) { if( !$userId ) { // IP users should not be hidden $this->BlockHideName = false; } else if( $expiry !== 'infinity' ) { // Bad expiry. return array('ipb_expiry_temp'); } else if( User::edits($userId) > self::HIDEUSER_CONTRIBLIMIT ) { // Typically, the user should have a handful of edits. // Disallow hiding users with many edits for performance. return array('ipb_hide_invalid'); }		}

# Create block # Note: for a user block, ipb_address is only for display purposes $block = new Block( $this->BlockAddress, $userId, $wgUser->getId,			$reasonstr, wfTimestampNow, 0, $expiry, $this->BlockAnonOnly,			$this->BlockCreateAccount, $this->BlockEnableAutoblock, $this->BlockHideName,			$this->BlockEmail, isset( $this->BlockAllowUsertalk ) ? $this->BlockAllowUsertalk : $wgBlockAllowsUTEdit		);

# Should this be privately logged? $suppressLog = (bool)$this->BlockHideName; if ( wfRunHooks('BlockIp', array(&$block, &$wgUser)) ) { # Try to insert block. Is there a conflicting block? if ( !$block->insert ) { # Show form unless the user is already aware of this... if ( !$this->BlockReblock ) { return array( 'ipb_already_blocked' ); # Otherwise, try to update the block... } else { # This returns direct blocks before autoblocks/rangeblocks, since we should # be sure the user is blocked by now it should work for our purposes $currentBlock = Block::newFromDB( $this->BlockAddress, $userId ); if( $block->equals( $currentBlock ) ) { return array( 'ipb_already_blocked' ); }					# If the name was hidden and the blocking user cannot hide # names, then don't allow any block changes... if( $currentBlock->mHideName && !$wgUser->isAllowed('hideuser') ) { return array( 'hookaborted' ); }					$currentBlock->delete; $block->insert; # If hiding/unhiding a name, this should go in the private logs $suppressLog = $suppressLog || (bool)$currentBlock->mHideName; $log_action = 'reblock'; # Unset _deleted fields if requested if( $currentBlock->mHideName && !$this->BlockHideName ) { self::unsuppressUserName( $this->BlockAddress, $userId ); }				}			} else { $log_action = 'block'; }			wfRunHooks('BlockIpComplete', array($block, $wgUser));

# Set *_deleted fields if requested if( $this->BlockHideName ) { self::suppressUserName( $this->BlockAddress, $userId ); }

if ( $this->BlockWatchUser &&				# Only show watch link when this is no range block				$block->mRangeStart == $block->mRangeEnd) { $wgUser->addWatch ( Title::makeTitle( NS_USER, $this->BlockAddress ) ); }			# Block constructor sanitizes certain block options on insert $this->BlockEmail = $block->mBlockEmail; $this->BlockEnableAutoblock = $block->mEnableAutoblock;

# Prepare log parameters $logParams = array; $logParams[] = $expirestr; $logParams[] = $this->blockLogFlags;

# Make log entry, if the name is hidden, put it in the oversight log $log_type = $suppressLog ? 'suppress' : 'block'; $log = new LogPage( $log_type ); $log->addEntry( $log_action, Title::makeTitle( NS_USER, $this->BlockAddress ),			 $reasonstr, $logParams );

# Report to the user return array; } else { return array('hookaborted'); }	}	public static function suppressUserName( $name, $userId ) { $op = '|'; // bitwise OR		return self::setUsernameBitfields( $name, $userId, $op ); }	public static function unsuppressUserName( $name, $userId ) { $op = '&'; // bitwise AND return self::setUsernameBitfields( $name, $userId, $op ); }	private static function setUsernameBitfields( $name, $userId, $op ) { if( $op !== '|' && $op !== '&' ) return false; // sanity check $dbw = wfGetDB( DB_MASTER ); $delUser = Revision::DELETED_USER | Revision::DELETED_RESTRICTED; $delAction = LogPage::DELETED_ACTION | Revision::DELETED_RESTRICTED; # Normalize user name $userTitle = Title::makeTitleSafe( NS_USER, $name ); $userDbKey = $userTitle->getDBKey; # To suppress, we OR the current bitfields with Revision::DELETED_USER # to put a 1 in the username *_deleted bit. To unsuppress we AND the # current bitfields with the inverse of Revision::DELETED_USER. The # username bit is made to 0 (x & 0 = 0), while others are unchanged (x & 1 = x). # The same goes for the sysop-restricted *_deleted bit. if( $op == '&' ) { $delUser = "~{$delUser}"; $delAction = "~{$delAction}"; }		# Hide name from live edits $dbw->update( 'revision', array("rev_deleted = rev_deleted $op $delUser"),			array('rev_user' => $userId), __METHOD__ ); # Hide name from deleted edits $dbw->update( 'archive', array("ar_deleted = ar_deleted $op $delUser"),			array('ar_user_text' => $name), __METHOD__ ); # Hide name from logs $dbw->update( 'logging', array("log_deleted = log_deleted $op $delUser"),			array('log_user' => $userId, "log_type != 'suppress'"), __METHOD__ ); $dbw->update( 'logging', array("log_deleted = log_deleted $op $delAction"),			array('log_namespace' => NS_USER, 'log_title' => $userDbKey, "log_type != 'suppress'"), __METHOD__ ); # Hide name from RC		$dbw->update( 'recentchanges', array("rc_deleted = rc_deleted $op $delUser"),			array('rc_user_text' => $name), __METHOD__ ); # Hide name from live images $dbw->update( 'oldimage', array("oi_deleted = oi_deleted $op $delUser"),			array('oi_user_text' => $name), __METHOD__ ); # Hide name from deleted images # WMF - schema change pending # $dbw->update( 'filearchive', array("fa_deleted = fa_deleted $op $delUser"),		#	array('fa_user_text' => $name), __METHOD__ ); # Done! return true; }

/**	 * UI entry point for blocking * Wraps around doBlock */	function doSubmit {		global $wgOut; $retval = $this->doBlock; if(empty($retval)) { $titleObj = SpecialPage::getTitleFor( 'Blockip' ); $wgOut->redirect( $titleObj->getFullURL( 'action=success&ip='. urlencode( $this->BlockAddress ) ) ); return; }		$this->showForm( $retval ); }

function showSuccess { global $wgOut;

$wgOut->setPagetitle( wfMsg( 'blockip' ) ); $wgOut->setSubtitle( wfMsg( 'blockipsuccesssub' ) ); $text = wfMsgExt( 'blockipsuccesstext', array( 'parse' ), $this->BlockAddress ); $wgOut->addHTML( $text ); }

function showLogFragment( $out, $title ) { global $wgUser; $out->addHTML( Xml::element( 'h2', NULL, LogPage::logName( 'block' ) ) ); $count = LogEventsList::showLogExtract( $out, 'block', $title->getPrefixedText, '', 10 ); if($count > 10){ $out->addHTML( $wgUser->getSkin->link( SpecialPage::getTitleFor( 'Log' ), wfMsgHtml( 'blocklog-fulllog' ), array, array(					'type' => 'block',					'page' => $title->getPrefixedText ) ) ); }	}

/**	 * Return a comma-delimited list of "flags" to be passed to the log * reader for this block, to provide more information in the logs *	 * @return array */	private function blockLogFlags { global $wgBlockAllowsUTEdit; $flags = array; if( $this->BlockAnonOnly && IP::isIPAddress( $this->BlockAddress ) ) // when blocking a user the option 'anononly' is not available/has no effect -> do not write this into log $flags[] = 'anononly'; if( $this->BlockCreateAccount ) $flags[] = 'nocreate'; if( !$this->BlockEnableAutoblock ) $flags[] = 'noautoblock'; if ( $this->BlockEmail ) $flags[] = 'noemail'; if ( !$this->BlockAllowUsertalk && $wgBlockAllowsUTEdit ) $flags[] = 'nousertalk'; if ( $this->BlockHideName ) $flags[] = 'hiddenname'; return implode( ',', $flags ); }

/**	 * Builds unblock and block list links *	 * @return string */	private function getConvenienceLinks { global $wgUser, $wgLang; $skin = $wgUser->getSkin; if( $this->BlockAddress ) $links[] = $this->getContribsLink( $skin ); $links[] = $this->getUnblockLink( $skin ); $links[] = $this->getBlockListLink( $skin ); $links[] = $skin->makeLink ( 'MediaWiki:Ipbreason-dropdown', wfMsgHtml( 'ipb-edit-dropdown' ) ); return '<p class="mw-ipb-conveniencelinks">'. $wgLang->pipeList( $links ). ' ';	}	/**	 * Build a convenient link to a user or IP's contribs * form *	 * @param $skin Skin to use * @return string */	private function getContribsLink( $skin ) { $contribsPage = SpecialPage::getTitleFor( 'Contributions', $this->BlockAddress ); return $skin->link( $contribsPage, wfMsgExt( 'ipb-blocklist-contribs', 'escape', $this->BlockAddress ) ); }

/**	 * Build a convenient link to unblock the given username or IP	 * address, if available; otherwise link to a blank unblock * form *	 * @param $skin Skin to use * @return string */	private function getUnblockLink( $skin ) { $list = SpecialPage::getTitleFor( 'Ipblocklist' ); if( $this->BlockAddress ) { $addr = htmlspecialchars( strtr( $this->BlockAddress, '_', ' ' ) ); return $skin->makeKnownLinkObj( $list, wfMsgHtml( 'ipb-unblock-addr', $addr ),				'action=unblock&ip=' . urlencode( $this->BlockAddress ) ); } else { return $skin->makeKnownLinkObj( $list, wfMsgHtml( 'ipb-unblock' ),	'action=unblock' ); }	}

/**	 * Build a convenience link to the block list *	 * @param $skin Skin to use * @return string */	private function getBlockListLink( $skin ) { $list = SpecialPage::getTitleFor( 'Ipblocklist' ); if( $this->BlockAddress ) { $addr = htmlspecialchars( strtr( $this->BlockAddress, '_', ' ' ) ); return $skin->makeKnownLinkObj( $list, wfMsgHtml( 'ipb-blocklist-addr', $addr ),				'ip=' . urlencode( $this->BlockAddress ) ); } else { return $skin->makeKnownLinkObj( $list, wfMsgHtml( 'ipb-blocklist' ) ); }	}	/**	* Block a list of selected users * @param array $users * @param string $reason * @param string $tag replaces user pages * @param string $talkTag replaces user talk pages * @returns array, list of html-safe usernames */	public static function doMassUserBlock( $users, $reason = , $tag = , $talkTag = '' ) { global $wgUser; $counter = $blockSize = 0; $safeUsers = array; $log = new LogPage( 'block' ); foreach( $users as $name ) { # Enforce limits $counter++; $blockSize++; # Lets not go *too* fast if( $blockSize >= 20 ) { $blockSize = 0; wfWaitForSlaves( 5 ); }			$u = User::newFromName( $name, false ); // If user doesn't exist, it ought to be an IP then if( is_null($u) || (!$u->getId && !IP::isIPAddress( $u->getName )) ) { continue; }			$userTitle = $u->getUserPage; $userTalkTitle = $u->getTalkPage; $userpage = new Article( $userTitle ); $usertalk = new Article( $userTalkTitle ); $safeUsers[] =  . $userTitle->getText . ; $expirestr = $u->getId ? 'indefinite' : '1 week'; $expiry = Block::parseExpiryInput( $expirestr ); $anonOnly = IP::isIPAddress( $u->getName ) ? 1 : 0;			// Create the block $block = new Block( $u->getName, // victim				$u->getId, // uid				$wgUser->getId, // blocker				$reason, // comment				wfTimestampNow, // block time				0, // auto ?				$expiry, // duration				$anonOnly, // anononly?				1, // block account creation?				1, // autoblocking?				0, // suppress name?				0 // block from sending email?			); $oldblock = Block::newFromDB( $u->getName, $u->getId ); if( !$oldblock ) { $block->insert; # Prepare log parameters $logParams = array; $logParams[] = $expirestr; if( $anonOnly ) { $logParams[] = 'anononly'; }				$logParams[] = 'nocreate'; # Add log entry $log->addEntry( 'block', $userTitle, $reason, $logParams ); }			# Tag userpage! (check length to avoid mistakes) if( strlen($tag) > 2 ) { $userpage->doEdit( $tag, $reason, EDIT_MINOR ); }			if( strlen($talkTag) > 2 ) { $usertalk->doEdit( $talkTag, $reason, EDIT_MINOR ); }		}		return $safeUsers; } }

<div style="border:1px solid #f3f3ff; padding-left: 0.5em !important; background-color: #f3f3ff; border-width: 0 0 0 1.4em; clear:right; float:right;">

Overview
First read the UPGRADE text file included in MediaWiki.


 * 1) Check the requirements
 * 2) Read the release notes
 * 3) Back up existing files and the database
 * 4) Unpack the new files
 * 5) Run the update script to check the database
 * 6) Upgrade extensions
 * 7) Test the update

Check requirements
MediaWiki requires:

PHP

 * PHP 5.0.x or later
 * We recommend using PHP 5.1.x or 5.2.x.
 * Note that PHP 5.0.x under certain 64-bit platforms contains bugs which cause fundamental problems with MediaWiki. If running on an x86_64 (AMD64, EMT64) or other affected 64-bit platform, PHP 5.1.x or 5.2.x is required.

DBMS

 * MySQL 4.0.x or later
 * Version 3.x has not been supported since 1.6.0

or


 * PostgreSQL 8.1 or later
 * You will also require the <tt>tsearch2</tt> and <tt>plpgsql</tt> modules, if running Postgres 8.1 or 8.2
 * see also Upgrading Postgres

Read the release notes
Within the distribution tarball, or within the files checked out/exported from SVN, there are a number of files with capitalised filenames, one of which contains the RELEASE-NOTES. Now's the time to open it up and find out what's been changed in this release.

Back up existing files and the database
While the upgrade scripts are well-maintained and robust, things could still go pear-shaped. Before proceeding to update the database schema, make a full backup of the wiki, including both the database and the files:


 * the wiki's content, from the database. It may be a good idea to create an XML dump in addition to the SQL database dump.
 * MySQL:
 * text dump: <tt>mysqldump --all-databases > file.txt</tt>
 * xml dump: <tt>mysqldump --all-databases --xml > file.xml</tt>
 * PostgreSQL:
 * output for pg_restore: <tt>pg_dump --create -Fc -i yourwiki > yourwiki.db.dump</tt>
 * images and other media files (the contents of the <tt>images</tt> directory)
 * configuration files, i.e. <tt>LocalSettings.php</tt> and <tt>AdminSettings.php</tt> (if present)
 * MediaWiki's program files, including all skins and extensions, especially if you modified them.

See Manual:Backing up a wiki for instructions.

Using a tarball package
If using a tarball package, decompress the file on the server where the old version of mediawiki is running. When untarring a tarball package normally a new directory for the new wiki version will be created and you will have to copy the new files and directories to your current wiki directory. To instead replace the existing files while decompressing and do this in one go:

$ tar xvfz mediawiki-.tar.gz -C /path/to/your/wiki/ --strip-components=1

On older Linux distributions (f.e. RHEL/CentOS 4.x) use:

$ tar xvfz mediawiki-.tar.gz -C /path/to/your/wiki/ --strip-path=1

For those people using an old version of tar (eg. GNU tar 1.13.x) which do not have --strip-components or --strip-path flags can uncompress the archive without the -C option and thereafter recursively copy the contents of the latest package into the previous wiki directory structure, as follows:

$ tar xvfz mediawiki-.tar.gz $ cp -r mediawiki-/* /path/to/your/wiki/

(Open)Solaris users should use gtar, or:

$ gzip -dc mediawiki-.tar.gz | tar xf -

Using Subversion
If using Subversion, export the files into a clean location. Replace all existing files with the new versions, preserving the directory structure. The core code is now up to date.

Run the update script
Note: Make sure there's an <tt>AdminSettings.php</tt> file in the wiki root, and it contains up to date information. The user needs full permissions (shell access) on the database.

The preferred method of performing the update is using the command-line maintenance script, although it is also possible to re-run the web-based installer.

Shell
From the command line, or an SSH shell or similar, change to the <tt>maintenance</tt> directory and execute the update script:

$ php update.php --aconf ../AdminSettings.php

(Note for simple installations on Windows (e. g. with XAMPP): You can run the update.php by executing it (doubleclick) when your .php-files are associated with the PHP.exe (if not, rightclick update.php, select Open With and browse to PHP.exe). Ensure that your server-application (e. g. Apache) and your database (e. g. MySQL) are running).

MediaWiki will inspect the existing schema and update it to work with the new code, adding tables and columns as needed.

What to do in case of "No superuser credentials" error
In case the scripts abort with a message:

"No superuser credentials could be found. Please provide the details of a user with appropriate permissions to update the database. See AdminSettings.sample for more details"

This means that you should check that you have defined $wgDBadminuser and $wgDBadminpassword in your LocalSettings.php file (in the main directory). These are the user and password needed by this script in order to access to the database. See the example in the AdminSettings.sample file; you can copy that snippet in the LocalSettings.php file and change the user/password for your own database.

Alternative 1: phpShell
If full permissions/shell access is not available, look at options such as phpShell, which emulates a command prompt using PHP functions. This might not work on some locked-down hosting setups.

When using such a solution, it is often desirable to skip the countdown period at the start of the script. Pass the <tt>--quick</tt> option to the update script to do so:

$ php update.php --quick

You may have to use "php5" instead of "php" if different versions are installed. For phpshell, the commandLine.inc and LocalSettings.php files may have to be edited so the script doesn't quit because phpshell is a run from the web server. It may instead be possible to sidestep this problem by running:

$ unset REQUEST_METHOD; php update.php --quick

This unsets the REQUEST_METHOD environment variable, the existence of which causes the script to quit, and then executes the script.

Alternative 2: Re-run the installer
An alternative to running the update script from the shell is to re-run the web installer. To do this, follow these steps:


 * 1) Rename <tt>LocalSettings.php</tt> to <tt>LocalSettings.old.php</tt>
 * 2) Make the <tt>config</tt> directory writable by the web server
 * 3) Make sure the database user has full rights to the database
 * 4) Browse to the wiki and start the installer
 * Fill in the form fields with the same values as was done during the previous version's installation.
 * When the installation process starts, the script will detect existing MediaWiki tables, and upgrade their schema.
 * When this is complete, a new <tt>LocalSettings.php</tt> will be generated.
 * 1) Delete the new configuration file (<tt>LocalSettings.php</tt>)
 * 2) Rename <tt>LocalSettings.old.php</tt> back to <tt>LocalSettings.php</tt>
 * 3) Restore permissions on the <tt>config</tt> directory

Upgrade extensions
Certain extensions have been updated in order to work with the new version of MediaWiki. Be sure to upgrade to the latest versions of such extensions. You might need to perform manual updates to custom extensions.

Test the update
Once the upgrade has completed, browse to the wiki and check basics such as viewing and editing pages to ensure things still work as expected.

Visit Special:Version and check that the version shown is correct.