27 private $userOptionsLookup;
30 public const CONSTRUCTOR_OPTIONS = [
31 'TranslateUseSandbox',
34 public function __construct(
36 UserOptionsLookup $userOptionsLookup,
38 ServiceOptions $options
40 $this->stash = $stash;
41 $this->userOptionsLookup = $userOptionsLookup;
42 $this->translateSandbox = $translateSandbox;
45 'ManageTranslatorSandbox',
46 'translate-sandboxmanage',
47 $options->get(
'TranslateUseSandbox' )
51 public function doesWrites() {
55 protected function getGroupName() {
59 public function execute( $params ) {
61 $this->checkPermissions();
62 $out = $this->getOutput();
63 $out->addModuleStyles(
65 'ext.translate.special.managetranslatorsandbox.styles',
66 'mediawiki.ui.button',
68 'mediawiki.codex.messagebox.styles',
71 $out->addModules(
'ext.translate.special.managetranslatorsandbox' );
77 private function showPage():
void {
78 $out = $this->getOutput();
80 $nojs = Html::errorBox(
81 $this->msg(
'tux-nojs' )->escaped(),
85 $out->addHTML( $nojs );
89 <div
class=
"grid tsb-container">
91 <div
class=
"nine columns pane filter">{$this->makeFilter()}</div>
92 <div
class=
"three columns pane search">{$this->makeSearchBox()}</div>
94 <div
class=
"row tsb-body">
95 <div
class=
"four columns pane requests">
97 <div
class=
"request-footer">
98 <span
class=
"selected-counter">
99 {$this->msg(
'tsb-selected-count' )->numParams( 0 )->escaped()}
102 <a href=
"#" class=
"older-requests-indicator"></a>
105 <div
class=
"eight columns pane details"></div>
112 private function makeFilter():
string {
113 return $this->msg(
'tsb-filter-pending' )->escaped();
116 private function makeSearchBox():
string {
118 <input
class=
"request-filter-box right"
119 placeholder=
"{$this->msg( 'tsb-search-requests' )->escaped()}" type=
"search" />
123 private function makeList():
string {
126 $users = $this->translateSandbox->getUsers();
129 foreach ( $users as $user ) {
130 $reminders = $this->userOptionsLookup->getOption( $user,
'translate-sandbox-reminders' );
131 $reminders = $reminders ? explode(
'|', $reminders ) : [];
132 $remindersCount = count( $reminders );
133 if ( $remindersCount ) {
134 $lastReminderTimestamp =
new MWTimestamp( end( $reminders ) );
135 $lastReminderAgo = htmlspecialchars(
136 $this->getHumanTimestamp( $lastReminderTimestamp )
139 $lastReminderAgo =
'';
143 'username' => $user->getName(),
144 'email' => $user->getEmail(),
145 'gender' => $this->userOptionsLookup->getOption( $user,
'gender' ),
146 'registrationdate' => $user->getRegistration(),
147 'translations' => count( $this->stash->getTranslations( $user ) ),
148 'languagepreferences' => FormatJson::decode(
149 $this->userOptionsLookup->getOption( $user,
'translate-sandbox' )
151 'userid' => $user->getId(),
152 'reminderscount' => $remindersCount,
153 'lastreminder' => $lastReminderAgo,
158 usort( $requests, [ $this,
'translatorRequestSort' ] );
160 foreach ( $requests as $request ) {
161 $items[] = $this->makeRequestItem( $request );
164 $requestsList = implode(
"\n", $items );
167 <div
class=
"row request-header">
168 <div
class=
"four columns">
169 <button
class=
"language-selector unselected">
170 {$this->msg(
'tsb-all-languages-button-label' )->escaped()}
173 <div
class=
"five columns request-count"></div>
174 <div
class=
"three columns text-center">
175 <input
class=
"request-selector-all" name=
"request" type=
"checkbox" />
178 <div
class=
"requests-list">
184 private function makeRequestItem( array $request ):
string {
185 $requestdataEnc = htmlspecialchars( FormatJson::encode( $request ) );
186 $nameEnc = htmlspecialchars( $request[
'username'] );
189 Sanitizer::escapeIdForAttribute(
'tsb-request-' . $request[
'username'] )
191 $emailEnc = htmlspecialchars( $request[
'email'] );
192 $countEnc = htmlspecialchars( (
string)$request[
'translations'] );
193 $timestamp =
new MWTimestamp( $request[
'registrationdate'] );
194 $agoEnc = htmlspecialchars( $this->getHumanTimestamp( $timestamp ) );
197 <div
class=
"row request" data-data=
"$requestdataEnc" id=
"$nameEncForId">
198 <div
class=
"two columns amount">
199 <div
class=
"translation-count">$countEnc</div>
201 <div
class=
"seven columns request-info">
202 <div
class=
"row username">$nameEnc</div>
203 <div
class=
"row email" dir=
"ltr">$emailEnc</div>
205 <div
class=
"three columns approval text-center">
206 <input
class=
"row request-selector" name=
"request" type=
"checkbox" />
207 <div
class=
"row signup-age">$agoEnc</div>
213 private function getHumanTimestamp( MWTimestamp $ts ):
string {
214 return $this->getLanguage()->getHumanTimestamp( $ts,
null, $this->getUser() );
221 private function translatorRequestSort( array $a, array $b ):
int {
222 return $b[
'translations'] <=> $a[
'translations']
223 ?: $b[
'registrationdate'] <=> $a[
'registrationdate']
224 ?: strnatcasecmp( $a[
'username'], $b[
'username'] );