/** @var string */
public $action = 'submit';
- /** @var bool */
+ /** @var bool Whether an edit conflict needs to be resolved. Detected based on whether
+ * $editRevId is different from the current revision. When a conflict has successfully
+ * been resolved by a 3-way-merge, this field is set to false.
+ */
public $isConflict = false;
/** @var bool New page or new section */
/** @var bool Has a summary been preset using GET parameter &summary= ? */
public $hasPresetSummary = false;
- /** @var Revision|bool|null */
+ /** @var Revision|bool|null A revision object corresponding to $this->editRevId. */
public $mBaseRevision = false;
/** @var bool */
/** @var string */
public $edittime = '';
- /** @var int */
+ /** @var int ID of the current revision at the time editing was initiated on the client.
+ * This is used to detect and resolve edit conflicts.
+ *
+ * @note 0 if the page did not exist at that time.
+ * @note When starting an edit from an old revision, this still records the current
+ * revision at the time , not the one the edit is based on.
+ *
+ * @see $oldid
+ * @see getBaseRevision()
+ */
private $editRevId = null;
/** @var string */
/** @var string */
public $starttime = '';
- /** @var int */
+ /** @var int Revision ID the edit is based on, or 0 if it's the current revision.
+ * @see $editRevId
+ */
public $oldid = 0;
- /** @var int */
+ /** @var int Revision ID the edit is based on, adjusted when an edit conflict is resolved.
+ * @see $editRevId
+ * @see $oldid
+ * @see getparentRevId()
+ */
public $parentRevId = 0;
/** @var string */
wfDebug( "timestamp: {$timestamp}, edittime: {$this->edittime}\n" );
- // Check editRevId if set, which handles same-second timestamp collisions
+ // An edit conflict is detected if the current revision is different from the
+ // revision that was current when editing was initiated on the client.
+ // This is checked based on the timestamp and revision ID.
+ // TODO: the timestamp based check can probably go away now.
if ( $timestamp != $this->edittime
|| ( $this->editRevId !== null && $this->editRevId != $latest )
) {
private function mergeChangesIntoContent( &$editContent ) {
$db = wfGetDB( DB_MASTER );
- // This is the revision the editor started from
+ // This is the revision that was current at the time editing was initiated on the client,
+ // even if the edit was based on an old revision.
$baseRevision = $this->getBaseRevision();
$baseContent = $baseRevision ? $baseRevision->getContent() : null;
}
/**
- * @note: this method is very poorly named. If the user opened the form with ?oldid=X,
- * one might think of X as the "base revision", which is NOT what this returns.
- * @return Revision|null Current version when the edit was started
+ * Returns the revision that was current at the time editing was initiated on the client,
+ * even if the edit was based on an old revision.
+ *
+ * @warning: this method is very poorly named. If the user opened the form with ?oldid=X,
+ * one might think of X as the "base revision", which is NOT what this returns,
+ * see oldid for that. One might further assume that this corresponds to the $baseRevId
+ * parameter of WikiPage::doEditContent, which is not the case either.
+ * getExpectedParentRevision() would perhaps be a better name.
+ *
+ * @return Revision|null Current version when editing was initiated on the client
*/
public function getBaseRevision() {
if ( !$this->mBaseRevision ) {
ApiBase::PARAM_DFLT => implode( '|', $props ),
ApiBase::PARAM_ISMULTI => true,
ApiBase::PARAM_TYPE => $props,
+ ApiBase::PARAM_HELP_MSG_PER_VALUE => [],
],
];
}
$propValues[] = 'canUpload';
+ sort( $propValues );
return $propValues;
}
protected function getExamplesMessages() {
- return [
- 'action=query&meta=filerepoinfo&friprop=apiurl|name|displayname'
- => 'apihelp-query+filerepoinfo-example-simple',
- ];
+ $examples = [];
+
+ $props = array_intersect( [ 'apiurl', 'name', 'displayname' ], $this->getProps() );
+ if ( $props ) {
+ $examples['action=query&meta=filerepoinfo&friprop=' . implode( '|', $props )] =
+ 'apihelp-query+filerepoinfo-example-simple';
+ }
+
+ return $examples;
}
public function getHelpUrls() {
"apihelp-query+filearchive-example-simple": "Show a list of all deleted files.",
"apihelp-query+filerepoinfo-summary": "Return meta information about image repositories configured on the wiki.",
- "apihelp-query+filerepoinfo-param-prop": "Which repository properties to get (there may be more available on some wikis):\n;apiurl:URL to the repository API - helpful for getting image info from the host.\n;name:The key of the repository - used in e.g. <var>[[mw:Special:MyLanguage/Manual:$wgForeignFileRepos|$wgForeignFileRepos]]</var> and [[Special:ApiHelp/query+imageinfo|imageinfo]] return values.\n;displayname:The human-readable name of the repository wiki.\n;rooturl:Root URL for image paths.\n;local:Whether that repository is the local one or not.",
+ "apihelp-query+filerepoinfo-param-prop": "Which repository properties to get (properties available may vary on other wikis).",
+ "apihelp-query+filerepoinfo-paramvalue-prop-apiurl": "URL to the repository API - helpful for getting image info from the host.",
+ "apihelp-query+filerepoinfo-paramvalue-prop-articlepath": "Repository wiki's <var>[[mw:Special:MyLanguage/Manual:$wgArticlePath|$wgArticlePath]]</var> or equivalent.",
+ "apihelp-query+filerepoinfo-paramvalue-prop-canUpload": "Whether files can be uploaded to this repository, e.g. via CORS and shared authentication.",
+ "apihelp-query+filerepoinfo-paramvalue-prop-displayname": "The human-readable name of the repository wiki.",
+ "apihelp-query+filerepoinfo-paramvalue-prop-favicon": "Repository wiki's favicon URL, from <var>[[mw:Special:MyLanguage/Manual:$wgFavicon|$wgFavicon]]</var>.",
+ "apihelp-query+filerepoinfo-paramvalue-prop-fetchDescription": "Whether file description pages are fetched from this repository when viewing local file description pages.",
+ "apihelp-query+filerepoinfo-paramvalue-prop-initialCapital": "Whether file names implicitly start with a capital letter.",
+ "apihelp-query+filerepoinfo-paramvalue-prop-local": "Whether that repository is the local one or not.",
+ "apihelp-query+filerepoinfo-paramvalue-prop-name": "The key of the repository - used in e.g. <var>[[mw:Special:MyLanguage/Manual:$wgForeignFileRepos|$wgForeignFileRepos]]</var> and [[Special:ApiHelp/query+imageinfo|imageinfo]] return values.",
+ "apihelp-query+filerepoinfo-paramvalue-prop-rootUrl": "Root URL path for image paths.",
+ "apihelp-query+filerepoinfo-paramvalue-prop-scriptDirUrl": "Root URL path for the repository wiki's MediaWiki installation.",
+ "apihelp-query+filerepoinfo-paramvalue-prop-server": "Repository wiki's <var>[[mw:Special:MyLanguage/Manual:$wgServer|$wgServer]]</var> or equivalent.",
+ "apihelp-query+filerepoinfo-paramvalue-prop-thumbUrl": "Root URL path for thumbnail paths.",
+ "apihelp-query+filerepoinfo-paramvalue-prop-url": "Public zone URL path.",
"apihelp-query+filerepoinfo-example-simple": "Get information about file repositories.",
"apihelp-query+fileusage-summary": "Find all pages that use the given files.",
"apihelp-query+filearchive-example-simple": "{{doc-apihelp-example|query+filearchive}}",
"apihelp-query+filerepoinfo-summary": "{{doc-apihelp-summary|query+filerepoinfo}}",
"apihelp-query+filerepoinfo-param-prop": "{{doc-apihelp-param|query+filerepoinfo|prop}}",
+ "apihelp-query+filerepoinfo-paramvalue-prop-apiurl": "{{doc-apihelp-paramvalue|query+filerepoinfo|prop|apiurl}}",
+ "apihelp-query+filerepoinfo-paramvalue-prop-articlepath": "{{doc-apihelp-paramvalue|query+filerepoinfo|prop|articlepath}}",
+ "apihelp-query+filerepoinfo-paramvalue-prop-canUpload": "{{doc-apihelp-paramvalue|query+filerepoinfo|prop|canUpload}}",
+ "apihelp-query+filerepoinfo-paramvalue-prop-displayname": "{{doc-apihelp-paramvalue|query+filerepoinfo|prop|displayname}}",
+ "apihelp-query+filerepoinfo-paramvalue-prop-favicon": "{{doc-apihelp-paramvalue|query+filerepoinfo|prop|favicon}}",
+ "apihelp-query+filerepoinfo-paramvalue-prop-fetchDescription": "{{doc-apihelp-paramvalue|query+filerepoinfo|prop|fetchDescription}}",
+ "apihelp-query+filerepoinfo-paramvalue-prop-initialCapital": "{{doc-apihelp-paramvalue|query+filerepoinfo|prop|initialCapital}}",
+ "apihelp-query+filerepoinfo-paramvalue-prop-local": "{{doc-apihelp-paramvalue|query+filerepoinfo|prop|local}}",
+ "apihelp-query+filerepoinfo-paramvalue-prop-name": "{{doc-apihelp-paramvalue|query+filerepoinfo|prop|name}}",
+ "apihelp-query+filerepoinfo-paramvalue-prop-rootUrl": "{{doc-apihelp-paramvalue|query+filerepoinfo|prop|rootUrl}}",
+ "apihelp-query+filerepoinfo-paramvalue-prop-scriptDirUrl": "{{doc-apihelp-paramvalue|query+filerepoinfo|prop|scriptDirUrl}}",
+ "apihelp-query+filerepoinfo-paramvalue-prop-server": "{{doc-apihelp-paramvalue|query+filerepoinfo|prop|server}}",
+ "apihelp-query+filerepoinfo-paramvalue-prop-thumbUrl": "{{doc-apihelp-paramvalue|query+filerepoinfo|prop|thumbUrl}}",
+ "apihelp-query+filerepoinfo-paramvalue-prop-url": "{{doc-apihelp-paramvalue|query+filerepoinfo|prop|url}}",
"apihelp-query+filerepoinfo-example-simple": "{{doc-apihelp-example|query+filerepoinfo}}",
"apihelp-query+fileusage-summary": "{{doc-apihelp-summary|query+fileusage}}",
"apihelp-query+fileusage-param-prop": "{{doc-apihelp-param|query+fileusage|prop|paramvalues=1}}",
}
if ( $this->hasOption( 'dbgroupdefault' ) ) {
$wgDBDefaultGroup = $this->getOption( 'dbgroupdefault', null );
+
+ MediaWikiServices::getInstance()->getDBLoadBalancerFactory()->destroy();
}
if ( $this->getDbType() == self::DB_ADMIN && isset( $wgDBadminuser ) ) {
relPath( './tests/selenium/specs/**/*.js' ),
relPath( './extensions/*/tests/selenium/specs/**/*.js' ),
relPath( './extensions/VisualEditor/modules/ve-mw/tests/selenium/specs/**/*.js' ),
+ relPath( './extensions/Wikibase/repo/tests/selenium/specs/**/*.js' ),
relPath( './skins/*/tests/selenium/specs/**/*.js' )
],
// Patterns to exclude