Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F4121344
UndeletePage.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
20 KB
Referenced Files
None
Subscribers
None
UndeletePage.php
View Options
<?php
/**
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
namespace
MediaWiki\Page
;
use
ChangeTags
;
use
HTMLCacheUpdateJob
;
use
JobQueueGroup
;
use
LocalFile
;
use
ManualLogEntry
;
use
MediaWiki\Content\IContentHandlerFactory
;
use
MediaWiki\Content\ValidationParams
;
use
MediaWiki\HookContainer\HookContainer
;
use
MediaWiki\HookContainer\HookRunner
;
use
MediaWiki\Permissions\Authority
;
use
MediaWiki\Permissions\PermissionStatus
;
use
MediaWiki\Revision\ArchivedRevisionLookup
;
use
MediaWiki\Revision\RevisionRecord
;
use
MediaWiki\Revision\RevisionStore
;
use
MediaWiki\Status\Status
;
use
MediaWiki\Storage\PageUpdaterFactory
;
use
MediaWiki\Title\NamespaceInfo
;
use
Psr\Log\LoggerInterface
;
use
ReadOnlyError
;
use
RepoGroup
;
use
StatusValue
;
use
Wikimedia\Message\ITextFormatter
;
use
Wikimedia\Message\MessageValue
;
use
Wikimedia\Rdbms\IConnectionProvider
;
use
Wikimedia\Rdbms\IDatabase
;
use
Wikimedia\Rdbms\IDBAccessObject
;
use
Wikimedia\Rdbms\ReadOnlyMode
;
use
WikiPage
;
/**
* Backend logic for performing a page undelete action.
*
* @since 1.38
*/
class
UndeletePage
{
// Constants used as keys in the StatusValue returned by undelete()
public
const
FILES_RESTORED
=
'files'
;
public
const
REVISIONS_RESTORED
=
'revs'
;
/** @var Status|null */
private
$fileStatus
;
/** @var StatusValue|null */
private
$revisionStatus
;
/** @var string[] */
private
$timestamps
=
[];
/** @var int[] */
private
$fileVersions
=
[];
/** @var bool */
private
$unsuppress
=
false
;
/** @var string[] */
private
$tags
=
[];
/** @var WikiPage|null If not null, it means that we have to undelete it. */
private
$associatedTalk
;
private
HookRunner
$hookRunner
;
private
JobQueueGroup
$jobQueueGroup
;
private
IConnectionProvider
$dbProvider
;
private
ReadOnlyMode
$readOnlyMode
;
private
RepoGroup
$repoGroup
;
private
LoggerInterface
$logger
;
private
RevisionStore
$revisionStore
;
private
WikiPageFactory
$wikiPageFactory
;
private
PageUpdaterFactory
$pageUpdaterFactory
;
private
IContentHandlerFactory
$contentHandlerFactory
;
private
ArchivedRevisionLookup
$archivedRevisionLookup
;
private
NamespaceInfo
$namespaceInfo
;
private
ITextFormatter
$contLangMsgTextFormatter
;
private
ProperPageIdentity
$page
;
private
Authority
$performer
;
/**
* @internal Create via the UndeletePageFactory service.
*/
public
function
__construct
(
HookContainer
$hookContainer
,
JobQueueGroup
$jobQueueGroup
,
IConnectionProvider
$dbProvider
,
ReadOnlyMode
$readOnlyMode
,
RepoGroup
$repoGroup
,
LoggerInterface
$logger
,
RevisionStore
$revisionStore
,
WikiPageFactory
$wikiPageFactory
,
PageUpdaterFactory
$pageUpdaterFactory
,
IContentHandlerFactory
$contentHandlerFactory
,
ArchivedRevisionLookup
$archivedRevisionLookup
,
NamespaceInfo
$namespaceInfo
,
ITextFormatter
$contLangMsgTextFormatter
,
ProperPageIdentity
$page
,
Authority
$performer
)
{
$this
->
hookRunner
=
new
HookRunner
(
$hookContainer
);
$this
->
jobQueueGroup
=
$jobQueueGroup
;
$this
->
dbProvider
=
$dbProvider
;
$this
->
readOnlyMode
=
$readOnlyMode
;
$this
->
repoGroup
=
$repoGroup
;
$this
->
logger
=
$logger
;
$this
->
revisionStore
=
$revisionStore
;
$this
->
wikiPageFactory
=
$wikiPageFactory
;
$this
->
pageUpdaterFactory
=
$pageUpdaterFactory
;
$this
->
contentHandlerFactory
=
$contentHandlerFactory
;
$this
->
archivedRevisionLookup
=
$archivedRevisionLookup
;
$this
->
namespaceInfo
=
$namespaceInfo
;
$this
->
contLangMsgTextFormatter
=
$contLangMsgTextFormatter
;
$this
->
page
=
$page
;
$this
->
performer
=
$performer
;
}
/**
* Whether to remove all ar_deleted/fa_deleted restrictions of selected revs.
*
* @param bool $unsuppress
* @return self For chaining
*/
public
function
setUnsuppress
(
bool
$unsuppress
):
self
{
$this
->
unsuppress
=
$unsuppress
;
return
$this
;
}
/**
* Change tags to add to log entry (the user should be able to add the specified tags before this is called)
*
* @param string[] $tags
* @return self For chaining
*/
public
function
setTags
(
array
$tags
):
self
{
$this
->
tags
=
$tags
;
return
$this
;
}
/**
* If you don't want to undelete all revisions, pass an array of timestamps to undelete.
*
* @param string[] $timestamps
* @return self For chaining
*/
public
function
setUndeleteOnlyTimestamps
(
array
$timestamps
):
self
{
$this
->
timestamps
=
$timestamps
;
return
$this
;
}
/**
* If you don't want to undelete all file versions, pass an array of versions to undelete.
*
* @param int[] $fileVersions
* @return self For chaining
*/
public
function
setUndeleteOnlyFileVersions
(
array
$fileVersions
):
self
{
$this
->
fileVersions
=
$fileVersions
;
return
$this
;
}
/**
* Tests whether it's probably possible to undelete the associated talk page. This checks the replica,
* so it may not see the latest master change, and is useful e.g. for building the UI.
*
* @return StatusValue
*/
public
function
canProbablyUndeleteAssociatedTalk
():
StatusValue
{
if
(
$this
->
namespaceInfo
->
isTalk
(
$this
->
page
->
getNamespace
()
)
)
{
return
StatusValue
::
newFatal
(
'undelete-error-associated-alreadytalk'
);
}
// @todo FIXME: NamespaceInfo should work with PageIdentity
$thisWikiPage
=
$this
->
wikiPageFactory
->
newFromTitle
(
$this
->
page
);
$talkPage
=
$this
->
wikiPageFactory
->
newFromLinkTarget
(
$this
->
namespaceInfo
->
getTalkPage
(
$thisWikiPage
->
getTitle
()
)
);
// NOTE: The talk may exist, but have some deleted revision. That's fine.
if
(
!
$this
->
archivedRevisionLookup
->
hasArchivedRevisions
(
$talkPage
)
)
{
return
StatusValue
::
newFatal
(
'undelete-error-associated-notdeleted'
);
}
return
StatusValue
::
newGood
();
}
/**
* Whether to delete the associated talk page with the subject page
*
* @param bool $undelete
* @return self For chaining
*/
public
function
setUndeleteAssociatedTalk
(
bool
$undelete
):
self
{
if
(
!
$undelete
)
{
$this
->
associatedTalk
=
null
;
return
$this
;
}
// @todo FIXME: NamespaceInfo should accept PageIdentity
$thisWikiPage
=
$this
->
wikiPageFactory
->
newFromTitle
(
$this
->
page
);
$this
->
associatedTalk
=
$this
->
wikiPageFactory
->
newFromLinkTarget
(
$this
->
namespaceInfo
->
getTalkPage
(
$thisWikiPage
->
getTitle
()
)
);
return
$this
;
}
/**
* Same as undeleteUnsafe, but checks permissions.
*
* @param string $comment
* @return StatusValue
*/
public
function
undeleteIfAllowed
(
string
$comment
):
StatusValue
{
$status
=
$this
->
authorizeUndeletion
();
if
(
!
$status
->
isGood
()
)
{
return
$status
;
}
return
$this
->
undeleteUnsafe
(
$comment
);
}
/**
* @return PermissionStatus
*/
private
function
authorizeUndeletion
():
PermissionStatus
{
$status
=
PermissionStatus
::
newEmpty
();
$this
->
performer
->
authorizeWrite
(
'undelete'
,
$this
->
page
,
$status
);
if
(
$this
->
associatedTalk
)
{
$this
->
performer
->
authorizeWrite
(
'undelete'
,
$this
->
associatedTalk
,
$status
);
}
if
(
$this
->
tags
)
{
$status
->
merge
(
ChangeTags
::
canAddTagsAccompanyingChange
(
$this
->
tags
,
$this
->
performer
)
);
}
return
$status
;
}
/**
* 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).
*
* @note This method doesn't check user permissions. Use undeleteIfAllowed for that.
*
* @param string $comment
* @return StatusValue Good Status with the following value on success:
* [
* self::REVISIONS_RESTORED => number of text revisions restored,
* self::FILES_RESTORED => number of file revisions restored
* ]
* Fatal Status on failure.
*/
public
function
undeleteUnsafe
(
string
$comment
):
StatusValue
{
$hookStatus
=
$this
->
runPreUndeleteHook
(
$comment
);
if
(
!
$hookStatus
->
isGood
()
)
{
return
$hookStatus
;
}
// If both the set of text revisions and file revisions are empty,
// restore everything. Otherwise, just restore the requested items.
$restoreAll
=
$this
->
timestamps
===
[]
&&
$this
->
fileVersions
===
[];
$restoreText
=
$restoreAll
||
$this
->
timestamps
!==
[];
$restoreFiles
=
$restoreAll
||
$this
->
fileVersions
!==
[];
$resStatus
=
StatusValue
::
newGood
();
$filesRestored
=
0
;
if
(
$restoreFiles
&&
$this
->
page
->
getNamespace
()
===
NS_FILE
)
{
/** @var LocalFile $img */
$img
=
$this
->
repoGroup
->
getLocalRepo
()->
newFile
(
$this
->
page
);
$img
->
load
(
IDBAccessObject
::
READ_LATEST
);
$this
->
fileStatus
=
$img
->
restore
(
$this
->
fileVersions
,
$this
->
unsuppress
);
if
(
!
$this
->
fileStatus
->
isOK
()
)
{
return
$this
->
fileStatus
;
}
$filesRestored
=
$this
->
fileStatus
->
successCount
;
$resStatus
->
merge
(
$this
->
fileStatus
);
}
$textRestored
=
0
;
$pageCreated
=
false
;
$restoredRevision
=
null
;
$restoredPageIds
=
[];
if
(
$restoreText
)
{
$this
->
revisionStatus
=
$this
->
undeleteRevisions
(
$this
->
page
,
$this
->
timestamps
,
$comment
);
if
(
!
$this
->
revisionStatus
->
isOK
()
)
{
return
$this
->
revisionStatus
;
}
[
$textRestored
,
$pageCreated
,
$restoredRevision
,
$restoredPageIds
]
=
$this
->
revisionStatus
->
getValue
();
$resStatus
->
merge
(
$this
->
revisionStatus
);
}
$talkRestored
=
0
;
$talkCreated
=
false
;
$restoredTalkRevision
=
null
;
$restoredTalkPageIds
=
[];
if
(
$this
->
associatedTalk
)
{
$talkStatus
=
$this
->
canProbablyUndeleteAssociatedTalk
();
// if undeletion of the page fails we don't want to undelete the talk page
if
(
$talkStatus
->
isGood
()
&&
$resStatus
->
isGood
()
)
{
$talkStatus
=
$this
->
undeleteRevisions
(
$this
->
associatedTalk
,
[],
$comment
);
if
(
!
$talkStatus
->
isOK
()
)
{
return
$talkStatus
;
}
[
$talkRestored
,
$talkCreated
,
$restoredTalkRevision
,
$restoredTalkPageIds
]
=
$talkStatus
->
getValue
();
}
else
{
// Add errors as warnings since the talk page is secondary to the main action
foreach
(
$talkStatus
->
getMessages
()
as
$msg
)
{
$resStatus
->
warning
(
$msg
);
}
}
}
$resStatus
->
value
=
[
self
::
REVISIONS_RESTORED
=>
$textRestored
+
$talkRestored
,
self
::
FILES_RESTORED
=>
$filesRestored
];
if
(
!
$textRestored
&&
!
$filesRestored
&&
!
$talkRestored
)
{
$this
->
logger
->
debug
(
"Undelete: nothing undeleted..."
);
return
$resStatus
;
}
if
(
$textRestored
||
$filesRestored
)
{
$logEntry
=
$this
->
addLogEntry
(
$this
->
page
,
$comment
,
$textRestored
,
$filesRestored
);
if
(
$textRestored
)
{
$this
->
hookRunner
->
onPageUndeleteComplete
(
$this
->
page
,
$this
->
performer
,
$comment
,
$restoredRevision
,
$logEntry
,
$textRestored
,
$pageCreated
,
$restoredPageIds
);
}
}
if
(
$talkRestored
)
{
$talkRestoredComment
=
$this
->
contLangMsgTextFormatter
->
format
(
MessageValue
::
new
(
'undelete-talk-summary-prefix'
)->
plaintextParams
(
$comment
)
);
$logEntry
=
$this
->
addLogEntry
(
$this
->
associatedTalk
,
$talkRestoredComment
,
$talkRestored
,
0
);
$this
->
hookRunner
->
onPageUndeleteComplete
(
$this
->
associatedTalk
,
$this
->
performer
,
$talkRestoredComment
,
$restoredTalkRevision
,
$logEntry
,
$talkRestored
,
$talkCreated
,
$restoredTalkPageIds
);
}
return
$resStatus
;
}
/**
* @param string $comment
* @return StatusValue
*/
private
function
runPreUndeleteHook
(
string
$comment
):
StatusValue
{
$checkPages
=
[
$this
->
page
];
if
(
$this
->
associatedTalk
)
{
$checkPages
[]
=
$this
->
associatedTalk
;
}
foreach
(
$checkPages
as
$page
)
{
$hookStatus
=
StatusValue
::
newGood
();
$hookRes
=
$this
->
hookRunner
->
onPageUndelete
(
$page
,
$this
->
performer
,
$comment
,
$this
->
unsuppress
,
$this
->
timestamps
,
$this
->
fileVersions
,
$hookStatus
);
if
(
!
$hookRes
&&
!
$hookStatus
->
isGood
()
)
{
// Note: as per the PageUndeleteHook documentation, `return false` is ignored if $status is good.
return
$hookStatus
;
}
}
return
Status
::
newGood
();
}
/**
* @param ProperPageIdentity $page
* @param string $comment
* @param int $textRestored
* @param int $filesRestored
*
* @return ManualLogEntry
*/
private
function
addLogEntry
(
ProperPageIdentity
$page
,
string
$comment
,
int
$textRestored
,
int
$filesRestored
):
ManualLogEntry
{
$logEntry
=
new
ManualLogEntry
(
'delete'
,
'restore'
);
$logEntry
->
setPerformer
(
$this
->
performer
->
getUser
()
);
$logEntry
->
setTarget
(
$page
);
$logEntry
->
setComment
(
$comment
);
$logEntry
->
addTags
(
$this
->
tags
);
$logEntry
->
setParameters
(
[
':assoc:count'
=>
[
'revisions'
=>
$textRestored
,
'files'
=>
$filesRestored
,
],
]
);
$logid
=
$logEntry
->
insert
();
$logEntry
->
publish
(
$logid
);
return
$logEntry
;
}
/**
* This is the meaty bit -- It restores archived revisions of the given page
* to the revision table.
*
* @param ProperPageIdentity $page
* @param string[] $timestamps
* @param string $comment
* @throws ReadOnlyError
* @return StatusValue Status object containing the number of revisions restored on success
*/
private
function
undeleteRevisions
(
ProperPageIdentity
$page
,
array
$timestamps
,
string
$comment
):
StatusValue
{
if
(
$this
->
readOnlyMode
->
isReadOnly
()
)
{
throw
new
ReadOnlyError
();
}
$dbw
=
$this
->
dbProvider
->
getPrimaryDatabase
();
$dbw
->
startAtomic
(
__METHOD__
,
IDatabase
::
ATOMIC_CANCELABLE
);
$oldWhere
=
[
'ar_namespace'
=>
$page
->
getNamespace
(),
'ar_title'
=>
$page
->
getDBkey
(),
];
if
(
$timestamps
)
{
$oldWhere
[
'ar_timestamp'
]
=
array_map
(
[
$dbw
,
'timestamp'
],
$timestamps
);
}
$revisionStore
=
$this
->
revisionStore
;
$result
=
$revisionStore
->
newArchiveSelectQueryBuilder
(
$dbw
)
->
joinComment
()
->
leftJoin
(
'revision'
,
null
,
'ar_rev_id=rev_id'
)
->
field
(
'rev_id'
)
->
where
(
$oldWhere
)
->
orderBy
(
'ar_timestamp'
)
->
caller
(
__METHOD__
)->
fetchResultSet
();
$rev_count
=
$result
->
numRows
();
if
(
!
$rev_count
)
{
$this
->
logger
->
debug
(
__METHOD__
.
": no revisions to restore"
);
// Status value is count of revisions, whether the page has been created,
// last revision undeleted and all undeleted pages
$status
=
Status
::
newGood
(
[
0
,
false
,
null
,
[]
]
);
$status
->
error
(
"undelete-no-results"
);
$dbw
->
endAtomic
(
__METHOD__
);
return
$status
;
}
$result
->
seek
(
$rev_count
-
1
);
$latestRestorableRow
=
$result
->
current
();
// move back
$result
->
seek
(
0
);
$wikiPage
=
$this
->
wikiPageFactory
->
newFromTitle
(
$page
);
$created
=
true
;
$oldcountable
=
false
;
$updatedCurrentRevision
=
false
;
$restoredRevCount
=
0
;
$restoredPages
=
[];
// pass this to ArticleUndelete hook
$oldPageId
=
(
int
)
$latestRestorableRow
->
ar_page_id
;
// Grab the content to check consistency with global state before restoring the page.
// XXX: The only current use case is Wikibase, which tries to enforce uniqueness of
// certain things across all pages. There may be a better way to do that.
$revision
=
$revisionStore
->
newRevisionFromArchiveRow
(
$latestRestorableRow
,
0
,
$page
);
foreach
(
$revision
->
getSlotRoles
()
as
$role
)
{
$content
=
$revision
->
getContent
(
$role
,
RevisionRecord
::
RAW
);
// NOTE: article ID may not be known yet. validateSave() should not modify the database.
$contentHandler
=
$this
->
contentHandlerFactory
->
getContentHandler
(
$content
->
getModel
()
);
$validationParams
=
new
ValidationParams
(
$wikiPage
,
0
);
// @phan-suppress-next-line PhanTypeMismatchArgumentNullable RAW never returns null
$status
=
$contentHandler
->
validateSave
(
$content
,
$validationParams
);
if
(
!
$status
->
isOK
()
)
{
$dbw
->
endAtomic
(
__METHOD__
);
return
$status
;
}
}
$pageId
=
$wikiPage
->
insertOn
(
$dbw
,
$latestRestorableRow
->
ar_page_id
);
if
(
$pageId
===
false
)
{
// The page ID is reserved; let's pick another
$pageId
=
$wikiPage
->
insertOn
(
$dbw
);
if
(
$pageId
===
false
)
{
// The page title must be already taken (race condition)
$created
=
false
;
}
}
# Does this page already exist? We'll have to update it...
if
(
!
$created
)
{
# Load latest data for the current page (T33179)
$wikiPage
->
loadPageData
(
IDBAccessObject
::
READ_EXCLUSIVE
);
$pageId
=
$wikiPage
->
getId
();
$oldcountable
=
$wikiPage
->
isCountable
();
$previousTimestamp
=
false
;
$latestRevId
=
$wikiPage
->
getLatest
();
if
(
$latestRevId
)
{
$previousTimestamp
=
$revisionStore
->
getTimestampFromId
(
$latestRevId
,
IDBAccessObject
::
READ_LATEST
);
}
if
(
$previousTimestamp
===
false
)
{
$this
->
logger
->
debug
(
__METHOD__
.
": existing page refers to a page_latest that does not exist"
);
// Status value is count of revisions, whether the page has been created,
// last revision undeleted and all undeleted pages
$status
=
Status
::
newGood
(
[
0
,
false
,
null
,
[]
]
);
$status
->
error
(
'undeleterevision-missing'
);
$dbw
->
cancelAtomic
(
__METHOD__
);
return
$status
;
}
}
else
{
$previousTimestamp
=
0
;
}
// 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
(
!
$this
->
unsuppress
&&
(
$latestRestorableRow
->
ar_deleted
&
RevisionRecord
::
DELETED_TEXT
)
)
{
$dbw
->
cancelAtomic
(
__METHOD__
);
return
Status
::
newFatal
(
"undeleterevdel"
);
}
$updatedCurrentRevision
=
true
;
}
foreach
(
$result
as
$row
)
{
// Insert one revision at a time...maintaining deletion status
// unless we are specifically removing all restrictions...
$revision
=
$revisionStore
->
newRevisionFromArchiveRow
(
$row
,
0
,
$page
,
[
'page_id'
=>
$pageId
,
'deleted'
=>
$this
->
unsuppress
?
0
:
$row
->
ar_deleted
]
);
// This will also copy the revision to ip_changes if it was an IP edit.
$revision
=
$revisionStore
->
insertRevisionOn
(
$revision
,
$dbw
);
$restoredRevCount
++;
$this
->
hookRunner
->
onRevisionUndeleted
(
$revision
,
$row
->
ar_page_id
);
$restoredPages
[
$row
->
ar_page_id
]
=
true
;
}
// Now that it's safely stored, take it out of the archive
$dbw
->
newDeleteQueryBuilder
()
->
deleteFrom
(
'archive'
)
->
where
(
$oldWhere
)
->
caller
(
__METHOD__
)->
execute
();
// Status value is count of revisions, whether the page has been created,
// last revision undeleted and all undeleted pages
$status
=
Status
::
newGood
(
[
$restoredRevCount
,
$created
,
$revision
,
$restoredPages
]
);
// Was anything restored at all?
if
(
$restoredRevCount
)
{
if
(
$updatedCurrentRevision
)
{
// Attach the latest revision to the page...
// XXX: updateRevisionOn should probably move into a PageStore service.
$wasnew
=
$wikiPage
->
updateRevisionOn
(
$dbw
,
$revision
,
$created
?
0
:
$wikiPage
->
getLatest
()
);
}
else
{
$wasnew
=
false
;
}
if
(
$created
||
$wasnew
)
{
// Update site stats, link tables, etc
$user
=
$revision
->
getUser
(
RevisionRecord
::
RAW
);
$options
=
[
'created'
=>
$created
,
'oldcountable'
=>
$oldcountable
,
'restored'
=>
true
,
'causeAction'
=>
'undelete-page'
,
'causeAgent'
=>
$user
->
getName
(),
];
$updater
=
$this
->
pageUpdaterFactory
->
newDerivedPageDataUpdater
(
$wikiPage
);
$updater
->
prepareUpdate
(
$revision
,
$options
);
$updater
->
doUpdates
();
}
$this
->
hookRunner
->
onArticleUndelete
(
$wikiPage
->
getTitle
(),
$created
,
$comment
,
$oldPageId
,
$restoredPages
);
if
(
$page
->
getNamespace
()
===
NS_FILE
)
{
$job
=
HTMLCacheUpdateJob
::
newForBacklinks
(
$page
,
'imagelinks'
,
[
'causeAction'
=>
'undelete-file'
]
);
$this
->
jobQueueGroup
->
lazyPush
(
$job
);
}
}
$dbw
->
endAtomic
(
__METHOD__
);
return
$status
;
}
/**
* @internal BC method to be used by PageArchive only
* @return Status|null
*/
public
function
getFileStatus
():
?
Status
{
return
$this
->
fileStatus
;
}
/**
* @internal BC methods to be used by PageArchive only
* @return StatusValue|null
*/
public
function
getRevisionStatus
():
?
StatusValue
{
return
$this
->
revisionStatus
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Wed, Aug 19, 04:34 (15 h, 13 m ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
17/d1/648633ecd3a20405f3ef250e082e
Default Alt Text
UndeletePage.php (20 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment