Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F4183066
ManageMessageGroupsActionApi.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
10 KB
Referenced Files
None
Subscribers
None
ManageMessageGroupsActionApi.php
View Options
<?php
declare
(
strict_types
=
1
);
namespace
MediaWiki\Extension\Translate\MessageGroupProcessing
;
use
ApiBase
;
use
Exception
;
use
FormatJson
;
use
MediaWiki\Extension\Translate\MessageSync\MessageSourceChange
;
use
MediaWiki\Extension\Translate\Synchronization\MessageChangeStorage
;
use
MediaWiki\Extension\Translate\Utilities\StringComparators\SimpleStringComparator
;
use
MediaWiki\Extension\Translate\Utilities\Utilities
;
use
MediaWiki\Title\Title
;
use
MessageGroup
;
use
Wikimedia\ParamValidator\ParamValidator
;
/**
* API module for managing message group changes.
* Marks message as a rename of another message or as a new message.
* Updates the cdb file.
* @author Abijeet Patro
* @since 2019.10
* @license GPL-2.0-or-later
* @ingroup API TranslateAPI
*/
class
ManageMessageGroupsActionApi
extends
ApiBase
{
private
const
RIGHT
=
'translate-manage'
;
public
function
execute
():
void
{
$this
->
checkUserRightsAny
(
self
::
RIGHT
);
$params
=
$this
->
extractRequestParams
();
$groupId
=
$params
[
'groupId'
];
$op
=
$params
[
'operation'
];
$msgKey
=
$params
[
'messageKey'
];
$name
=
$params
[
'changesetName'
]
??
MessageChangeStorage
::
DEFAULT_NAME
;
$changesetModifiedTime
=
$params
[
'changesetModified'
];
$keyToRename
=
null
;
if
(
!
MessageChangeStorage
::
isValidCdbName
(
$name
)
)
{
$this
->
dieWithError
(
[
'apierror-translate-invalid-changeset-name'
,
wfEscapeWikiText
(
$name
)
],
'invalidchangeset'
);
}
$cdbPath
=
MessageChangeStorage
::
getCdbPath
(
$name
);
if
(
!
MessageChangeStorage
::
isModifiedSince
(
$cdbPath
,
$changesetModifiedTime
)
)
{
// Changeset file has been modified since the time the page was generated.
$this
->
dieWithError
(
[
'apierror-translate-changeset-modified'
]
);
}
if
(
$op
===
'rename'
)
{
if
(
!
isset
(
$params
[
'renameMessageKey'
]
)
)
{
$this
->
dieWithError
(
[
'apierror-missingparam'
,
'renameMessageKey'
]
);
}
$keyToRename
=
$params
[
'renameMessageKey'
];
}
$sourceChanges
=
MessageChangeStorage
::
getGroupChanges
(
$cdbPath
,
$groupId
);
if
(
$sourceChanges
->
getAllModifications
()
===
[]
)
{
$this
->
dieWithError
(
[
'apierror-translate-smg-nochanges'
]
);
}
$group
=
MessageGroups
::
getGroup
(
$groupId
);
if
(
$group
===
null
)
{
$this
->
dieWithError
(
'apierror-translate-invalidgroup'
,
'invalidgroup'
);
}
try
{
if
(
$op
===
'rename'
)
{
$this
->
handleRename
(
$group
,
$sourceChanges
,
$msgKey
,
$keyToRename
,
$group
->
getSourceLanguage
()
);
}
elseif
(
$op
===
'new'
)
{
$this
->
handleNew
(
$sourceChanges
,
$msgKey
,
$group
->
getSourceLanguage
()
);
}
else
{
$this
->
dieWithError
(
[
'apierror-translate-invalid-operation'
,
wfEscapeWikiText
(
$op
),
wfEscapeWikiText
(
implode
(
'/'
,
[
'new'
,
'rename'
]
)
)
],
'invalidoperation'
);
}
}
catch
(
Exception
$ex
)
{
// Log necessary parameters and rethrow.
$data
=
[
'op'
=>
$op
,
'newMsgKey'
=>
$msgKey
,
'msgKey'
=>
$keyToRename
,
'groupId'
=>
$group
->
getId
(),
'group'
=>
$group
->
getLabel
(),
'groupSourceLang'
=>
$group
->
getSourceLanguage
(),
'exception'
=>
$ex
];
error_log
(
"Error while running: ManageMessageGroupsActionApi::execute. Inputs:
\n
"
.
FormatJson
::
encode
(
$data
,
true
)
);
throw
$ex
;
}
// Write the source changes back to file.
MessageChangeStorage
::
writeGroupChanges
(
$sourceChanges
,
$groupId
,
$cdbPath
);
$this
->
getResult
()->
addValue
(
null
,
$this
->
getModuleName
(),
[
'success'
=>
1
]
);
}
/** Handles rename requests */
protected
function
handleRename
(
MessageGroup
$group
,
MessageSourceChange
$sourceChanges
,
string
$msgKey
,
string
$keyToRename
,
string
$sourceLanguage
):
void
{
$languages
=
$sourceChanges
->
getLanguages
();
foreach
(
$languages
as
$code
)
{
$msgState
=
$renameMsgState
=
null
;
$isSourceLang
=
$sourceLanguage
===
$code
;
if
(
$isSourceLang
)
{
$this
->
handleSourceRename
(
$sourceChanges
,
$code
,
$msgKey
,
$keyToRename
);
continue
;
}
// Check for changes with the new key, then with the old key.
// If there are no changes, we won't find anything at all, and
// can skip this languageCode.
$msg
=
$sourceChanges
->
findMessage
(
$code
,
$msgKey
,
[
MessageSourceChange
::
ADDITION
,
MessageSourceChange
::
RENAME
],
$msgState
);
// This case will arise if the message key has been changed in the source
// language, but has not been modified in this language code.
// NOTE: We are also searching under deletions. This means that if the source
// language key is renamed, but one of the non source language keys is removed,
// renaming it will not remove the translation, but only rename it. This
// scenario is highly unlikely though.
$msg
??=
$sourceChanges
->
findMessage
(
$code
,
$keyToRename
,
[
MessageSourceChange
::
DELETION
,
MessageSourceChange
::
CHANGE
,
MessageSourceChange
::
RENAME
],
$msgState
);
if
(
$msg
===
null
)
{
continue
;
}
// Check for the renamed message in the rename list, and deleted list.
$renameMsg
=
$sourceChanges
->
findMessage
(
$code
,
$keyToRename
,
[
MessageSourceChange
::
RENAME
,
MessageSourceChange
::
DELETION
],
$renameMsgState
);
// content / msg will not be present if the message was deleted from the wiki or
// was for some reason unavailable during processing incoming changes. We're going
// to try and load it here again from the database. Very rare chance of this happening.
if
(
$renameMsg
===
null
||
!
isset
(
$renameMsg
[
'content'
]
)
)
{
$title
=
Title
::
newFromText
(
Utilities
::
title
(
$keyToRename
,
$code
,
$group
->
getNamespace
()
),
$group
->
getNamespace
()
);
$renameContent
=
Utilities
::
getContentForTitle
(
$title
,
true
)
??
''
;
$renameMsg
=
[
'key'
=>
$keyToRename
,
'content'
=>
$renameContent
];
// If the message was found in changes, this will be set, otherwise set it
// to none
$renameMsgState
??=
MessageSourceChange
::
NONE
;
}
// Remove previous states
if
(
$msgState
===
MessageSourceChange
::
RENAME
)
{
$msgState
=
$sourceChanges
->
breakRename
(
$code
,
$msg
[
'key'
]
);
}
else
{
$sourceChanges
->
removeBasedOnType
(
$code
,
[
$msg
[
'key'
]
],
$msgState
);
}
if
(
$renameMsgState
===
MessageSourceChange
::
RENAME
)
{
$renameMsgState
=
$sourceChanges
->
breakRename
(
$code
,
$renameMsg
[
'key'
]
);
}
elseif
(
$renameMsgState
!==
MessageSourceChange
::
NONE
)
{
$sourceChanges
->
removeBasedOnType
(
$code
,
[
$keyToRename
],
$renameMsgState
);
}
// This is done in case the key has not been renamed in the non-source language.
$msg
[
'key'
]
=
$msgKey
;
// Add as rename
$stringComparator
=
new
SimpleStringComparator
();
$similarity
=
$stringComparator
->
getSimilarity
(
$msg
[
'content'
],
$renameMsg
[
'content'
]
);
$sourceChanges
->
addRename
(
$code
,
$msg
,
$renameMsg
,
$similarity
);
$sourceChanges
->
setRenameState
(
$code
,
$msgKey
,
$msgState
);
$sourceChanges
->
setRenameState
(
$code
,
$keyToRename
,
$renameMsgState
);
}
}
protected
function
handleSourceRename
(
MessageSourceChange
$sourceChanges
,
string
$code
,
string
$msgKey
,
string
$keyToRename
):
void
{
$msgState
=
$renameMsgState
=
null
;
$msg
=
$sourceChanges
->
findMessage
(
$code
,
$msgKey
,
[
MessageSourceChange
::
ADDITION
,
MessageSourceChange
::
RENAME
],
$msgState
);
$renameMsg
=
$sourceChanges
->
findMessage
(
$code
,
$keyToRename
,
[
MessageSourceChange
::
DELETION
,
MessageSourceChange
::
RENAME
],
$renameMsgState
);
if
(
$msg
===
null
||
$renameMsg
===
null
)
{
$this
->
dieWithError
(
'apierror-translate-rename-key-invalid'
);
}
if
(
$msgState
===
MessageSourceChange
::
RENAME
)
{
// @phan-suppress-next-line PhanTypeArraySuspiciousNullable T240141
$msgState
=
$sourceChanges
->
breakRename
(
$code
,
$msg
[
'key'
]
);
}
if
(
$renameMsgState
===
MessageSourceChange
::
RENAME
)
{
// @phan-suppress-next-line PhanTypeArraySuspiciousNullable T240141
$renameMsgState
=
$sourceChanges
->
breakRename
(
$code
,
$renameMsg
[
'key'
]
);
}
// Ensure that one of them is an ADDITION, and one is DELETION
if
(
$msgState
!==
MessageSourceChange
::
ADDITION
||
$renameMsgState
!==
MessageSourceChange
::
DELETION
)
{
$this
->
dieWithError
(
[
'apierror-translate-rename-state-invalid'
,
wfEscapeWikiText
(
$msgState
),
wfEscapeWikiText
(
$renameMsgState
)
]
);
}
// Remove previous states
$sourceChanges
->
removeAdditions
(
$code
,
[
$msgKey
]
);
$sourceChanges
->
removeDeletions
(
$code
,
[
$keyToRename
]
);
// Add as rename
$stringComparator
=
new
SimpleStringComparator
();
$similarity
=
$stringComparator
->
getSimilarity
(
// @phan-suppress-next-line PhanTypeArraySuspiciousNullable T240141
$msg
[
'content'
],
// @phan-suppress-next-line PhanTypeArraySuspiciousNullable T240141
$renameMsg
[
'content'
]
);
$sourceChanges
->
addRename
(
$code
,
$msg
,
$renameMsg
,
$similarity
);
}
/** Handles add message as new request */
protected
function
handleNew
(
MessageSourceChange
$sourceChanges
,
string
$msgKey
,
string
$sourceLang
):
void
{
$msgState
=
null
;
$languages
=
$sourceChanges
->
getLanguages
();
foreach
(
$languages
as
$code
)
{
$msg
=
$sourceChanges
->
findMessage
(
$code
,
$msgKey
,
[
MessageSourceChange
::
RENAME
],
$msgState
);
if
(
$code
===
$sourceLang
&&
$msg
===
null
)
{
$this
->
dieWithError
(
'apierror-translate-addition-key-invalid'
);
}
if
(
$code
===
$sourceLang
&&
$msgState
!==
MessageSourceChange
::
RENAME
)
{
$this
->
dieWithError
(
'apierror-translate-rename-msg-new'
);
}
// For any other language, it's possible for the message to be not found.
if
(
$msg
===
null
)
{
continue
;
}
// breakRename will add the message back to its previous state, nothing more to do
$sourceChanges
->
breakRename
(
$code
,
$msg
[
'key'
]
);
}
}
protected
function
getAllowedParams
():
array
{
return
[
'groupId'
=>
[
ParamValidator
::
PARAM_TYPE
=>
'string'
,
ParamValidator
::
PARAM_REQUIRED
=>
true
,
],
'renameMessageKey'
=>
[
ParamValidator
::
PARAM_TYPE
=>
'string'
,
ParamValidator
::
PARAM_REQUIRED
=>
false
,
],
'messageKey'
=>
[
ParamValidator
::
PARAM_TYPE
=>
'string'
,
ParamValidator
::
PARAM_REQUIRED
=>
true
,
],
'operation'
=>
[
ParamValidator
::
PARAM_TYPE
=>
[
'rename'
,
'new'
],
ParamValidator
::
PARAM_ISMULTI
=>
false
,
ParamValidator
::
PARAM_REQUIRED
=>
true
,
],
'changesetName'
=>
[
ParamValidator
::
PARAM_TYPE
=>
'string'
,
ParamValidator
::
PARAM_DEFAULT
=>
MessageChangeStorage
::
DEFAULT_NAME
],
'changesetModified'
=>
[
ParamValidator
::
PARAM_TYPE
=>
'integer'
,
ParamValidator
::
PARAM_REQUIRED
=>
true
,
]
];
}
public
function
isInternal
():
bool
{
return
true
;
}
public
function
needsToken
():
string
{
return
'csrf'
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Aug 20 2026, 13:59 (11 w, 5 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
84/4c/0ec9e8b7130c0bddf53e9c750219
Default Alt Text
ManageMessageGroupsActionApi.php (10 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment