Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F5981066
TranslateEditAddons.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
TranslateEditAddons.php
View Options
<?php
declare
(
strict_types
=
1
);
namespace
MediaWiki\Extension\Translate\TranslatorInterface
;
use
ManualLogEntry
;
use
MediaWiki\CommentStore\CommentStoreComment
;
use
MediaWiki\Content\TextContent
;
use
MediaWiki\Deferred\DeferredUpdates
;
use
MediaWiki\Extension\Translate\MessageGroupProcessing\MessageGroupStatesUpdaterJob
;
use
MediaWiki\Extension\Translate\MessageGroupProcessing\RevTagStore
;
use
MediaWiki\Extension\Translate\MessageLoading\MessageHandle
;
use
MediaWiki\Extension\Translate\PageTranslation\Hooks
as
PageTranslationHooks
;
use
MediaWiki\Extension\Translate\Services
;
use
MediaWiki\Extension\Translate\Statistics\MessageGroupStats
;
use
MediaWiki\Extension\Translate\TtmServer\TtmServer
;
use
MediaWiki\MediaWikiServices
;
use
MediaWiki\Parser\ParserOptions
;
use
MediaWiki\Revision\RevisionRecord
;
use
MediaWiki\Storage\EditResult
;
use
MediaWiki\Title\Title
;
use
MediaWiki\User\User
;
use
MediaWiki\User\UserIdentity
;
use
WikiPage
;
/**
* Various editing enhancements to the edit page interface.
* Partly succeeded by the new ajax-enhanced editor but kept for compatibility.
* Also has code that is still relevant, like the hooks on save.
*
* @author Niklas Laxström
* @author Siebrand Mazeland
* @license GPL-2.0-or-later
*/
class
TranslateEditAddons
{
/**
* Prevent translations to non-translatable languages for the group
* Hook: getUserPermissionsErrorsExpensive
*
* @param Title $title
* @param User $user
* @param string $action
* @param mixed &$result
*/
public
static
function
disallowLangTranslations
(
Title
$title
,
User
$user
,
string
$action
,
&
$result
):
bool
{
if
(
$action
!==
'edit'
)
{
return
true
;
}
$handle
=
new
MessageHandle
(
$title
);
if
(
!
$handle
->
isValid
()
)
{
return
true
;
}
if
(
$user
->
isAllowed
(
'translate-manage'
)
)
{
return
true
;
}
$langCode
=
$handle
->
getCode
();
if
(
$langCode
===
''
)
{
return
true
;
}
$group
=
$handle
->
getGroup
();
if
(
!
$group
)
{
return
true
;
}
$configHelper
=
Services
::
getInstance
()->
getConfigHelper
();
$isDisabled
=
$configHelper
->
isTargetLanguageDisabled
(
$group
,
$langCode
,
$reason
);
if
(
!
$isDisabled
)
{
return
true
;
}
if
(
$reason
===
null
)
{
$result
=
[
'translate-language-disabled'
];
}
else
{
$result
=
[
'translate-page-disabled'
,
$reason
];
}
return
false
;
}
/**
* Runs message checks, adds tp:transver tags and updates statistics.
* Hook: PageSaveComplete
*/
public
static
function
onSaveComplete
(
WikiPage
$wikiPage
,
UserIdentity
$userIdentity
,
string
$summary
,
int
$flags
,
RevisionRecord
$revisionRecord
,
EditResult
$editResult
):
void
{
global
$wgEnablePageTranslation
;
$content
=
$wikiPage
->
getContent
();
if
(
!
$content
instanceof
TextContent
)
{
// Screw it, not interested
return
;
}
$text
=
$content
->
getText
();
$title
=
$wikiPage
->
getTitle
();
$handle
=
new
MessageHandle
(
$title
);
if
(
!
$handle
->
isValid
()
)
{
return
;
}
// TODO: Convert this method to a listener for PageRevisionUpdatedEvent,
// which provides a better way to detect dummy revisions and null edits.
$isDummyRevision
=
!
$editResult
->
isNew
()
&&
(
$editResult
->
getOriginalRevisionId
()
===
$revisionRecord
->
getParentId
()
);
if
(
$isDummyRevision
)
{
// Dummy revisions should not unfuzzy the page, see T392321.
// Fuzziness propagation for dummy revisions is handled in
// HookHandler::onRevisionRecordInserted.
// Returning here also protects against infinite recursion
// when we insert a dummy revision below.
return
;
}
// Check if this is a null edit, i.e. no revision was created,
// or a dummy revision, i.e. the content didn't change.
// NOTE: $editResult->isNullEdit() does not distinguish between null
// edits and dummy revisions (T392333). We need that distinction though,
// since we want to create a dummy revision when we are informed about a
// null edit.
$isNullEdit
=
$editResult
->
getOriginalRevisionId
()
===
$revisionRecord
->
getId
();
// Update it.
$revId
=
$revisionRecord
->
getId
();
$mwServices
=
MediaWikiServices
::
getInstance
();
$fuzzy
=
$handle
->
needsFuzzy
(
$text
);
$parentId
=
$revisionRecord
->
getParentId
();
$revTagStore
=
Services
::
getInstance
()->
getRevTagStore
();
if
(
$isNullEdit
||
$parentId
==
0
)
{
// In this case the page_latest hasn't changed so we can rely on its fuzzy status
$wasFuzzy
=
$handle
->
isFuzzy
();
}
else
{
// In this case the page_latest will (probably) have changed. The above might work by chance
// since it reads from a replica database which might not have gotten the update yet, but
// don't trust it and read the fuzzy status of the parent ID from the database instead
$wasFuzzy
=
$revTagStore
->
isRevIdFuzzy
(
$title
->
getArticleID
(),
$parentId
);
}
if
(
!
$fuzzy
&&
$wasFuzzy
)
{
$title
=
$mwServices
->
getTitleFactory
()->
newFromPageIdentity
(
$wikiPage
);
$user
=
$mwServices
->
getUserFactory
()->
newFromUserIdentity
(
$userIdentity
);
if
(
!
$mwServices
->
getPermissionManager
()->
userCan
(
'unfuzzy'
,
$user
,
$title
)
)
{
// No permission to unfuzzy this unit so leave it fuzzy
$fuzzy
=
true
;
}
elseif
(
$isNullEdit
)
{
$entry
=
new
ManualLogEntry
(
'translationreview'
,
'unfuzzy'
);
// Generate a log entry and null revision for the otherwise
// invisible unfuzzying
// NOTE: This will trigger onSaveComplete again! We have to be
// careful to avoid infinite recursion!
$nullRevision
=
$mwServices
->
getPageUpdaterFactory
()
->
newPageUpdater
(
$wikiPage
,
$userIdentity
)
->
saveDummyRevision
(
CommentStoreComment
::
newUnsavedComment
(
$summary
!==
''
?
$summary
:
wfMessage
(
"translate-unfuzzy-comment"
)
),
EDIT_SILENT
);
// Set $revId to the revision ID of the dummy revision so it (rather than the previous revision)
// has the fuzzy and transver tags updated below.
$revId
=
$nullRevision
->
getId
();
$entry
->
setAssociatedRevId
(
$revId
);
$entry
->
setPerformer
(
$userIdentity
);
$entry
->
setTarget
(
$title
);
$logId
=
$entry
->
insert
();
$entry
->
publish
(
$logId
);
}
}
// If the edit is already fuzzy due to validation, !!FUZZY!!, or lacking permissions to unfuzzy
// then don't do revert checking (and don't set a transver below)
// Otherwise, if the edit undoes or rolls back (but not manually reverts) to a fuzzy revision
// then set the fuzzy status
$revertedTo
=
null
;
if
(
!
$fuzzy
&&
$editResult
->
isExactRevert
()
)
{
$method
=
$editResult
->
getRevertMethod
();
if
(
$method
!==
EditResult
::
REVERT_MANUAL
)
{
$revertedTo
=
$editResult
->
getOriginalRevisionId
();
// This is a paranoia check - if somehow getOriginalRevisionId returns null despite isExactRevert
// being true just handle it like it wasn't a revert rather than crashing
if
(
$revertedTo
!==
null
)
{
$fuzzy
=
$revTagStore
->
isRevIdFuzzy
(
$title
->
getArticleID
(),
$revertedTo
);
}
}
}
self
::
updateFuzzyTag
(
$title
,
$revId
,
$fuzzy
);
$group
=
$handle
->
getGroup
();
// Update translation stats - source language should always be up to date
if
(
$handle
->
getCode
()
!==
$group
->
getSourceLanguage
()
)
{
// This will update in-process cache immediately, but the value is saved
// to the database in a deferred update. See MessageGroupStats::queueUpdates.
// In case an error happens before that, the stats may be stale, but that
// would be fixed by the next update or purge.
MessageGroupStats
::
clear
(
$handle
);
}
// This job asks for stats, however the updated stats are written in a deferred update.
// To make it less likely that the job would be executed before the updated stats are
// written, create the job inside a deferred update too.
DeferredUpdates
::
addCallableUpdate
(
static
function
()
use
(
$handle
)
{
MessageGroupStatesUpdaterJob
::
onChange
(
$handle
);
}
);
$user
=
$mwServices
->
getUserFactory
()
->
newFromId
(
$userIdentity
->
getId
()
);
if
(
!
$fuzzy
)
{
Services
::
getInstance
()->
getHookRunner
()
->
onTranslate_newTranslation
(
$handle
,
$revId
,
$text
,
$user
);
}
elseif
(
$revertedTo
!==
null
)
{
// If a revert sets fuzzy status then also set tp:transver
// to the tp:transver of the version being reverted to
$oldTransver
=
$revTagStore
->
getTransver
(
$title
,
$revertedTo
);
if
(
$oldTransver
!==
null
)
{
$revTagStore
->
setTransver
(
$title
,
$revId
,
$oldTransver
);
}
}
TtmServer
::
onChange
(
$handle
);
if
(
$wgEnablePageTranslation
&&
$handle
->
isPageTranslation
()
)
{
// Updates for translatable pages only
$minor
=
(
bool
)(
$flags
&
EDIT_MINOR
);
PageTranslationHooks
::
onSectionSave
(
$wikiPage
,
$user
,
$content
,
$summary
,
$minor
,
$flags
,
$handle
);
}
}
/**
* @param Title $title
* @param int $revision
* @param bool $fuzzy Whether to fuzzy or not
*/
private
static
function
updateFuzzyTag
(
Title
$title
,
int
$revision
,
bool
$fuzzy
):
void
{
$dbw
=
MediaWikiServices
::
getInstance
()->
getConnectionProvider
()->
getPrimaryDatabase
();
$conds
=
[
'rt_page'
=>
$title
->
getArticleID
(),
'rt_type'
=>
RevTagStore
::
FUZZY_TAG
,
'rt_revision'
=>
$revision
];
// Replace the existing fuzzy tag, if any
if
(
$fuzzy
)
{
$index
=
array_keys
(
$conds
);
$dbw
->
newReplaceQueryBuilder
()
->
replaceInto
(
'revtag'
)
->
uniqueIndexFields
(
$index
)
->
row
(
$conds
)
->
caller
(
__METHOD__
)
->
execute
();
}
else
{
$dbw
->
newDeleteQueryBuilder
()
->
deleteFrom
(
'revtag'
)
->
where
(
$conds
)
->
caller
(
__METHOD__
)
->
execute
();
}
}
/**
* Adds tag which identifies the revision of source message at that time.
* This is used to show diff against current version of source message
* when updating a translation.
* Hook: Translate:newTranslation
*/
public
static
function
updateTransverTag
(
MessageHandle
$handle
,
int
$revision
,
string
$text
,
User
$user
):
bool
{
if
(
$user
->
isAllowed
(
'bot'
)
)
{
return
false
;
}
$group
=
$handle
->
getGroup
();
$title
=
$handle
->
getTitle
();
$name
=
$handle
->
getKey
()
.
'/'
.
$group
->
getSourceLanguage
();
$definitionTitle
=
Title
::
makeTitleSafe
(
$title
->
getNamespace
(),
$name
);
if
(
!
$definitionTitle
||
!
$definitionTitle
->
exists
()
)
{
return
true
;
}
$definitionRevision
=
$definitionTitle
->
getLatestRevID
();
$revTagStore
=
Services
::
getInstance
()->
getRevTagStore
();
$revTagStore
->
setTransver
(
$title
,
$revision
,
$definitionRevision
);
return
true
;
}
/** Hook: ArticlePrepareTextForEdit */
public
static
function
disablePreSaveTransform
(
WikiPage
$wikiPage
,
ParserOptions
$popts
):
void
{
global
$wgTranslateUsePreSaveTransform
;
if
(
!
$wgTranslateUsePreSaveTransform
)
{
$handle
=
new
MessageHandle
(
$wikiPage
->
getTitle
()
);
if
(
$handle
->
isMessageNamespace
()
&&
!
$handle
->
isDoc
()
)
{
$popts
->
setPreSaveTransform
(
false
);
}
}
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Sat, Oct 3, 21:42 (1 w, 2 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
8f/b7/4969be70a4e5821eca4291eefa52
Default Alt Text
TranslateEditAddons.php (10 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment