X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2Fspecials%2FSpecialUndelete.php;h=740207d6b9bc8b7852621359bb883632fbeeb407;hb=89539f2aa1b158fdcc703ad053e2580cb97a6385;hp=d5c24c2722a08549afe06435d8d785164a483a22;hpb=576a39f6fac711e0d0a39f2c74825e5d5334b56e;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/specials/SpecialUndelete.php b/includes/specials/SpecialUndelete.php index d5c24c2722..740207d6b9 100644 --- a/includes/specials/SpecialUndelete.php +++ b/includes/specials/SpecialUndelete.php @@ -20,730 +20,8 @@ * @file * @ingroup SpecialPage */ -use MediaWiki\MediaWikiServices; -/** - * Used to show archived pages and eventually restore them. - * - * @ingroup SpecialPage - */ -class PageArchive { - /** @var Title */ - protected $title; - - /** @var Status */ - protected $fileStatus; - - /** @var Status */ - protected $revisionStatus; - - /** @var Config */ - protected $config; - - function __construct( $title, Config $config = null ) { - if ( is_null( $title ) ) { - throw new MWException( __METHOD__ . ' given a null title.' ); - } - $this->title = $title; - if ( $config === null ) { - wfDebug( __METHOD__ . ' did not have a Config object passed to it' ); - $config = MediaWikiServices::getInstance()->getMainConfig(); - } - $this->config = $config; - } - - public function doesWrites() { - return true; - } - - /** - * List all deleted pages recorded in the archive table. Returns result - * wrapper with (ar_namespace, ar_title, count) fields, ordered by page - * namespace/title. - * - * @return ResultWrapper - */ - public static function listAllPages() { - $dbr = wfGetDB( DB_REPLICA ); - - return self::listPages( $dbr, '' ); - } - - /** - * List deleted pages recorded in the archive table matching the - * given title prefix. - * Returns result wrapper with (ar_namespace, ar_title, count) fields. - * - * @param string $prefix Title prefix - * @return ResultWrapper - */ - public static function listPagesByPrefix( $prefix ) { - $dbr = wfGetDB( DB_REPLICA ); - - $title = Title::newFromText( $prefix ); - if ( $title ) { - $ns = $title->getNamespace(); - $prefix = $title->getDBkey(); - } else { - // Prolly won't work too good - // @todo handle bare namespace names cleanly? - $ns = 0; - } - - $conds = [ - 'ar_namespace' => $ns, - 'ar_title' . $dbr->buildLike( $prefix, $dbr->anyString() ), - ]; - - return self::listPages( $dbr, $conds ); - } - - /** - * @param IDatabase $dbr - * @param string|array $condition - * @return bool|ResultWrapper - */ - protected static function listPages( $dbr, $condition ) { - return $dbr->select( - [ 'archive' ], - [ - 'ar_namespace', - 'ar_title', - 'count' => 'COUNT(*)' - ], - $condition, - __METHOD__, - [ - 'GROUP BY' => [ 'ar_namespace', 'ar_title' ], - 'ORDER BY' => [ 'ar_namespace', 'ar_title' ], - 'LIMIT' => 100, - ] - ); - } - - /** - * List the revisions of the given page. Returns result wrapper with - * (ar_minor_edit, ar_timestamp, ar_user, ar_user_text, ar_comment) fields. - * - * @return ResultWrapper - */ - function listRevisions() { - $dbr = wfGetDB( DB_REPLICA ); - - $tables = [ 'archive' ]; - - $fields = [ - 'ar_minor_edit', 'ar_timestamp', 'ar_user', 'ar_user_text', - 'ar_comment', 'ar_len', 'ar_deleted', 'ar_rev_id', 'ar_sha1', - ]; - - if ( $this->config->get( 'ContentHandlerUseDB' ) ) { - $fields[] = 'ar_content_format'; - $fields[] = 'ar_content_model'; - } - - $conds = [ 'ar_namespace' => $this->title->getNamespace(), - 'ar_title' => $this->title->getDBkey() ]; - - $options = [ 'ORDER BY' => 'ar_timestamp DESC' ]; - - $join_conds = []; - - ChangeTags::modifyDisplayQuery( - $tables, - $fields, - $conds, - $join_conds, - $options, - '' - ); - - return $dbr->select( $tables, - $fields, - $conds, - __METHOD__, - $options, - $join_conds - ); - } - - /** - * List the deleted file revisions for this page, if it's a file page. - * Returns a result wrapper with various filearchive fields, or null - * if not a file page. - * - * @return ResultWrapper - * @todo Does this belong in Image for fuller encapsulation? - */ - function listFiles() { - if ( $this->title->getNamespace() != NS_FILE ) { - return null; - } - - $dbr = wfGetDB( DB_REPLICA ); - return $dbr->select( - 'filearchive', - ArchivedFile::selectFields(), - [ 'fa_name' => $this->title->getDBkey() ], - __METHOD__, - [ 'ORDER BY' => 'fa_timestamp DESC' ] - ); - } - - /** - * Return a Revision object containing data for the deleted revision. - * Note that the result *may* or *may not* have a null page ID. - * - * @param string $timestamp - * @return Revision|null - */ - function getRevision( $timestamp ) { - $dbr = wfGetDB( DB_REPLICA ); - - $fields = [ - 'ar_rev_id', - 'ar_text', - 'ar_comment', - 'ar_user', - 'ar_user_text', - 'ar_timestamp', - 'ar_minor_edit', - 'ar_flags', - 'ar_text_id', - 'ar_deleted', - 'ar_len', - 'ar_sha1', - ]; - - if ( $this->config->get( 'ContentHandlerUseDB' ) ) { - $fields[] = 'ar_content_format'; - $fields[] = 'ar_content_model'; - } - - $row = $dbr->selectRow( 'archive', - $fields, - [ 'ar_namespace' => $this->title->getNamespace(), - 'ar_title' => $this->title->getDBkey(), - 'ar_timestamp' => $dbr->timestamp( $timestamp ) ], - __METHOD__ ); - - if ( $row ) { - return Revision::newFromArchiveRow( $row, [ 'title' => $this->title ] ); - } - - return null; - } - - /** - * Return the most-previous revision, either live or deleted, against - * the deleted revision given by timestamp. - * - * May produce unexpected results in case of history merges or other - * unusual time issues. - * - * @param string $timestamp - * @return Revision|null Null when there is no previous revision - */ - function getPreviousRevision( $timestamp ) { - $dbr = wfGetDB( DB_REPLICA ); - - // Check the previous deleted revision... - $row = $dbr->selectRow( 'archive', - 'ar_timestamp', - [ 'ar_namespace' => $this->title->getNamespace(), - 'ar_title' => $this->title->getDBkey(), - 'ar_timestamp < ' . - $dbr->addQuotes( $dbr->timestamp( $timestamp ) ) ], - __METHOD__, - [ - 'ORDER BY' => 'ar_timestamp DESC', - 'LIMIT' => 1 ] ); - $prevDeleted = $row ? wfTimestamp( TS_MW, $row->ar_timestamp ) : false; - - $row = $dbr->selectRow( [ 'page', 'revision' ], - [ 'rev_id', 'rev_timestamp' ], - [ - 'page_namespace' => $this->title->getNamespace(), - 'page_title' => $this->title->getDBkey(), - 'page_id = rev_page', - 'rev_timestamp < ' . - $dbr->addQuotes( $dbr->timestamp( $timestamp ) ) ], - __METHOD__, - [ - 'ORDER BY' => 'rev_timestamp DESC', - 'LIMIT' => 1 ] ); - $prevLive = $row ? wfTimestamp( TS_MW, $row->rev_timestamp ) : false; - $prevLiveId = $row ? intval( $row->rev_id ) : null; - - if ( $prevLive && $prevLive > $prevDeleted ) { - // Most prior revision was live - return Revision::newFromId( $prevLiveId ); - } elseif ( $prevDeleted ) { - // Most prior revision was deleted - return $this->getRevision( $prevDeleted ); - } - - // No prior revision on this page. - return null; - } - - /** - * Get the text from an archive row containing ar_text, ar_flags and ar_text_id - * - * @param object $row Database row - * @return string - */ - function getTextFromRow( $row ) { - if ( is_null( $row->ar_text_id ) ) { - // An old row from MediaWiki 1.4 or previous. - // Text is embedded in this row in classic compression format. - return Revision::getRevisionText( $row, 'ar_' ); - } - - // New-style: keyed to the text storage backend. - $dbr = wfGetDB( DB_REPLICA ); - $text = $dbr->selectRow( 'text', - [ 'old_text', 'old_flags' ], - [ 'old_id' => $row->ar_text_id ], - __METHOD__ ); - - return Revision::getRevisionText( $text ); - } - - /** - * Fetch (and decompress if necessary) the stored text of the most - * recently edited deleted revision of the page. - * - * If there are no archived revisions for the page, returns NULL. - * - * @return string|null - */ - function getLastRevisionText() { - $dbr = wfGetDB( DB_REPLICA ); - $row = $dbr->selectRow( 'archive', - [ 'ar_text', 'ar_flags', 'ar_text_id' ], - [ 'ar_namespace' => $this->title->getNamespace(), - 'ar_title' => $this->title->getDBkey() ], - __METHOD__, - [ 'ORDER BY' => 'ar_timestamp DESC' ] ); - - if ( $row ) { - return $this->getTextFromRow( $row ); - } - - return null; - } - - /** - * Quick check if any archived revisions are present for the page. - * - * @return bool - */ - function isDeleted() { - $dbr = wfGetDB( DB_REPLICA ); - $n = $dbr->selectField( 'archive', 'COUNT(ar_title)', - [ 'ar_namespace' => $this->title->getNamespace(), - 'ar_title' => $this->title->getDBkey() ], - __METHOD__ - ); - - return ( $n > 0 ); - } - - /** - * Restore the given (or all) text and file revisions for the page. - * Once restored, the items will be removed from the archive tables. - * The deletion log will be updated with an undeletion notice. - * - * This also sets Status objects, $this->fileStatus and $this->revisionStatus - * (depending what operations are attempted). - * - * @param array $timestamps Pass an empty array to restore all revisions, - * otherwise list the ones to undelete. - * @param string $comment - * @param array $fileVersions - * @param bool $unsuppress - * @param User $user User performing the action, or null to use $wgUser - * @param string|string[] $tags Change tags to add to log entry - * ($user should be able to add the specified tags before this is called) - * @return array(number of file revisions restored, number of image revisions - * restored, log message) on success, false on failure. - */ - function undelete( $timestamps, $comment = '', $fileVersions = [], - $unsuppress = false, User $user = null, $tags = null - ) { - // If both the set of text revisions and file revisions are empty, - // restore everything. Otherwise, just restore the requested items. - $restoreAll = empty( $timestamps ) && empty( $fileVersions ); - - $restoreText = $restoreAll || !empty( $timestamps ); - $restoreFiles = $restoreAll || !empty( $fileVersions ); - - if ( $restoreFiles && $this->title->getNamespace() == NS_FILE ) { - $img = wfLocalFile( $this->title ); - $img->load( File::READ_LATEST ); - $this->fileStatus = $img->restore( $fileVersions, $unsuppress ); - if ( !$this->fileStatus->isOK() ) { - return false; - } - $filesRestored = $this->fileStatus->successCount; - } else { - $filesRestored = 0; - } - - if ( $restoreText ) { - $this->revisionStatus = $this->undeleteRevisions( $timestamps, $unsuppress, $comment ); - if ( !$this->revisionStatus->isOK() ) { - return false; - } - - $textRestored = $this->revisionStatus->getValue(); - } else { - $textRestored = 0; - } - - // Touch the log! - - if ( $textRestored && $filesRestored ) { - $reason = wfMessage( 'undeletedrevisions-files' ) - ->numParams( $textRestored, $filesRestored )->inContentLanguage()->text(); - } elseif ( $textRestored ) { - $reason = wfMessage( 'undeletedrevisions' )->numParams( $textRestored ) - ->inContentLanguage()->text(); - } elseif ( $filesRestored ) { - $reason = wfMessage( 'undeletedfiles' )->numParams( $filesRestored ) - ->inContentLanguage()->text(); - } else { - wfDebug( "Undelete: nothing undeleted...\n" ); - - return false; - } - - if ( trim( $comment ) != '' ) { - $reason .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . $comment; - } - - if ( $user === null ) { - global $wgUser; - $user = $wgUser; - } - - $logEntry = new ManualLogEntry( 'delete', 'restore' ); - $logEntry->setPerformer( $user ); - $logEntry->setTarget( $this->title ); - $logEntry->setComment( $reason ); - $logEntry->setTags( $tags ); - - Hooks::run( 'ArticleUndeleteLogEntry', [ $this, &$logEntry, $user ] ); - - $logid = $logEntry->insert(); - $logEntry->publish( $logid ); - - return [ $textRestored, $filesRestored, $reason ]; - } - - /** - * This is the meaty bit -- It restores archived revisions of the given page - * to the revision table. - * - * @param array $timestamps Pass an empty array to restore all revisions, - * otherwise list the ones to undelete. - * @param bool $unsuppress Remove all ar_deleted/fa_deleted restrictions of seletected revs - * @param string $comment - * @throws ReadOnlyError - * @return Status Status object containing the number of revisions restored on success - */ - private function undeleteRevisions( $timestamps, $unsuppress = false, $comment = '' ) { - if ( wfReadOnly() ) { - throw new ReadOnlyError(); - } - - $dbw = wfGetDB( DB_MASTER ); - $dbw->startAtomic( __METHOD__ ); - - $restoreAll = empty( $timestamps ); - - # Does this page already exist? We'll have to update it... - $article = WikiPage::factory( $this->title ); - # Load latest data for the current page (T33179) - $article->loadPageData( 'fromdbmaster' ); - $oldcountable = $article->isCountable(); - - $page = $dbw->selectRow( 'page', - [ 'page_id', 'page_latest' ], - [ 'page_namespace' => $this->title->getNamespace(), - 'page_title' => $this->title->getDBkey() ], - __METHOD__, - [ 'FOR UPDATE' ] // lock page - ); - - if ( $page ) { - $makepage = false; - # Page already exists. Import the history, and if necessary - # we'll update the latest revision field in the record. - - # Get the time span of this page - $previousTimestamp = $dbw->selectField( 'revision', 'rev_timestamp', - [ 'rev_id' => $page->page_latest ], - __METHOD__ ); - - if ( $previousTimestamp === false ) { - wfDebug( __METHOD__ . ": existing page refers to a page_latest that does not exist\n" ); - - $status = Status::newGood( 0 ); - $status->warning( 'undeleterevision-missing' ); - $dbw->endAtomic( __METHOD__ ); - - return $status; - } - } else { - # Have to create a new article... - $makepage = true; - $previousTimestamp = 0; - } - - $oldWhere = [ - 'ar_namespace' => $this->title->getNamespace(), - 'ar_title' => $this->title->getDBkey(), - ]; - if ( !$restoreAll ) { - $oldWhere['ar_timestamp'] = array_map( [ &$dbw, 'timestamp' ], $timestamps ); - } - - $fields = [ - 'ar_id', - 'ar_rev_id', - 'rev_id', - 'ar_text', - 'ar_comment', - 'ar_user', - 'ar_user_text', - 'ar_timestamp', - 'ar_minor_edit', - 'ar_flags', - 'ar_text_id', - 'ar_deleted', - 'ar_page_id', - 'ar_len', - 'ar_sha1' - ]; - - if ( $this->config->get( 'ContentHandlerUseDB' ) ) { - $fields[] = 'ar_content_format'; - $fields[] = 'ar_content_model'; - } - - /** - * Select each archived revision... - */ - $result = $dbw->select( - [ 'archive', 'revision' ], - $fields, - $oldWhere, - __METHOD__, - /* options */ - [ 'ORDER BY' => 'ar_timestamp' ], - [ 'revision' => [ 'LEFT JOIN', 'ar_rev_id=rev_id' ] ] - ); - - $rev_count = $result->numRows(); - if ( !$rev_count ) { - wfDebug( __METHOD__ . ": no revisions to restore\n" ); - - $status = Status::newGood( 0 ); - $status->warning( "undelete-no-results" ); - $dbw->endAtomic( __METHOD__ ); - - return $status; - } - - // We use ar_id because there can be duplicate ar_rev_id even for the same - // page. In this case, we may be able to restore the first one. - $restoreFailedArIds = []; - - // Map rev_id to the ar_id that is allowed to use it. When checking later, - // if it doesn't match, the current ar_id can not be restored. - - // Value can be an ar_id or -1 (-1 means no ar_id can use it, since the - // rev_id is taken before we even start the restore). - $allowedRevIdToArIdMap = []; - - $latestRestorableRow = null; - - foreach ( $result as $row ) { - if ( $row->ar_rev_id ) { - // rev_id is taken even before we start restoring. - if ( $row->ar_rev_id === $row->rev_id ) { - $restoreFailedArIds[] = $row->ar_id; - $allowedRevIdToArIdMap[$row->ar_rev_id] = -1; - } else { - // rev_id is not taken yet in the DB, but it might be taken - // by a prior revision in the same restore operation. If - // not, we need to reserve it. - if ( isset( $allowedRevIdToArIdMap[$row->ar_rev_id] ) ) { - $restoreFailedArIds[] = $row->ar_id; - } else { - $allowedRevIdToArIdMap[$row->ar_rev_id] = $row->ar_id; - $latestRestorableRow = $row; - } - } - } else { - // If ar_rev_id is null, there can't be a collision, and a - // rev_id will be chosen automatically. - $latestRestorableRow = $row; - } - } - - $result->seek( 0 ); // move back - - $oldPageId = 0; - if ( $latestRestorableRow !== null ) { - $oldPageId = (int)$latestRestorableRow->ar_page_id; // pass this to ArticleUndelete hook - - // grab the content to check consistency with global state before restoring the page. - $revision = Revision::newFromArchiveRow( $latestRestorableRow, - [ - 'title' => $article->getTitle(), // used to derive default content model - ] - ); - $user = User::newFromName( $revision->getUserText( Revision::RAW ), false ); - $content = $revision->getContent( Revision::RAW ); - - // NOTE: article ID may not be known yet. prepareSave() should not modify the database. - $status = $content->prepareSave( $article, 0, -1, $user ); - if ( !$status->isOK() ) { - $dbw->endAtomic( __METHOD__ ); - - return $status; - } - } - - $newid = false; // newly created page ID - $restored = 0; // number of revisions restored - /** @var Revision $revision */ - $revision = null; - - // If there are no restorable revisions, we can skip most of the steps. - if ( $latestRestorableRow === null ) { - $failedRevisionCount = $rev_count; - } else { - if ( $makepage ) { - // Check the state of the newest to-be version... - if ( !$unsuppress - && ( $latestRestorableRow->ar_deleted & Revision::DELETED_TEXT ) - ) { - $dbw->endAtomic( __METHOD__ ); - - return Status::newFatal( "undeleterevdel" ); - } - // Safe to insert now... - $newid = $article->insertOn( $dbw, $latestRestorableRow->ar_page_id ); - if ( $newid === false ) { - // The old ID is reserved; let's pick another - $newid = $article->insertOn( $dbw ); - } - $pageId = $newid; - } else { - // Check if a deleted revision will become the current revision... - if ( $latestRestorableRow->ar_timestamp > $previousTimestamp ) { - // Check the state of the newest to-be version... - if ( !$unsuppress - && ( $latestRestorableRow->ar_deleted & Revision::DELETED_TEXT ) - ) { - $dbw->endAtomic( __METHOD__ ); - - return Status::newFatal( "undeleterevdel" ); - } - } - - $newid = false; - $pageId = $article->getId(); - } - - foreach ( $result as $row ) { - // Check for key dupes due to needed archive integrity. - if ( $row->ar_rev_id && $allowedRevIdToArIdMap[$row->ar_rev_id] !== $row->ar_id ) { - continue; - } - // Insert one revision at a time...maintaining deletion status - // unless we are specifically removing all restrictions... - $revision = Revision::newFromArchiveRow( $row, - [ - 'page' => $pageId, - 'title' => $this->title, - 'deleted' => $unsuppress ? 0 : $row->ar_deleted - ] ); - - $revision->insertOn( $dbw ); - $restored++; - - Hooks::run( 'ArticleRevisionUndeleted', - [ &$this->title, $revision, $row->ar_page_id ] ); - } - - // Now that it's safely stored, take it out of the archive - // Don't delete rows that we failed to restore - $toDeleteConds = $oldWhere; - $failedRevisionCount = count( $restoreFailedArIds ); - if ( $failedRevisionCount > 0 ) { - $toDeleteConds[] = 'ar_id NOT IN ( ' . $dbw->makeList( $restoreFailedArIds ) . ' )'; - } - - $dbw->delete( 'archive', - $toDeleteConds, - __METHOD__ ); - } - - $status = Status::newGood( $restored ); - - if ( $failedRevisionCount > 0 ) { - $status->warning( - wfMessage( 'undeleterevision-duplicate-revid', $failedRevisionCount ) ); - } - - // Was anything restored at all? - if ( $restored ) { - $created = (bool)$newid; - // Attach the latest revision to the page... - $wasnew = $article->updateIfNewerOn( $dbw, $revision ); - if ( $created || $wasnew ) { - // Update site stats, link tables, etc - $article->doEditUpdates( - $revision, - User::newFromName( $revision->getUserText( Revision::RAW ), false ), - [ - 'created' => $created, - 'oldcountable' => $oldcountable, - 'restored' => true - ] - ); - } - - Hooks::run( 'ArticleUndelete', [ &$this->title, $created, $comment, $oldPageId ] ); - if ( $this->title->getNamespace() == NS_FILE ) { - DeferredUpdates::addUpdate( new HTMLCacheUpdate( $this->title, 'imagelinks' ) ); - } - } - - $dbw->endAtomic( __METHOD__ ); - - return $status; - } - - /** - * @return Status - */ - function getFileStatus() { - return $this->fileStatus; - } - - /** - * @return Status - */ - function getRevisionStatus() { - return $this->revisionStatus; - } -} +use Wikimedia\Rdbms\ResultWrapper; /** * Special page allowing users with the appropriate permissions to view @@ -767,6 +45,10 @@ class SpecialUndelete extends SpecialPage { /** @var Title */ private $mTargetObj; + /** + * @var string Search prefix + */ + private $mSearchPrefix; function __construct() { parent::__construct( 'Undelete', 'deletedhistory' ); @@ -953,29 +235,70 @@ class SpecialUndelete extends SpecialPage { function showSearchForm() { $out = $this->getOutput(); $out->setPageTitle( $this->msg( 'undelete-search-title' ) ); - $out->addHTML( - Xml::openElement( 'form', [ 'method' => 'get', 'action' => wfScript() ] ) . - Xml::fieldset( $this->msg( 'undelete-search-box' )->text() ) . + $fuzzySearch = $this->getRequest()->getVal( 'fuzzy', true ); + + $out->enableOOUI(); + + $fields[] = new OOUI\ActionFieldLayout( + new OOUI\TextInputWidget( [ + 'name' => 'prefix', + 'inputId' => 'prefix', + 'infusable' => true, + 'value' => $this->mSearchPrefix, + 'autofocus' => true, + ] ), + new OOUI\ButtonInputWidget( [ + 'label' => $this->msg( 'undelete-search-submit' )->text(), + 'flags' => [ 'primary', 'progressive' ], + 'inputId' => 'searchUndelete', + 'type' => 'submit', + ] ), + [ + 'label' => new OOUI\HtmlSnippet( + $this->msg( + $fuzzySearch ? 'undelete-search-full' : 'undelete-search-prefix' + )->parse() + ), + 'align' => 'left', + ] + ); + + $fieldset = new OOUI\FieldsetLayout( [ + 'label' => $this->msg( 'undelete-search-box' )->text(), + 'items' => $fields, + ] ); + + $form = new OOUI\FormLayout( [ + 'method' => 'get', + 'action' => wfScript(), + ] ); + + $form->appendContent( + $fieldset, + new OOUI\HtmlSnippet( Html::hidden( 'title', $this->getPageTitle()->getPrefixedDBkey() ) . - Html::rawElement( - 'label', - [ 'for' => 'prefix' ], - $this->msg( 'undelete-search-prefix' )->parse() - ) . - Xml::input( - 'prefix', - 20, - $this->mSearchPrefix, - [ 'id' => 'prefix', 'autofocus' => '' ] - ) . ' ' . - Xml::submitButton( $this->msg( 'undelete-search-submit' )->text() ) . - Xml::closeElement( 'fieldset' ) . - Xml::closeElement( 'form' ) + Html::hidden( 'fuzzy', $fuzzySearch ) + ) + ); + + $out->addHTML( + new OOUI\PanelLayout( [ + 'expanded' => false, + 'padded' => true, + 'framed' => true, + 'content' => $form, + ] ) ); # List undeletable articles if ( $this->mSearchPrefix ) { - $result = PageArchive::listPagesByPrefix( $this->mSearchPrefix ); + // For now, we enable search engine match only when specifically asked to + // by using fuzzy=1 parameter. + if ( $fuzzySearch ) { + $result = PageArchive::listPagesBySearch( $this->mSearchPrefix ); + } else { + $result = PageArchive::listPagesByPrefix( $this->mSearchPrefix ); + } $this->showList( $result ); } } @@ -999,7 +322,7 @@ class SpecialUndelete extends SpecialPage { $linkRenderer = $this->getLinkRenderer(); $undelete = $this->getPageTitle(); - $out->addHTML( "