[PLUGINS][Repeat] Added note_repeat entity, fixed visual discrepancies, and completed the expected functionality.
[ENTITY][Note] Removed repeat_off from table. It is now part of the Repeat plugin.
This commit is contained in:
parent
73e772e576
commit
cf09b48e92
@ -34,6 +34,7 @@ use App\Util\Exception\InvalidFormException;
|
|||||||
use App\Util\Exception\NoLoggedInUser;
|
use App\Util\Exception\NoLoggedInUser;
|
||||||
use App\Util\Exception\NoSuchNoteException;
|
use App\Util\Exception\NoSuchNoteException;
|
||||||
use App\Util\Exception\RedirectException;
|
use App\Util\Exception\RedirectException;
|
||||||
|
use Plugin\Repeat\Entity\NoteRepeat;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use function App\Core\I18n\_m;
|
use function App\Core\I18n\_m;
|
||||||
@ -52,7 +53,7 @@ class Repeat extends Controller
|
|||||||
{
|
{
|
||||||
$user = Common::ensureLoggedIn();
|
$user = Common::ensureLoggedIn();
|
||||||
$opts = ['actor_id' => $user->getId(), 'repeat_of' => $id];
|
$opts = ['actor_id' => $user->getId(), 'repeat_of' => $id];
|
||||||
$note_already_repeated = DB::count('note', $opts) >= 1;
|
$note_already_repeated = DB::count('note_repeat', $opts) >= 1;
|
||||||
if (is_null($note_already_repeated)) {
|
if (is_null($note_already_repeated)) {
|
||||||
throw new NoSuchNoteException();
|
throw new NoSuchNoteException();
|
||||||
}
|
}
|
||||||
@ -71,15 +72,38 @@ class Repeat extends Controller
|
|||||||
|
|
||||||
$form_add_to_repeat->handleRequest($request);
|
$form_add_to_repeat->handleRequest($request);
|
||||||
if ($form_add_to_repeat->isSubmitted()) {
|
if ($form_add_to_repeat->isSubmitted()) {
|
||||||
|
|
||||||
if (!is_null($note)) {
|
if (!is_null($note)) {
|
||||||
DB::persist(Note::create([
|
$actor_id = $user->getId();
|
||||||
'actor_id' => $user->getId(),
|
$content = $note->getContent();
|
||||||
'repeat_of' => $note->getId(),
|
|
||||||
'content' => $note->getContent(),
|
// Create a new note with the same content as the original
|
||||||
|
$repeat = Note::create([
|
||||||
|
'actor_id' => $actor_id,
|
||||||
|
'content' => $content,
|
||||||
'content_type' => $note->getContentType(),
|
'content_type' => $note->getContentType(),
|
||||||
'rendered' => $note->getRendered(),
|
'rendered' => $note->getRendered(),
|
||||||
'is_local' => true,
|
'is_local' => true,
|
||||||
]));
|
]);
|
||||||
|
DB::persist($repeat);
|
||||||
|
|
||||||
|
// Update DB
|
||||||
|
DB::flush();
|
||||||
|
|
||||||
|
// Find the id of the note we just created
|
||||||
|
$repeat_id = $repeat->getId();
|
||||||
|
$og_id = $note->getId();
|
||||||
|
|
||||||
|
// Add it to note_repeat table
|
||||||
|
if (!is_null($repeat_id)) {
|
||||||
|
DB::persist(NoteRepeat::create([
|
||||||
|
'id' => $repeat_id,
|
||||||
|
'actor_id' => $actor_id,
|
||||||
|
'repeat_of' => $og_id
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update DB one last time
|
||||||
DB::flush();
|
DB::flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +130,7 @@ class Repeat extends Controller
|
|||||||
public function repeatRemoveNote(Request $request, int $id): array
|
public function repeatRemoveNote(Request $request, int $id): array
|
||||||
{
|
{
|
||||||
$user = Common::ensureLoggedIn();
|
$user = Common::ensureLoggedIn();
|
||||||
$opts = ['note_id' => $id, 'actor_id' => $user->getId()];
|
$opts = ['id' => $id];
|
||||||
$remove_repeat_note = DB::find('note', $opts);
|
$remove_repeat_note = DB::find('note', $opts);
|
||||||
if (is_null($remove_repeat_note)) {
|
if (is_null($remove_repeat_note)) {
|
||||||
throw new NoSuchNoteException();
|
throw new NoSuchNoteException();
|
||||||
|
92
plugins/Repeat/Entity/NoteRepeat.php
Normal file
92
plugins/Repeat/Entity/NoteRepeat.php
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
// {{{ License
|
||||||
|
|
||||||
|
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||||
|
//
|
||||||
|
// GNU social is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// GNU social is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// }}}
|
||||||
|
|
||||||
|
namespace Plugin\Repeat\Entity;
|
||||||
|
|
||||||
|
use App\Core\Entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entity for notices
|
||||||
|
*
|
||||||
|
* @category DB
|
||||||
|
* @package GNUsocial
|
||||||
|
*
|
||||||
|
* @author Eliseu Amaro <mail@eliseuama.ro>
|
||||||
|
* @copyright 2020-2021 Free Software Foundation, Inc http://www.fsf.org
|
||||||
|
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||||
|
*/
|
||||||
|
class NoteRepeat extends Entity
|
||||||
|
{
|
||||||
|
private int $id;
|
||||||
|
private int $actor_id;
|
||||||
|
private int $repeat_of;
|
||||||
|
|
||||||
|
public function setId(int $id): self
|
||||||
|
{
|
||||||
|
$this->id = $id;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getId(): int
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setActorId(int $actor_id): self
|
||||||
|
{
|
||||||
|
$this->actor_id = $actor_id;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getActorId(): ?int
|
||||||
|
{
|
||||||
|
return $this->actor_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRepeatOf(int $repeat_of): self
|
||||||
|
{
|
||||||
|
$this->repeat_of = $repeat_of;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRepeatOf(): int
|
||||||
|
{
|
||||||
|
return $this->repeat_of;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function schemaDef(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => 'note_repeat',
|
||||||
|
'fields' => [
|
||||||
|
'id' => ['type' => 'int', 'not null' => true, 'description' => 'The id of the repeat itself'],
|
||||||
|
'actor_id' => ['type' => 'int', 'not null' => true, 'foreign key' => true, 'target' => 'Actor.id', 'multiplicity' => 'one to one', 'description' => 'Who made this repeat'],
|
||||||
|
'repeat_of' => ['type' => 'int', 'not null' => true, 'foreign key' => true, 'target' => 'Note.id', 'multiplicity' => 'one to one', 'description' => 'Note this is a repeat of'],
|
||||||
|
],
|
||||||
|
'primary key' => ['id'],
|
||||||
|
'foreign keys' => [
|
||||||
|
'note_repeat_of_id_fkey' => ['note', ['repeat_of' => 'id']],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -25,11 +25,13 @@ use App\Core\DB\DB;
|
|||||||
use App\Core\Event;
|
use App\Core\Event;
|
||||||
use App\Core\Router\RouteLoader;
|
use App\Core\Router\RouteLoader;
|
||||||
use App\Core\Router\Router;
|
use App\Core\Router\Router;
|
||||||
|
use App\Util\Exception\DuplicateFoundException;
|
||||||
use App\Util\Exception\InvalidFormException;
|
use App\Util\Exception\InvalidFormException;
|
||||||
use App\Util\Exception\NoSuchNoteException;
|
use App\Util\Exception\NoSuchNoteException;
|
||||||
use App\Core\Modules\NoteHandlerPlugin;
|
use App\Core\Modules\NoteHandlerPlugin;
|
||||||
use App\Entity\Note;
|
use App\Entity\Note;
|
||||||
use App\Util\Common;
|
use App\Util\Common;
|
||||||
|
use App\Util\Exception\NotFoundException;
|
||||||
use App\Util\Exception\RedirectException;
|
use App\Util\Exception\RedirectException;
|
||||||
use App\Util\Formatting;
|
use App\Util\Formatting;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
@ -53,21 +55,30 @@ class Repeat extends NoteHandlerPlugin
|
|||||||
return Event::next;
|
return Event::next;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If note is repeat, "is_repeated" is 1
|
// If note is repeated, "is_repeated" is 1
|
||||||
$opts = ['actor_id' => $user->getId(), 'repeat_of' => $note->getId()];
|
$opts = ['repeat_of' => $note->getId()];
|
||||||
$is_repeated = DB::count('note', $opts) >= 1;
|
try {
|
||||||
|
if (DB::findOneBy('note_repeat', $opts)) {
|
||||||
|
return Event::next;
|
||||||
|
}
|
||||||
|
} catch (DuplicateFoundException $e) {
|
||||||
|
} catch (NotFoundException $e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
$is_repeat = DB::count('note_repeat', ['id' => $note->getId()]) >= 1;
|
||||||
|
|
||||||
|
|
||||||
// Generating URL for repeat action route
|
// Generating URL for repeat action route
|
||||||
$args = ['id' => $note->getId()];
|
$args = ['id' => $note->getId()];
|
||||||
$type = Router::ABSOLUTE_PATH;
|
$type = Router::ABSOLUTE_PATH;
|
||||||
$repeat_action_url = $is_repeated ?
|
$repeat_action_url = $is_repeat ?
|
||||||
Router::url('repeat_remove', $args, $type) :
|
Router::url('repeat_remove', $args, $type) :
|
||||||
Router::url('repeat_add', $args, $type);
|
Router::url('repeat_add', $args, $type);
|
||||||
|
|
||||||
// Concatenating get parameter to redirect the user to where he came from
|
// Concatenating get parameter to redirect the user to where he came from
|
||||||
$repeat_action_url .= '?from=' . substr($request->getQueryString(), 2);
|
$repeat_action_url .= '?from=' . substr($request->getQueryString(), 2);
|
||||||
|
|
||||||
$extra_classes = $is_repeated ? "note-actions-set" : "note-actions-unset";
|
$extra_classes = $is_repeat ? "note-actions-set" : "note-actions-unset";
|
||||||
$repeat_action = [
|
$repeat_action = [
|
||||||
"url" => $repeat_action_url,
|
"url" => $repeat_action_url,
|
||||||
"classes" => "button-container repeat-button-container $extra_classes",
|
"classes" => "button-container repeat-button-container $extra_classes",
|
||||||
@ -89,6 +100,17 @@ class Repeat extends NoteHandlerPlugin
|
|||||||
return Event::next;
|
return Event::next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function onGetAdditionalTemplateVars(array $vars, array &$result)
|
||||||
|
{
|
||||||
|
$note_id = $vars['note_id'];
|
||||||
|
|
||||||
|
$opts = ['id' => $note_id];
|
||||||
|
$is_repeat = DB::count('note_repeat', $opts) >= 1;
|
||||||
|
|
||||||
|
$result = ['is_repeat' => (bool)$is_repeat];
|
||||||
|
return Event::stop;
|
||||||
|
}
|
||||||
|
|
||||||
public function onAddRoute(RouteLoader $r): bool
|
public function onAddRoute(RouteLoader $r): bool
|
||||||
{
|
{
|
||||||
// Add/remove note to/from repeats
|
// Add/remove note to/from repeats
|
||||||
|
@ -1,22 +1,54 @@
|
|||||||
{% extends '/cards/note/view.html.twig' %}
|
{% extends '/cards/note/view.html.twig' %}
|
||||||
|
|
||||||
|
{% block note_author_repeated %}
|
||||||
|
{# Microformat's h-card properties indicates a face icon is a "u-logo" #}
|
||||||
|
<a href="{{ actor_url }}" class="note-author u-url">
|
||||||
|
<strong class="note-author-fullname">
|
||||||
|
{% if fullname is not null %}
|
||||||
|
{{ fullname }}
|
||||||
|
{% else %}
|
||||||
|
{{ nickname }}
|
||||||
|
{% endif %}
|
||||||
|
</strong>
|
||||||
|
<em class="note-author-nickname">{{ nickname }} {{ "repeated the following note" | trans }}</em>
|
||||||
|
</a>
|
||||||
|
{% endblock note_author_repeated %}
|
||||||
|
|
||||||
{% macro macro_note(note, replies) %}
|
{% macro macro_note(note, replies) %}
|
||||||
{% set nickname = note.getActorNickname() %}
|
{% set nickname = note.getActorNickname() %}
|
||||||
{% set fullname = note.getActorFullname() %}
|
{% set fullname = note.getActorFullname() %}
|
||||||
{% set actor_url = note.getActor().getUrl() %}
|
{% set actor_url = note.getActor().getUrl() %}
|
||||||
{% set repeat_of = note.getRepeatOf() %}
|
{% set additional_vars = handle_event('GetAdditionalTemplateVars', {'note_id': note.getId(), 'actor_id': note.getActorId()}) %}
|
||||||
|
|
||||||
<article class="h-entry hentry note">
|
{% if additional_vars['is_repeat'] %}
|
||||||
{{ block('note_sidebar') }}
|
<article class="h-entry hentry note">
|
||||||
<div class="note-wrapper">
|
{{ block('note_sidebar') }}
|
||||||
<div tabindex="0" title="{{ 'Begin a note by the user: ' | trans }} {{ nickname }}." class="note-info">
|
<div class="note-wrapper">
|
||||||
{{ block('note_author') }}
|
<div tabindex="0" title="{{ 'Begin a note by the user: ' | trans }} {{ nickname }}." class="note-info">
|
||||||
{{ block('note_actions') }}
|
{{ block('note_author_repeated') }}
|
||||||
|
{{ block('note_actions') }}
|
||||||
|
</div>
|
||||||
|
<section tabindex="0" role="dialog" class="e-content entry-content note-content">
|
||||||
|
{{ _self.macro_note_minimal(note) }}
|
||||||
|
</section>
|
||||||
|
{{ block('note_replies') }}
|
||||||
</div>
|
</div>
|
||||||
<section tabindex="0" role="dialog" class="e-content entry-content note-content">
|
</article>
|
||||||
{{ _self.macro_note_minimal(note) }}
|
{% else %}
|
||||||
</section>
|
<article class="h-entry hentry note">
|
||||||
{{ block('note_replies') }}
|
{{ block('note_sidebar') }}
|
||||||
</div>
|
<div class="note-wrapper">
|
||||||
</article>
|
<div tabindex="0" title="{{ 'Begin a note by the user: ' | trans }} {{ nickname }}." class="note-info">
|
||||||
|
{{ block('note_author') }}
|
||||||
|
{{ block('note_actions') }}
|
||||||
|
</div>
|
||||||
|
<section tabindex="0" role="dialog" class="e-content entry-content note-content">
|
||||||
|
{{ block('note_text') }}
|
||||||
|
{{ block('note_attachments') }}
|
||||||
|
{{ block('note_links') }}
|
||||||
|
</section>
|
||||||
|
{{ block('note_replies') }}
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
{% endif %}
|
||||||
{% endmacro macro_note %}
|
{% endmacro macro_note %}
|
@ -13,7 +13,7 @@
|
|||||||
<div class="page">
|
<div class="page">
|
||||||
<div class="main">
|
<div class="main">
|
||||||
{{ noteView.macro_note_minimal(note) }}
|
{{ noteView.macro_note_minimal(note) }}
|
||||||
{{ form(remove_favourite) }}
|
{{ form(remove_repeat) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock body %}
|
{% endblock body %}
|
||||||
|
@ -162,7 +162,7 @@ class Note extends Entity
|
|||||||
return $this->conversation;
|
return $this->conversation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setRepeatOf(?int $repeat_of): self
|
/* public function setRepeatOf(?int $repeat_of): self
|
||||||
{
|
{
|
||||||
$this->repeat_of = $repeat_of;
|
$this->repeat_of = $repeat_of;
|
||||||
return $this;
|
return $this;
|
||||||
@ -171,7 +171,7 @@ class Note extends Entity
|
|||||||
public function getRepeatOf(): ?int
|
public function getRepeatOf(): ?int
|
||||||
{
|
{
|
||||||
return $this->repeat_of;
|
return $this->repeat_of;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
public function setScope(int $scope): self
|
public function setScope(int $scope): self
|
||||||
{
|
{
|
||||||
@ -349,7 +349,7 @@ class Note extends Entity
|
|||||||
'is_local' => ['type' => 'bool', 'description' => 'was this note generated by a local actor'],
|
'is_local' => ['type' => 'bool', 'description' => 'was this note generated by a local actor'],
|
||||||
'source' => ['type' => 'varchar', 'foreign key' => true, 'length' => 32, 'target' => 'NoteSource.code', 'multiplicity' => 'many to one', 'description' => 'fkey to source of note, like "web", "im", or "clientname"'],
|
'source' => ['type' => 'varchar', 'foreign key' => true, 'length' => 32, 'target' => 'NoteSource.code', 'multiplicity' => 'many to one', 'description' => 'fkey to source of note, like "web", "im", or "clientname"'],
|
||||||
'conversation' => ['type' => 'int', 'foreign key' => true, 'target' => 'Conversation.id', 'multiplicity' => 'one to one', 'description' => 'the local conversation id'],
|
'conversation' => ['type' => 'int', 'foreign key' => true, 'target' => 'Conversation.id', 'multiplicity' => 'one to one', 'description' => 'the local conversation id'],
|
||||||
'repeat_of' => ['type' => 'int', 'foreign key' => true, 'target' => 'Note.id', 'multiplicity' => 'one to one', 'description' => 'note this is a repeat of'],
|
// 'repeat_of' => ['type' => 'int', 'foreign key' => true, 'target' => 'Note.id', 'multiplicity' => 'one to one', 'description' => 'note this is a repeat of'],
|
||||||
'scope' => ['type' => 'int', 'not null' => true, 'default' => VisibilityScope::PUBLIC, 'description' => 'bit map for distribution scope; 0 = everywhere; 1 = this server only; 2 = addressees; 4 = groups; 8 = followers; 16 = messages; null = default'],
|
'scope' => ['type' => 'int', 'not null' => true, 'default' => VisibilityScope::PUBLIC, 'description' => 'bit map for distribution scope; 0 = everywhere; 1 = this server only; 2 = addressees; 4 = groups; 8 = followers; 16 = messages; null = default'],
|
||||||
'url' => ['type' => 'text', 'description' => 'Permalink to Note'],
|
'url' => ['type' => 'text', 'description' => 'Permalink to Note'],
|
||||||
'language' => ['type' => 'int', 'foreign key' => true, 'target' => 'Language.id', 'multiplicity' => 'one to many', 'description' => 'The language for this note'],
|
'language' => ['type' => 'int', 'foreign key' => true, 'target' => 'Language.id', 'multiplicity' => 'one to many', 'description' => 'The language for this note'],
|
||||||
@ -361,7 +361,7 @@ class Note extends Entity
|
|||||||
'note_created_id_is_local_idx' => ['created', 'is_local'],
|
'note_created_id_is_local_idx' => ['created', 'is_local'],
|
||||||
'note_actor_created_idx' => ['actor_id', 'created'],
|
'note_actor_created_idx' => ['actor_id', 'created'],
|
||||||
'note_is_local_created_actor_idx' => ['is_local', 'created', 'actor_id'],
|
'note_is_local_created_actor_idx' => ['is_local', 'created', 'actor_id'],
|
||||||
'note_repeat_of_created_idx' => ['repeat_of', 'created'],
|
// 'note_repeat_of_created_idx' => ['repeat_of', 'created'],
|
||||||
'note_conversation_created_idx' => ['conversation', 'created'],
|
'note_conversation_created_idx' => ['conversation', 'created'],
|
||||||
'note_reply_to_idx' => ['reply_to'],
|
'note_reply_to_idx' => ['reply_to'],
|
||||||
],
|
],
|
||||||
|
@ -134,6 +134,15 @@ class Runtime implements RuntimeExtensionInterface, EventSubscriberInterface
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getAdditionalTemplateVars(array $vars): array
|
||||||
|
{
|
||||||
|
$result = [];
|
||||||
|
if (Event::handle('GetAdditionalTemplateVars', [$vars, &$result]) !== Event::stop) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{% extends 'stdgrid.html.twig' %}
|
{% extends 'stdgrid.html.twig' %}
|
||||||
|
|
||||||
{% set override_import = handle_override_template_import('/network/feed.html.twig', '/cards/note/view.html.twig') %}
|
{% set override_import = handle_override_template_import('/network/feed.html.twig', '/cards/note/view.html.twig') %}
|
||||||
{% import override_import as noteView %}
|
{% import override_import as noteView %}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user