Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F4060738
ApiVisualEditor.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
21 KB
Referenced Files
None
Subscribers
None
ApiVisualEditor.php
View Options
<?php
/**
* Parsoid/RESTBase+MediaWiki API wrapper.
*
* @file
* @ingroup Extensions
* @copyright 2011-2021 VisualEditor Team and others; see AUTHORS.txt
* @license MIT
*/
namespace
MediaWiki\Extension\VisualEditor
;
use
MediaWiki\Api\ApiBase
;
use
MediaWiki\Api\ApiBlockInfoTrait
;
use
MediaWiki\Api\ApiMain
;
use
MediaWiki\Api\ApiResult
;
use
MediaWiki\Config\Config
;
use
MediaWiki\Content\ContentHandler
;
use
MediaWiki\Content\Transform\ContentTransformer
;
use
MediaWiki\Content\WikitextContent
;
use
MediaWiki\Context\DerivativeContext
;
use
MediaWiki\Context\RequestContext
;
use
MediaWiki\EditPage\EditPage
;
use
MediaWiki\EditPage\IntroMessageBuilder
;
use
MediaWiki\EditPage\PreloadedContentBuilder
;
use
MediaWiki\EditPage\TextboxBuilder
;
use
MediaWiki\Language\RawMessage
;
use
MediaWiki\Logger\LoggerFactory
;
use
MediaWiki\MediaWikiServices
;
use
MediaWiki\Page\Article
;
use
MediaWiki\Page\PageReference
;
use
MediaWiki\Page\WikiPageFactory
;
use
MediaWiki\Permissions\PermissionManager
;
use
MediaWiki\Registration\ExtensionRegistry
;
use
MediaWiki\Request\DerivativeRequest
;
use
MediaWiki\Revision\RevisionLookup
;
use
MediaWiki\SpecialPage\SpecialPageFactory
;
use
MediaWiki\Title\Title
;
use
MediaWiki\User\Options\UserOptionsLookup
;
use
MediaWiki\User\TempUser\TempUserCreator
;
use
MediaWiki\User\User
;
use
MediaWiki\User\UserFactory
;
use
MediaWiki\User\UserIdentity
;
use
MediaWiki\Watchlist\WatchlistManager
;
use
MessageLocalizer
;
use
Wikimedia\Assert\Assert
;
use
Wikimedia\ParamValidator\ParamValidator
;
use
Wikimedia\Stats\StatsFactory
;
class
ApiVisualEditor
extends
ApiBase
{
use
ApiBlockInfoTrait
;
use
ApiParsoidTrait
;
private
RevisionLookup
$revisionLookup
;
private
TempUserCreator
$tempUserCreator
;
private
UserFactory
$userFactory
;
private
UserOptionsLookup
$userOptionsLookup
;
private
WatchlistManager
$watchlistManager
;
private
ContentTransformer
$contentTransformer
;
private
WikiPageFactory
$wikiPageFactory
;
private
IntroMessageBuilder
$introMessageBuilder
;
private
PreloadedContentBuilder
$preloadedContentBuilder
;
private
SpecialPageFactory
$specialPageFactory
;
private
VisualEditorParsoidClientFactory
$parsoidClientFactory
;
public
function
__construct
(
ApiMain
$main
,
string
$name
,
RevisionLookup
$revisionLookup
,
TempUserCreator
$tempUserCreator
,
UserFactory
$userFactory
,
UserOptionsLookup
$userOptionsLookup
,
WatchlistManager
$watchlistManager
,
ContentTransformer
$contentTransformer
,
StatsFactory
$statsFactory
,
WikiPageFactory
$wikiPageFactory
,
IntroMessageBuilder
$introMessageBuilder
,
PreloadedContentBuilder
$preloadedContentBuilder
,
SpecialPageFactory
$specialPageFactory
,
VisualEditorParsoidClientFactory
$parsoidClientFactory
)
{
parent
::
__construct
(
$main
,
$name
);
$this
->
setLogger
(
LoggerFactory
::
getInstance
(
'VisualEditor'
)
);
$this
->
setStatsFactory
(
$statsFactory
);
$this
->
revisionLookup
=
$revisionLookup
;
$this
->
tempUserCreator
=
$tempUserCreator
;
$this
->
userFactory
=
$userFactory
;
$this
->
userOptionsLookup
=
$userOptionsLookup
;
$this
->
watchlistManager
=
$watchlistManager
;
$this
->
contentTransformer
=
$contentTransformer
;
$this
->
wikiPageFactory
=
$wikiPageFactory
;
$this
->
introMessageBuilder
=
$introMessageBuilder
;
$this
->
preloadedContentBuilder
=
$preloadedContentBuilder
;
$this
->
specialPageFactory
=
$specialPageFactory
;
$this
->
parsoidClientFactory
=
$parsoidClientFactory
;
}
/**
* @inheritDoc
*/
protected
function
getParsoidClient
():
ParsoidClient
{
return
$this
->
parsoidClientFactory
->
createParsoidClient
(
$this
->
getRequest
()->
getHeader
(
'Cookie'
)
);
}
/**
* @see EditPage::getUserForPermissions
*/
private
function
getUserForPermissions
():
User
{
$user
=
$this
->
getUser
();
if
(
$this
->
tempUserCreator
->
shouldAutoCreate
(
$user
,
'edit'
)
)
{
return
$this
->
userFactory
->
newUnsavedTempUser
(
$this
->
tempUserCreator
->
getStashedName
(
$this
->
getRequest
()->
getSession
()
)
);
}
return
$user
;
}
/**
* @see ApiParse::getUserForPreview
*/
private
function
getUserForPreview
():
UserIdentity
{
$user
=
$this
->
getUser
();
if
(
$this
->
tempUserCreator
->
shouldAutoCreate
(
$user
,
'edit'
)
)
{
return
$this
->
userFactory
->
newUnsavedTempUser
(
$this
->
tempUserCreator
->
getStashedName
(
$this
->
getRequest
()->
getSession
()
)
);
}
return
$user
;
}
/**
* Run wikitext through the parser's Pre-Save-Transform
*
* @param Title $title The title of the page to use as the parsing context
* @param string $wikitext The wikitext to transform
* @return string The transformed wikitext
*/
protected
function
pstWikitext
(
Title
$title
,
$wikitext
)
{
$content
=
ContentHandler
::
makeContent
(
$wikitext
,
$title
,
CONTENT_MODEL_WIKITEXT
);
return
$this
->
contentTransformer
->
preSaveTransform
(
$content
,
$title
,
$this
->
getUserForPreview
(),
$this
->
wikiPageFactory
->
newFromTitle
(
$title
)->
makeParserOptions
(
$this
->
getContext
()
)
)
->
serialize
(
'text/x-wiki'
);
}
/**
* @inheritDoc
* @suppress PhanPossiblyUndeclaredVariable False positives
*/
public
function
execute
()
{
$user
=
$this
->
getUser
();
$params
=
$this
->
extractRequestParams
();
$permissionManager
=
$this
->
getPermissionManager
();
$title
=
Title
::
newFromText
(
$params
[
'page'
]
);
if
(
$title
&&
$title
->
isSpecialPage
()
)
{
// Convert Special:CollabPad/MyPage to MyPage so we can parsefragment properly
[
$special
,
$subPage
]
=
$this
->
specialPageFactory
->
resolveAlias
(
$title
->
getDBkey
()
);
if
(
$special
===
'CollabPad'
)
{
$title
=
Title
::
newFromText
(
$subPage
);
}
}
if
(
!
$title
)
{
$this
->
dieWithError
(
[
'apierror-invalidtitle'
,
wfEscapeWikiText
(
$params
[
'page'
]
)
]
);
}
if
(
!
$title
->
canExist
()
)
{
$this
->
dieWithError
(
'apierror-pagecannotexist'
);
}
wfDebugLog
(
'visualeditor'
,
"called on '$title' with paction: '{$params['paction']}'"
);
switch
(
$params
[
'paction'
]
)
{
case
'parse'
:
case
'wikitext'
:
case
'metadata'
:
// Dirty hack to provide the correct context for FlaggedRevs when it generates edit notices
// and save dialog checkboxes. (T307852)
// FIXME Don't write to globals! Eww.
RequestContext
::
getMain
()->
setTitle
(
$title
);
$preloaded
=
false
;
$restbaseHeaders
=
null
;
$section
=
$params
[
'section'
]
??
null
;
// Get information about current revision
if
(
$title
->
exists
()
)
{
$latestRevision
=
$this
->
revisionLookup
->
getRevisionByTitle
(
$title
);
if
(
!
$latestRevision
)
{
$this
->
dieWithError
(
[
'apierror-missingrev-title'
,
wfEscapeWikiText
(
$title
->
getPrefixedText
()
)
],
'nosuchrevid'
);
}
if
(
isset
(
$params
[
'oldid'
]
)
)
{
$revision
=
$this
->
revisionLookup
->
getRevisionById
(
$params
[
'oldid'
]
);
if
(
!
$revision
)
{
$this
->
dieWithError
(
[
'apierror-nosuchrevid'
,
$params
[
'oldid'
]
]
);
}
}
else
{
$revision
=
$latestRevision
;
}
$baseTimestamp
=
$latestRevision
->
getTimestamp
();
$oldid
=
$revision
->
getId
();
// If requested, request HTML from Parsoid/RESTBase
if
(
$params
[
'paction'
]
===
'parse'
)
{
$wikitext
=
$params
[
'wikitext'
]
??
null
;
if
(
$wikitext
!==
null
)
{
$stash
=
$params
[
'stash'
];
if
(
$params
[
'pst'
]
)
{
$wikitext
=
$this
->
pstWikitext
(
$title
,
$wikitext
);
}
if
(
$section
!==
null
)
{
$sectionContent
=
new
WikitextContent
(
$wikitext
);
$page
=
$this
->
wikiPageFactory
->
newFromTitle
(
$title
);
$newSectionContent
=
$page
->
replaceSectionAtRev
(
$section
,
$sectionContent
,
''
,
$oldid
);
'@phan-var WikitextContent $newSectionContent'
;
$wikitext
=
$newSectionContent
->
getText
();
}
$response
=
$this
->
transformWikitext
(
$title
,
$wikitext
,
false
,
$oldid
,
$stash
);
}
else
{
$response
=
$this
->
requestRestbasePageHtml
(
$revision
);
}
$content
=
$response
[
'body'
];
$restbaseHeaders
=
$response
[
'headers'
];
}
elseif
(
$params
[
'paction'
]
===
'wikitext'
)
{
$apiParams
=
[
'action'
=>
'query'
,
'revids'
=>
$oldid
,
'prop'
=>
'revisions'
,
'rvprop'
=>
'content|ids'
];
$apiParams
[
'rvsection'
]
=
$section
;
$context
=
new
DerivativeContext
(
$this
->
getContext
()
);
$context
->
setRequest
(
new
DerivativeRequest
(
$context
->
getRequest
(),
$apiParams
,
/* was posted? */
true
)
);
$api
=
new
ApiMain
(
$context
,
/* enable write? */
true
);
$api
->
execute
();
$result
=
$api
->
getResult
()->
getResultData
();
$pid
=
$title
->
getArticleID
();
$content
=
false
;
if
(
isset
(
$result
[
'query'
][
'pages'
][
$pid
][
'revisions'
]
)
)
{
foreach
(
$result
[
'query'
][
'pages'
][
$pid
][
'revisions'
]
as
$revArr
)
{
// Check 'revisions' is an array (T193718)
if
(
is_array
(
$revArr
)
&&
$revArr
[
'revid'
]
===
$oldid
)
{
$content
=
$revArr
[
'content'
];
}
}
}
}
}
else
{
$revision
=
null
;
}
// Use $title as the context page in every processed message (T300184)
$localizerWithTitle
=
new
class
(
$this
,
$title
)
implements
MessageLocalizer
{
private
MessageLocalizer
$base
;
private
PageReference
$page
;
public
function
__construct
(
MessageLocalizer
$base
,
PageReference
$page
)
{
$this
->
base
=
$base
;
$this
->
page
=
$page
;
}
/**
* @inheritDoc
*/
public
function
msg
(
$key
,
...
$params
)
{
return
$this
->
base
->
msg
(
$key
,
...
$params
)->
page
(
$this
->
page
);
}
};
if
(
!
$title
->
exists
()
||
$section
===
'new'
)
{
if
(
isset
(
$params
[
'wikitext'
]
)
)
{
$content
=
$params
[
'wikitext'
];
if
(
$params
[
'pst'
]
)
{
$content
=
$this
->
pstWikitext
(
$title
,
$content
);
}
}
else
{
$contentObj
=
$this
->
preloadedContentBuilder
->
getPreloadedContent
(
$title
->
toPageIdentity
(),
$user
,
$params
[
'preload'
],
$params
[
'preloadparams'
]
??
[],
$section
);
$dfltContent
=
$section
===
'new'
?
null
:
$this
->
preloadedContentBuilder
->
getDefaultContent
(
$title
->
toPageIdentity
()
);
$preloaded
=
$dfltContent
?
!
$contentObj
->
equals
(
$dfltContent
)
:
!
$contentObj
->
isEmpty
();
$content
=
$contentObj
->
serialize
();
}
if
(
$content
!==
''
&&
$params
[
'paction'
]
!==
'wikitext'
)
{
$response
=
$this
->
transformWikitext
(
$title
,
$content
,
false
,
null
,
true
);
$content
=
$response
[
'body'
];
$restbaseHeaders
=
$response
[
'headers'
];
}
$baseTimestamp
=
wfTimestampNow
();
$oldid
=
0
;
}
// Look at protection status to set up notices + surface class(es)
$builder
=
new
TextboxBuilder
();
$protectedClasses
=
$builder
->
getTextboxProtectionCSSClasses
(
$title
);
// Simplified EditPage::getEditPermissionStatus()
// TODO: Use API
// action=query&prop=info&intestactions=edit&intestactionsdetail=full&errorformat=html&errorsuselocal=1
$status
=
$permissionManager
->
getPermissionStatus
(
'edit'
,
$this
->
getUserForPermissions
(),
$title
,
PermissionManager
::
RIGOR_FULL
);
if
(
!
$status
->
isGood
()
)
{
// Show generic permission errors, including page protection, user blocks, etc.
$notice
=
$this
->
getOutput
()->
formatPermissionStatus
(
$status
,
'edit'
);
// That method returns wikitext (eww), hack to get it parsed:
$notice
=
(
new
RawMessage
(
'$1'
,
[
$notice
]
)
)->
page
(
$title
)->
parseAsBlock
();
// Invent a message key 'permissions-error' to store in $notices
// (This probably shouldn't use the notices system…)
$notices
=
[
'permissions-error'
=>
$notice
];
}
else
{
$notices
=
$this
->
introMessageBuilder
->
getIntroMessages
(
IntroMessageBuilder
::
LESS_FRAMES
,
[
// This message was not shown by VisualEditor before it was switched to use
// IntroMessageBuilder, and it may be unexpected to display it now, so skip it.
'editpage-head-copy-warn'
,
// This message was not shown by VisualEditor previously, and on many Wikipedias it's
// technically non-empty but hidden with CSS, and not a real edit notice (T337633).
'editnotice-notext'
,
],
$localizerWithTitle
,
$title
->
toPageIdentity
(),
$revision
,
$user
,
$params
[
'editintro'
],
null
,
false
,
$section
);
}
// Will be false e.g. if user is blocked or page is protected
$canEdit
=
$status
->
isGood
();
$blockinfo
=
null
;
// Blocked user notice
if
(
$permissionManager
->
isBlockedFrom
(
$user
,
$title
,
true
)
)
{
$block
=
$user
->
getBlock
();
if
(
$block
)
{
// Already added to $notices via #getPermissionStatus above.
// Add block info for MobileFrontend:
$blockinfo
=
$this
->
getBlockDetails
(
$block
);
}
}
// HACK: Build a fake EditPage so we can get checkboxes from it
// Deliberately omitting ,0 so oldid comes from request
$article
=
new
Article
(
$title
);
$editPage
=
new
EditPage
(
$article
);
$req
=
$this
->
getRequest
();
$req
->
setVal
(
'format'
,
$editPage
->
contentFormat
);
// By reference for some reason (T54466)
$editPage
->
importFormData
(
$req
);
$states
=
[
'minor'
=>
$this
->
userOptionsLookup
->
getOption
(
$user
,
'minordefault'
)
&&
$title
->
exists
(),
'watch'
=>
$this
->
userOptionsLookup
->
getOption
(
$user
,
'watchdefault'
)
||
(
$this
->
userOptionsLookup
->
getOption
(
$user
,
'watchcreations'
)
&&
!
$title
->
exists
()
)
||
$this
->
watchlistManager
->
isWatched
(
$user
,
$title
),
];
$checkboxesDef
=
$editPage
->
getCheckboxesDefinition
(
$states
);
$checkboxesMessagesList
=
[];
foreach
(
$checkboxesDef
as
&
$options
)
{
if
(
isset
(
$options
[
'tooltip'
]
)
)
{
$checkboxesMessagesList
[]
=
"accesskey-{$options['tooltip']}"
;
$checkboxesMessagesList
[]
=
"tooltip-{$options['tooltip']}"
;
}
if
(
isset
(
$options
[
'title-message'
]
)
)
{
$checkboxesMessagesList
[]
=
$options
[
'title-message'
];
if
(
!
is_string
(
$options
[
'title-message'
]
)
)
{
// Extract only the key. Any parameters are included in the fake message definition
// passed via $checkboxesMessages. (This changes $checkboxesDef by reference.)
$options
[
'title-message'
]
=
$this
->
msg
(
$options
[
'title-message'
]
)->
getKey
();
}
}
$checkboxesMessagesList
[]
=
$options
[
'label-message'
];
if
(
!
is_string
(
$options
[
'label-message'
]
)
)
{
// Extract only the key. Any parameters are included in the fake message definition
// passed via $checkboxesMessages. (This changes $checkboxesDef by reference.)
$options
[
'label-message'
]
=
$this
->
msg
(
$options
[
'label-message'
]
)->
getKey
();
}
}
$checkboxesMessages
=
[];
foreach
(
$checkboxesMessagesList
as
$messageSpecifier
)
{
// $messageSpecifier may be a string or a Message object
$message
=
$this
->
msg
(
$messageSpecifier
);
$checkboxesMessages
[
$message
->
getKey
()
]
=
$message
->
plain
();
}
foreach
(
$checkboxesDef
as
&
$value
)
{
// Don't convert the boolean to empty string with formatversion=1
$value
[
ApiResult
::
META_BC_BOOLS
]
=
[
'default'
];
}
$copyrightWarning
=
EditPage
::
getCopyrightWarning
(
$title
,
'parse'
,
$this
);
// Copied from EditPage::maybeActivateTempUserCreate
// Used by code in MobileFrontend and DiscussionTools.
// TODO Make them use API
// action=query&prop=info&intestactions=edit&intestactionsautocreate=1
$wouldautocreate
=
!
$user
->
isRegistered
()
&&
$this
->
tempUserCreator
->
isAutoCreateAction
(
'edit'
)
&&
$permissionManager
->
userHasRight
(
$user
,
'createaccount'
);
// phpcs:disable MediaWiki.WhiteSpace.SpaceBeforeSingleLineComment.NewLineComment
/** @phpcs-require-sorted-array */
$result
=
[
// --------------------------------------------------------------------------------
// This should match ArticleTarget#getWikitextDataPromiseForDoc and ArticleTarget#storeDocState
// --------------------------------------------------------------------------------
'basetimestamp'
=>
$baseTimestamp
,
'blockinfo'
=>
$blockinfo
,
// only used by MobileFrontend EditorGateway
'canEdit'
=>
$canEdit
,
'checkboxesDef'
=>
$checkboxesDef
,
'checkboxesMessages'
=>
$checkboxesMessages
,
// 'content' => ..., // optional, see below
'copyrightWarning'
=>
$copyrightWarning
,
// 'etag' => ..., // optional, see below
'notices'
=>
$notices
,
'oldid'
=>
$oldid
,
// 'preloaded' => ..., // optional, see below
'protectedClasses'
=>
implode
(
' '
,
$protectedClasses
),
'result'
=>
'success'
,
// probably unused?
'starttimestamp'
=>
wfTimestampNow
(),
'wouldautocreate'
=>
$wouldautocreate
,
];
// phpcs:enable MediaWiki.WhiteSpace.SpaceBeforeSingleLineComment.NewLineComment
if
(
isset
(
$restbaseHeaders
[
'etag'
]
)
)
{
$result
[
'etag'
]
=
$restbaseHeaders
[
'etag'
];
}
if
(
isset
(
$params
[
'badetag'
]
)
)
{
$badetag
=
$params
[
'badetag'
];
$goodetag
=
$result
[
'etag'
]
??
''
;
$this
->
getLogger
()->
info
(
__METHOD__
.
": Client reported bad ETag: {badetag}, expected: {goodetag}"
,
[
'badetag'
=>
$badetag
,
'goodetag'
=>
$goodetag
,
]
);
}
if
(
isset
(
$content
)
)
{
Assert
::
postcondition
(
is_string
(
$content
),
'Content expected'
);
$result
[
'content'
]
=
$content
;
$result
[
'preloaded'
]
=
$preloaded
;
}
break
;
case
'templatesused'
:
// HACK: Build a fake EditPage so we can get checkboxes from it
// Deliberately omitting ,0 so oldid comes from request
$article
=
new
Article
(
$title
);
$editPage
=
new
EditPage
(
$article
);
$result
=
$editPage
->
makeTemplatesOnThisPageList
(
$editPage
->
getTemplates
()
);
break
;
case
'parsefragment'
:
$wikitext
=
$params
[
'wikitext'
];
if
(
$wikitext
===
null
)
{
$this
->
dieWithError
(
[
'apierror-missingparam'
,
'wikitext'
]
);
}
if
(
$params
[
'pst'
]
)
{
$wikitext
=
$this
->
pstWikitext
(
$title
,
$wikitext
);
}
$content
=
$this
->
transformWikitext
(
$title
,
$wikitext
,
true
)[
'body'
];
Assert
::
postcondition
(
is_string
(
$content
),
'Content expected'
);
$result
=
[
'result'
=>
'success'
,
'content'
=>
$content
];
break
;
}
if
(
is_array
(
$result
)
&&
isset
(
$result
[
'content'
]
)
&&
is_string
(
$result
[
'content'
]
)
)
{
// Protect content from being corrupted by conversion to Unicode NFC.
// Without this, MediaWiki::Api::ApiResult::addValue can break html tags.
// See T382756
$result
[
'content'
]
=
$this
->
makeSafeHtmlForNfc
(
$result
[
'content'
]
);
}
$this
->
getResult
()->
addValue
(
null
,
$this
->
getModuleName
(),
$result
);
}
/**
* Protect html-like content from being corrupted by conversion to Unicode NFC.
*
* Encodes U+0338 COMBINING LONG SOLIDUS OVERLAY as an html numeric character reference.
* Otherwise, conversion to Unicode NFC can break html tags by converting
* '>' + U+0338 to U+226F (NOT GREATER THAN), and
* '<' + U+0338 to U+226E (NOT LESS THAN)
*
* Note we cannot just search for those two combinations, because sequences of combining
* characters can get reordered, e.g. '>' + U+0339 + U+0338 will become U+226F + U+0339.
* See https://unicode.org/reports/tr15/
*
* @param string $html
* @return string
*/
public
static
function
makeSafeHtmlForNfc
(
string
$html
)
{
$html
=
str_replace
(
"
\u
{0338}"
,
'̸'
,
$html
);
return
$html
;
}
/**
* Check if the configured allowed namespaces include the specified namespace
*
* @param Config $config
* @param int $namespaceId Namespace ID
* @return bool
*/
public
static
function
isAllowedNamespace
(
Config
$config
,
int
$namespaceId
):
bool
{
return
in_array
(
$namespaceId
,
self
::
getAvailableNamespaceIds
(
$config
),
true
);
}
/**
* Get a list of allowed namespace IDs
*
* @param Config $config
* @return int[]
*/
public
static
function
getAvailableNamespaceIds
(
Config
$config
):
array
{
$namespaceInfo
=
MediaWikiServices
::
getInstance
()->
getNamespaceInfo
();
$configuredNamespaces
=
array_replace
(
ExtensionRegistry
::
getInstance
()->
getAttribute
(
'VisualEditorAvailableNamespaces'
),
$config
->
get
(
'VisualEditorAvailableNamespaces'
)
);
$normalized
=
[];
foreach
(
$configuredNamespaces
as
$id
=>
$enabled
)
{
// Convert canonical namespace names to IDs
$id
=
$namespaceInfo
->
getCanonicalIndex
(
strtolower
(
$id
)
)
??
$id
;
$normalized
[
$id
]
=
$enabled
&&
$namespaceInfo
->
exists
(
$id
);
}
ksort
(
$normalized
);
return
array_keys
(
array_filter
(
$normalized
)
);
}
/**
* Check if the configured allowed content models include the specified content model
*
* @param Config $config
* @param string $contentModel Content model ID
* @return bool
*/
public
static
function
isAllowedContentType
(
Config
$config
,
string
$contentModel
):
bool
{
$availableContentModels
=
array_merge
(
ExtensionRegistry
::
getInstance
()->
getAttribute
(
'VisualEditorAvailableContentModels'
),
$config
->
get
(
'VisualEditorAvailableContentModels'
)
);
return
(
bool
)(
$availableContentModels
[
$contentModel
]
??
false
);
}
/**
* @inheritDoc
*/
public
function
getAllowedParams
()
{
return
[
'page'
=>
[
ParamValidator
::
PARAM_REQUIRED
=>
true
,
],
'badetag'
=>
null
,
'format'
=>
[
ParamValidator
::
PARAM_DEFAULT
=>
'jsonfm'
,
ParamValidator
::
PARAM_TYPE
=>
[
'json'
,
'jsonfm'
],
],
'paction'
=>
[
ParamValidator
::
PARAM_REQUIRED
=>
true
,
ParamValidator
::
PARAM_TYPE
=>
[
'parse'
,
'metadata'
,
'templatesused'
,
'wikitext'
,
'parsefragment'
,
],
],
'wikitext'
=>
[
ParamValidator
::
PARAM_TYPE
=>
'text'
,
ParamValidator
::
PARAM_DEFAULT
=>
null
,
],
'section'
=>
null
,
'stash'
=>
false
,
'oldid'
=>
[
ParamValidator
::
PARAM_TYPE
=>
'integer'
,
],
'editintro'
=>
null
,
'pst'
=>
false
,
'preload'
=>
null
,
'preloadparams'
=>
[
ParamValidator
::
PARAM_ISMULTI
=>
true
,
],
];
}
/**
* @inheritDoc
*/
public
function
needsToken
()
{
return
false
;
}
/**
* @inheritDoc
*/
public
function
isInternal
()
{
return
true
;
}
/**
* @inheritDoc
*/
public
function
isWriteMode
()
{
return
false
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Aug 16 2026, 08:06 (12 w, 14 h ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
d5/95/35d59715c3ae5ff2b70eff1ba45f
Default Alt Text
ApiVisualEditor.php (21 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment