Allow snips to be archived
This commit is contained in:
parent
50a7ab7985
commit
47ea226ed7
44
migrations/Version20250510142748.php
Normal file
44
migrations/Version20250510142748.php
Normal 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);
|
||||
}
|
||||
}
|
@ -151,4 +151,19 @@ class SnipController extends AbstractController
|
||||
'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()]);
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ readonly class SnipFilterRequest implements CachableDtoInterface
|
||||
public const string VISIBILITY_ALL = 'all';
|
||||
public const string VISIBILITY_VISIBLE = 'visible';
|
||||
public const string VISIBILITY_HIDDEN = 'hidden';
|
||||
public const string VISIBILITY_ARCHIVED = 'archived';
|
||||
|
||||
public const string SORT_NAME = 'name';
|
||||
public const string SORT_DATE = 'date';
|
||||
|
@ -36,6 +36,9 @@ class Snip
|
||||
#[ORM\Column]
|
||||
private bool $visible = true;
|
||||
|
||||
#[ORM\Column]
|
||||
private bool $archived = false;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->snipContents = new ArrayCollection();
|
||||
@ -145,4 +148,16 @@ class Snip
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isArchived(): ?bool
|
||||
{
|
||||
return $this->archived;
|
||||
}
|
||||
|
||||
public function setArchived(bool $archived): static
|
||||
{
|
||||
$this->archived = $archived;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ class SnipRepository extends ServiceEntityRepository
|
||||
->setParameter('user', $user)
|
||||
;
|
||||
|
||||
$showArchived = false;
|
||||
switch ($request->visibility) {
|
||||
case SnipFilterRequest::VISIBILITY_ALL:
|
||||
break;
|
||||
@ -59,9 +60,13 @@ class SnipRepository extends ServiceEntityRepository
|
||||
case SnipFilterRequest::VISIBILITY_HIDDEN:
|
||||
$qb->andWhere('s.visible = false');
|
||||
break;
|
||||
case SnipFilterRequest::VISIBILITY_ARCHIVED:
|
||||
$showArchived = true;
|
||||
break;
|
||||
default:
|
||||
throw new \InvalidArgumentException('Invalid visibility option: ', $request->visibility);
|
||||
}
|
||||
$qb->andWhere('s.archived = ' . ($showArchived ? 'true' : 'false'));
|
||||
|
||||
switch ($request->sort) {
|
||||
case SnipFilterRequest::SORT_NAME:
|
||||
@ -73,6 +78,7 @@ class SnipRepository extends ServiceEntityRepository
|
||||
default:
|
||||
throw new \InvalidArgumentException('Invalid sort option: ', $request->sort);
|
||||
}
|
||||
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
|
||||
@ -82,6 +88,7 @@ class SnipRepository extends ServiceEntityRepository
|
||||
->createQueryBuilder('s')
|
||||
->where('s.public = true')
|
||||
->andWhere('s.visible = true')
|
||||
->andWhere('s.archived = false')
|
||||
->orderBy('s.createdAt', 'DESC')
|
||||
;
|
||||
|
||||
|
@ -17,3 +17,9 @@
|
||||
<i class="fa fa-lock"></i>
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
{% if snip.archived %}
|
||||
<span class="badge bg-secondary">
|
||||
Archived
|
||||
</span>
|
||||
{% endif %}
|
@ -37,7 +37,7 @@
|
||||
<br>
|
||||
<h5>Visibility</h5>
|
||||
<div class="list-group">
|
||||
{% for visibilityOption in ['all', 'visible', 'hidden'] %}
|
||||
{% for visibilityOption in ['all', 'visible', 'hidden', 'archived'] %}
|
||||
<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 %}">
|
||||
Show {{ visibilityOption|capitalize }}
|
||||
|
@ -13,17 +13,26 @@
|
||||
</a>
|
||||
{% endif %}
|
||||
{% 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}) }}">
|
||||
<i class="fa fa-history" aria-hidden="true"></i> Versions
|
||||
</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">
|
||||
<i class="fa fa-trash"></i> Delete
|
||||
</a>
|
||||
{% 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
|
||||
</a>
|
||||
<br><br>
|
||||
@ -58,10 +67,10 @@
|
||||
{{ parent() }}
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
|
||||
<script>
|
||||
const codeBlocks = document.querySelectorAll('code.hljs');
|
||||
const codeBlocks = document.querySelectorAll('code.hljs');
|
||||
|
||||
codeBlocks.forEach((block) => {
|
||||
hljs.highlightElement(block);
|
||||
});
|
||||
codeBlocks.forEach((block) => {
|
||||
hljs.highlightElement(block);
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
Loading…
x
Reference in New Issue
Block a user