[TOOLS] Fix (most) issues found by PHPStan
This commit is contained in:
parent
8fd02ef152
commit
b1262919da
@ -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.',
|
||||
];
|
||||
}
|
||||
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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'],
|
||||
],
|
||||
];
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
{
|
||||
|
49
src/Util/Exception/BugFoundException.php
Normal file
49
src/Util/Exception/BugFoundException.php
Normal 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']}");
|
||||
}
|
||||
}
|
@ -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.');
|
||||
}
|
||||
}
|
||||
|
@ -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.');
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)) {
|
||||
|
Loading…
Reference in New Issue
Block a user