Total Complexity | 60 |
Total Lines | 887 |
Duplicated Lines | 0 % |
Changes | 3 | ||
Bugs | 0 | Features | 0 |
Complex classes like UsersTable often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use UsersTable, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
47 | class UsersTable extends AppTable |
||
48 | { |
||
49 | /** |
||
50 | * Max lenght for username. |
||
51 | * |
||
52 | * Constrained to 191 due to InnoDB index max-length on MySQL 5.6. |
||
53 | */ |
||
54 | public const USERNAME_MAXLENGTH = 191; |
||
55 | |||
56 | /** |
||
57 | * {@inheritDoc} |
||
58 | */ |
||
59 | protected $_defaultConfig = [ |
||
60 | 'user_name_disallowed_chars' => ['\'', ';', '&', '<', '>'], |
||
61 | ]; |
||
62 | |||
63 | /** |
||
64 | * {@inheritDoc} |
||
65 | */ |
||
66 | public function initialize(array $config) |
||
67 | { |
||
68 | $this->addBehavior( |
||
69 | 'Cron.Cron', |
||
70 | [ |
||
71 | 'registerGc' => [ |
||
72 | 'id' => 'User.registerGc', |
||
73 | 'due' => '+1 day', |
||
74 | ], |
||
75 | 'userBlockGc' => [ |
||
76 | 'id' => 'User.userBlockGc', |
||
77 | 'due' => '+15 minutes', |
||
78 | ], |
||
79 | ] |
||
80 | ); |
||
81 | |||
82 | $avatarRootDir = Configure::read('Saito.Settings.uploadDirectory'); |
||
83 | $this->addBehavior( |
||
84 | 'Proffer.Proffer', |
||
85 | [ |
||
86 | 'avatar' => [ // The name of your upload field (filename) |
||
87 | 'root' => $avatarRootDir, |
||
88 | 'dir' => 'avatar_dir', // field for upload directory |
||
89 | 'thumbnailSizes' => [ |
||
90 | 'square' => ['w' => 100, 'h' => 100], |
||
91 | ], |
||
92 | // Options are Imagick, Gd or Gmagick |
||
93 | 'thumbnailMethod' => 'Gd', |
||
94 | ], |
||
95 | ] |
||
96 | ); |
||
97 | $this->getEventManager()->on(new AvatarFilenameListener($avatarRootDir)); |
||
98 | |||
99 | $this->hasOne( |
||
100 | 'UserOnline', |
||
101 | ['dependent' => true, 'foreignKey' => 'user_id'] |
||
102 | ); |
||
103 | |||
104 | $this->hasMany( |
||
105 | 'Bookmarks', |
||
106 | ['foreignKey' => 'user_id', 'dependent' => true] |
||
107 | ); |
||
108 | $this->hasMany('Drafts', ['dependent' => true]); |
||
109 | $this->hasMany('UserIgnores', ['foreignKey' => 'user_id']); |
||
110 | $this->hasMany( |
||
111 | 'Entries', |
||
112 | [ |
||
113 | 'foreignKey' => 'user_id', |
||
114 | 'conditions' => ['Entries.user_id' => 'Users.id'], |
||
115 | ] |
||
116 | ); |
||
117 | $this->hasMany( |
||
118 | 'ImageUploader.Uploads', |
||
119 | ['dependent' => true, 'foreignKey' => 'user_id'] |
||
120 | ); |
||
121 | $this->hasMany( |
||
122 | 'UserReads', |
||
123 | ['foreignKey' => 'user_id', 'dependent' => true] |
||
124 | ); |
||
125 | $this->hasMany( |
||
126 | 'UserBlocks', |
||
127 | [ |
||
128 | 'foreignKey' => 'user_id', |
||
129 | 'dependent' => true, |
||
130 | 'sort' => [ |
||
131 | 'UserBlocks.ended IS NULL DESC', |
||
132 | 'UserBlocks.ended' => 'DESC', |
||
133 | 'UserBlocks.id' => 'DESC', |
||
134 | ], |
||
135 | ] |
||
136 | ); |
||
137 | } |
||
138 | |||
139 | /** |
||
140 | * {@inheritDoc} |
||
141 | */ |
||
142 | public function validationDefault(Validator $validator) |
||
143 | { |
||
144 | $validator->setProvider( |
||
145 | 'saito', |
||
146 | 'Saito\Validation\SaitoValidationProvider' |
||
147 | ); |
||
148 | |||
149 | $validator |
||
|
|||
150 | ->setProvider( |
||
151 | 'proffer', |
||
152 | 'Proffer\Model\Validation\ProfferRules' |
||
153 | ) |
||
154 | ->allowEmpty('avatar_dir') |
||
155 | ->allowEmpty('avatar') |
||
156 | ->add( |
||
157 | 'avatar', |
||
158 | 'avatar-extension', |
||
159 | [ |
||
160 | 'rule' => ['extension', ['jpg', 'jpeg', 'png']], |
||
161 | 'message' => __('user.avatar.error.extension', ['jpg, jpeg, png']), |
||
162 | ] |
||
163 | ) |
||
164 | ->add( |
||
165 | 'avatar', |
||
166 | 'avatar-size', |
||
167 | [ |
||
168 | 'rule' => ['fileSize', Validation::COMPARE_LESS, '3MB'], |
||
169 | 'message' => __('user.avatar.error.size', ['3']), |
||
170 | ] |
||
171 | ) |
||
172 | ->add( |
||
173 | 'avatar', |
||
174 | 'avatar-mime', |
||
175 | [ |
||
176 | 'rule' => ['mimetype', ['image/jpeg', 'image/png']], |
||
177 | 'message' => __('user.avatar.error.mime'), |
||
178 | ] |
||
179 | ) |
||
180 | ->add( |
||
181 | 'avatar', |
||
182 | 'avatar-dimension', |
||
183 | [ |
||
184 | 'rule' => [ |
||
185 | 'dimensions', |
||
186 | [ |
||
187 | 'min' => ['w' => 100, 'h' => 100], |
||
188 | 'max' => ['w' => 1500, 'h' => 1500], |
||
189 | ], |
||
190 | ], |
||
191 | 'message' => __( |
||
192 | 'user.avatar.error.dimension', |
||
193 | ['100x100', '1500x1500'] |
||
194 | ), |
||
195 | 'provider' => 'proffer', |
||
196 | ] |
||
197 | ); |
||
198 | |||
199 | $validator |
||
200 | ->notEmpty('password') |
||
201 | ->add( |
||
202 | 'password', |
||
203 | [ |
||
204 | 'pwConfirm' => [ |
||
205 | 'rule' => [$this, 'validateConfirmPassword'], |
||
206 | 'last' => true, |
||
207 | 'message' => __('error_password_confirm'), |
||
208 | ], |
||
209 | ] |
||
210 | ); |
||
211 | |||
212 | $validator |
||
213 | ->notEmpty('password_old') |
||
214 | ->add( |
||
215 | 'password_old', |
||
216 | [ |
||
217 | 'pwCheckOld' => [ |
||
218 | 'rule' => [$this, 'validateCheckOldPassword'], |
||
219 | 'last' => true, |
||
220 | 'message' => 'validation_error_pwCheckOld', |
||
221 | ], |
||
222 | ] |
||
223 | ); |
||
224 | |||
225 | $validator |
||
226 | ->notEmpty('username', __('error_no_name')) |
||
227 | ->add( |
||
228 | 'username', |
||
229 | [ |
||
230 | 'isUnique' => [ |
||
231 | 'rule' => 'validateIsUniqueCiString', |
||
232 | 'provider' => 'saito', |
||
233 | 'last' => true, |
||
234 | 'message' => __('error_name_reserved'), |
||
235 | ], |
||
236 | 'isUsernameEqual' => [ |
||
237 | 'on' => 'create', |
||
238 | 'last' => true, |
||
239 | 'rule' => [$this, 'validateUsernameEqual'], |
||
240 | ], |
||
241 | 'hasAllowedChars' => [ |
||
242 | 'rule' => [$this, 'validateHasAllowedChars'], |
||
243 | 'message' => __( |
||
244 | 'model.user.validate.username.hasAllowedChars' |
||
245 | ), |
||
246 | ], |
||
247 | 'isNotEmoji' => [ |
||
248 | 'rule' => 'utf8', |
||
249 | 'message' => __( |
||
250 | 'model.user.validate.username.hasAllowedChars' |
||
251 | ), |
||
252 | ], |
||
253 | 'maxLength' => [ |
||
254 | 'last' => true, |
||
255 | 'message' => __('vld.users.username.maxlength', self::USERNAME_MAXLENGTH), |
||
256 | 'rule' => ['maxLength', self::USERNAME_MAXLENGTH], |
||
257 | ], |
||
258 | ] |
||
259 | ); |
||
260 | |||
261 | $validator |
||
262 | ->notEmpty('user_email') |
||
263 | ->add( |
||
264 | 'user_email', |
||
265 | [ |
||
266 | 'isUnique' => [ |
||
267 | 'rule' => 'validateUnique', |
||
268 | 'provider' => 'table', |
||
269 | 'last' => true, |
||
270 | 'message' => __('error_email_reserved'), |
||
271 | ], |
||
272 | 'isEmail' => [ |
||
273 | 'rule' => ['email', true], |
||
274 | 'last' => true, |
||
275 | 'message' => __('error_email_wrong'), |
||
276 | ], |
||
277 | ] |
||
278 | ); |
||
279 | |||
280 | $validator->add( |
||
281 | 'user_forum_refresh_time', |
||
282 | [ |
||
283 | 'numeric' => ['rule' => 'numeric'], |
||
284 | 'greaterNull' => ['rule' => ['comparison', '>=', 0]], |
||
285 | 'maxLength' => ['rule' => ['maxLength', 3]], |
||
286 | ] |
||
287 | ); |
||
288 | |||
289 | $validator->add( |
||
290 | 'user_type', |
||
291 | [ |
||
292 | 'allowedType' => [ |
||
293 | 'rule' => [$this, 'validateUserRoleExists'], |
||
294 | ], |
||
295 | ] |
||
296 | ); |
||
297 | |||
298 | $validator->notEmpty('registered'); |
||
299 | |||
300 | $validator->add( |
||
301 | 'logins', |
||
302 | ['numeric' => ['rule' => ['numeric']]] |
||
303 | ); |
||
304 | |||
305 | $validator->add( |
||
306 | 'personal_messages', |
||
307 | ['bool' => ['rule' => ['boolean']]] |
||
308 | ); |
||
309 | |||
310 | $validator->add( |
||
311 | 'user_lock', |
||
312 | ['bool' => ['rule' => ['boolean']]] |
||
313 | ); |
||
314 | |||
315 | $validator |
||
316 | ->notEmpty('activate_code') |
||
317 | ->add( |
||
318 | 'activate_code', |
||
319 | [ |
||
320 | 'numeric' => ['rule' => ['numeric']], |
||
321 | 'between' => ['rule' => ['range', 0, 9999999]], |
||
322 | ] |
||
323 | ); |
||
324 | |||
325 | $validator->add( |
||
326 | 'user_signatures_hide', |
||
327 | ['bool' => ['rule' => ['boolean']]] |
||
328 | ); |
||
329 | |||
330 | $validator->add( |
||
331 | 'user_signature_images_hide', |
||
332 | ['bool' => ['rule' => ['boolean']]] |
||
333 | ); |
||
334 | |||
335 | $validator->add( |
||
336 | 'user_automaticaly_mark_as_read', |
||
337 | ['bool' => ['rule' => ['boolean']]] |
||
338 | ); |
||
339 | |||
340 | $validator->add( |
||
341 | 'user_sort_last_answer', |
||
342 | ['bool' => ['rule' => ['boolean']]] |
||
343 | ); |
||
344 | |||
345 | $validator |
||
346 | ->allowEmpty('user_color_new_postings') |
||
347 | ->add( |
||
348 | 'user_color_new_postings', |
||
349 | [ |
||
350 | 'hexformat' => [ |
||
351 | 'rule' => ['custom', '/^#?[a-f0-9]{0,6}$/i'], |
||
352 | ], |
||
353 | ] |
||
354 | ); |
||
355 | $validator |
||
356 | ->allowEmpty('user_color_old_postings') |
||
357 | ->add( |
||
358 | 'user_color_old_postings', |
||
359 | [ |
||
360 | 'hexformat' => [ |
||
361 | 'rule' => ['custom', '/^#?[a-f0-9]{0,6}$/i'], |
||
362 | ], |
||
363 | ] |
||
364 | ); |
||
365 | $validator |
||
366 | ->allowEmpty('user_color_actual_posting') |
||
367 | ->add( |
||
368 | 'user_color_actual_posting', |
||
369 | [ |
||
370 | 'hexformat' => [ |
||
371 | 'rule' => ['custom', '/^#?[a-f0-9]{0,6}$/i'], |
||
372 | ], |
||
373 | ] |
||
374 | ); |
||
375 | |||
376 | return $validator; |
||
377 | } |
||
378 | |||
379 | /** |
||
380 | * {@inheritDoc} |
||
381 | */ |
||
382 | protected function _initializeSchema(TableSchema $table) |
||
383 | { |
||
384 | $table->setColumnType('avatar', 'proffer.file'); |
||
385 | $table->setColumnType('user_category_custom', 'serialize'); |
||
386 | |||
387 | return $table; |
||
388 | } |
||
389 | |||
390 | /** |
||
391 | * set last refresh |
||
392 | * |
||
393 | * @param int $userId user-ID |
||
394 | * @param DateTimeInterface|null $lastRefresh last refresh |
||
395 | * @return void |
||
396 | */ |
||
397 | public function setLastRefresh(int $userId, DateTimeInterface $lastRefresh = null) |
||
398 | { |
||
399 | Stopwatch::start('Users->setLastRefresh()'); |
||
400 | $data['last_refresh_tmp'] = bDate(); |
||
401 | |||
402 | if ($lastRefresh) { |
||
403 | $data['last_refresh'] = $lastRefresh; |
||
404 | } |
||
405 | |||
406 | $this->query() |
||
407 | ->update() |
||
408 | ->set($data) |
||
409 | ->where(['id' => $userId]) |
||
410 | ->execute(); |
||
411 | |||
412 | Stopwatch::end('Users->setLastRefresh()'); |
||
413 | } |
||
414 | |||
415 | /** |
||
416 | * Increment logins |
||
417 | * |
||
418 | * @param Entity $user user |
||
419 | * @param int $amount amount |
||
420 | * @return void |
||
421 | * @throws \Exception |
||
422 | */ |
||
423 | public function incrementLogins(Entity $user, $amount = 1) |
||
424 | { |
||
425 | $data = [ |
||
426 | 'logins' => $user->get('logins') + $amount, |
||
427 | 'last_login' => bDate(), |
||
428 | ]; |
||
429 | $this->patchEntity($user, $data); |
||
430 | if (!$this->save($user)) { |
||
431 | throw new \Exception('Increment logins failed.'); |
||
432 | } |
||
433 | } |
||
434 | |||
435 | /** |
||
436 | * get userlist |
||
437 | * |
||
438 | * @return array |
||
439 | */ |
||
440 | public function userlist() |
||
441 | { |
||
442 | return $this->find( |
||
443 | 'list', |
||
444 | ['keyField' => 'id', 'valueField' => 'username'] |
||
445 | )->toArray(); |
||
446 | } |
||
447 | |||
448 | /** |
||
449 | * Removes a user and all his data execpt for his entries |
||
450 | * |
||
451 | * @param int $userId user-ID |
||
452 | * @return bool |
||
453 | */ |
||
454 | public function deleteAllExceptEntries(int $userId) |
||
455 | { |
||
456 | $user = $this->get($userId); |
||
457 | if (empty($user)) { |
||
458 | return false; |
||
459 | } |
||
460 | |||
461 | try { |
||
462 | $this->Entries->anonymizeEntriesFromUser($userId); |
||
463 | $this->UserIgnores->deleteUser($userId); |
||
464 | $this->delete($user); |
||
465 | } catch (\Exception $e) { |
||
466 | return false; |
||
467 | } |
||
468 | $this->dispatchDbEvent('Cmd.Cache.clear', ['cache' => 'Thread']); |
||
469 | |||
470 | return true; |
||
471 | } |
||
472 | |||
473 | /** |
||
474 | * Updates the hashed password if hash-algo is out-of-date |
||
475 | * |
||
476 | * @param int $userId user-ID |
||
477 | * @param string $password password |
||
478 | * @return void |
||
479 | */ |
||
480 | public function autoUpdatePassword(int $userId, string $password): void |
||
481 | { |
||
482 | $user = $this->get($userId, ['fields' => ['id', 'password']]); |
||
483 | $oldPassword = $user->get('password'); |
||
484 | $needsRehash = $this->getPasswordHasher()->needsRehash($oldPassword); |
||
485 | if ($needsRehash) { |
||
486 | $user->set('password', $password); |
||
487 | $this->save($user); |
||
488 | } |
||
489 | } |
||
490 | |||
491 | /** |
||
492 | * Post processing when updating a username. |
||
493 | * |
||
494 | * @param Entity $entity The updated entity. |
||
495 | * @return void |
||
496 | */ |
||
497 | protected function updateUsername(Entity $entity) |
||
498 | { |
||
499 | // Using associating with $this->Entries->updateAll() not working in |
||
500 | // Cake 3.8. |
||
501 | $Entries = TableRegistry::getTableLocator()->get('Entries'); |
||
502 | $Entries->updateAll( |
||
503 | ['name' => $entity->get('username')], |
||
504 | ['user_id' => $entity->get('id')] |
||
505 | ); |
||
506 | |||
507 | $Entries->updateAll( |
||
508 | ['edited_by' => $entity->get('username')], |
||
509 | ['edited_by' => $entity->getOriginal('username')] |
||
510 | ); |
||
511 | |||
512 | $this->dispatchDbEvent('Cmd.Cache.clear', ['cache' => 'Thread']); |
||
513 | } |
||
514 | |||
515 | /** |
||
516 | * {@inheritDoc} |
||
517 | */ |
||
518 | public function afterSave(Event $event, Entity $entity, \ArrayObject $options) |
||
519 | { |
||
520 | if ($entity->isDirty('username')) { |
||
521 | $this->updateUsername($entity); |
||
522 | } |
||
523 | } |
||
524 | |||
525 | /** |
||
526 | * {@inheritDoc} |
||
527 | */ |
||
528 | public function beforeSave( |
||
529 | Event $event, |
||
530 | Entity $entity, |
||
531 | \ArrayObject $options |
||
532 | ) { |
||
533 | if ($entity->isDirty('password')) { |
||
534 | $hashedPassword = $this->getPasswordHasher()->hash($entity->get('password')); |
||
535 | $entity->set('password', $hashedPassword); |
||
536 | } |
||
537 | } |
||
538 | |||
539 | /** |
||
540 | * {@inheritDoc} |
||
541 | */ |
||
542 | public function beforeValidate( |
||
543 | Event $event, |
||
544 | Entity $entity, |
||
545 | \ArrayObject $options, |
||
546 | Validator $validator |
||
547 | ) { |
||
548 | if ($entity->isDirty('user_forum_refresh_time')) { |
||
549 | $time = $entity->get('user_forum_refresh_time'); |
||
550 | if (empty($time)) { |
||
551 | $entity->set('user_forum_refresh_time', 0); |
||
552 | } |
||
553 | } |
||
554 | } |
||
555 | |||
556 | /** |
||
557 | * validate old password |
||
558 | * |
||
559 | * @param string $value value |
||
560 | * @param array $context context |
||
561 | * @return bool |
||
562 | */ |
||
563 | public function validateCheckOldPassword($value, array $context) |
||
564 | { |
||
565 | $userId = $context['data']['id']; |
||
566 | $oldPasswordHash = $this->get($userId, ['fields' => ['password']]) |
||
567 | ->get('password'); |
||
568 | |||
569 | return $this->getPasswordHasher()->check($value, $oldPasswordHash); |
||
570 | } |
||
571 | |||
572 | /** |
||
573 | * validate confirm password |
||
574 | * |
||
575 | * @param string $value value |
||
576 | * @param array $context context |
||
577 | * @return bool |
||
578 | */ |
||
579 | public function validateConfirmPassword($value, array $context) |
||
580 | { |
||
581 | if ($value === $context['data']['password_confirm']) { |
||
582 | return true; |
||
583 | } |
||
584 | |||
585 | return false; |
||
586 | } |
||
587 | |||
588 | /** |
||
589 | * Validate allowed chars |
||
590 | * |
||
591 | * @param string $value value |
||
592 | * @param array $context context |
||
593 | * @return bool |
||
594 | */ |
||
595 | public function validateHasAllowedChars($value, array $context) |
||
596 | { |
||
597 | foreach ($this->getConfig('user_name_disallowed_chars') as $char) { |
||
598 | if (mb_strpos($value, $char) !== false) { |
||
599 | return false; |
||
600 | } |
||
601 | } |
||
602 | |||
603 | return true; |
||
604 | } |
||
605 | |||
606 | /** |
||
607 | * Check if the role exists |
||
608 | * |
||
609 | * @param string $value value |
||
610 | * @param array $context context |
||
611 | * @return bool|string |
||
612 | */ |
||
613 | public function validateUserRoleExists($value, array $context) |
||
623 | } |
||
624 | |||
625 | /** |
||
626 | * checks if equal username exists |
||
627 | * |
||
628 | * @param string $value value |
||
629 | * @param array $context context |
||
630 | * @return bool|string |
||
631 | */ |
||
632 | public function validateUsernameEqual($value, array $context) |
||
633 | { |
||
634 | Stopwatch::start('validateUsernameEqual'); |
||
650 | } |
||
651 | |||
652 | /** |
||
653 | * Registers new user |
||
654 | * |
||
655 | * @param array $data data |
||
656 | * @param bool $activate activate |
||
657 | * @return EntityInterface |
||
658 | */ |
||
659 | public function register($data, $activate = false): EntityInterface |
||
660 | { |
||
661 | $defaults = [ |
||
662 | 'registered' => bDate(), |
||
663 | 'user_type' => 'user', |
||
664 | ]; |
||
665 | $fields = [ |
||
666 | 'password', |
||
667 | 'registered', |
||
668 | 'user_email', |
||
669 | 'user_type', |
||
670 | 'username', |
||
671 | ]; |
||
672 | |||
673 | if ($activate !== true) { |
||
674 | $defaults['activate_code'] = mt_rand(1000000, 9999999); |
||
675 | $fields[] = 'activate_code'; |
||
676 | } |
||
677 | |||
678 | $data = array_merge($data, $defaults); |
||
679 | |||
680 | $fieldFilter = (new FieldFilter())->setConfig('register', $fields); |
||
681 | if (!$fieldFilter->requireFields($data, 'register')) { |
||
682 | throw new \RuntimeException( |
||
683 | 'Required fields for registration were not provided.', |
||
684 | 1563789683 |
||
685 | ); |
||
686 | } |
||
687 | |||
688 | $user = $this->newEntity($data, ['fields' => $fields]); |
||
689 | $errors = $user->getErrors(); |
||
690 | if (!empty($errors)) { |
||
691 | return $user; |
||
692 | } |
||
693 | $user = $this->save($user); |
||
694 | if ($user !== false) { |
||
695 | $this->dispatchDbEvent('saito.core.user.register.after', [ |
||
696 | 'subject' => $user, |
||
697 | 'table' => $this, |
||
698 | ]); |
||
699 | } |
||
700 | |||
701 | return $user; |
||
702 | } |
||
703 | |||
704 | /** |
||
705 | * Garbage collection for registration |
||
706 | * |
||
707 | * Deletes all timed out and unactivated registrations |
||
708 | * |
||
709 | * @return void |
||
710 | */ |
||
711 | public function registerGc() |
||
712 | { |
||
713 | $this->deleteAll( |
||
714 | [ |
||
715 | 'activate_code >' => 0, |
||
716 | 'registered <' => bDate(time() - 86400), |
||
717 | ] |
||
718 | ); |
||
719 | } |
||
720 | |||
721 | /** |
||
722 | * calls garbage collection for UserBlock |
||
723 | * |
||
724 | * UserBlock is lazy-loaded rarely and gc may not trigger often enough (at |
||
725 | * least with manual blocking and ignore blocking only) |
||
726 | * |
||
727 | * @return void |
||
728 | */ |
||
729 | public function userBlockGc() |
||
730 | { |
||
731 | $this->UserBlocks->gc(); |
||
732 | } |
||
733 | |||
734 | /** |
||
735 | * activates user |
||
736 | * |
||
737 | * @param int $userId user-ID |
||
738 | * @param string $code activation code |
||
739 | * @return array|bool false if activation failed; array with status and |
||
740 | * user data on success |
||
741 | * @throws \InvalidArgumentException |
||
742 | */ |
||
743 | public function activate(int $userId, string $code) |
||
744 | { |
||
745 | try { |
||
746 | $user = $this->get($userId); |
||
747 | } catch (RecordNotFoundException $e) { |
||
748 | throw new \InvalidArgumentException(); |
||
749 | } |
||
750 | |||
751 | $activateCode = strval($user->get('activate_code')); |
||
752 | |||
753 | if (empty($activateCode)) { |
||
754 | return ['status' => 'already', 'User' => $user]; |
||
755 | } elseif ($activateCode !== $code) { |
||
756 | return false; |
||
757 | } |
||
758 | |||
759 | $user->set('activate_code', 0); |
||
760 | $user = $this->save($user); |
||
761 | if ($user === false) { |
||
762 | return false; |
||
763 | } |
||
764 | |||
765 | $this->dispatchDbEvent('saito.core.user.activate.after', [ |
||
766 | 'subject' => $user, |
||
767 | 'table' => $this, |
||
768 | ]); |
||
769 | |||
770 | return ['status' => 'activated', 'User' => $user]; |
||
771 | } |
||
772 | |||
773 | /** |
||
774 | * Count solved posting for a user. |
||
775 | * |
||
776 | * |
||
777 | * @param int $userId user-ID |
||
778 | * @return int count |
||
779 | */ |
||
780 | public function countSolved($userId) |
||
781 | { |
||
782 | $count = $this->find() |
||
783 | ->select(['Users.id']) |
||
784 | ->where(['Users.id' => $userId]) |
||
785 | ->join( |
||
786 | [ |
||
787 | 'Entries' => [ |
||
788 | 'table' => $this->Entries->getTable(), |
||
789 | 'type' => 'INNER', |
||
790 | 'conditions' => [ |
||
791 | [ |
||
792 | 'Entries.solves >' => '0', |
||
793 | 'Entries.user_id' => $userId, |
||
794 | ], |
||
795 | ], |
||
796 | ], |
||
797 | 'Root' => [ |
||
798 | 'table' => $this->Entries->getTable(), |
||
799 | 'type' => 'INNER', |
||
800 | // Don't answers to own question. |
||
801 | 'conditions' => [ |
||
802 | 'Root.id = Entries.solves', |
||
803 | 'Root.user_id != Users.id', |
||
804 | ], |
||
805 | ], |
||
806 | ] |
||
807 | ); |
||
808 | |||
809 | return $count->count(); |
||
810 | } |
||
811 | |||
812 | /** |
||
813 | * Set view categories preferences |
||
814 | * |
||
815 | * ## $category |
||
816 | * |
||
817 | * - 'all': set to all categories |
||
818 | * - array: (cat_id1 => true|1|'1', cat_id2 => true|1|'1') |
||
819 | * - int: set to single category_id |
||
820 | * |
||
821 | * @param int $userId user-ID |
||
822 | * @param string|int|array $category category |
||
823 | * @return void |
||
824 | * @throws \InvalidArgumentException |
||
825 | */ |
||
826 | public function setCategory($userId, $category) |
||
827 | { |
||
828 | $User = $this->find()->select(['id' => $userId])->first(); |
||
829 | if (!$User) { |
||
830 | throw new \InvalidArgumentException( |
||
831 | "Can't find user with id $userId.", |
||
832 | 1420807691 |
||
833 | ); |
||
834 | } |
||
835 | |||
836 | if ($category === 'all') { |
||
837 | //=if show all cateogries |
||
838 | $active = -1; |
||
839 | } elseif (is_array($category)) { |
||
840 | //=if set a custom set of categories |
||
841 | $active = 0; |
||
842 | |||
843 | $availableCats = $this->Entries->Categories->find('list')->toArray( |
||
844 | ); |
||
845 | $categories = array_intersect_key($category, $availableCats); |
||
846 | if (count($categories) === 0) { |
||
847 | throw new \InvalidArgumentException(); |
||
848 | } |
||
849 | $newCats = []; |
||
850 | foreach ($categories as $cat => $v) { |
||
851 | $newCats[$cat] = ($v === true || $v === 1 || $v === '1'); |
||
852 | } |
||
853 | $User->set('user_category_custom', $newCats); |
||
854 | } else { |
||
855 | //=if set a single category |
||
856 | $category = (int)$category; |
||
857 | if ( |
||
858 | $category > 0 && $this->Entries->Categories->exists((int)$category) |
||
859 | ) { |
||
860 | $active = $category; |
||
861 | } else { |
||
862 | throw new \InvalidArgumentException(); |
||
863 | } |
||
864 | } |
||
865 | |||
866 | $User->set('user_category_active', $active); |
||
867 | $this->save($User); |
||
868 | } |
||
869 | |||
870 | /** |
||
871 | * Get default password hasher for hashing user passwords. |
||
872 | * |
||
873 | * @return PasswordHasherInterface |
||
874 | */ |
||
875 | public function getPasswordHasher(): PasswordHasherInterface |
||
876 | { |
||
877 | return PasswordHasherFactory::build(DefaultPasswordHasher::class); |
||
878 | } |
||
879 | |||
880 | /** |
||
881 | * Finds a user with additional profil informations from associated tables |
||
882 | * |
||
883 | * @param Query $query query |
||
884 | * @param array $options options |
||
885 | * @return Query |
||
886 | */ |
||
887 | public function findProfile(Query $query, array $options): Query |
||
888 | { |
||
889 | $query |
||
890 | // ->enableHydration(false) |
||
891 | ->contain( |
||
892 | [ |
||
893 | 'UserIgnores' => function ($query) { |
||
894 | return $query->enableHydration(false)->select( |
||
895 | ['blocked_user_id', 'user_id'] |
||
896 | ); |
||
897 | }, |
||
898 | ] |
||
899 | ); |
||
900 | |||
901 | return $query; |
||
902 | } |
||
903 | |||
904 | /** |
||
905 | * Find all sorted by username |
||
906 | * |
||
907 | * @param Query $query query |
||
908 | * @param array $options options |
||
909 | * @return Query |
||
910 | */ |
||
911 | public function findPaginated(Query $query, array $options) |
||
912 | { |
||
913 | $query |
||
914 | ->contain(['UserOnline']) |
||
915 | ->order(['Users.username' => 'ASC']); |
||
916 | |||
917 | return $query; |
||
918 | } |
||
919 | |||
920 | /** |
||
921 | * Find the latest, successfully registered user |
||
922 | * |
||
923 | * @param Query $query query |
||
924 | * @param array $options options |
||
925 | * @return Query |
||
926 | */ |
||
927 | public function findLatest(Query $query, array $options) |
||
934 | } |
||
935 | } |
||
936 |
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.