Messing with voters
This commit is contained in:
parent
b991ae8d00
commit
b7381eafa3
@ -47,17 +47,21 @@ if (! location.pathname.includes('login')) {
|
||||
const userMenu = document.querySelector('.dropdown-trigger');
|
||||
const userCaret = document.querySelector('#user-caret');
|
||||
|
||||
document.querySelector('#for-vocabs').addEventListener('click', function () {
|
||||
vocabs.classList.toggle('is-hidden');
|
||||
const forVocabs = document.querySelector('#for-vocabs')
|
||||
|
||||
if (forVocabs) {
|
||||
forVocabs.addEventListener('click', function () {
|
||||
vocabs.classList.toggle('is-hidden');
|
||||
|
||||
if (this.firstElementChild.classList.contains('fa-angle-right')) {
|
||||
this.firstElementChild.classList.remove('fa-angle-right');
|
||||
this.firstElementChild.classList.add('fa-angle-down');
|
||||
} else {
|
||||
this.firstElementChild.classList.remove('fa-angle-down');
|
||||
this.firstElementChild.classList.add('fa-angle-right');
|
||||
}
|
||||
});
|
||||
if (this.firstElementChild.classList.contains('fa-angle-right')) {
|
||||
this.firstElementChild.classList.remove('fa-angle-right');
|
||||
this.firstElementChild.classList.add('fa-angle-down');
|
||||
} else {
|
||||
this.firstElementChild.classList.remove('fa-angle-down');
|
||||
this.firstElementChild.classList.add('fa-angle-right');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
userMenu.addEventListener('click', function () {
|
||||
document.querySelector('.dropdown').classList.toggle('is-active');
|
||||
|
@ -3,12 +3,14 @@
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\VocabFuncContext;
|
||||
use App\Repository\VocabFuncContextRepository;
|
||||
use App\Security\Voter\VocabVoter;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
|
||||
class VocabFuncContextController extends AbstractController
|
||||
{
|
||||
@ -17,10 +19,8 @@ class VocabFuncContextController extends AbstractController
|
||||
{
|
||||
$roles = $this->getUser()->getRoles();
|
||||
|
||||
if (! in_array('ROLE_REVISOR', $roles)
|
||||
&& ! in_array('ROLE_ADMIN', $roles)
|
||||
) {
|
||||
$this->addFlash('warning', 'Only revisors and administrators can edit vocabularies');
|
||||
if (in_array('ROLE_READER', $roles)) {
|
||||
$this->addFlash('warning', 'Only editors, revisors and administrators can view vocabularies');
|
||||
return $this->redirectToRoute('app_home');
|
||||
}
|
||||
|
||||
@ -35,9 +35,16 @@ class VocabFuncContextController extends AbstractController
|
||||
#[Route('/vocabs/functional_context/add', name: 'app_vocab_func_context_add')]
|
||||
public function addTerm(Request $request, EntityManagerInterface $em): Response
|
||||
{
|
||||
$term = $request->getPayload()->get('_term');
|
||||
|
||||
$vocab = new VocabFuncContext;
|
||||
try {
|
||||
$this->denyAccessUnlessGranted(VocabVoter::EDIT, $vocab);
|
||||
}
|
||||
catch (AccessDeniedException) {
|
||||
$this->addFlash('warning', 'Only revisors and administrators can edit vocabularies');
|
||||
return $this->redirectToRoute('app_home');
|
||||
}
|
||||
|
||||
$term = $request->getPayload()->get('_term');
|
||||
$vocab->setTerm($term);
|
||||
$em->persist($vocab);
|
||||
$em->flush();
|
||||
|
@ -7,7 +7,7 @@ use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity()]
|
||||
#[ORM\Table(name: 'lis_contesto_funz')]
|
||||
class VocabFuncContext
|
||||
class VocabFuncContext implements \App\VocabInterface
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
|
49
src/Security/Voter/VocabVoter.php
Normal file
49
src/Security/Voter/VocabVoter.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Security\Voter;
|
||||
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
|
||||
final class VocabVoter extends Voter
|
||||
{
|
||||
public const EDIT = 'VOCAB_EDIT';
|
||||
public const DELETE = 'VOCAB_DELETE';
|
||||
public const VIEW = 'VOCAB_VIEW';
|
||||
|
||||
protected function supports(string $attribute, mixed $subject): bool
|
||||
{
|
||||
// replace with your own logic
|
||||
// https://symfony.com/doc/current/security/voters.html
|
||||
return in_array($attribute, [self::EDIT, self::VIEW, self::DELETE])
|
||||
&& $subject instanceof \App\VocabInterface;
|
||||
}
|
||||
|
||||
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
|
||||
{
|
||||
$user = $token->getUser();
|
||||
|
||||
// if the user is anonymous, do not grant access
|
||||
if (!$user instanceof UserInterface) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$roles = $user->getRoles();
|
||||
|
||||
// TODO: Better way to check roles?
|
||||
switch ($attribute) {
|
||||
case self::EDIT:
|
||||
case self::DELETE:
|
||||
return in_array('ROLE_ADMIN', $roles)
|
||||
|| in_array('ROLE_REVISOR', $roles);
|
||||
break;
|
||||
|
||||
case self::VIEW:
|
||||
return ! in_array('ROLE_READER', $roles);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
7
src/VocabInterface.php
Normal file
7
src/VocabInterface.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
interface VocabInterface
|
||||
{
|
||||
}
|
@ -33,7 +33,7 @@
|
||||
</span>
|
||||
<span class="pl-2">Profile</span>
|
||||
</a>
|
||||
{% if 'ROLE_ADMIN' in app.user.roles %}
|
||||
{% if is_granted('ROLE_ADMIN') %}
|
||||
<a href="/admin" class="dropdown-item">
|
||||
<span class="icon is-small">
|
||||
<i class="fa fa-cogs"></i>
|
||||
@ -57,6 +57,7 @@
|
||||
<div class="columns mb-0">
|
||||
<div class="column is-one-fifth arcoa-menu mb-0">
|
||||
<aside class="menu pl-4">
|
||||
{% if 'ROLE_READER' not in app.user.roles %}
|
||||
<p class="menu-label has-text-white mt-3 pt-5 pl-5 is-size-6">
|
||||
Vocabularies
|
||||
<span class="icon is-clickable pl-4" id="for-vocabs">
|
||||
@ -201,6 +202,7 @@
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
<p class="menu-label has-text-white mt-3 pt-5 pl-5 is-size-6">
|
||||
Records
|
||||
<span class="icon is-clickable pl-4" id="for-records">
|
||||
|
@ -3,6 +3,16 @@
|
||||
{% block title %}Home | ArCOA{% endblock %}
|
||||
|
||||
{% block rightpanel %}
|
||||
{% for message in app.flashes('warning') %}
|
||||
<article class="message is-warning mb-6 mt-3 ml-auto mr-auto" style="max-width: 35vw">
|
||||
<div class="message-header">
|
||||
<p>Warning</p>
|
||||
<button class="delete" aria-label="delete"></button>
|
||||
</div>
|
||||
<div class="message-body">{{ message }}</div>
|
||||
</article>
|
||||
{% endfor %}
|
||||
|
||||
<div class="has-text-centered">
|
||||
<img width="200px" src="{{ asset('img/Logo-ArCOA-def.png') }}" />
|
||||
</div>
|
||||
@ -11,15 +21,6 @@
|
||||
<h2 class="is-size-3 mb-3 has-text-centered">Archivi e Collezioni dell'Oriente Antico</h2>
|
||||
<h2 class="is-size-3 mb-6 has-text-centered">Archives and Collections of the Ancient Near East</h2>
|
||||
|
||||
{% for message in app.flashes('warning') %}
|
||||
<article class="message is-warning" style="max-width: 35vw; margin: 0 auto;">
|
||||
<div class="message-header">
|
||||
<p>Warning</p>
|
||||
<button class="delete" aria-label="delete"></button>
|
||||
</div>
|
||||
<div class="message-body">{{ message }}</div>
|
||||
</article>
|
||||
{% endfor %}
|
||||
|
||||
<div class="columns" style="max-width: 35vw; margin: 0 auto;">
|
||||
<div class="column mt-6 mb-5">
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
{% block rightpanel %}
|
||||
<div class="container" style="max-width: 50vw">
|
||||
<h1 class="is-size-1 mt-0 has-text-centered">Edit vocabulary</h1>
|
||||
<h1 class="is-size-1 mt-0 has-text-centered">Vocabulary</h1>
|
||||
<h2 class="is-size-3 mt-4 has-text-centered">Functional context</h2>
|
||||
|
||||
<div class="container mt-6">
|
||||
@ -18,6 +18,7 @@
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% if is_granted('ROLE_ADMIN') or is_granted('ROLE_REVISOR') %}
|
||||
<form method="post" action="{{ path('app_vocab_func_context_add') }}">
|
||||
<label class="label">Add new term</label>
|
||||
<div class="field has-addons">
|
||||
@ -34,15 +35,17 @@
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
<h3 class="mt-6 mb-5 is-size-5"><strong>Terms in vocabulary</strong></h3>
|
||||
<table class="table is-fullwidth" id="terms">
|
||||
<tr><th>Term</th><th>Actions</th></tr>
|
||||
<tr><th>Term</th>{% if is_granted('ROLE_ADMIN') or is_granted('ROLE_REVISOR') %}<th>Actions</th>{% endif %}</tr>
|
||||
{% for term in terms %}
|
||||
<tr data-row-id="{{ term.id }}">
|
||||
<td>
|
||||
<input class="input" type="text" value="{{ term.term }}" disabled data-term-id="{{ term.id }}" />
|
||||
</td>
|
||||
{% if is_granted('ROLE_ADMIN') or is_granted('ROLE_REVISOR') %}
|
||||
<td>
|
||||
<div class="buttons">
|
||||
<button class="button is-link" data-id-edit="{{ term.id }}">
|
||||
@ -59,6 +62,7 @@
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
Loading…
Reference in New Issue
Block a user