Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F4129006
AggregateGroupsActionApi.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
12 KB
Referenced Files
None
Subscribers
None
AggregateGroupsActionApi.php
View Options
<?php
declare
(
strict_types
=
1
);
namespace
MediaWiki\Extension\Translate\MessageGroupProcessing
;
use
AggregateMessageGroup
;
use
ApiBase
;
use
ApiMain
;
use
JobQueueGroup
;
use
ManualLogEntry
;
use
MediaWiki\Extension\Translate\MessageLoading\RebuildMessageIndexJob
;
use
MediaWiki\Extension\Translate\MessageProcessing\MessageGroupMetadata
;
use
MediaWiki\Logger\LoggerFactory
;
use
Wikimedia\ParamValidator\ParamValidator
;
/**
* API module for managing aggregate message groups
* Only supports aggregate message groups defined inside the wiki.
* Aggregate message group defined in YAML configuration cannot be altered.
* @author Santhosh Thottingal
* @author Niklas Laxström
* @copyright Copyright © 2012-2013, Santhosh Thottingal
* @license GPL-2.0-or-later
* @ingroup API TranslateAPI
*/
class
AggregateGroupsActionApi
extends
ApiBase
{
private
JobQueueGroup
$jobQueueGroup
;
protected
static
string
$right
=
'translate-manage'
;
private
const
NO_LANGUAGE_CODE
=
'-'
;
private
MessageGroupMetadata
$messageGroupMetadata
;
private
AggregateGroupManager
$aggregateGroupManager
;
public
function
__construct
(
ApiMain
$main
,
string
$action
,
JobQueueGroup
$jobQueueGroup
,
MessageGroupMetadata
$messageGroupMetadata
,
AggregateGroupManager
$aggregateGroupManager
)
{
parent
::
__construct
(
$main
,
$action
);
$this
->
jobQueueGroup
=
$jobQueueGroup
;
$this
->
messageGroupMetadata
=
$messageGroupMetadata
;
$this
->
aggregateGroupManager
=
$aggregateGroupManager
;
}
public
function
execute
():
void
{
$this
->
checkUserRightsAny
(
self
::
$right
);
$block
=
$this
->
getUser
()->
getBlock
();
if
(
$block
&&
$block
->
isSitewide
()
)
{
$this
->
dieBlocked
(
$block
);
}
$params
=
$this
->
extractRequestParams
();
$action
=
$params
[
'do'
];
$output
=
[];
if
(
$action
===
'associate'
||
$action
===
'dissociate'
)
{
// Group is mandatory only for these two actions
if
(
!
isset
(
$params
[
'group'
]
)
)
{
$this
->
dieWithError
(
[
'apierror-missingparam'
,
'group'
]
);
}
if
(
!
isset
(
$params
[
'aggregategroup'
]
)
)
{
$this
->
dieWithError
(
[
'apierror-missingparam'
,
'aggregategroup'
]
);
}
$aggregateGroup
=
$params
[
'aggregategroup'
];
$subgroups
=
$this
->
messageGroupMetadata
->
getSubgroups
(
$aggregateGroup
);
if
(
$subgroups
===
null
)
{
// For a newly created aggregate group, it may contain no subgroups, but null
// means the group does not exist or something has gone wrong.
$this
->
dieWithError
(
'apierror-translate-invalidaggregategroup'
,
'invalidaggregategroup'
);
}
$subgroupId
=
$params
[
'group'
];
$group
=
MessageGroups
::
getGroup
(
$subgroupId
);
$addLogEntry
=
false
;
// Add or remove from the list
if
(
$action
===
'associate'
)
{
if
(
$group
===
null
||
!
$this
->
aggregateGroupManager
->
supportsAggregation
(
$group
)
)
{
$this
->
dieWithError
(
'apierror-translate-invalidgroup'
,
'invalidgroup'
);
}
$messageGroupLanguage
=
$group
->
getSourceLanguage
();
$aggregateGroupLanguage
=
$this
->
messageGroupMetadata
->
get
(
$aggregateGroup
,
'sourcelanguagecode'
);
// If source language is not set, user shouldn't be prevented from associating a message group
if
(
$aggregateGroupLanguage
!==
false
&&
$messageGroupLanguage
!==
$aggregateGroupLanguage
)
{
$this
->
dieWithError
(
[
'apierror-translate-grouplanguagemismatch'
,
$messageGroupLanguage
,
$aggregateGroupLanguage
]
);
}
$subgroups
[]
=
$subgroupId
;
$uniqueSubgroups
=
array_unique
(
$subgroups
);
if
(
$uniqueSubgroups
===
$subgroups
)
{
// A new group will actually be added, add a log entry
$addLogEntry
=
true
;
}
$subgroups
=
$uniqueSubgroups
;
$output
[
'groupUrl'
]
=
$this
->
aggregateGroupManager
->
getTargetTitleByGroup
(
$group
)->
getFullURL
();
}
elseif
(
$action
===
'dissociate'
)
{
// Allow removal of non-existing groups
$subgroups
=
array_flip
(
$subgroups
);
if
(
isset
(
$subgroups
[
$subgroupId
]
)
)
{
unset
(
$subgroups
[
$subgroupId
]
);
$addLogEntry
=
true
;
}
$subgroups
=
array_flip
(
$subgroups
);
}
$this
->
messageGroupMetadata
->
setSubgroups
(
$aggregateGroup
,
$subgroups
);
$logParams
=
[
'aggregategroup'
=>
$this
->
messageGroupMetadata
->
get
(
$aggregateGroup
,
'name'
),
'aggregategroup-id'
=>
$aggregateGroup
,
];
if
(
$addLogEntry
)
{
/* To allow removing no longer existing groups from aggregate message groups,
* the message group object $group might not always be available.
* In this case we need to fake some title. */
$title
=
$this
->
aggregateGroupManager
->
getTargetTitleByGroupId
(
$subgroupId
);
$entry
=
new
ManualLogEntry
(
'pagetranslation'
,
$action
);
$entry
->
setPerformer
(
$this
->
getUser
()
);
$entry
->
setTarget
(
$title
);
// @todo
// $entry->setComment( $comment );
$entry
->
setParameters
(
$logParams
);
$logId
=
$entry
->
insert
();
$entry
->
publish
(
$logId
);
}
}
elseif
(
$action
===
'remove'
)
{
if
(
!
isset
(
$params
[
'aggregategroup'
]
)
)
{
$this
->
dieWithError
(
[
'apierror-missingparam'
,
'aggregategroup'
]
);
}
$aggregateGroupId
=
$params
[
'aggregategroup'
];
$group
=
MessageGroups
::
getGroup
(
$aggregateGroupId
);
if
(
!(
$group
instanceof
AggregateMessageGroup
)
)
{
$this
->
dieWithError
(
'apierror-translate-invalidaggregategroupname'
,
'invalidaggregategroupname'
);
}
$this
->
messageGroupMetadata
->
deleteGroup
(
$params
[
'aggregategroup'
]
);
$logger
=
LoggerFactory
::
getInstance
(
'Translate'
);
$logger
->
info
(
'Aggregate group {groupId} has been deleted.'
,
[
'groupId'
=>
$aggregateGroupId
]
);
}
elseif
(
$action
===
'add'
)
{
if
(
!
isset
(
$params
[
'groupname'
]
)
)
{
$this
->
dieWithError
(
[
'apierror-missingparam'
,
'groupname'
]
);
}
$name
=
trim
(
$params
[
'groupname'
]
);
if
(
strlen
(
$name
)
===
0
)
{
$this
->
dieWithError
(
'apierror-translate-invalidaggregategroupname'
,
'invalidaggregategroupname'
);
}
if
(
!
isset
(
$params
[
'groupdescription'
]
)
)
{
$this
->
dieWithError
(
[
'apierror-missingparam'
,
'groupdescription'
]
);
}
$desc
=
trim
(
$params
[
'groupdescription'
]
);
$aggregateGroupId
=
self
::
generateAggregateGroupId
(
$name
);
// Throw error if group already exists
$nameExists
=
MessageGroups
::
labelExists
(
$name
);
if
(
$nameExists
)
{
$this
->
dieWithError
(
'apierror-translate-duplicateaggregategroup'
,
'duplicateaggregategroup'
);
}
// ID already exists: Generate a new ID by adding a number to it.
$idExists
=
MessageGroups
::
getGroup
(
$aggregateGroupId
);
if
(
$idExists
)
{
$i
=
1
;
do
{
$tempId
=
$aggregateGroupId
.
'-'
.
$i
;
$idExists
=
MessageGroups
::
getGroup
(
$tempId
);
$i
++;
}
while
(
$idExists
);
$aggregateGroupId
=
$tempId
;
}
$sourceLanguageCode
=
trim
(
$params
[
'groupsourcelanguagecode'
]
);
$this
->
messageGroupMetadata
->
set
(
$aggregateGroupId
,
'name'
,
$name
);
$this
->
messageGroupMetadata
->
set
(
$aggregateGroupId
,
'description'
,
$desc
);
if
(
$sourceLanguageCode
!==
self
::
NO_LANGUAGE_CODE
)
{
$this
->
messageGroupMetadata
->
set
(
$aggregateGroupId
,
'sourcelanguagecode'
,
$sourceLanguageCode
);
}
$this
->
messageGroupMetadata
->
setSubgroups
(
$aggregateGroupId
,
[]
);
// Once new aggregate group added, we need to show all the pages that can be added to that.
$output
[
'groups'
]
=
$this
->
getIncludableGroups
();
$output
[
'aggregategroupId'
]
=
$aggregateGroupId
;
// @todo Logging
}
elseif
(
$action
===
'update'
)
{
if
(
!
isset
(
$params
[
'groupname'
]
)
)
{
$this
->
dieWithError
(
[
'apierror-missingparam'
,
'groupname'
]
);
}
$name
=
trim
(
$params
[
'groupname'
]
);
if
(
strlen
(
$name
)
===
0
)
{
$this
->
dieWithError
(
'apierror-translate-invalidaggregategroupname'
,
'invalidaggregategroupname'
);
}
$desc
=
trim
(
$params
[
'groupdescription'
]
);
$aggregateGroupId
=
$params
[
'aggregategroup'
];
$newLanguageCode
=
trim
(
$params
[
'groupsourcelanguagecode'
]
);
$oldName
=
$this
->
messageGroupMetadata
->
get
(
$aggregateGroupId
,
'name'
);
$oldDesc
=
$this
->
messageGroupMetadata
->
get
(
$aggregateGroupId
,
'description'
);
$currentLanguageCode
=
$this
->
messageGroupMetadata
->
get
(
$aggregateGroupId
,
'sourcelanguagecode'
);
if
(
$newLanguageCode
!==
self
::
NO_LANGUAGE_CODE
&&
$newLanguageCode
!==
$currentLanguageCode
)
{
$groupsWithDifferentLanguage
=
$this
->
getGroupsWithDifferentLanguage
(
$aggregateGroupId
,
$newLanguageCode
);
if
(
count
(
$groupsWithDifferentLanguage
)
)
{
$this
->
dieWithError
(
[
'apierror-translate-messagegroup-aggregategrouplanguagemismatch'
,
implode
(
', '
,
$groupsWithDifferentLanguage
),
$newLanguageCode
,
count
(
$groupsWithDifferentLanguage
)
]
);
}
}
// Error if the label exists already
$exists
=
MessageGroups
::
labelExists
(
$name
);
if
(
$exists
&&
$oldName
!==
$name
)
{
$this
->
dieWithError
(
'apierror-translate-duplicateaggregategroup'
,
'duplicateaggregategroup'
);
}
if
(
$oldName
===
$name
&&
$oldDesc
===
$desc
&&
$newLanguageCode
===
$currentLanguageCode
)
{
$this
->
dieWithError
(
'apierror-translate-invalidupdate'
,
'invalidupdate'
);
}
$this
->
messageGroupMetadata
->
set
(
$aggregateGroupId
,
'name'
,
$name
);
$this
->
messageGroupMetadata
->
set
(
$aggregateGroupId
,
'description'
,
$desc
);
if
(
$newLanguageCode
===
self
::
NO_LANGUAGE_CODE
)
{
$this
->
messageGroupMetadata
->
clearMetadata
(
$aggregateGroupId
,
[
'sourcelanguagecode'
]
);
}
else
{
$this
->
messageGroupMetadata
->
set
(
$aggregateGroupId
,
'sourcelanguagecode'
,
$newLanguageCode
);
}
}
// If we got this far, nothing has failed
$output
[
'result'
]
=
'ok'
;
$this
->
getResult
()->
addValue
(
null
,
$this
->
getModuleName
(),
$output
);
// Cache needs to be cleared after any changes to groups
MessageGroups
::
singleton
()->
recache
();
$this
->
jobQueueGroup
->
push
(
RebuildMessageIndexJob
::
newJob
()
);
}
/**
* Aggregate groups have an explicit source language that should match with
* the associated message group source language. Thus, we check which of the subgroups
* of an aggregate group don't have a matching source language.
*/
private
function
getGroupsWithDifferentLanguage
(
string
$aggregateGroupId
,
string
$sourceLanguageCode
):
array
{
$groupsWithDifferentLanguage
=
[];
$subgroups
=
$this
->
messageGroupMetadata
->
getSubgroups
(
$aggregateGroupId
);
foreach
(
$subgroups
as
$group
)
{
$messageGroup
=
MessageGroups
::
getGroup
(
$group
);
$messageGroupLanguage
=
$messageGroup
->
getSourceLanguage
();
if
(
$messageGroupLanguage
!==
$sourceLanguageCode
)
{
$groupsWithDifferentLanguage
[]
=
$messageGroup
->
getLabel
();
}
}
return
$groupsWithDifferentLanguage
;
}
protected
function
generateAggregateGroupId
(
string
$aggregateGroupName
,
string
$prefix
=
'agg-'
):
string
{
// The database field has a maximum limit of 200 bytes
if
(
strlen
(
$aggregateGroupName
)
+
strlen
(
$prefix
)
>=
200
)
{
return
$prefix
.
substr
(
sha1
(
$aggregateGroupName
),
0
,
5
);
}
else
{
$pattern
=
'/[
\x
00-
\x
1f
\x
23
\x
27
\x
2c
\x
2e
\x
3c
\x
3e
\x
5b
\x
5d
\x
7b
\x
7c
\x
7d
\x
7f
\s
]+/i'
;
return
$prefix
.
preg_replace
(
$pattern
,
'_'
,
$aggregateGroupName
);
}
}
public
function
isWriteMode
():
bool
{
return
true
;
}
public
function
needsToken
():
string
{
return
'csrf'
;
}
protected
function
getAllowedParams
():
array
{
return
[
'do'
=>
[
ParamValidator
::
PARAM_TYPE
=>
[
'associate'
,
'dissociate'
,
'remove'
,
'add'
,
'update'
],
ParamValidator
::
PARAM_REQUIRED
=>
true
,
],
'aggregategroup'
=>
[
ParamValidator
::
PARAM_TYPE
=>
'string'
,
],
'group'
=>
[
// Not providing list of values, to allow dissociation of unknown groups
ParamValidator
::
PARAM_TYPE
=>
'string'
,
],
'groupname'
=>
[
ParamValidator
::
PARAM_TYPE
=>
'string'
,
],
'groupdescription'
=>
[
ParamValidator
::
PARAM_TYPE
=>
'string'
,
],
'groupsourcelanguagecode'
=>
[
ParamValidator
::
PARAM_TYPE
=>
'string'
,
ParamValidator
::
PARAM_DEFAULT
=>
self
::
NO_LANGUAGE_CODE
,
],
'token'
=>
[
ParamValidator
::
PARAM_TYPE
=>
'string'
,
ParamValidator
::
PARAM_REQUIRED
=>
true
,
],
];
}
protected
function
getExamplesMessages
():
array
{
return
[
'action=aggregategroups&do=associate&group=groupId&aggregategroup=aggregateGroupId'
=>
'apihelp-aggregategroups-example-1'
,
];
}
private
function
getIncludableGroups
():
array
{
$groups
=
MessageGroups
::
getAllGroups
();
$pages
=
[];
foreach
(
$groups
as
$group
)
{
if
(
$this
->
aggregateGroupManager
->
supportsAggregation
(
$group
)
)
{
$pages
[
$group
->
getId
()]
=
$group
->
getLabel
(
$this
->
getContext
()
);
}
}
return
$pages
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Wed, Aug 19, 09:34 (2 w, 4 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
42/97/1a07b82f1b3ffc38cb4ae90858b4
Default Alt Text
AggregateGroupsActionApi.php (12 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment