[TOOLS] Fix (most) issues found by PHPStan

This commit is contained in:
Hugo Sales 2021-11-25 23:08:30 +00:00
parent 8fd02ef152
commit b1262919da
Signed by: someonewithpc
GPG Key ID: 7D0C7EAFC9D835A0
18 changed files with 221 additions and 134 deletions

View File

@ -1,6 +1,6 @@
<?php
declare(strict_types=1);
declare(strict_types = 1);
// {{{ License
@ -27,8 +27,11 @@ use App\Core\Controller;
use App\Core\DB\DB;
use App\Core\Event;
use App\Core\Form;
use function App\Core\I18n\_m;
use App\Core\Log;
use App\Core\Router\Router;
use App\Util\Common;
use App\Util\Exception\ClientException;
use App\Util\Exception\InvalidFormException;
use App\Util\Exception\NoLoggedInUser;
use App\Util\Exception\NoSuchNoteException;
@ -36,24 +39,23 @@ use App\Util\Exception\RedirectException;
use Plugin\Favourite\Entity\Favourite as FavouriteEntity;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\Request;
use function App\Core\I18n\_m;
class Favourite extends Controller
{
/**
* @throws RedirectException
* @throws NoSuchNoteException
* @throws InvalidFormException
* @throws \App\Util\Exception\ServerException
* @throws InvalidFormException
* @throws NoLoggedInUser
* @throws NoSuchNoteException
* @throws RedirectException
*/
public function favouriteAddNote(Request $request, int $id): bool|array
{
$user = Common::ensureLoggedIn();
$opts = ['id' => $id];
$user = Common::ensureLoggedIn();
$actor_id = $user->getId();
$opts = ['id' => $id];
$add_favourite_note = DB::find('note', $opts);
if (is_null($add_favourite_note)) {
if (\is_null($add_favourite_note)) {
throw new NoSuchNoteException();
}
@ -62,7 +64,7 @@ class Favourite extends Controller
[
'label' => _m('Favourite note!'),
'attr' => [
'title' => _m('Favourite this note!')
'title' => _m('Favourite this note!'),
],
],
],
@ -71,41 +73,52 @@ class Favourite extends Controller
$form_add_to_favourite->handleRequest($request);
if ($form_add_to_favourite->isSubmitted()) {
$opts = ['note_id' => $id, 'actor_id' => $user->getId()];
$opts = ['note_id' => $id, 'actor_id' => $user->getId()];
$note_already_favourited = DB::find('favourite', $opts);
if (is_null($note_already_favourited)) {
if (\is_null($note_already_favourited)) {
$opts = ['note_id' => $id, 'actor_id' => $user->getId()];
DB::persist(FavouriteEntity::create($opts));
DB::flush();
}
if (array_key_exists('from', $get_params = $this->params())) {
# TODO anchor on element id
throw new RedirectException($get_params['from']);
// Redirect user to where they came from
// Prevent open redirect
if (!\is_null($from = $this->string('from'))) {
if (Router::isAbsolute($from)) {
Log::warning("Actor {$actor_id} attempted to reply to a note and then get redirected to another host, or the URL was invalid ({$from})");
throw new ClientException(_m('Can not redirect to outside the website from here'), 400); // 400 Bad request (deceptive)
} else {
// TODO anchor on element id
throw new RedirectException($from);
}
} else {
// If we don't have a URL to return to, go to the instance root
throw new RedirectException('root');
}
}
return [
'_template' => 'favourite/add_to_favourites.html.twig',
'note' => $add_favourite_note,
'add_favourite' => $form_add_to_favourite->createView(),
'_template' => 'favourite/add_to_favourites.html.twig',
'note' => $add_favourite_note,
'add_favourite' => $form_add_to_favourite->createView(),
];
}
/**
* @throws RedirectException
* @throws NoSuchNoteException
* @throws InvalidFormException
* @throws \App\Util\Exception\ServerException
* @throws InvalidFormException
* @throws NoLoggedInUser
* @throws NoSuchNoteException
* @throws RedirectException
*/
public function favouriteRemoveNote(Request $request, int $id): array
{
$user = Common::ensureLoggedIn();
$opts = ['note_id' => $id, 'actor_id' => $user->getId()];
$user = Common::ensureLoggedIn();
$actor_id = $user->getId();
$opts = ['note_id' => $id, 'actor_id' => $user->getId()];
$remove_favourite_note = DB::find('favourite', $opts);
if (is_null($remove_favourite_note)) {
if (\is_null($remove_favourite_note)) {
throw new NoSuchNoteException();
}
@ -114,7 +127,7 @@ class Favourite extends Controller
[
'label' => _m('Remove favourite'),
'attr' => [
'title' => _m('Remove note from favourites.')
'title' => _m('Remove note from favourites.'),
],
],
],
@ -127,17 +140,27 @@ class Favourite extends Controller
DB::flush();
}
if (array_key_exists('from', $get_params = $this->params())) {
# TODO anchor on element id
throw new RedirectException($get_params['from']);
// Redirect user to where they came from
// Prevent open redirect
if (!\is_null($from = $this->string('from'))) {
if (Router::isAbsolute($from)) {
Log::warning("Actor {$actor_id} attempted to reply to a note and then get redirected to another host, or the URL was invalid ({$from})");
throw new ClientException(_m('Can not redirect to outside the website from here'), 400); // 400 Bad request (deceptive)
} else {
// TODO anchor on element id
throw new RedirectException($from);
}
} else {
// If we don't have a URL to return to, go to the instance root
throw new RedirectException('root');
}
}
$note = DB::find('note', ['id' => $id]);
return [
'_template' => 'favourite/remove_from_favourites.html.twig',
'note' => $note,
'remove_favourite' => $form_remove_favourite->createView(),
'_template' => 'favourite/remove_from_favourites.html.twig',
'note' => $note,
'remove_favourite' => $form_remove_favourite->createView(),
];
}
@ -155,8 +178,8 @@ class Favourite extends Controller
Event::handle('FormatNoteList', [$notes, &$notes_out]);
return [
'_template' => 'network/feed.html.twig',
'notes' => $notes_out,
'_template' => 'network/feed.html.twig',
'notes' => $notes_out,
'page_title' => 'Favourites timeline.',
];
}
@ -170,9 +193,9 @@ class Favourite extends Controller
/**
* Reverse favourites stream
*
* @return array template
* @throws NoLoggedInUser user not logged in
*
* @return array template
*/
public function reverseFavouritesByActorId(Request $request, int $id): array
{
@ -189,8 +212,8 @@ class Favourite extends Controller
Event::handle('FormatNoteList', [$notes, &$notes_out]);
return [
'_template' => 'network/feed.html.twig',
'notes' => $notes,
'_template' => 'network/feed.html.twig',
'notes' => $notes,
'page_title' => 'Reverse favourites timeline.',
];
}

View File

@ -26,17 +26,18 @@ namespace Plugin\Repeat\Controller;
use App\Core\Controller;
use App\Core\DB\DB;
use App\Core\Form;
use App\Entity\Actor;
use Component\Posting\Posting;
use function App\Core\I18n\_m;
use App\Core\Log;
use App\Core\Router\Router;
use App\Entity\Actor;
use App\Entity\Language;
use App\Entity\Note;
use App\Util\Common;
use App\Util\Exception\ClientException;
use App\Util\Exception\NoLoggedInUser;
use App\Util\Exception\NoSuchNoteException;
use App\Util\Exception\RedirectException;
use Component\Posting\Posting;
use Plugin\Repeat\Entity\NoteRepeat;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\Request;
@ -54,9 +55,9 @@ class Repeat extends Controller
*/
public function repeatAddNote(Request $request, int $id): bool|array
{
$user = Common::ensureLoggedIn();
$user = Common::ensureLoggedIn();
$actor_id = $user->getId();
$actor_id = $user->getId();
$opts = ['actor_id' => $actor_id, 'repeat_of' => $id];
$note_already_repeated = DB::count('note_repeat', $opts) >= 1;
@ -88,9 +89,10 @@ class Repeat extends Controller
// Create a new note with the same content as the original
$repeat = Posting::storeLocalNote(
actor: Actor::getById($actor_id),
content:$note->getContent(),
content: $note->getContent(),
content_type: $note->getContentType(),
processed_attachments: $note->getAttachmentsWithTitle()
language: Language::getFromId($note->getLanguageId())->getLocale(),
processed_attachments: $note->getAttachmentsWithTitle(),
);
// Find the id of the note we just created
@ -112,13 +114,13 @@ class Repeat extends Controller
// Redirect user to where they came from
// Prevent open redirect
if (\array_key_exists('from', (array) $get_params = $this->params())) {
if (Router::isAbsolute($get_params['from'])) {
Log::warning("Actor {$actor_id} attempted to reply to a note and then get redirected to another host, or the URL was invalid ({$get_params['from']})");
if (!\is_null($from = $this->string('from'))) {
if (Router::isAbsolute($from)) {
Log::warning("Actor {$actor_id} attempted to reply to a note and then get redirected to another host, or the URL was invalid ({$from})");
throw new ClientException(_m('Can not redirect to outside the website from here'), 400); // 400 Bad request (deceptive)
} else {
// TODO anchor on element id
throw new RedirectException($get_params['from']);
throw new RedirectException($from);
}
} else {
// If we don't have a URL to return to, go to the instance root
@ -143,6 +145,7 @@ class Repeat extends Controller
public function repeatRemoveNote(Request $request, int $id): array
{
$user = Common::ensureLoggedIn();
$actor_id = $user->getId();
$opts = ['id' => $id];
$remove_repeat_note = DB::find('note', $opts);
if (\is_null($remove_repeat_note)) {
@ -176,16 +179,17 @@ class Repeat extends Controller
// Redirect user to where they came from
// Prevent open redirect
if (\array_key_exists('from', (array) $get_params = $this->params())) {
if (Router::isAbsolute($get_params['from'])) {
Log::warning("Actor {$actor_id} attempted to reply to a note and then get redirected to another host, or the URL was invalid ({$get_params['from']})");
if (!\is_null($from = $this->string('from'))) {
if (Router::isAbsolute($from)) {
Log::warning("Actor {$actor_id} attempted to reply to a note and then get redirected to another host, or the URL was invalid ({$from})");
throw new ClientException(_m('Can not redirect to outside the website from here'), 400); // 400 Bad request (deceptive)
} else {
// TODO anchor on element id
throw new RedirectException($get_params['from']);
throw new RedirectException($from);
}
} else {
throw new RedirectException('root'); // If we don't have a URL to return to, go to the instance root
// If we don't have a URL to return to, go to the instance root
throw new RedirectException('root');
}
}

View File

@ -39,6 +39,7 @@ use App\Util\Exception\ClientException;
use App\Util\Exception\InvalidFormException;
use App\Util\Exception\NoSuchNoteException;
use App\Util\Exception\RedirectException;
use App\Util\Form\FormFields;
use Component\Posting\Posting;
use Plugin\Reply\Entity\NoteReply;
use Symfony\Component\Form\Extension\Core\Type\FileType;
@ -70,13 +71,10 @@ class Reply extends Controller
throw new NoSuchNoteException();
}
// TODO shouldn't this be the posting form?
$form = Form::create([
['content', TextareaType::class, [
'label' => _m('Reply'),
'label_attr' => ['class' => 'section-form-label'],
'help' => _m('Please input your reply.'),
],
],
['content', TextareaType::class, ['label' => _m('Reply'), 'label_attr' => ['class' => 'section-form-label'], 'help' => _m('Please input your reply.')]],
FormFields::language($user->getActor(), context_actor: $note->getActor(), label: 'Note language', help: null),
['attachments', FileType::class, ['label' => ' ', 'multiple' => true, 'required' => false]],
['replyform', SubmitType::class, ['label' => _m('Submit')]],
]);
@ -91,6 +89,7 @@ class Reply extends Controller
actor: Actor::getWithPK($actor_id),
content: $data['content'],
content_type: 'text/plain', // TODO
language: $data['language'],
attachments: $data['attachments'],
);
@ -116,13 +115,13 @@ class Reply extends Controller
// Redirect user to where they came from
// Prevent open redirect
if (\array_key_exists('from', (array) $get_params = $this->params())) {
if (Router::isAbsolute($get_params['from'])) {
Log::warning("Actor {$actor_id} attempted to reply to a note and then get redirected to another host, or the URL was invalid ({$get_params['from']})");
if (!\is_null($from = $this->string('from'))) {
if (Router::isAbsolute($from)) {
Log::warning("Actor {$actor_id} attempted to reply to a note and then get redirected to another host, or the URL was invalid ({$from})");
throw new ClientException(_m('Can not redirect to outside the website from here'), 400); // 400 Bad request (deceptive)
} else {
// TODO anchor on element id
throw new RedirectException($get_params['from']);
throw new RedirectException($from);
}
} else {
// If we don't have a URL to return to, go to the instance root

View File

@ -1,6 +1,6 @@
<?php
declare(strict_types=1);
declare(strict_types = 1);
// {{{ License
@ -26,7 +26,6 @@ namespace Plugin\Reply\Entity;
use App\Core\DB\DB;
use App\Core\Entity;
use App\Entity\Note;
use function PHPUnit\Framework\isEmpty;
/**
* Entity for notices
@ -87,16 +86,16 @@ class NoteReply extends Entity
where reply_to = :note_id
order by n.created DESC
EOF,
['note_id' => $note->getId()]
['note_id' => $note->getId()],
);
}
public static function getReplyToNote(Note $note): ?int
{
$result = DB::dql('select nr.reply_to from note_reply nr '
. 'where nr.note_id = :note_id', ['note_id' => $note->getId()]);
. 'where nr.note_id = :note_id', ['note_id' => $note->getId()], );
if (!isEmpty($result)) {
if (!empty($result)) {
return $result['reply_to'];
}
@ -106,19 +105,19 @@ class NoteReply extends Entity
public static function schemaDef(): array
{
return [
'name' => 'note_reply',
'name' => 'note_reply',
'fields' => [
'note_id' => ['type' => 'int', 'not null' => true, 'foreign key' => true, 'target' => 'Note.id', 'multiplicity' => 'one to one', 'description' => 'The id of the reply itself'],
'note_id' => ['type' => 'int', 'not null' => true, 'foreign key' => true, 'target' => 'Note.id', 'multiplicity' => 'one to one', 'description' => 'The id of the reply itself'],
'actor_id' => ['type' => 'int', 'not null' => true, 'foreign key' => true, 'target' => 'Actor.id', 'multiplicity' => 'one to one', 'description' => 'Who made this reply'],
'reply_to' => ['type' => 'int', 'not null' => true, 'foreign key' => true, 'target' => 'Note.id', 'multiplicity' => 'one to one', 'description' => 'Note this is a reply of'],
],
'primary key' => ['note_id'],
'foreign keys' => [
'note_id_to_id_fkey' => ['note', ['note_id' => 'id']],
'note_reply_to_id_fkey' => ['note', ['reply_to' => 'id']],
'note_id_to_id_fkey' => ['note', ['note_id' => 'id']],
'note_reply_to_id_fkey' => ['note', ['reply_to' => 'id']],
'actor_reply_to_id_fkey' => ['actor', ['actor_id' => 'id']],
],
'indexes' => [
'indexes' => [
'note_reply_to_idx' => ['reply_to'],
],
];

View File

@ -44,7 +44,7 @@ use App\Entity\Note;
use App\Util\Common;
use App\Util\Exception\ClientException;
use App\Util\Exception\NotFoundException;
use NotImplementedException;
use App\Util\Exception\NotImplementedException;
use Symfony\Component\HttpFoundation\Request;
class Network extends Controller

View File

@ -45,6 +45,7 @@ use App\Core\Form;
use function App\Core\I18n\_m;
use App\Core\Log;
use App\Entity\ActorLanguage;
use App\Entity\Language;
use App\Entity\UserNotificationPrefs;
use App\Util\Common;
use App\Util\Exception\AuthenticationException;
@ -63,6 +64,7 @@ use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\SubmitButton;
use Symfony\Component\HttpFoundation\Request;
// }}} Imports
@ -202,12 +204,14 @@ class UserPanel extends Controller
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$go_back = $form->get('go_back')->isClicked();
/** @var SubmitButton $button */
$button = $form->get('go_back');
$go_back = $button->isClicked();
$data = $form->getData();
asort($data); // Sort by the order value
$data = array_keys($data); // This keeps the order and gives us a unique number for each
foreach ($data as $order => $locale) {
$lang = Cache::getHashMapKey('languages', $locale);
$lang = Language::getFromLocale($locale);
$actor_lang = DB::getReference('actor_language', ['actor_id' => $user->getId(), 'language_id' => $lang->getId()]);
$actor_lang->setOrdering($order + 1);
}

View File

@ -29,13 +29,14 @@ use App\Entity\LocalUser;
use App\Entity\Note;
use App\Util\Common;
use App\Util\Exception\ConfigurationException;
use App\Util\Exception\NotImplementedException;
use Functional as F;
use InvalidArgumentException;
use NotImplementedException;
use Redis;
use RedisCluster;
use Symfony\Component\Cache\Adapter;
use Symfony\Component\Cache\Adapter\ChainAdapter;
use Symfony\Component\Cache\CacheItem;
abstract class Cache
{
@ -154,7 +155,7 @@ abstract class Cache
* Retrieve a list from the cache, with a different implementation
* for redis and others, trimming to $max_count if given
*
* @param callable(?Item $item, bool &$save): string|object|array<int,mixed> $calculate
* @param callable(?CacheItem $item, bool &$save): (string|object|array<int,mixed>) $calculate
*/
public static function getList(string $key, callable $calculate, string $pool = 'default', ?int $max_count = null, ?int $left = null, ?int $right = null, float $beta = 1.0): array
{
@ -191,7 +192,8 @@ abstract class Cache
return self::$redis[$pool]->lRange($key, $left ?? 0, ($right ?? $max_count ?? 0) - 1);
} else {
return self::get($key, function () use ($calculate, $max_count) {
$res = $calculate(null);
$save = true;
$res = $calculate(null, $save);
if ($max_count != -1) {
$res = \array_slice($res, 0, $max_count);
}
@ -261,7 +263,7 @@ abstract class Cache
* Retrieve a hashmap from the cache, with a different implementation
* for redis and others. Different from lists, works with string map_keys
*
* @param callable(?Item $item, bool &$save): string|object|array<string,mixed> $calculate
* @param callable(?CacheItem $item, bool &$save): (string|object|array<string,mixed>) $calculate
* @TODO cleanup
*/
public static function getHashMap(string $map_key, callable $calculate, string $pool = 'default', float $beta = 1.0): array
@ -286,7 +288,7 @@ abstract class Cache
$save = true; // Pass by reference
$res = $calculate(null, $save);
if ($save) {
self::setHashMap($map_key, $res, $pool, $beta);
self::setHashMap($map_key, $res, $pool);
return $res;
}
}
@ -314,7 +316,7 @@ abstract class Cache
self::$redis[$pool]->exec();
}
} else {
self::set($map_key, \array_slice($value, 0, $max_count), $pool);
self::set($map_key, $value, $pool);
}
}

View File

@ -49,6 +49,7 @@ use App\Core\I18n\I18n;
use App\Core\Queue\Queue;
use App\Core\Router\Router;
use App\Kernel;
use App\Security\EmailVerifier;
use App\Util\Common;
use App\Util\Exception\ConfigurationException;
use App\Util\Formatting;
@ -172,6 +173,7 @@ class GNUsocial implements EventSubscriberInterface
Router::setRouter($this->router);
HTTPClient::setClient($this->client);
Formatting::setTwig($this->twig);
EmailVerifier::setHelpers($this->email_verify_helper, $this->mailer_helper);
Cache::setupCache();
DB::initTableMap();

View File

@ -165,7 +165,7 @@ class AttachmentThumbnail extends Entity
public static function sizeStrToInt(string $size)
{
return self::SIZE_MAP[$size] ?? self::SIZE_MAP[self::SIZE_SMALL];
return self::SIZE_MAP[$size] ?? self::SIZE_SMALL;
}
private ?Attachment $attachment = null;

View File

@ -1,6 +1,6 @@
<?php
declare(strict_types=1);
declare(strict_types = 1);
// {{{ License
// This file is part of GNU social - https://www.gnu.org/software/social
@ -21,6 +21,7 @@ declare(strict_types=1);
namespace App\Security;
use function App\Core\I18n\_m;
use App\Core\Router\Router;
use App\Entity\LocalUser;
use App\Util\Common;
@ -41,8 +42,12 @@ use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
use Symfony\Component\Security\Guard\AuthenticatorInterface;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
use function App\Core\I18n\_m;
/**
* User authenticator
@ -67,10 +72,6 @@ class Authenticator extends AbstractFormLoginAuthenticator implements Authentica
$this->csrfTokenManager = $csrfTokenManager;
}
/**
* @param Request $request
* @return bool
*/
public function supports(Request $request): bool
{
return self::LOGIN_ROUTE === $request->attributes->get('_route') && $request->isMethod('POST');
@ -92,10 +93,11 @@ class Authenticator extends AbstractFormLoginAuthenticator implements Authentica
* Get a user given credentials and a CSRF token
*
* @param array<string, string> $credentials result of self::getCredentials
* @param UserProviderInterface $userProvider
* @return ?LocalUser
*
* @throws NoSuchActorException
* @throws ServerException
*
* @return ?LocalUser
*/
public function getUser($credentials, UserProviderInterface $userProvider): ?LocalUser
{
@ -110,7 +112,7 @@ class Authenticator extends AbstractFormLoginAuthenticator implements Authentica
} elseif (Nickname::isValid($credentials['nickname_or_email'])) {
$user = LocalUser::getByNickname($credentials['nickname_or_email']);
}
if (is_null($user)) {
if (\is_null($user)) {
throw new NoSuchActorException('No such local user.');
}
$credentials['nickname'] = $user->getNickname();
@ -124,8 +126,8 @@ class Authenticator extends AbstractFormLoginAuthenticator implements Authentica
/**
* @param array<string, string> $credentials result of self::getCredentials
* @param LocalUser $user
* @return bool
* @param LocalUser $user
*
* @throws ServerException
*/
public function checkCredentials($credentials, $user): bool
@ -144,7 +146,7 @@ class Authenticator extends AbstractFormLoginAuthenticator implements Authentica
{
$nickname = $token->getUser();
if ($nickname instanceof Stringable) {
$nickname = (string)$nickname;
$nickname = (string) $nickname;
} elseif ($nickname instanceof UserInterface) {
$nickname = $nickname->getUserIdentifier();
}

View File

@ -4,7 +4,8 @@ declare(strict_types = 1);
namespace App\Security;
use Doctrine\ORM\EntityManagerInterface;
use App\Core\DB\DB;
use App\Entity\LocalUser;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Mailer\MailerInterface;
@ -12,22 +13,23 @@ use Symfony\Component\Security\Core\User\UserInterface;
use SymfonyCasts\Bundle\VerifyEmail\Exception\VerifyEmailExceptionInterface;
use SymfonyCasts\Bundle\VerifyEmail\VerifyEmailHelperInterface;
class EmailVerifier
abstract class EmailVerifier
{
private $verifyEmailHelper;
private $mailer;
private $entityManager;
private static $verifyEmailHelper;
private static $mailer;
public function __construct(VerifyEmailHelperInterface $helper, MailerInterface $mailer, EntityManagerInterface $manager)
public static function setHelpers(VerifyEmailHelperInterface $helper, MailerInterface $mailer)
{
$this->verifyEmailHelper = $helper;
$this->mailer = $mailer;
$this->entityManager = $manager;
self::$verifyEmailHelper = $helper;
self::$mailer = $mailer;
}
public function sendEmailConfirmation(string $verifyEmailRouteName, UserInterface $user, TemplatedEmail $email): void
/**
* @param LocalUser $user
*/
public static function sendEmailConfirmation(string $verifyEmailRouteName, UserInterface $user, TemplatedEmail $email): void
{
$signatureComponents = $this->verifyEmailHelper->generateSignature(
$signatureComponents = self::$verifyEmailHelper->generateSignature(
$verifyEmailRouteName,
$user->getId(),
$user->getOutgoingEmail(),
@ -41,19 +43,19 @@ class EmailVerifier
$email->context($context);
$this->mailer->send($email);
self::$mailer->send($email);
}
/**
* @param LocalUser $user
*
* @throws VerifyEmailExceptionInterface
*/
public function handleEmailConfirmation(Request $request, UserInterface $user): void
public static function handleEmailConfirmation(Request $request, UserInterface $user): void
{
$this->verifyEmailHelper->validateEmailConfirmation($request->getUri(), $user->getId(), $user->getOutgoingEmail());
$user->setIsVerified(true);
$this->entityManager->persist($user);
$this->entityManager->flush();
self::$verifyEmailHelper->validateEmailConfirmation($request->getUri(), $user->getId(), $user->getOutgoingEmail());
$user->setIsEmailVerified(true);
DB::persist($user);
DB::flush();
}
}

View File

@ -99,7 +99,7 @@ abstract class Common
/**
* Set sysadmin's configuration preferences for GNU social
*
* @param $transient keep this setting in memory only
* @param bool $transient keep this setting in memory only
*/
public static function setConfig(string $section, string $setting, $value, bool $transient = false): void
{

View File

@ -0,0 +1,49 @@
<?php
declare(strict_types = 1);
// {{{ License
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU social is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
// }}}
/**
* Class for 'assertion' exceptions. Logs when an unexpected state is found and is treated as a ServerException downstream
* HTTP code 500
*
* @category Exception
* @package GNUsocial
*
* @author Hugo Sales <hugo@hsal.es>
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
namespace App\Util\Exception;
use App\Core\Log;
use Throwable;
class BugFoundException extends ServerException
{
public function __construct(string $log_message, string $message = '', int $code = 500, ?Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
$frame = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, limit: 2)[1];
Log::critical("{$log_message} in {$frame['file']}:{$frame['line']}");
}
}

View File

@ -43,13 +43,13 @@ class EmailTakenException extends EmailException
public function __construct(?Actor $actor = null, ?string $msg = null, int $code = 400)
{
$this->profile = $actor;
$this->actor = $actor;
parent::__construct($msg, $code);
}
protected function defaultMessage(): string
{
// TRANS: Validation error in form for registration, profile and group settings, etc.
// TRANS: Validation error in form for registration, actor and group settings, etc.
return _m('Email is already in use on this server.');
}
}

View File

@ -52,13 +52,13 @@ class NicknameTakenException extends NicknameException
public function __construct(?Actor $actor = null, ?string $msg = null, int $code = 400)
{
$this->profile = $actor;
$this->actor = $actor;
parent::__construct($msg, $code);
}
protected function defaultMessage(): string
{
// TRANS: Validation error in form for registration, profile and group settings, etc.
// TRANS: Validation error in form for registration, actor and group settings, etc.
return _m('Nickname is already in use on this server.');
}
}

View File

@ -1,9 +1,10 @@
<?php
declare(strict_types=1);
declare(strict_types = 1);
namespace App\Util\Form;
use function App\Core\I18n\_m;
use App\Entity\Actor;
use App\Entity\Language;
use App\Util\Common;
@ -12,7 +13,6 @@ use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
use function App\Core\I18n\_m;
abstract class FormFields
{
@ -29,13 +29,13 @@ abstract class FormFields
return [
'password', RepeatedType::class,
[
'type' => PasswordType::class,
'type' => PasswordType::class,
'first_options' => [
'label' => _m('Password'),
'label_attr' => ['class' => 'section-form-label'],
'attr' => array_merge(['placeholder' => _m('********'), 'required' => $options['required'] ?? true], $options['attr'] ?? []),
'constraints' => $constraints,
'help' => _m('Write a password with at least {min_length} characters, and a maximum of {max_length}.', ['min_length' => Common::config('password', 'min_length'), 'max_length' => Common::config('password', 'max_length')]),
'help' => _m('Write a password with at least {min_length} characters, and a maximum of {max_length}.', ['min_length' => Common::config('password', 'min_length'), 'max_length' => Common::config('password', 'max_length')]),
],
'second_options' => [
'label' => _m('Repeat Password'),
@ -45,8 +45,8 @@ abstract class FormFields
'required' => $options['required'] ?? true,
'constraints' => $constraints,
],
'mapped' => false,
'required' => $options['required'] ?? true,
'mapped' => false,
'required' => $options['required'] ?? true,
'invalid_message' => _m('The password fields must match'),
],
];
@ -67,24 +67,24 @@ abstract class FormFields
'constraints' => [
new NotBlank(['message' => _m('Please enter a password')]),
new Length(['min' => Common::config('password', 'min_length'), 'minMessage' => _m(['Your password should be at least # characters'], ['count' => Common::config('password', 'min_length')]),
'max' => Common::config('password', 'max_length'), 'maxMessage' => _m(['Your password should be at most # characters'], ['count' => Common::config('password', 'max_length')]),]),
],],
'max' => Common::config('password', 'max_length'), 'maxMessage' => _m(['Your password should be at most # characters'], ['count' => Common::config('password', 'max_length')]), ]),
], ],
];
}
public static function language(Actor $actor, ?Actor $context_actor, string $label, string $help, bool $multiple = false, bool $required = true, ?bool $use_short_display = null): array
public static function language(Actor $actor, ?Actor $context_actor, string $label, ?string $help = null, bool $multiple = false, bool $required = true, ?bool $use_short_display = null): array
{
[$language_choices, $preferred_language_choices] = Language::getSortedLanguageChoices($actor, $context_actor, use_short_display: $use_short_display);
return [
'language' . ($multiple ? 's' : ''),
ChoiceType::class,
[
'label' => _m($label),
'label' => _m($label),
'preferred_choices' => $preferred_language_choices,
'choices' => $language_choices,
'required' => $required,
'multiple' => $multiple,
'help' => _m($help),
'choices' => $language_choices,
'required' => $required,
'multiple' => $multiple,
'help' => _m($help),
],
];
}

View File

@ -24,7 +24,7 @@ declare(strict_types = 1);
namespace App\Util;
use App\Core\DB\DB;
use App\Core\Log;
use App\Util\Exception\BugFoundException;
use App\Util\Exception\DuplicateFoundException;
use App\Util\Exception\NicknameEmptyException;
use App\Util\Exception\NicknameException;
@ -155,7 +155,7 @@ class Nickname
} catch (NotFoundException) {
// continue
} catch (DuplicateFoundException) {
Log::critial("Duplicate entry in `local_user` for nickname={$nickname}");
throw new BugFoundException("Duplicate entry in `local_user` for nickname={$nickname}");
}
break;
// @codeCoverageIgnoreStart

View File

@ -22,6 +22,7 @@ declare(strict_types = 1);
namespace App\Util;
use function App\Core\I18n\_m;
use App\Util\Exception\BugFoundException;
use App\Util\Exception\ServerException;
use App\Util\Exception\TemporaryFileException;
use LogicException;
@ -129,7 +130,7 @@ class TemporaryFile extends SplFileInfo
if (!\is_null($this->resource) && $this->resource !== false) {
$path = $this->getRealPath();
if ($path === false) {
throw new BugFoundException();
throw new BugFoundException('Tried to cleanup a file but it\'s real path is false, while resource isn\'t');
}
$this->close();
if (file_exists($path)) {