Allow snips to be archived

This commit is contained in:
Tim 2025-05-10 16:48:03 +02:00
parent 50a7ab7985
commit 47ea226ed7
8 changed files with 107 additions and 10 deletions

View File

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20250510142748 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql(<<<'SQL'
ALTER TABLE snip DROP INDEX IDX_FEBD97966A1E45F3, ADD UNIQUE INDEX UNIQ_FEBD97966A1E45F3 (active_version_id)
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE snip ADD archived TINYINT(1) NOT NULL
SQL);
$this->addSql(<<<'SQL'
UPDATE snip SET archived = 0
SQL);
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql(<<<'SQL'
ALTER TABLE snip DROP INDEX UNIQ_FEBD97966A1E45F3, ADD INDEX IDX_FEBD97966A1E45F3 (active_version_id)
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE snip DROP archived
SQL);
}
}

View File

@ -151,4 +151,19 @@ class SnipController extends AbstractController
'form' => $form->createView(), 'form' => $form->createView(),
]); ]);
} }
#[Route('/archive/{snip}', name: '_archive')]
public function archive(Snip $snip): Response
{
$this->denyAccessUnlessGranted(SnipVoter::EDIT, $snip);
$snip->setArchived(!$snip->isArchived());
$this->repository->save($snip);
if ($snip->isArchived()) {
$this->addFlash('success', sprintf('Snip "%s" archived', $snip));
} else {
$this->addFlash('success', sprintf('Snip "%s" unarchived', $snip));
}
return $this->redirectToRoute('snip_single', ['snip' => $snip->getId()]);
}
} }

View File

@ -7,6 +7,7 @@ readonly class SnipFilterRequest implements CachableDtoInterface
public const string VISIBILITY_ALL = 'all'; public const string VISIBILITY_ALL = 'all';
public const string VISIBILITY_VISIBLE = 'visible'; public const string VISIBILITY_VISIBLE = 'visible';
public const string VISIBILITY_HIDDEN = 'hidden'; public const string VISIBILITY_HIDDEN = 'hidden';
public const string VISIBILITY_ARCHIVED = 'archived';
public const string SORT_NAME = 'name'; public const string SORT_NAME = 'name';
public const string SORT_DATE = 'date'; public const string SORT_DATE = 'date';

View File

@ -36,6 +36,9 @@ class Snip
#[ORM\Column] #[ORM\Column]
private bool $visible = true; private bool $visible = true;
#[ORM\Column]
private bool $archived = false;
public function __construct() public function __construct()
{ {
$this->snipContents = new ArrayCollection(); $this->snipContents = new ArrayCollection();
@ -145,4 +148,16 @@ class Snip
return $this; return $this;
} }
public function isArchived(): ?bool
{
return $this->archived;
}
public function setArchived(bool $archived): static
{
$this->archived = $archived;
return $this;
}
} }

View File

@ -50,6 +50,7 @@ class SnipRepository extends ServiceEntityRepository
->setParameter('user', $user) ->setParameter('user', $user)
; ;
$showArchived = false;
switch ($request->visibility) { switch ($request->visibility) {
case SnipFilterRequest::VISIBILITY_ALL: case SnipFilterRequest::VISIBILITY_ALL:
break; break;
@ -59,9 +60,13 @@ class SnipRepository extends ServiceEntityRepository
case SnipFilterRequest::VISIBILITY_HIDDEN: case SnipFilterRequest::VISIBILITY_HIDDEN:
$qb->andWhere('s.visible = false'); $qb->andWhere('s.visible = false');
break; break;
case SnipFilterRequest::VISIBILITY_ARCHIVED:
$showArchived = true;
break;
default: default:
throw new \InvalidArgumentException('Invalid visibility option: ', $request->visibility); throw new \InvalidArgumentException('Invalid visibility option: ', $request->visibility);
} }
$qb->andWhere('s.archived = ' . ($showArchived ? 'true' : 'false'));
switch ($request->sort) { switch ($request->sort) {
case SnipFilterRequest::SORT_NAME: case SnipFilterRequest::SORT_NAME:
@ -73,6 +78,7 @@ class SnipRepository extends ServiceEntityRepository
default: default:
throw new \InvalidArgumentException('Invalid sort option: ', $request->sort); throw new \InvalidArgumentException('Invalid sort option: ', $request->sort);
} }
return $qb->getQuery()->getResult(); return $qb->getQuery()->getResult();
} }
@ -82,6 +88,7 @@ class SnipRepository extends ServiceEntityRepository
->createQueryBuilder('s') ->createQueryBuilder('s')
->where('s.public = true') ->where('s.public = true')
->andWhere('s.visible = true') ->andWhere('s.visible = true')
->andWhere('s.archived = false')
->orderBy('s.createdAt', 'DESC') ->orderBy('s.createdAt', 'DESC')
; ;

View File

@ -17,3 +17,9 @@
<i class="fa fa-lock"></i> <i class="fa fa-lock"></i>
</span> </span>
{% endif %} {% endif %}
{% if snip.archived %}
<span class="badge bg-secondary">
Archived
</span>
{% endif %}

View File

@ -37,7 +37,7 @@
<br> <br>
<h5>Visibility</h5> <h5>Visibility</h5>
<div class="list-group"> <div class="list-group">
{% for visibilityOption in ['all', 'visible', 'hidden'] %} {% for visibilityOption in ['all', 'visible', 'hidden', 'archived'] %}
<a href="{{ path('snip_index', {visibility: visibilityOption}) }}" <a href="{{ path('snip_index', {visibility: visibilityOption}) }}"
class="list-group-item list-group-item-action {% if request.visibility is same as(visibilityOption) %}list-group-item-primary{% endif %}"> class="list-group-item list-group-item-action {% if request.visibility is same as(visibilityOption) %}list-group-item-primary{% endif %}">
Show {{ visibilityOption|capitalize }} Show {{ visibilityOption|capitalize }}

View File

@ -13,17 +13,26 @@
</a> </a>
{% endif %} {% endif %}
{% if is_granted('edit', snip) %} {% if is_granted('edit', snip) %}
<a class="btn btn-warning" href="{{ path('snip_edit', {snip: snip.id}) }}">
<i class="fa fa-pencil" aria-hidden="true"></i> Edit
</a>
<a class="btn btn-info" href="{{ path('version_index', {snip: snip.id}) }}"> <a class="btn btn-info" href="{{ path('version_index', {snip: snip.id}) }}">
<i class="fa fa-history" aria-hidden="true"></i> Versions <i class="fa fa-history" aria-hidden="true"></i> Versions
</a> </a>
<a class="btn btn-warning" href="{{ path('snip_edit', {snip: snip.id}) }}">
<i class="fa fa-pencil" aria-hidden="true"></i> Edit
</a>
{% if snip.archived %}
<a href="{{ path('snip_archive', {snip: snip.id}) }}" class="btn btn-secondary">
<i class="fa fa-undo"></i> Unarchive
</a>
{% else %}
<a href="{{ path('snip_archive', {snip: snip.id}) }}" class="btn btn-secondary">
<i class="fa fa-archive"></i> Archive
</a>
{% endif %}
<a href="{{ path('snip_delete', {snip: snip.id}) }}" class="btn btn-danger"> <a href="{{ path('snip_delete', {snip: snip.id}) }}" class="btn btn-danger">
<i class="fa fa-trash"></i> Delete <i class="fa fa-trash"></i> Delete
</a> </a>
{% endif %} {% endif %}
<a href="{{ path('snip_raw', {snip: snip.id}) }}" class="btn btn-danger"> <a href="{{ path('snip_raw', {snip: snip.id}) }}" class="btn btn-primary">
<i class="fa fa-eye"></i> Raw <i class="fa fa-eye"></i> Raw
</a> </a>
<br><br> <br><br>
@ -58,10 +67,10 @@
{{ parent() }} {{ parent() }}
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
<script> <script>
const codeBlocks = document.querySelectorAll('code.hljs'); const codeBlocks = document.querySelectorAll('code.hljs');
codeBlocks.forEach((block) => { codeBlocks.forEach((block) => {
hljs.highlightElement(block); hljs.highlightElement(block);
}); });
</script> </script>
{% endblock %} {% endblock %}