Strimoid/Strimoid

View on GitHub
app/Http/Controllers/UserController.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php

namespace Strimoid\Http\Controllers;

use Carbon\Carbon;
use GuzzleHttp\Psr7\Uri;
use Illuminate\Contracts\Auth\PasswordBroker;
use Illuminate\Contracts\Routing\ResponseFactory;
use Illuminate\Contracts\View\View;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\Support\Str;
use Strimoid\Contracts\Repositories\UserRepository;
use Strimoid\Facades\PDP;
use Strimoid\Models\User;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class UserController extends BaseController
{
    public function __construct(protected UserRepository $users, protected PasswordBroker $passwords, private readonly ResponseFactory $responseFactory, private readonly Redirector $redirector, private readonly \Illuminate\Mail\Mailer $mailer, private readonly \Illuminate\Translation\Translator $translator, private readonly \Illuminate\Contracts\View\Factory $viewFactory, private readonly \Illuminate\Cache\CacheManager $cacheManager, private readonly \Illuminate\Contracts\Auth\Guard $guard)
    {
    }

    public function showJSONList(): JsonResponse
    {
        $users = [];

        foreach (User::all() as $user) {
            $users[] = [
                'value' => $user->name,
                'avatar' => $user->getAvatarPath(),
            ];
        }

        return $this->responseFactory->json($users)
            ->setPublic()
            ->setMaxAge(3600);
    }

    public function changePassword(Request $request): RedirectResponse
    {
        $this->validate($request, [
            'password' => 'required|confirmed|min:6',
            'old_password' => 'required|user_password',
        ]);

        user()->password = $request->get('password');
        user()->save();

        return $this->redirector->action('SettingsController@showSettings')
            ->with('success_msg', 'Hasło zostało zmienione.');
    }

    public function changeEmail(Request $request): RedirectResponse
    {
        $this->validate($request, [
            'email' => 'required|email|unique_email:users|real_email',
        ]);

        $email = Str::lower($request->input('email'));

        user()->new_email = $email;
        user()->email_change_token = Str::random(16);

        user()->save();

        $this->mailer->send('emails.auth.email_change', ['user' => user()], function ($message) use ($email): void {
            $message->to($email, user()->name)->subject('Potwierdź zmianę adresu email');
        });

        return $this->redirector->action('SettingsController@showSettings')
            ->with('success_msg', 'Na podany adres email został wysłany link umożliwiający potwierdzenie zmiany.');
    }

    public function confirmEmailChange($token): RedirectResponse
    {
        if ($token !== user()->email_change_token) {
            return $this->redirector->to('')->with('danger_msg', 'Błędny token.');
        }

        user()->email = user()->new_email;
        user()->unset(['email_change_token', 'new_email']);
        user()->save();

        return $this->redirector->to('')->with('success_msg', 'Adres email został zmieniony.');
    }

    public function remindPassword(Request $request): View|RedirectResponse
    {
        if ($request->has('email')) {
            $this->validate($request, ['email' => 'required|email']);

            $response = $this->passwords->sendResetLink($request->only('email'));

            switch ($response) {
                case PasswordBroker::RESET_LINK_SENT:
                    return $this->redirector->back()->with('status', $this->translator->trans($response));
                case PasswordBroker::INVALID_USER:
                    return $this->redirector->back()->withErrors(['email' => $this->translator->trans($response)]);
            }
        }

        return $this->viewFactory->make('user.remind');
    }

    public function showPasswordResetForm($token = null): View
    {
        if (!$token) {
            throw new NotFoundHttpException();
        }

        return $this->viewFactory->make('user.reset')->with('token', $token);
    }

    public function resetPassword(Request $request): RedirectResponse
    {
        $this->validate($request, [
            'token' => 'required',
            'email' => 'required|email',
            'password' => 'required|confirmed',
        ]);

        $credentials = $request->only(
            'email',
            'password',
            'password_confirmation',
            'token'
        );

        $response = $this->passwords->reset($credentials, function ($user, $password) use ($request) {
            // Email confirmed, we may activate account if user didn't that yet
            if ($user->activation_token) {
                $cacheKey = 'registration.' . md5((string) $request->getClientIp());

                if ($this->cacheManager->has($cacheKey)) {
                    abort(500);
                }

                $user->unset('activation_token');
                $user->is_activated = true;
            }

            $user->password = $password;
            $user->save();

            $this->guard->login($user);
        });

        return match ($response) {
            PasswordBroker::PASSWORD_RESET => $this->redirector->back('/'),
            default => $this->redirector->back()
                ->withInput($request->only('email'))
                ->withErrors(['email' => $this->translator->trans($response)]),
        };
    }

    public function showLoginForm(): View
    {
        return $this->viewFactory->make('user.login');
    }

    public function showRemoveAccountForm(): View
    {
        return $this->viewFactory->make('user.remove');
    }

    public function removeAccount(Request $request): RedirectResponse
    {
        $this->validate($request, [
            'password' => 'required|confirmed|user_password',
        ]);

        user()->removed_at = Carbon::now();
        user()->type = 'deleted';

        user()->save();

        $this->guard->logout();

        return $this->redirector->to('')->with('success_msg', 'Twoje konto zostało usunięte.');
    }

    public function showProfile(User $user, string $type = 'all'): View
    {
        if ($user->removed_at) {
            abort(404, 'Użytkownik usunął konto.');
        }

        $data = [];

        if ($type === 'contents') {
            $data['contents'] = $user->contents()->orderBy('created_at', 'desc')->paginate(15);
        } elseif ($type === 'comments') {
            $data['comments'] = $user->comments()->orderBy('created_at', 'desc')->paginate(15);
        } elseif ($type === 'comment_replies') {
            $data['replies'] = $user->commentReplies()->orderBy('created_at', 'desc')->paginate(15);
        } elseif ($type === 'entries') {
            $data['entries'] = $user->entries()->orderBy('created_at', 'desc')->paginate(15);
        } elseif ($type === 'entry_replies') {
            $data['replies'] = $user->entryReplies()->orderBy('created_at', 'desc')->paginate(15);
        } elseif ($type === 'moderated') {
            $data['moderated'] = $user->moderatedGroups()->paginate(25);
        } else {
            $data['actions'] = $user->actions()->with('element')->orderBy('created_at', 'desc')->paginate(15);
        }

        $data['type'] = $type;
        $data['user'] = $user;

        return $this->viewFactory->make('user.profile', $data);
    }

    public function saveProfile(Request $request): RedirectResponse
    {
        $this->validate($request, [
            'sex' => 'in:male,female,unknown',
            'avatar' => 'image|max:1024',
            'age' => 'integer|min:1900|max:2020',
            'location' => 'max:32',
            'description' => 'max:250',
        ]);

        $user = user();

        $data = $request->only(['sex', 'age', 'location', 'description']);
        $user->fill($data);

        if ($request->hasFile('avatar')) {
            $user->setAvatar($request->file('avatar')->getRealPath());
        }

        $user->save();

        return $this->redirector->route('user_settings')->with('success_msg', 'Zmiany zostały zapisane.');
    }

    public function blockUser($user): JsonResponse
    {
        user()->blockedUsers()->attach($user);
        $this->cacheManager->tags(['user.blocked-users', 'u.' . $this->guard->id()])->flush();

        return $this->responseFactory->json(['status' => 'ok']);
    }

    public function unblockUser($user): JsonResponse
    {
        user()->blockedUsers()->detach($user);
        $this->cacheManager->tags(['user.blocked-users', 'u.' . $this->guard->id()])->flush();

        return $this->responseFactory->json(['status' => 'ok']);
    }

    public function observeUser($user): JsonResponse
    {
        user()->followedUsers()->attach($user);

        return $this->responseFactory->json(['status' => 'ok']);
    }

    public function unobserveUser($user): JsonResponse
    {
        user()->followedUsers()->detach($user);

        return $this->responseFactory->json(['status' => 'ok']);
    }

    public function blockDomain($domain): JsonResponse
    {
        $domain = (new Uri($domain))->getHost();
        $domain = PDP::resolve($domain)->registrableDomain()->toString();

        if (!$domain) {
            return $this->responseFactory->json([
                'status' => 'error', 'error' => 'Nieprawidłowa domena',
            ]);
        }

        user()->push('_blocked_domains', $domain, true);

        return $this->responseFactory->json(['status' => 'ok', 'domain' => $domain]);
    }

    public function unblockDomain($domain): JsonResponse
    {
        user()->pull('_blocked_domains', $domain);

        return $this->responseFactory->json(['status' => 'ok']);
    }
}