1 | <?php |
||||
2 | /****************************************************************************** |
||||
3 | * Wikipedia Account Creation Assistance tool * |
||||
4 | * ACC Development Team. Please see team.json for a list of contributors. * |
||||
5 | * * |
||||
6 | * This is free and unencumbered software released into the public domain. * |
||||
7 | * Please see LICENSE.md for the full licencing statement. * |
||||
8 | ******************************************************************************/ |
||||
9 | |||||
10 | namespace Waca\Pages\Request; |
||||
11 | |||||
12 | use Exception; |
||||
13 | use Waca\DataObjects\Comment; |
||||
14 | use Waca\DataObjects\Domain; |
||||
15 | use Waca\DataObjects\Request; |
||||
16 | use Waca\DataObjects\RequestData; |
||||
17 | use Waca\DataObjects\RequestForm; |
||||
18 | use Waca\DataObjects\RequestQueue; |
||||
19 | use Waca\Exceptions\ApplicationLogicException; |
||||
20 | use Waca\Exceptions\OptimisticLockFailedException; |
||||
21 | use Waca\Helpers\BanHelper; |
||||
22 | use Waca\Helpers\MarkdownRenderingHelper; |
||||
23 | use Waca\SessionAlert; |
||||
24 | use Waca\Tasks\PublicInterfacePageBase; |
||||
25 | use Waca\Validation\RequestValidationHelper; |
||||
26 | use Waca\Validation\ValidationError; |
||||
27 | use Waca\WebRequest; |
||||
28 | |||||
29 | class PageRequestAccount extends PublicInterfacePageBase |
||||
30 | { |
||||
31 | /** @var RequestValidationHelper do not use directly. */ |
||||
32 | private $validationHelper; |
||||
33 | |||||
34 | /** |
||||
35 | * Main function for this page, when no specific actions are called. |
||||
36 | * @return void |
||||
37 | * @throws OptimisticLockFailedException |
||||
38 | * @throws Exception |
||||
39 | */ |
||||
40 | protected function main() |
||||
41 | { |
||||
42 | // dual mode page |
||||
43 | if (WebRequest::wasPosted()) { |
||||
44 | $request = $this->createNewRequest(null); |
||||
45 | $this->handleFormPost($request); |
||||
46 | } |
||||
47 | else { |
||||
48 | $this->requestClientHints(); |
||||
49 | $this->handleFormRefilling(); |
||||
50 | |||||
51 | $this->setTemplate('request/request-form.tpl'); |
||||
52 | } |
||||
53 | } |
||||
54 | |||||
55 | /** |
||||
56 | * Handles dynamic request forms. |
||||
57 | * @return void |
||||
58 | * @throws OptimisticLockFailedException |
||||
59 | * @throws Exception |
||||
60 | */ |
||||
61 | protected function dynamic() |
||||
62 | { |
||||
63 | $database = $this->getDatabase(); |
||||
64 | |||||
65 | $pathInfo = WebRequest::pathInfo(); |
||||
66 | $domain = Domain::getByShortName($pathInfo[1], $database); |
||||
67 | if ($domain === false || !$domain->isEnabled()) { |
||||
68 | throw new ApplicationLogicException("This form is not available at this time."); |
||||
69 | } |
||||
70 | |||||
71 | $form = RequestForm::getByPublicEndpoint($database, $pathInfo[2], $domain->getId()); |
||||
72 | |||||
73 | if ($form === false || !$form->isEnabled()) { |
||||
74 | throw new ApplicationLogicException("This form is not available at this time."); |
||||
75 | } |
||||
76 | |||||
77 | // dual mode page |
||||
78 | if (WebRequest::wasPosted()) { |
||||
79 | $request = $this->createNewRequest($form); |
||||
80 | $this->handleFormPost($request); |
||||
81 | } |
||||
82 | else { |
||||
83 | $this->requestClientHints(); |
||||
84 | $this->handleFormRefilling(); |
||||
85 | |||||
86 | $renderer = new MarkdownRenderingHelper(); |
||||
87 | $this->assign('formPreamble', $renderer->doRender($form->getFormContent())); |
||||
88 | $this->assign('formUsernameHelp', $renderer->doRenderInline($form->getUsernameHelp())); |
||||
89 | $this->assign('formEmailHelp', $renderer->doRenderInline($form->getEmailHelp())); |
||||
90 | $this->assign('formCommentsHelp', $renderer->doRenderInline($form->getCommentHelp())); |
||||
91 | |||||
92 | $this->setTemplate('request/request-form-dynamic.tpl'); |
||||
93 | } |
||||
94 | } |
||||
95 | |||||
96 | /** |
||||
97 | * @param RequestForm|null $form |
||||
98 | * |
||||
99 | * @return Request |
||||
100 | * @throws ApplicationLogicException |
||||
101 | */ |
||||
102 | protected function createNewRequest(?RequestForm $form): Request |
||||
103 | { |
||||
104 | $database = $this->getDatabase(); |
||||
105 | |||||
106 | $request = new Request(); |
||||
107 | |||||
108 | if ($form === null) { |
||||
109 | $domain = 1; |
||||
110 | } |
||||
111 | else { |
||||
112 | $domain = $form->getDomain(); |
||||
113 | $request->setOriginForm($form->getId()); |
||||
114 | } |
||||
115 | |||||
116 | $request->setQueue(RequestQueue::getDefaultQueue($database, $domain)->getId()); |
||||
117 | $request->setDatabase($database); |
||||
118 | $request->setDomain($domain); |
||||
119 | |||||
120 | $request->setName(trim(WebRequest::postString('name'))); |
||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
121 | $request->setEmail(WebRequest::postEmail('email')); |
||||
122 | |||||
123 | $request->setIp(WebRequest::remoteAddress()); |
||||
124 | $request->setForwardedIp(WebRequest::forwardedAddress()); |
||||
125 | |||||
126 | $request->setUserAgent(WebRequest::userAgent()); |
||||
127 | |||||
128 | return $request; |
||||
129 | } |
||||
130 | |||||
131 | /** |
||||
132 | * @return Comment|null |
||||
133 | */ |
||||
134 | private function createComment() |
||||
135 | { |
||||
136 | $commentText = WebRequest::postString('comments'); |
||||
137 | if ($commentText === null || trim($commentText) === '') { |
||||
138 | return null; |
||||
139 | } |
||||
140 | |||||
141 | $comment = new Comment(); |
||||
142 | $comment->setDatabase($this->getDatabase()); |
||||
143 | |||||
144 | $comment->setVisibility('requester'); |
||||
145 | $comment->setUser(null); |
||||
146 | $comment->setComment($commentText); |
||||
147 | |||||
148 | return $comment; |
||||
149 | } |
||||
150 | |||||
151 | /** |
||||
152 | * @param Request $request |
||||
153 | * |
||||
154 | * @return ValidationError[] |
||||
155 | */ |
||||
156 | protected function validateRequest($request) |
||||
157 | { |
||||
158 | $validationHelper = $this->getRequestValidationHelper(); |
||||
159 | |||||
160 | // These are arrays of ValidationError. |
||||
161 | $nameValidation = $validationHelper->validateName($request); |
||||
162 | $emailValidation = $validationHelper->validateEmail($request, WebRequest::postEmail('emailconfirm')); |
||||
163 | $otherValidation = $validationHelper->validateOther($request); |
||||
164 | |||||
165 | $validationErrors = array_merge($nameValidation, $emailValidation, $otherValidation); |
||||
166 | |||||
167 | return $validationErrors; |
||||
168 | } |
||||
169 | |||||
170 | /** |
||||
171 | * @param Request $request |
||||
172 | * |
||||
173 | * @param Comment|null $comment |
||||
174 | * |
||||
175 | * @throws OptimisticLockFailedException |
||||
176 | * @throws Exception |
||||
177 | */ |
||||
178 | protected function saveAsEmailConfirmation(Request $request, $comment) |
||||
179 | { |
||||
180 | $request->generateEmailConfirmationHash(); |
||||
181 | $request->save(); |
||||
182 | |||||
183 | if ($comment !== null) { |
||||
184 | $comment->setRequest($request->getId()); |
||||
185 | $comment->save(); |
||||
186 | } |
||||
187 | |||||
188 | $trustedIp = $this->getXffTrustProvider()->getTrustedClientIp( |
||||
189 | $request->getIp(), |
||||
190 | $request->getForwardedIp()); |
||||
191 | |||||
192 | $this->assign("ip", $trustedIp); |
||||
193 | $this->assign("id", $request->getId()); |
||||
194 | $this->assign("hash", $request->getEmailConfirm()); |
||||
195 | |||||
196 | // Sends the confirmation email to the user. |
||||
197 | // FIXME: domains |
||||
198 | /** @var Domain $domain */ |
||||
199 | $domain = Domain::getById(1, $this->getDatabase()); |
||||
200 | $this->getEmailHelper()->sendMail( |
||||
201 | $domain->getEmailReplyAddress(), |
||||
202 | $request->getEmail(), |
||||
203 | "[ACC #{$request->getId()}] English Wikipedia Account Request", |
||||
204 | $this->fetchTemplate('request/confirmation-mail.tpl')); |
||||
205 | |||||
206 | $this->redirect('emailConfirmationRequired'); |
||||
207 | } |
||||
208 | |||||
209 | /** |
||||
210 | * @param Request $request |
||||
211 | * |
||||
212 | * @param Comment|null $comment |
||||
213 | * |
||||
214 | * @throws OptimisticLockFailedException |
||||
215 | * @throws Exception |
||||
216 | */ |
||||
217 | protected function saveWithoutEmailConfirmation(Request $request, $comment) |
||||
218 | { |
||||
219 | $request->setEmailConfirm(0); // fixme Since it can't be null |
||||
220 | $request->save(); |
||||
221 | |||||
222 | if ($comment !== null) { |
||||
223 | $comment->setRequest($request->getId()); |
||||
224 | $comment->save(); |
||||
225 | } |
||||
226 | |||||
227 | $this->getNotificationHelper()->requestReceived($request); |
||||
228 | |||||
229 | $this->redirect('requestSubmitted'); |
||||
230 | } |
||||
231 | |||||
232 | /** |
||||
233 | * @return RequestValidationHelper |
||||
234 | */ |
||||
235 | protected function getRequestValidationHelper(): RequestValidationHelper |
||||
236 | { |
||||
237 | $banHelper = new BanHelper($this->getDatabase(), $this->getXffTrustProvider(), null); |
||||
238 | |||||
239 | if ($this->validationHelper === null) { |
||||
240 | $this->validationHelper = new RequestValidationHelper( |
||||
241 | $banHelper, |
||||
242 | $this->getDatabase(), |
||||
243 | $this->getAntiSpoofProvider(), |
||||
244 | $this->getXffTrustProvider(), |
||||
245 | $this->getHttpHelper(), |
||||
246 | $this->getTorExitProvider(), |
||||
247 | $this->getSiteConfiguration()); |
||||
248 | } |
||||
249 | |||||
250 | return $this->validationHelper; |
||||
251 | } |
||||
252 | |||||
253 | /** |
||||
254 | * @param Request $request |
||||
255 | * |
||||
256 | * @return void |
||||
257 | * @throws OptimisticLockFailedException |
||||
258 | */ |
||||
259 | protected function handleFormPost(Request $request): void |
||||
260 | { |
||||
261 | $comment = $this->createComment(); |
||||
262 | |||||
263 | $validationErrors = $this->validateRequest($request); |
||||
264 | |||||
265 | if (count($validationErrors) > 0) { |
||||
266 | foreach ($validationErrors as $validationError) { |
||||
267 | SessionAlert::error($validationError->getErrorMessage()); |
||||
268 | } |
||||
269 | |||||
270 | // Preserve the data after an error |
||||
271 | WebRequest::setSessionContext('accountReq', |
||||
272 | array( |
||||
273 | 'username' => WebRequest::postString('name'), |
||||
274 | 'email' => WebRequest::postEmail('email'), |
||||
275 | 'comments' => WebRequest::postString('comments'), |
||||
276 | ) |
||||
277 | ); |
||||
278 | |||||
279 | // Validation error, bomb out early. |
||||
280 | $this->redirect(); |
||||
281 | |||||
282 | return; |
||||
283 | } |
||||
284 | |||||
285 | // actually save the request to the database |
||||
286 | if ($this->getSiteConfiguration()->getEmailConfirmationEnabled()) { |
||||
287 | $this->saveAsEmailConfirmation($request, $comment); |
||||
288 | $this->savePrivateData($request); |
||||
289 | } |
||||
290 | else { |
||||
291 | $this->saveWithoutEmailConfirmation($request, $comment); |
||||
292 | $this->savePrivateData($request); |
||||
293 | } |
||||
294 | |||||
295 | $this->getRequestValidationHelper()->postSaveValidations($request); |
||||
296 | } |
||||
297 | |||||
298 | /** |
||||
299 | * @return void |
||||
300 | */ |
||||
301 | protected function handleFormRefilling(): void |
||||
302 | { |
||||
303 | // set the form values from the session context |
||||
304 | $context = WebRequest::getSessionContext('accountReq'); |
||||
305 | if ($context !== null && is_array($context)) { |
||||
306 | $this->assign('username', $context['username']); |
||||
307 | $this->assign('email', $context['email']); |
||||
308 | $this->assign('comments', $context['comments']); |
||||
309 | } |
||||
310 | |||||
311 | // Clear it for a refresh |
||||
312 | WebRequest::setSessionContext('accountReq', null); |
||||
313 | } |
||||
314 | |||||
315 | private function requestClientHints() |
||||
316 | { |
||||
317 | $hints = $this->getSiteConfiguration()->getAcceptClientHints(); |
||||
318 | |||||
319 | $this->headerQueue[] = "Accept-CH: " . implode(', ', $hints); |
||||
320 | } |
||||
321 | |||||
322 | private function savePrivateData(Request $request) |
||||
323 | { |
||||
324 | foreach ($this->getSiteConfiguration()->getAcceptClientHints() as $header) |
||||
325 | { |
||||
326 | $value = WebRequest::httpHeader($header); |
||||
327 | |||||
328 | if($value === null){ |
||||
329 | continue; |
||||
330 | } |
||||
331 | |||||
332 | $d = new RequestData(); |
||||
333 | $d->setDatabase($request->getDatabase()); |
||||
334 | $d->setRequest($request->getId()); |
||||
335 | |||||
336 | $d->setType(RequestData::TYPE_CLIENTHINT); |
||||
337 | $d->setName($header); |
||||
338 | $d->setValue(WebRequest::httpHeader($header)); |
||||
0 ignored issues
–
show
It seems like
Waca\WebRequest::httpHeader($header) can also be of type null ; however, parameter $value of Waca\DataObjects\RequestData::setValue() does only seem to accept string , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
339 | |||||
340 | $d->save(); |
||||
341 | } |
||||
342 | } |
||||
343 | } |