[PLUGIN][AttachmentCollections] Restore functionality

Some minor corrections
This commit is contained in:
Diogo Peralta Cordeiro 2021-12-28 04:39:09 +00:00
parent a03429ba03
commit bb4149e092
Signed by: diogo
GPG Key ID: 18D2D35001FBFAB0
9 changed files with 118 additions and 81 deletions

View File

@ -94,7 +94,7 @@ class Attachment extends Controller
'title' => $res['title'], 'title' => $res['title'],
'attachment' => $res['attachment'], 'attachment' => $res['attachment'],
'note' => $res['note'], 'note' => $res['note'],
'right_panel_vars' => ['attachment_id' => $attachment_id], 'right_panel_vars' => ['attachment_id' => $attachment_id, 'note_id' => $note_id],
]; ];
}); });
} catch (NotFoundException) { } catch (NotFoundException) {

View File

@ -0,0 +1,11 @@
<?php
declare(strict_types = 1);
namespace Component\Collection;
use App\Core\Modules\Component;
class Collection extends Component
{
}

View File

@ -2,9 +2,10 @@
declare(strict_types = 1); declare(strict_types = 1);
namespace Plugin\AttachmentCollections\Entity; namespace Component\Collection\Entity;
use App\Core\Entity; use App\Core\Entity;
use function mb_substr;
class Collection extends Entity class Collection extends Entity
{ {
@ -12,7 +13,7 @@ class Collection extends Entity
// {{{ Autocode // {{{ Autocode
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
private int $id; private int $id;
private ?string $name = null; private string $name;
private int $actor_id; private int $actor_id;
public function setId(int $id): self public function setId(int $id): self
@ -26,13 +27,13 @@ class Collection extends Entity
return $this->id; return $this->id;
} }
public function setName(?string $name): self public function setName(string $name): self
{ {
$this->name = \is_null($name) ? null : mb_substr($name, 0, 255); $this->name = mb_substr($name, 0, 255);
return $this; return $this;
} }
public function getName(): ?string public function getName(): string
{ {
return $this->name; return $this->name;
} }
@ -54,10 +55,10 @@ class Collection extends Entity
public static function schemaDef() public static function schemaDef()
{ {
return [ return [
'name' => 'attachment_collection', 'name' => 'collection',
'fields' => [ 'fields' => [
'id' => ['type' => 'serial', 'not null' => true, 'description' => 'unique identifier'], 'id' => ['type' => 'serial', 'not null' => true, 'description' => 'unique identifier'],
'name' => ['type' => 'varchar', 'length' => 255, 'description' => 'collection\'s name'], 'name' => ['type' => 'varchar', 'length' => 255, 'not null' => true, 'description' => 'collection\'s name'],
'actor_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Actor.id', 'multiplicity' => 'one to many', 'not null' => true, 'description' => 'foreign key to actor table'], 'actor_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Actor.id', 'multiplicity' => 'one to many', 'not null' => true, 'description' => 'foreign key to actor table'],
], ],
'primary key' => ['id'], 'primary key' => ['id'],

View File

@ -33,7 +33,6 @@ namespace Plugin\AttachmentCollections;
use App\Core\DB\DB; use App\Core\DB\DB;
use App\Core\Event; use App\Core\Event;
use App\Util\Exception\RedirectException;
use App\Core\Form; use App\Core\Form;
use function App\Core\I18n\_m; use function App\Core\I18n\_m;
use App\Core\Modules\Plugin; use App\Core\Modules\Plugin;
@ -42,11 +41,12 @@ use App\Core\Router\Router;
use App\Entity\Feed; use App\Entity\Feed;
use App\Entity\LocalUser; use App\Entity\LocalUser;
use App\Util\Common; use App\Util\Common;
use App\Util\Exception\RedirectException;
use App\Util\Formatting; use App\Util\Formatting;
use App\Util\Nickname; use App\Util\Nickname;
use Component\Collection\Entity\Collection;
use Plugin\AttachmentCollections\Controller as C; use Plugin\AttachmentCollections\Controller as C;
use Plugin\AttachmentCollections\Entity\Collection; use Plugin\AttachmentCollections\Entity\AttachmentCollectionEntry;
use Plugin\AttachmentCollections\Entity\CollectionEntry;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\Extension\Core\Type\TextType;
@ -80,6 +80,7 @@ class AttachmentCollections extends Plugin
); );
return Event::next; return Event::next;
} }
public function onCreateDefaultFeeds(int $actor_id, LocalUser $user, int &$ordering) public function onCreateDefaultFeeds(int $actor_id, LocalUser $user, int &$ordering)
{ {
DB::persist(Feed::create([ DB::persist(Feed::create([
@ -91,6 +92,7 @@ class AttachmentCollections extends Plugin
])); ]));
return Event::next; return Event::next;
} }
/** /**
* Append Attachment Collections widget to the right panel. * Append Attachment Collections widget to the right panel.
* It's compose of two forms: one to select collections to add * It's compose of two forms: one to select collections to add
@ -106,23 +108,23 @@ class AttachmentCollections extends Plugin
return Event::next; return Event::next;
} }
$colls = DB::dql( $collections = DB::findBy(Collection::class, ['actor_id' => $user->getId()]);
'select coll from Plugin\AttachmentCollections\Entity\Collection coll where coll.actor_id = :id',
['id' => $user->getId()],
);
// add to collection form // add to collection form
$attachment_id = $vars['vars']['attachment_id']; $attachment_id = $vars['vars']['attachment_id'];
$note_id = $vars['vars']['note_id'];
$choices = []; $choices = [];
foreach ($colls as $col) { foreach ($collections as $col) {
$choices[$col->getName()] = $col->getId(); $choices[$col->getName()] = $col->getId();
} }
$already_selected = DB::dql( $already_selected = DB::dql(
'select entry.collection_id from attachment_album_entry entry ' <<<'EOF'
. 'inner join attachment_collection collection ' SELECT entry.collection_id FROM \Plugin\AttachmentCollections\Entity\AttachmentCollectionEntry AS entry
. 'with collection.id = entry.collection_id ' INNER JOIN \Component\Collection\Entity\Collection AS attachment_collection
. 'where entry.attachment_id = :aid and collection.actor_id = :id', WITH attachment_collection.id = entry.collection_id
['aid' => $attachment_id, 'id' => $user->getId()], WHERE entry.attachment_id = :attach_id AND entry.note_id = :note_id AND attachment_collection.actor_id = :id
EOF,
['attach_id' => $attachment_id, 'note_id' => $note_id, 'id' => $user->getId()],
); );
$already_selected = array_map(fn ($x) => $x['collection_id'], $already_selected); $already_selected = array_map(fn ($x) => $x['collection_id'], $already_selected);
$add_form = Form::create([ $add_form = Form::create([
@ -150,24 +152,23 @@ class AttachmentCollections extends Plugin
$collections = $add_form->getData()['collections']; $collections = $add_form->getData()['collections'];
$removed = array_filter($already_selected, fn ($x) => !\in_array($x, $collections)); $removed = array_filter($already_selected, fn ($x) => !\in_array($x, $collections));
$added = array_filter($collections, fn ($x) => !\in_array($x, $already_selected)); $added = array_filter($collections, fn ($x) => !\in_array($x, $already_selected));
if (\count($removed)) { if (\count($removed) > 0) {
DB::dql( DB::dql(<<<'EOF'
'delete from Plugin\AttachmentCollections\Entity\CollectionEntry entry ' DELETE FROM \Plugin\AttachmentCollections\Entity\AttachmentCollectionEntry AS entry
. 'where entry.attachment_id = :aid and entry.collection_id in (' WHERE entry.attachment_id = :attach_id AND entry.note_id = :note_id
. 'select collection.id from Plugin\AttachmentCollections\Entity\Collection collection ' AND entry.collection_id IN (
. 'where collection.id in (:ids) ' SELECT album.id FROM \Component\Collection\Entity\Collection AS album
// prevent user from deleting something (s)he doesn't own: WHERE album.actor_id = :user_id
. 'and collection.actor_id = :id' AND album.id IN (:ids)
. ')', )
['aid' => $attachment_id, 'id' => $user->getId(), 'ids' => $removed], EOF, ['attach_id' => $attachment_id, 'note_id' => $note_id, 'user_id' => $user->getId(), 'ids' => $removed]);
);
} }
$collection_ids = array_map(fn ($x) => $x->getId(), $colls);
foreach ($added as $cid) { foreach ($added as $cid) {
// prevent user from putting something in a collection (s)he doesn't own: // prevent user from putting something in a collection (s)he doesn't own:
if (\in_array($cid, $collection_ids)) { if (\in_array($cid, $collections)) {
DB::persist(CollectionEntry::create([ DB::persist(AttachmentCollectionEntry::create([
'attachment_id' => $attachment_id, 'attachment_id' => $attachment_id,
'note_id' => $note_id,
'collection_id' => $cid, 'collection_id' => $cid,
])); ]));
} }
@ -200,9 +201,9 @@ class AttachmentCollections extends Plugin
'actor_id' => $user->getId(), 'actor_id' => $user->getId(),
]); ]);
DB::persist($coll); DB::persist($coll);
DB::flush(); DB::persist(AttachmentCollectionEntry::create([
DB::persist(CollectionEntry::create([
'attachment_id' => $attachment_id, 'attachment_id' => $attachment_id,
'note_id' => $note_id,
'collection_id' => $coll->getId(), 'collection_id' => $coll->getId(),
])); ]));
DB::flush(); DB::flush();
@ -212,13 +213,14 @@ class AttachmentCollections extends Plugin
$res[] = Formatting::twigRenderFile( $res[] = Formatting::twigRenderFile(
'AttachmentCollections/widget.html.twig', 'AttachmentCollections/widget.html.twig',
[ [
'has_collections' => $colls, 'has_collections' => $collections,
'add_form' => $add_form->createView(), 'add_form' => $add_form->createView(),
'create_form' => $create_form->createView(), 'create_form' => $create_form->createView(),
], ],
); );
return Event::next; return Event::next;
} }
/** /**
* Output our dedicated stylesheet * Output our dedicated stylesheet
* *

View File

@ -24,13 +24,14 @@ declare(strict_types = 1);
namespace Plugin\AttachmentCollections\Controller; namespace Plugin\AttachmentCollections\Controller;
use App\Core\DB\DB; use App\Core\DB\DB;
use App\Util\Exception\RedirectException;
use App\Core\Form; use App\Core\Form;
use function App\Core\I18n\_m; use function App\Core\I18n\_m;
use App\Core\Router\Router; use App\Core\Router\Router;
use App\Entity\LocalUser;
use App\Util\Common; use App\Util\Common;
use App\Util\Exception\RedirectException;
use Component\Collection\Entity\Collection;
use Component\Feed\Util\FeedController; use Component\Feed\Util\FeedController;
use Plugin\AttachmentCollections\Entity\Collection;
use Symfony\Component\Form\Extension\Core\Type\SubmitType; use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@ -39,13 +40,15 @@ class Controller extends FeedController
{ {
public function collectionsByActorNickname(Request $request, string $nickname): array public function collectionsByActorNickname(Request $request, string $nickname): array
{ {
$user = DB::findOneBy('local_user', ['nickname' => $nickname]); $user = DB::findOneBy(LocalUser::class, ['nickname' => $nickname]);
return self::collectionsView($request, $user->getId(), $nickname); return self::collectionsView($request, $user->getId(), $nickname);
} }
public function collectionsViewByActorId(Request $request, int $id): array public function collectionsViewByActorId(Request $request, int $id): array
{ {
return self::collectionsView($request, $id, null); return self::collectionsView($request, $id, null);
} }
/** /**
* Generate Collections page * Generate Collections page
* *
@ -56,11 +59,8 @@ class Controller extends FeedController
*/ */
public function collectionsView(Request $request, int $id, ?string $nickname): array public function collectionsView(Request $request, int $id, ?string $nickname): array
{ {
$collections = DB::dql( $collections = DB::findBy(Collection::class, ['actor_id' => $id]);
'select collection from Plugin\AttachmentCollections\Entity\Collection collection '
. 'where collection.actor_id = :id',
['id' => $id],
);
// create collection form // create collection form
$create = null; $create = null;
if (Common::user()?->getId() === $id) { if (Common::user()?->getId() === $id) {
@ -92,16 +92,17 @@ class Controller extends FeedController
} }
// We need to inject some functions in twig, // We need to inject some functions in twig,
// but i don't want to create an enviroment for this // but I don't want to create an environment for this
// as twig docs suggest in https://twig.symfony.com/doc/2.x/advanced.html#functions. // as twig docs suggest in https://twig.symfony.com/doc/2.x/advanced.html#functions.
// //
// Instead, I'm using an anonymous class to encapsulate // Instead, I'm using an anonymous class to encapsulate
// the functions and passing how the class to the template. // the functions and passing that class to the template.
// It's suggested at https://stackoverflow.com/a/50364502. // This is suggested at https://stackoverflow.com/a/50364502.
$fn = new class($id, $nickname, $request) { $fn = new class($id, $nickname, $request) {
private $id; private $id;
private $nick; private $nick;
private $request; private $request;
public function __construct($id, $nickname, $request) public function __construct($id, $nickname, $request)
{ {
$this->id = $id; $this->id = $id;
@ -156,6 +157,7 @@ class Controller extends FeedController
} }
return $edit->createView(); return $edit->createView();
} }
// creating the remove form // creating the remove form
public function rmForm($collection) public function rmForm($collection)
{ {
@ -189,23 +191,29 @@ class Controller extends FeedController
public function collectionNotesByNickname(Request $request, string $nickname, int $cid): array public function collectionNotesByNickname(Request $request, string $nickname, int $cid): array
{ {
$user = DB::findOneBy('local_user', ['nickname' => $nickname]); $user = DB::findOneBy(LocalUser::class, ['nickname' => $nickname]);
return self::collectionNotesByActorId($request, $user->getId(), $cid); return self::collectionNotesByActorId($request, $user->getId(), $cid);
} }
public function collectionNotesByActorId(Request $request, int $id, int $cid): array public function collectionNotesByActorId(Request $request, int $id, int $cid): array
{ {
$collection = DB::findOneBy('attachment_collection', ['id' => $cid]); $collection = DB::findOneBy(Collection::class, ['id' => $cid]);
$attchs = DB::dql( [$attachs, $notes] = DB::dql(
'select attch from attachment_album_entry entry ' <<<'EOF'
. 'left join Component\Attachment\Entity\Attachment attch ' SELECT attach, notice FROM \Plugin\AttachmentCollections\Entity\AttachmentCollectionEntry AS entry
. 'with entry.attachment_id = attch.id ' LEFT JOIN \Component\Attachment\Entity\Attachment AS attach
. 'where entry.collection_id = :cid', WITH entry.attachment_id = attach.id
LEFT JOIN \App\Entity\Note AS notice
WITH entry.note_id = notice.id
WHERE entry.collection_id = :cid
EOF,
['cid' => $cid], ['cid' => $cid],
); );
return [ return [
'_template' => 'AttachmentCollections/collection.html.twig', '_template' => 'AttachmentCollections/collection.html.twig',
'page_title' => $collection->getName(), 'page_title' => $collection->getName(),
'attachments' => $attchs, 'attachments' => array_values($attachs),
'bare_notes' => array_values($notes),
]; ];
} }
} }

View File

@ -6,12 +6,13 @@ namespace Plugin\AttachmentCollections\Entity;
use App\Core\Entity; use App\Core\Entity;
class CollectionEntry extends Entity class AttachmentCollectionEntry extends Entity
{ {
// These tags are meant to be literally included and will be populated with the appropriate fields, setters and getters by `bin/generate_entity_fields` // These tags are meant to be literally included and will be populated with the appropriate fields, setters and getters by `bin/generate_entity_fields`
// {{{ Autocode // {{{ Autocode
// @codeCoverageIgnoreStart // @codeCoverageIgnoreStart
private int $id; private int $id;
private int $note_id;
private int $attachment_id; private int $attachment_id;
private int $collection_id; private int $collection_id;
@ -26,6 +27,17 @@ class CollectionEntry extends Entity
return $this->id; return $this->id;
} }
public function setNoteId(int $note_id): self
{
$this->note_id = $note_id;
return $this;
}
public function getNoteId(): int
{
return $this->note_id;
}
public function setAttachmentId(int $attachment_id): self public function setAttachmentId(int $attachment_id): self
{ {
$this->attachment_id = $attachment_id; $this->attachment_id = $attachment_id;
@ -54,11 +66,12 @@ class CollectionEntry extends Entity
public static function schemaDef() public static function schemaDef()
{ {
return [ return [
'name' => 'attachment_album_entry', 'name' => 'attachment_collection_entry',
'fields' => [ 'fields' => [
'id' => ['type' => 'serial', 'not null' => true, 'description' => 'unique identifier'], 'id' => ['type' => 'serial', 'not null' => true, 'description' => 'unique identifier'],
'note_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Note.id', 'multiplicity' => 'one to one', 'not null' => true, 'description' => 'foreign key to note table'],
'attachment_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Attachment.id', 'multiplicity' => 'one to one', 'not null' => true, 'description' => 'foreign key to attachment table'], 'attachment_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Attachment.id', 'multiplicity' => 'one to one', 'not null' => true, 'description' => 'foreign key to attachment table'],
'collection_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'AttachmentCollection.id', 'multiplicity' => 'one to one', 'not null' => true, 'description' => 'foreign key to attachment_collection table'], 'collection_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Collection.id', 'multiplicity' => 'one to one', 'not null' => true, 'description' => 'foreign key to collection table'],
], ],
'primary key' => ['id'], 'primary key' => ['id'],
]; ];

View File

@ -13,10 +13,11 @@
{# Backwards compatibility with hAtom 0.1 #} {# Backwards compatibility with hAtom 0.1 #}
<main class="feed" tabindex="0" role="feed"> <main class="feed" tabindex="0" role="feed">
<div class="h-feed hfeed notes"> <div class="h-feed hfeed notes">
{% for attachment in attachments %} {% for key, attachment in attachments %}
<section class="section-widget section-padding"> <section class="section-widget section-padding">
{% include '/cards/attachments/view.html.twig' with {'attachment': attachment, 'note': null} only%} {% include '/cards/attachments/view.html.twig' with {'attachment': attachment, 'note': bare_notes[key], 'title': attachment.getBestTitle(bare_notes[key])} only %}
<a class="section-widget-button-like" href="{{ path('attachment_download', {'id': attachment.id}) }}"> {{ 'Download link' | trans }}</a> <a class="section-widget-button-like"
href="{{ attachment.getDownloadUrl(bare_notes[key]) }}"> {{ 'Download link' | trans }}</a>
</section> </section>
{% else %} {% else %}
<div id="empty-notes"><h1>{% trans %}No attachments here.{% endtrans %}</h1></div> <div id="empty-notes"><h1>{% trans %}No attachments here.{% endtrans %}</h1></div>

View File

@ -20,23 +20,23 @@
{% endif %} {% endif %}
<div class="h-entry hentry note attachment-collections-list"> <div class="h-entry hentry note attachment-collections-list">
<h3>{{ 'Your collections' | trans }}</h3> <h3>{{ 'Your collections' | trans }}</h3>
{% for col in collections %} {% for col in collections %}
<div class="attachment-collection-item"> <div class="attachment-collection-item">
<a class="name" href="{{ fn.getUrl(col.id) }}">{{ col.name }}</a> <a class="name" href="{{ fn.getUrl(col.id) }}">{{ col.name }}</a>
<details title="Expand if you want to edit the collection's name"> <details title="Expand if you want to edit the collection's name">
<summary> <summary>
<div class="collection-action">{{ icon('edit') | raw }}</div> <div class="collection-action">{{ icon('edit') | raw }}</div>
</summary> </summary>
{{ form(fn.editForm(col)) }} {{ form(fn.editForm(col)) }}
</details> </details>
<details title="Expand if you want to delete the collection"> <details title="Expand if you want to delete the collection">
<summary> <summary>
<div class="collection-action">{{ icon('delete') | raw }}</div> <div class="collection-action">{{ icon('delete') | raw }}</div>
</summary> </summary>
{{ form(fn.rmForm(col)) }} {{ form(fn.rmForm(col)) }}
</details> </details>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
</main> </main>

View File

@ -9,7 +9,8 @@
{{ form(add_form) }} {{ form(add_form) }}
</fieldset> </fieldset>
<details class="section-widget-subtitle-details section-padding" title="Expand if you want to access more options."> <details class="section-widget-subtitle-details section-padding"
title="Expand if you want to access more options.">
<summary class="section-subtitle-summary"> <summary class="section-subtitle-summary">
<strong>{% trans %}Other options{% endtrans %}</strong> <strong>{% trans %}Other options{% endtrans %}</strong>
{{ icon('arrow-down', 'icon icon-details-close') | raw }} {{ icon('arrow-down', 'icon icon-details-close') | raw }}