Translate extension for MediaWiki
 
Loading...
Searching...
No Matches
ManageTranslatorSandboxSpecialPage.php
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\Extension\Translate\TranslatorSandbox;
5
6use FormatJson;
7use Html;
8use MediaWiki\Config\ServiceOptions;
9use MediaWiki\User\UserOptionsLookup;
10use MWTimestamp;
11use Sanitizer;
12use SpecialPage;
14use User;
15
24class ManageTranslatorSandboxSpecialPage extends SpecialPage {
26 private $stash;
28 private $userOptionsLookup;
29
30 public const CONSTRUCTOR_OPTIONS = [
31 'TranslateUseSandbox',
32 ];
33
34 public function __construct(
36 UserOptionsLookup $userOptionsLookup,
37 ServiceOptions $options
38 ) {
39 $this->stash = $stash;
40 $this->userOptionsLookup = $userOptionsLookup;
41
42 parent::__construct(
43 'ManageTranslatorSandbox',
44 'translate-sandboxmanage',
45 $options->get( 'TranslateUseSandbox' )
46 );
47 }
48
49 public function doesWrites() {
50 return true;
51 }
52
53 protected function getGroupName() {
54 return 'translation';
55 }
56
57 public function execute( $params ) {
58 $this->setHeaders();
59 $this->checkPermissions();
60 $out = $this->getOutput();
61 $out->addModuleStyles(
62 [
63 'ext.translate.special.managetranslatorsandbox.styles',
64 'mediawiki.ui.button',
65 'jquery.uls.grid',
66 ]
67 );
68 $out->addModules( 'ext.translate.special.managetranslatorsandbox' );
69
70 $this->showPage();
71 }
72
74 private function showPage(): void {
75 $out = $this->getOutput();
76
77 $nojs = Html::errorBox(
78 $this->msg( 'tux-nojs' )->plain(),
79 '',
80 'tux-nojs'
81 );
82 $out->addHTML( $nojs );
83
84 $out->addHTML(
85 <<<HTML
86<div class="grid tsb-container">
87 <div class="row">
88 <div class="nine columns pane filter">{$this->makeFilter()}</div>
89 <div class="three columns pane search">{$this->makeSearchBox()}</div>
90 </div>
91 <div class="row tsb-body">
92 <div class="four columns pane requests">
93 {$this->makeList()}
94 <div class="request-footer">
95 <span class="selected-counter">
96 {$this->msg( 'tsb-selected-count' )->numParams( 0 )->escaped()}
97 </span>
98 &nbsp;
99 <a href="#" class="older-requests-indicator"></a>
100 </div>
101 </div>
102 <div class="eight columns pane details"></div>
103 </div>
104</div>
105HTML
106 );
107 }
108
109 private function makeFilter(): string {
110 return $this->msg( 'tsb-filter-pending' )->escaped();
111 }
112
113 private function makeSearchBox(): string {
114 return <<<HTML
115<input class="request-filter-box right"
116 placeholder="{$this->msg( 'tsb-search-requests' )->escaped()}" type="search" />
117HTML;
118 }
119
120 private function makeList(): string {
121 $items = [];
122 $requests = [];
123 $users = TranslateSandbox::getUsers();
124
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 )
134 );
135 } else {
136 $lastReminderAgo = '';
137 }
138
139 $requests[] = [
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' )
147 ),
148 'userid' => $user->getId(),
149 'reminderscount' => $remindersCount,
150 'lastreminder' => $lastReminderAgo,
151 ];
152 }
153
154 // Sort the requests based on translations and registration date
155 usort( $requests, [ $this, 'translatorRequestSort' ] );
156
157 foreach ( $requests as $request ) {
158 $items[] = $this->makeRequestItem( $request );
159 }
160
161 $requestsList = implode( "\n", $items );
162
163 return <<<HTML
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()}
168 </button>
169 </div>
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" />
173 </div>
174</div>
175<div class="requests-list">
176 {$requestsList}
177</div>
178HTML;
179 }
180
181 private function makeRequestItem( array $request ): string {
182 $requestdataEnc = htmlspecialchars( FormatJson::encode( $request ) );
183 $nameEnc = htmlspecialchars( $request['username'] );
184 $nameEncForId =
185 htmlspecialchars(
186 Sanitizer::escapeIdForAttribute( 'tsb-request-' . $request['username'] )
187 );
188 $emailEnc = htmlspecialchars( $request['email'] );
189 $countEnc = htmlspecialchars( (string)$request['translations'] );
190 $timestamp = new MWTimestamp( $request['registrationdate'] );
191 $agoEnc = htmlspecialchars( $this->getHumanTimestamp( $timestamp ) );
192
193 return <<<HTML
194<div class="row request" data-data="$requestdataEnc" id="$nameEncForId">
195 <div class="two columns amount">
196 <div class="translation-count">$countEnc</div>
197 </div>
198 <div class="seven columns request-info">
199 <div class="row username">$nameEnc</div>
200 <div class="row email" dir="ltr">$emailEnc</div>
201 </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>
205 </div>
206</div>
207HTML;
208 }
209
210 private function getHumanTimestamp( MWTimestamp $ts ): string {
211 return $this->getLanguage()->getHumanTimestamp( $ts, null, $this->getUser() );
212 }
213
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'] );
222 }
223}
Utility class for the sandbox feature of Translate.