Add conservation place

This commit is contained in:
Nicolò P 2024-11-13 16:30:03 +01:00
parent 184991e8cf
commit 99383daf3e
10 changed files with 481 additions and 7 deletions

View File

@ -0,0 +1,104 @@
<?php
namespace App\Controller;
use App\Entity\Collection;
use App\Security\Voter\RecordVoter;
use App\Entity\ConservationPlace;
use App\RecordStatus;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
class ConservationPlaceController extends AbstractController
{
#[Route('/conservation_place/{id<\d+>}', name: 'app_conservation_place')]
public function index(ConservationPlace $conservationPlace, EntityManagerInterface $em): Response
{
return $this->render('conservation_place/index.html.twig', [
'controller_name' => 'ConservationPlaceController',
'record' => $conservationPlace,
]);
}
#[Route('/conservation_place', name: 'app_conservation_place_landing')]
public function landing(EntityManagerInterface $em): Response
{
$repo = $em->getRepository(ConservationPlace::class);
$records = $repo->findBy([], ['id' => 'DESC']);
$count = count($records);
$records = array_slice($records, 0, 15);
return $this->render('conservation_place/landing.html.twig', [
'controller_name' => 'ConservationPlaceController',
'records' => $records,
'count' => $count,
]);
}
#[Route('/conservation_place/delete/{id<\d+>}', name: 'app_conservation_place_del')]
public function delete(ConservationPlace $conservationPlace, EntityManagerInterface $em): Response
{
try {
$this->denyAccessUnlessGranted(RecordVoter::DELETE, $conservationPlace);
}
catch (AccessDeniedException) {
$this->addFlash('warning', 'You are not authorized to delete this record');
return $this->redirectToRoute('app_home');
}
if ($conservationPlace) {
$em->remove($conservationPlace);
$em->flush();
}
$this->addFlash('notice', 'Record deleted successfully');
return $this->redirectToRoute('app_conservation_place_landing');
}
/**
* @todo Move clone logic to __clone() in Entity or Repository
*/
#[Route('/conservation_place/copy/{id<\d+>}', name: 'app_conservation_place_copy')]
public function copy(ConservationPlace $conservationPlace, EntityManagerInterface $em): Response
{
try {
$this->denyAccessUnlessGranted(RecordVoter::EDIT, $conservationPlace);
}
catch (AccessDeniedException) {
$this->addFlash('warning', 'You are not authorized to copy this record');
return $this->redirectToRoute('app_home');
}
$user = $this->getUser();
$editor = "{$user->getFirstname()} {$user->getLastName()}";
$copy = clone $conservationPlace;
$copy->setEditor($editor);
$copy->setOwner($editor);
$copy->setCreator($user->getUsername());
$repo = $em->getRepository(Collection::class);
$copy->setCollections(
$repo->findAllByConservationPlace($conservationPlace->getId())
);
/*
$repo = $em->getRepository(Collector::class);
$copy->setCollectors(
$repo->findAllByBibliography($bibliography->getId())
);
*/
$copy->setPlace("{$conservationPlace->getPlace()} - Copy");
$copy->setModifiedAt(new \DateTimeImmutable());
$copy->setStatus(RecordStatus::Draft->value);
$em->persist($copy);
$em->flush();
$this->addFlash('notice', 'Record copied successfully');
return $this->redirectToRoute('app_conservation_place', ['id' => $copy->getId()]);
}
}

View File

@ -3,15 +3,15 @@
namespace App\Entity;
use App\RecordInterface;
use App\Repository\ConservationRepository;
use App\Repository\ConservationPlaceRepository;
use DateTimeImmutable;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\DBAL\Types\Types;
use App\RecordStatus;
use Doctrine\Common\Collections\Collection as DoctrineCollection;
#[ORM\Entity(repositoryClass: ConservationRepository::class)]
#[ORM\Table(name: 'collection')]
#[ORM\Entity(repositoryClass: ConservationPlaceRepository::class)]
#[ORM\Table(name: 'conservation_place')]
class ConservationPlace implements RecordInterface
{
#[ORM\Id]
@ -35,10 +35,10 @@ class ConservationPlace implements RecordInterface
private ?string $region = null;
#[ORM\Column(name: 'prov_cons', type: Types::TEXT)]
private ?int $province = null;
private ?string $province = null;
#[ORM\Column(name: 'com_cons', type: Types::TEXT)]
private ?int $municipality = null;
private ?string $municipality = null;
#[ORM\Column(name: 'desc_cons', type: Types::TEXT)]
private ?string $description = null;
@ -291,7 +291,7 @@ class ConservationPlace implements RecordInterface
return $this->collections;
}
public function setBibliographies(DoctrineCollection $collections): static
public function setCollections(DoctrineCollection $collections): static
{
$this->collections = $collections;

View File

@ -48,4 +48,29 @@ class CollectionRepository extends ServiceEntityRepository
return $collections;
}
public function findAllByConservationPlace(int $placeId): ?ArrayCollection
{
$rsm = new ResultSetMappingBuilder($this->getEntityManager());
$rsm->addRootEntityFromClassMetadata('App\Entity\Collection', 'c');
$query = $this->getEntityManager()->createNativeQuery(
"SELECT
id,
stato,
editor,
tit_coll,
data_coll
FROM collection c
JOIN rel_conservazione_collezione
ON Collezione_id_coll = id
WHERE Conservazione_id_cons = :placeId",
$rsm
);
$query->setParameter('placeId', $placeId);
$collections = new ArrayCollection($query->getResult());
return $collections;
}
}

View File

@ -0,0 +1,78 @@
<?php
namespace App\Repository;
use App\Entity\Bibliography;
use App\Entity\ConservationPlace;
use App\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Query\ResultSetMappingBuilder;
/**
* @extends ServiceEntityRepository<ConservationPlace>
*/
class ConservationPlaceRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, ConservationPlace::class);
}
public function hasCreatorEditor(string $creator): bool
{
$em = $this->getEntityManager();
$repo = $em->getRepository(User::class);
$creator = $repo->findOneBy(['username' => $creator]);
return in_array('ROLE_EDITOR', $creator->getRoles());
}
public function findAllByCollection(int $collectionId): ?ArrayCollection
{
$rsm = new ResultSetMappingBuilder($this->getEntityManager());
$rsm->addRootEntityFromClassMetadata('App\Entity\ConservationPlace', 'c');
$query = $this->getEntityManager()->createNativeQuery(
"SELECT
id,
stato,
editor,
luogo_cons,
com_cons
FROM conservation_place c
JOIN rel_conservazione_collezione
ON Conservazione_id_cons = id
WHERE Collezione_id_coll = :collId",
$rsm
);
$query->setParameter('collId', $collectionId);
return new ArrayCollection($query->getResult());
}
public function findAllByObject(int $objectId): ?ArrayCollection
{
$rsm = new ResultSetMappingBuilder($this->getEntityManager());
$rsm->addRootEntityFromClassMetadata('App\Entity\ConservationPlace', 'c');
$query = $this->getEntityManager()->createNativeQuery(
"SELECT
id,
stato,
editor,
luogo_cons,
com_cons
FROM conservation_place c
JOIN rel_conservazione_reperto
ON Conservazione_id_cons = id
WHERE Reperto_id_rep = :objectId",
$rsm
);
$query->setParameter('objectId', $objectId);
return new ArrayCollection($query->getResult());
}
}

View File

@ -4,6 +4,17 @@
{% block rightpanel %}
<div class="container" style="max-width: 60vw" data-controller="delete-record">
<p class="pb-3">
<a class="button is-link is-outlined"
href="{{ path('app_bibliography_landing') }}">
Back to index
<span class="icon ml-2">
<i class="fa fa-arrow-left"></i>
</span>
</a>
</p>
<h1 class="is-size-1 mt-0 has-text-centered">Bibliography</h1>
<h2 class="is-size-3 mt-3 has-text-centered">{{ record.citation }}</h2>

View File

@ -4,6 +4,17 @@
{% block rightpanel %}
<div class="container" style="max-width: 60vw" data-controller="delete-record">
<p class="pb-3">
<a class="button is-link is-outlined"
href="{{ path('app_collection_landing') }}">
Back to index
<span class="icon ml-2">
<i class="fa fa-arrow-left"></i>
</span>
</a>
</p>
<h1 class="is-size-1 mt-0 has-text-centered">Collection</h1>
<h2 class="is-size-3 mt-3 has-text-centered">{{ record.title }}</h2>

View File

