commit
dd9e72339c
|
@ -47,9 +47,21 @@ user_auth_login:
|
||||||
user_auth_logout:
|
user_auth_logout:
|
||||||
path: /auth/logout
|
path: /auth/logout
|
||||||
|
|
||||||
user_account_invites:
|
user_account:
|
||||||
path: /account/invites
|
path: /account
|
||||||
controller: App\Controller\AccountController::invites
|
controller: App\Controller\AccountController::account
|
||||||
|
requirements:
|
||||||
|
method: GET
|
||||||
|
|
||||||
|
user_account_token_create:
|
||||||
|
path: /profile/api/token/create
|
||||||
|
controller: App\Controller\AccountController::addApiToken
|
||||||
|
requirements:
|
||||||
|
method: GET
|
||||||
|
|
||||||
|
user_account_token_revoke:
|
||||||
|
path: /profile/api/token/revoke/{key}
|
||||||
|
controller: App\Controller\AccountController::revokeApiToken
|
||||||
requirements:
|
requirements:
|
||||||
method: GET
|
method: GET
|
||||||
|
|
||||||
|
|
|
@ -2,22 +2,60 @@
|
||||||
|
|
||||||
namespace App\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Entity\ApiToken;
|
||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
|
use App\Repository\ApiTokenRepository;
|
||||||
use App\Repository\InviteRepository;
|
use App\Repository\InviteRepository;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
class AccountController extends AbstractController
|
class AccountController extends AbstractController
|
||||||
{
|
{
|
||||||
public function invites(InviteRepository $inviteRepo): Response
|
public function account(InviteRepository $inviteRepo, ApiTokenRepository $apiTokenRepo): Response
|
||||||
{
|
{
|
||||||
/** @var User $user */
|
/** @var User $user */
|
||||||
if (null === $user = $this->getUser()) {
|
if (null === $user = $this->getUser()) {
|
||||||
throw $this->createAccessDeniedException('User not found exception');
|
throw $this->createAccessDeniedException('User not found exception');
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->render('Account/invites.html.twig', [
|
return $this->render(
|
||||||
|
'Account/account.html.twig', [
|
||||||
'invites' => $inviteRepo->findInvitesByUser($user),
|
'invites' => $inviteRepo->findInvitesByUser($user),
|
||||||
|
'tokens' => $apiTokenRepo->findBy(['user' => $user->getId()]),
|
||||||
|
'user' => $user,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function addApiToken(EntityManagerInterface $em): Response
|
||||||
|
{
|
||||||
|
/** @var User|null $user */
|
||||||
|
if (null === $user = $this->getUser()) {
|
||||||
|
throw $this->createAccessDeniedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
$token = new ApiToken($user);
|
||||||
|
$em->persist($token);
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
$this->addFlash('success', 'API token created.');
|
||||||
|
|
||||||
|
return $this->redirectToRoute('user_account');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function revokeApiToken(string $key, ApiTokenRepository $repo, EntityManagerInterface $em): Response
|
||||||
|
{
|
||||||
|
$token = $repo->findOneBy(['key' => $key]);
|
||||||
|
|
||||||
|
if (null === $token || $token->getUser() !== $this->getUser()) {
|
||||||
|
throw $this->createNotFoundException('Token not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
$em->remove($token);
|
||||||
|
$em->flush();
|
||||||
|
|
||||||
|
$this->addFlash('success', 'API token removed.');
|
||||||
|
|
||||||
|
return $this->redirectToRoute('user_account');
|
||||||
|
}
|
||||||
}
|
}
|
26
templates/Account/account.html.twig
Normal file
26
templates/Account/account.html.twig
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{% extends 'base.html.twig' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<ul class="nav nav-tabs" id="myTab" role="tablist">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link active" id="home-tab" data-toggle="tab" href="#profile" role="tab" aria-controls="home" aria-selected="true">Profile</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" id="profile-tab" data-toggle="tab" href="#invites" role="tab" aria-controls="profile" aria-selected="false">Invites</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" id="contact-tab" data-toggle="tab" href="#api" role="tab" aria-controls="contact" aria-selected="false">RSS / API</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div class="tab-content" id="myTabContent">
|
||||||
|
<div class="tab-pane fade show active" id="profile" role="tabpanel" aria-labelledby="home-tab">
|
||||||
|
{% include 'Account/profile.html.twig' with {'user': user} %}
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="invites" role="tabpanel" aria-labelledby="profile-tab">
|
||||||
|
{% include 'Account/invites.html.twig' with {'invites': invites} %}
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane fade" id="api" role="tabpanel" aria-labelledby="contact-tab">
|
||||||
|
{% include 'Account/api.html.twig' with {'tokens': tokens} %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
34
templates/Account/api.html.twig
Normal file
34
templates/Account/api.html.twig
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<div class="row mb-3 mt-3">
|
||||||
|
<a role="button" class="btn btn-primary" href="{{ path('user_account_token_create') }}">Create token</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Token</th>
|
||||||
|
<th scope="col">RSS link</th>
|
||||||
|
<th scope="col">Created</th>
|
||||||
|
<th scope="col">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
{% for token in tokens %}
|
||||||
|
<tr>
|
||||||
|
<th scope="row">
|
||||||
|
<code>{{ token.key }}</code>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
{# We can't use constant() in the hash so it's necessay to hard-code query parameter here #}
|
||||||
|
<input class="form-control" type="text" value="{{ url('api_v1_rss_last') }}?{{ {'api-key': token.key} | url_encode }}" readonly onclick="this.select();">
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ token.createdAt | date('Y-m-d H:i:s') }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a role="button" class="btn btn-danger" href="{{ path('user_account_token_revoke', {'key': token.key}) }}" onclick="return confirm('Are you sure?')">Revoke</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
|
@ -1,7 +1,6 @@
|
||||||
{% extends 'base.html.twig' %}
|
<div class="row mb-3 mt-3">
|
||||||
|
<button type="button" class="btn btn-primary disabled" title="Not implemented">Request more</button>
|
||||||
{% block content %}
|
</div>
|
||||||
<h1>Your invites</h1>
|
|
||||||
|
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -27,4 +26,3 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{% endblock %}
|
|
||||||
|
|
24
templates/Account/profile.html.twig
Normal file
24
templates/Account/profile.html.twig
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">Field</th>
|
||||||
|
<th scope="col">Value</th>
|
||||||
|
<th scope="col">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Username</th>
|
||||||
|
<td>{{ user.username }}</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">Email</th>
|
||||||
|
<td>{{ user.email }}</td>
|
||||||
|
<td>
|
||||||
|
<button type="button" class="btn disabled" title="Not implemented">Change</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
|
@ -36,8 +36,8 @@
|
||||||
<i class="fas fa-user"></i> {{ app.user.username }}
|
<i class="fas fa-user"></i> {{ app.user.username }}
|
||||||
</a>
|
</a>
|
||||||
<div class="dropdown-menu">
|
<div class="dropdown-menu">
|
||||||
<a href="{{ path('user_account_invites') }}" class="dropdown-item"><i class="fas fa-user-plus"></i> Invites</a>
|
<a href="{{ path('user_account') }}" class="dropdown-item"><i class="fas fa-wrench"></i> Profile</a>
|
||||||
<a href="{{ path('user_auth_logout') }}" class="dropdown-item"><i class="fas fa-sign-out-alt"></i> Logout</a>
|
<a href="{{ logout_path('main') }}" class="dropdown-item"><i class="fas fa-sign-out-alt"></i> Logout</a>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -62,6 +62,16 @@
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<div id="alerts">
|
||||||
|
{% for type, messages in app.flashes %}
|
||||||
|
{% for message in messages %}
|
||||||
|
<div class="alert alert-{{ type }}" role="alert">
|
||||||
|
{{ message }}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
{% block content %}{% endblock %}
|
{% block content %}{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue