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(),
|
'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_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';
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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')
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -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 %}
|
@ -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 }}
|
||||||
|
@ -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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user