@ -4,6 +4,17 @@
{% block rightpanel %}
<div class="container" style="max-width: 60vw" data-controller="delete-record">
<p class="pb-3">
<a class="button is-link is-outlined"
href="{{ path('app_collector_landing') }}">
Back to index
<span class="icon ml-2">
<i class="fa fa-arrow-left"></i>
</span>
</a>
</p>
<h1 class="is-size-1 mt-0 has-text-centered">Collector</h1>
<h2 class="is-size-3 mt-3 has-text-centered">{{ record.name }}</h2>

View File

@ -0,0 +1,125 @@
{% extends 'data_entry.html.twig' %}
{% block title %}Conservation place - {{ record.place }} | ArCOA{% endblock %}
{% block rightpanel %}
<div class="container" style="max-width: 60vw" data-controller="delete-record">
<p class="pb-3">
<a class="button is-link is-outlined"
href="{{ path('app_conservation_place_landing') }}">
Back to index
<span class="icon ml-2">
<i class="fa fa-arrow-left"></i>
</span>
</a>
</p>
<h1 class="is-size-1 mt-0 has-text-centered">Conservation place</h1>
<h2 class="is-size-3 mt-3 has-text-centered">{{ record.place }}</h2>
{% for message in app.flashes('notice') %}
<div class=" mt-4 notification is-success"
data-controller="notification"
data-notification-target="notif">
<button class="delete" data-action="click->notification#close"></button>
{{ message }}
</div>
{% endfor %}
<article class="message is-info mt-3">
<div class="message-body">
<p>
<strong>Last modified:</strong> {{ record.modifiedAt.format('Y-m-d') }}
at {{ record.modifiedAt.format('H:i:s') }}
</p>
<p><strong>Editor:</strong> {{ record.editor }}</p>
</div>
</article>
<div class="card p-5">
{% if not is_granted('ROLE_READER') %}
<div class="columns">
<div class="column is-half"></div>
<div class="column has-text-right">
{% if is_granted('ROLE_REVISOR') or
is_granted('ROLE_ADMIN') or
record.editableStatus %}
<button class="button is-link">
Edit
<span class="icon ml-2">
<i class="fa fa-edit"></i>
</span>
</button>
{% endif %}
<a href="{{ path('app_conservation_place_copy', {'id' : record.id}) }}"
class="button is-link">
Copy
<span class="icon ml-2">
<i class="fa fa-copy"></i>
</span>
</a>
{% if is_granted('ROLE_REVISOR') or is_granted('ROLE_ADMIN') %}
<button data-url="{{ path('app_conservation_place_del', {'id' : record.id}) }}"
class="button is-danger"
data-delete-record-target="path" data-action="click->delete-record#warn">
Delete
<span class="icon ml-2">
<i class="fa fa-trash"></i>
</span>
</button>
{% endif %}
</div>
</div>
{% endif %}
<div class="tabs is-boxed is-fullwidth">
<ul>
<li class="is-active">Record</li>
<li>Relations</li>
</ul>
</div>
<div class="data-tabs" id="record">
<table class="table is-fullwidth pt-4 record">
<tr><th>Record ID</th><td>{{ record.id }}</td></tr>
<tr><th>Status</th><td>{{ record.getStatus() }}</td></tr>
<tr><th>Editor(s)</th><td>{{ record.editor }}</td></tr>
<tr><th>Conservation place</th><td data-delete-record-target="name">{{ record.place }}</td></tr>
<tr><th>Region</th><td>{{ record.region }}</td></tr>
<tr><th>Province</th><td>{{ record.province }}</td></tr>
<tr><th>Municipality</th><td>{{ record.municipality }}</td></tr>
<tr><th>Description</th><td>{{ record.description }}</td></tr>
<tr><th>Short description</th><td>{{ record.shortDescription }}</td></tr>
<tr><th>External identifier(s)</th><td>{{ record.externalIdentifier }}</td></tr>
<tr><th>External link(s)</th><td>{{ record.link }}</td></tr>
<tr><th>Subject headings</th><td>{{ record.subjectHeadings }}</td></tr>
<tr><th>ArCOA URI</th><td>arcoa.cnr.it/conservation_place/{{ record.id }}</td></tr>
<tr><th>Editorial notes</th><td>{{ record.notes }}</td></tr>
</table>
</div>
<div class="data-tabs is-hidden" id="relations">
Some stuff...
</div>
</div>
<div class="modal" data-delete-record-target="modal">
<div class="modal-background"></div>
<div class="modal-card">
<header class="modal-card-head">
<span class="icon is-large has-text-warning">
<i class="fa fa-warning fa-2x"></i>
</span>
<p class="modal-card-title has-text-danger pl-2"><strong>Delete record?</strong></p>
<button class="delete" aria-label="close"></button>
</header>
<section class="modal-card-body">
<p class="is-size-5" data-delete-record-target="message"></p>
</section>
<footer class="modal-card-foot">
<div class="buttons is-right">
<button class="button is-link" data-action="click->delete-record#delete">Confirm</button>
<button class="button is-light" id="cancel">Cancel</button>
</div>
</footer>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,109 @@
{% extends 'data_entry.html.twig' %}
{% block title %}Conservation place | ArCOA{% endblock %}
{% block rightpanel %}
<div class="container" style="max-width: 60vw" data-controller="delete-record">
<h1 class="is-size-1 mt-0 has-text-centered">Conservation place</h1>
<h2 class="is-size-3 mt-3 has-text-centered">Choose action</h2>
{% for message in app.flashes('notice') %}
<div class=" mt-4 notification is-success"
data-controller="notification"
data-notification-target="notif">
<button class="delete" data-action="click->notification#close"></button>
{{ message }}
</div>
{% endfor %}
<div class="card p-5 mt-6 pt-6 pb-6">
<div class="columns">
<div class="column is-half has-text-centered">
<button class="button is-medium">
Search
<span class="icon ml-2">
<i class="fa fa-search"></i>
</span>
</button>
</div>
{% if is_granted('ROLE_ADMIN') or is_granted('ROLE_REVISOR') or is_granted('ROLE_EDITOR') %}
<div class="column has-text-centered">
<a href="" class="button is-link is-medium">
Add new
<span class="icon ml-2">
<i class="fa fa-plus"></i>
</span>
</a>
</div>
{% endif %}
</div>
</div>
<h3 class="has-text-centered is-size-4 mt-6">Records</h3>
<p class="pt-4 pb-4"><strong>{{ count }} result(s) found</strong></p>
<table class="table is-hoverable is-fullwidth mt-5 has-text-centered results">
<tr>
<th>ID</th>
<th>Place</th>
<th>Status</th>
<th>Editor</th>
<th>Municipality</th>
<th>Last modified</th>
<th>Actions</th>
</tr>
{% for record in records %}
<tr>
<td><a href="{{ path('app_conservation_place', {'id' : record.id}) }}">{{ record.id }}</a></td>
<td>
<a data-delete-record-target="name" href="{{ path('app_conservation_place', {'id' : record.id}) }}">
{{ record.place }}
</a>
</td>
<td>{{ record.status }}</td>
<td>{{ record.owner }}</td>
<td style="max-width: 350px;">{{ record.municipality }}</td>
<td>
{{ record.modifiedAt.format('Y-m-d') }}<br>
{{ record.editor }} at {{ record.modifiedAt.format('H:i:s') }}
</td>
<td style="min-width: 120px;">
<div class="buttons">
<button class="button is-small is-link" title="Edit record">
<span class="icon">
<i class="fa fa-edit"></i>
</span>
</button>
<button data-url="{{ path('app_conservation_place_del', {'id' : record.id}) }}"
class="button is-small is-danger" title="Delete record"
data-delete-record-target="path" data-action="click->delete-record#warn">
<span class="icon">
<i class="fa fa-trash"></i>
</span>
</button>
</div>
</td>
</tr>
{% endfor %}
</table>
<div class="modal" data-delete-record-target="modal">
<div class="modal-background"></div>
<div class="modal-card">
<header class="modal-card-head">
<span class="icon is-large has-text-warning">
<i class="fa fa-warning fa-2x"></i>
</span>
<p class="modal-card-title has-text-danger pl-2"><strong>Delete record?</strong></p>
<button class="delete" aria-label="close"></button>
</header>
<section class="modal-card-body">
<p class="is-size-5" data-delete-record-target="message"></p>
</section>
<footer class="modal-card-foot">
<div class="buttons is-right">
<button class="button is-link" data-action="click->delete-record#delete">Confirm</button>
<button class="button is-light" id="cancel">Cancel</button>
</div>
</footer>
</div>
</div>
</div>
{% endblock %}

View File

@ -240,7 +240,7 @@
</a>
</li>
<li class="pt-1 pb-1">
<a href="">
<a href="{{ path('app_conservation_place_landing') }}">
Conservation place
</a>
</li>