28 private $userOptionsLookup;
30 public const CONSTRUCTOR_OPTIONS = [
31 'TranslateUseSandbox',
34 public function __construct(
36 UserOptionsLookup $userOptionsLookup,
37 ServiceOptions $options
39 $this->stash = $stash;
40 $this->userOptionsLookup = $userOptionsLookup;
43 'ManageTranslatorSandbox',
44 'translate-sandboxmanage',
45 $options->get(
'TranslateUseSandbox' )
49 public function doesWrites() {
53 protected function getGroupName() {
57 public function execute( $params ) {
59 $this->checkPermissions();
60 $out = $this->getOutput();
61 $out->addModuleStyles(
63 'ext.translate.special.managetranslatorsandbox.styles',
64 'mediawiki.ui.button',
68 $out->addModules(
'ext.translate.special.managetranslatorsandbox' );
74 private function showPage():
void {
75 $out = $this->getOutput();
77 $nojs = Html::errorBox(
78 $this->msg(
'tux-nojs' )->plain(),
82 $out->addHTML( $nojs );
86<div
class=
"grid tsb-container">
88 <div
class=
"nine columns pane filter">{$this->makeFilter()}</div>
89 <div
class=
"three columns pane search">{$this->makeSearchBox()}</div>
91 <div
class=
"row tsb-body">
92 <div
class=
"four columns pane requests">
94 <div
class=
"request-footer">
95 <span
class=
"selected-counter">
96 {$this->msg(
'tsb-selected-count' )->numParams( 0 )->escaped()}
99 <a href=
"#" class=
"older-requests-indicator"></a>
102 <div
class=
"eight columns pane details"></div>
109 private function makeFilter():
string {
110 return $this->msg(
'tsb-filter-pending' )->escaped();
113 private function makeSearchBox():
string {
115<input
class=
"request-filter-box right"
116 placeholder=
"{$this->msg( 'tsb-search-requests' )->escaped()}" type=
"search" />
120 private function makeList():
string {
123 $users = TranslateSandbox::getUsers();
126 foreach ( $users as $user ) {
127 $reminders = $this->userOptionsLookup->getOption( $user,
'translate-sandbox-reminders' );
128 $reminders = $reminders ? explode(
'|', $reminders ) : [];
129 $remindersCount = count( $reminders );
130 if ( $remindersCount ) {
131 $lastReminderTimestamp =
new MWTimestamp( end( $reminders ) );
132 $lastReminderAgo = htmlspecialchars(
133 $this->getHumanTimestamp( $lastReminderTimestamp )
136 $lastReminderAgo =
'';
140 'username' => $user->getName(),
141 'email' => $user->getEmail(),
142 'gender' => $this->userOptionsLookup->getOption( $user,
'gender' ),
143 'registrationdate' => $user->getRegistration(),
144 'translations' => count( $this->stash->getTranslations( $user ) ),
145 'languagepreferences' => FormatJson::decode(
146 $this->userOptionsLookup->getOption( $user,
'translate-sandbox' )
148 'userid' => $user->getId(),
149 'reminderscount' => $remindersCount,
150 'lastreminder' => $lastReminderAgo,
155 usort( $requests, [ $this,
'translatorRequestSort' ] );
157 foreach ( $requests as $request ) {
158 $items[] = $this->makeRequestItem( $request );
161 $requestsList = implode(
"\n", $items );
164<div
class=
"row request-header">
165 <div
class=
"four columns">
166 <button
class=
"language-selector unselected">
167 {$this->msg(
'tsb-all-languages-button-label' )->escaped()}
170 <div
class=
"five columns request-count"></div>
171 <div
class=
"three columns text-center">
172 <input
class=
"request-selector-all" name=
"request" type=
"checkbox" />
175<div
class=
"requests-list">
181 private function makeRequestItem( array $request ):
string {
182 $requestdataEnc = htmlspecialchars( FormatJson::encode( $request ) );
183 $nameEnc = htmlspecialchars( $request[
'username'] );
186 Sanitizer::escapeIdForAttribute(
'tsb-request-' . $request[
'username'] )
188 $emailEnc = htmlspecialchars( $request[
'email'] );
189 $countEnc = htmlspecialchars( (
string)$request[
'translations'] );
190 $timestamp =
new MWTimestamp( $request[
'registrationdate'] );
191 $agoEnc = htmlspecialchars( $this->getHumanTimestamp( $timestamp ) );
194<div
class=
"row request" data-data=
"$requestdataEnc" id=
"$nameEncForId">
195 <div
class=
"two columns amount">
196 <div
class=
"translation-count">$countEnc</div>
198 <div
class=
"seven columns request-info">
199 <div
class=
"row username">$nameEnc</div>
200 <div
class=
"row email" dir=
"ltr">$emailEnc</div>
202 <div
class=
"three columns approval text-center">
203 <input
class=
"row request-selector" name=
"request" type=
"checkbox" />
204 <div
class=
"row signup-age">$agoEnc</div>
210 private function getHumanTimestamp( MWTimestamp $ts ):
string {
211 return $this->getLanguage()->getHumanTimestamp( $ts,
null, $this->getUser() );
218 private function translatorRequestSort( array $a, array $b ):
int {
219 return $b[
'translations'] <=> $a[
'translations']
220 ?: $b[
'registrationdate'] <=> $a[
'registrationdate']
221 ?: strnatcasecmp( $a[
'username'], $b[
'username'] );