[COMPONENTS] Documentation work [PLUGINS] Documentation workexperimental
@@ -30,13 +30,11 @@ | |||
<summary> | |||
{{ icon('filter', 'icon icon-feed-actions') | raw }} {# button-container #} | |||
</summary> | |||
<div class="feed-actions-details-dropdown"> | |||
<menu> | |||
{% for block in handle_event('AddFeedActions', app.request, notes is defined and notes is not empty) %} | |||
{{ block | raw }} | |||
{% endfor %} | |||
</menu> | |||
</div> | |||
<menu class="feed-actions-details-dropdown"> | |||
{% for block in handle_event('AddFeedActions', app.request, notes is defined and notes is not empty) %} | |||
{{ block | raw }} | |||
{% endfor %} | |||
</menu> | |||
</details> | |||
</nav> | |||
</header> | |||
@@ -1,9 +1,7 @@ | |||
<?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 | |||
@@ -18,9 +16,15 @@ declare(strict_types = 1); | |||
// | |||
// 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/>. | |||
// }}} | |||
/** | |||
* @author Hugo Sales <hugo@hsal.es> | |||
* @author Eliseu Amaro <mail@eliseuama.ro> | |||
* @copyright 2021-2022 Free Software Foundation, Inc http://www.fsf.org | |||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later | |||
*/ | |||
namespace Component\Conversation; | |||
use App\Core\Cache; | |||
@@ -34,7 +38,6 @@ use App\Entity\Activity; | |||
use App\Entity\Actor; | |||
use App\Entity\Note; | |||
use App\Util\Common; | |||
use App\Util\Exception\RedirectException; | |||
use App\Util\Formatting; | |||
use Component\Conversation\Entity\Conversation as ConversationEntity; | |||
use Component\Conversation\Entity\ConversationMute; | |||
@@ -101,6 +104,8 @@ class Conversation extends Component | |||
* 'id' (HTML markup id used to redirect user to this anchor upon performing the action) | |||
* | |||
* @throws \App\Util\Exception\ServerException | |||
* | |||
* @return bool EventHook | |||
*/ | |||
public function onAddNoteActions(Request $request, Note $note, array &$actions): bool | |||
{ | |||
@@ -134,7 +139,14 @@ class Conversation extends Component | |||
} | |||
/** | |||
* Posting event to add extra info to a note | |||
* Posting event to add extra information to Component\Posting form data | |||
* | |||
* @param array $data Transport data to be filled with reply_to_id | |||
* | |||
* @throws \App\Util\Exception\ClientException | |||
* @throws \App\Util\Exception\NoSuchNoteException | |||
* | |||
* @return bool EventHook | |||
*/ | |||
public function onPostingModifyData(Request $request, Actor $actor, array &$data): bool | |||
{ | |||
@@ -153,6 +165,8 @@ class Conversation extends Component | |||
* | |||
* @param array $vars Contains information related to Note currently being rendered | |||
* @param array $result Contains keys 'actors', and 'action'. Needed to construct a string, stating who ($result['actors']), has already performed a reply ($result['action']), in the given Note (vars['note']) | |||
* | |||
* @return bool EventHook | |||
*/ | |||
public function onAppendCardNote(array $vars, array &$result): bool | |||
{ | |||
@@ -184,8 +198,10 @@ class Conversation extends Component | |||
* | |||
* @param \App\Entity\Actor $actor The Actor currently attempting to post a Note | |||
* @param null|\App\Entity\Actor $context_actor The 'owner' of the current route (e.g. Group or Actor), used to target it | |||
* | |||
* @return bool EventHook | |||
*/ | |||
public function onPostingGetContextActor(Request $request, Actor $actor, ?Actor &$context_actor) | |||
public function onPostingGetContextActor(Request $request, Actor $actor, ?Actor &$context_actor): bool | |||
{ | |||
$to_note_id = $request->query->get('reply_to_id'); | |||
if (!\is_null($to_note_id)) { | |||
@@ -198,10 +214,8 @@ class Conversation extends Component | |||
/** | |||
* Add minimal Note card to RightPanel template | |||
* | |||
* @throws RedirectException | |||
*/ | |||
public function onPrependPostingForm(Request $request, array &$elements) | |||
public function onPrependPostingForm(Request $request, array &$elements): bool | |||
{ | |||
$elements[] = Formatting::twigRenderFile('cards/note/macro_note_minimal_wrapper.html.twig', ['note' => Note::getById((int) $request->query->get('reply_to_id'))]); | |||
return Event::next; | |||
@@ -213,6 +227,8 @@ class Conversation extends Component | |||
* | |||
* @param \App\Entity\Note $note Note being deleted | |||
* @param \App\Entity\Actor $actor Actor that performed the delete action | |||
* | |||
* @return bool EventHook | |||
*/ | |||
public function onNoteDeleteRelated(Note &$note, Actor $actor): bool | |||
{ | |||
@@ -233,7 +249,7 @@ class Conversation extends Component | |||
* | |||
* @return bool EventHook | |||
*/ | |||
public function onAddExtraNoteActions(Request $request, Note $note, array &$actions) | |||
public function onAddExtraNoteActions(Request $request, Note $note, array &$actions): bool | |||
{ | |||
if (\is_null($user = Common::user())) { | |||
return Event::next; | |||
@@ -261,7 +277,14 @@ class Conversation extends Component | |||
return Event::next; | |||
} | |||
public function onNewNotificationShould(Activity $activity, Actor $actor) | |||
/** | |||
* Prevents new Notifications to appear for muted conversations | |||
* | |||
* @param Activity $activity Notification Activity | |||
* | |||
* @return bool EventHook | |||
*/ | |||
public function onNewNotificationShould(Activity $activity, Actor $actor): bool | |||
{ | |||
if ($activity->getObjectType() === 'note' && ConversationMute::isMuted($activity, $actor)) { | |||
return Event::stop; | |||
@@ -26,7 +26,6 @@ namespace Component\Conversation\Entity; | |||
use App\Core\DB\DB; | |||
use App\Core\Entity; | |||
use App\Core\Router\Router; | |||
use App\Entity\Note; | |||
/** | |||
* Entity class for Conversations | |||
@@ -42,7 +42,12 @@ class LeftPanel extends Component | |||
return Event::next; | |||
} | |||
public function onAppendFeed(Actor $actor, string $title, string $route, array $route_params) | |||
/** | |||
* @throws \App\Util\Exception\DuplicateFoundException | |||
* @throws \App\Util\Exception\ServerException | |||
* @throws ClientException | |||
*/ | |||
public function onAppendFeed(Actor $actor, string $title, string $route, array $route_params): bool | |||
{ | |||
$cache_key = Feed::cacheKey($actor); | |||
$feeds = Feed::getFeeds($actor); | |||
@@ -21,6 +21,13 @@ declare(strict_types = 1); | |||
// }}} | |||
/** | |||
* @author Eliseu Amaro <mail@eliseuama.ro> | |||
* @author Diogo Peralta Cordeiro <@diogo.site> | |||
* @copyright 2021-2022 Free Software Foundation, Inc http://www.fsf.org | |||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later | |||
*/ | |||
namespace Plugin\Favourite; | |||
use App\Core\Cache; | |||
@@ -49,10 +56,10 @@ class Favourite extends NoteHandlerPlugin | |||
* | |||
* A new notification is then handled, informing all interested Actors of this action | |||
* | |||
* @param int $note_id Note id being favoured | |||
* @param int $actor_id Actor performing favourite Activity | |||
* | |||
* @throws \App\Util\Exception\ServerException | |||
* @throws \Doctrine\ORM\OptimisticLockException | |||
* @throws \Doctrine\ORM\ORMException | |||
* @throws \Doctrine\ORM\TransactionRequiredException | |||
*/ | |||
public static function favourNote(int $note_id, int $actor_id, string $source = 'web'): ?Activity | |||
{ | |||
@@ -84,20 +91,21 @@ class Favourite extends NoteHandlerPlugin | |||
* | |||
* Informs all interested Actors of this action, handling out the NewNotification event | |||
* | |||
* @param int $note_id Note id being unfavoured | |||
* @param int $actor_id Actor undoing favourite Activity | |||
* | |||
* @throws \App\Util\Exception\ServerException | |||
* @throws \Doctrine\ORM\OptimisticLockException | |||
* @throws \Doctrine\ORM\ORMException | |||
* @throws \Doctrine\ORM\TransactionRequiredException | |||
*/ | |||
public static function unfavourNote(int $note_id, int $actor_id, string $source = 'web'): ?Activity | |||
{ | |||
$note_already_favoured = Cache::get( | |||
FavouriteEntity::cacheKeys($note_id, $actor_id)['favourite'], | |||
fn () => DB::findOneBy('note_favourite', ['note_id' => $note_id, 'actor_id' => $actor_id], return_null: true), | |||
static fn () => DB::findOneBy('note_favourite', ['note_id' => $note_id, 'actor_id' => $actor_id], return_null: true), | |||
); | |||
$activity = null; | |||
if (!\is_null($note_already_favoured)) { | |||
DB::remove($note_already_favoured); | |||
DB::removeBy(FavouriteEntity::class, ['note_id' => $note_id, 'actor_id' => $actor_id]); | |||
Cache::delete(FavouriteEntity::cacheKeys($note_id, $actor_id)['favourite']); | |||
$favourite_activity = DB::findBy('activity', ['verb' => 'favourite', 'object_type' => 'note', 'actor_id' => $actor_id, 'object_id' => $note_id], order_by: ['created' => 'DESC'])[0]; | |||
$activity = Activity::create([ | |||
@@ -118,11 +126,10 @@ class Favourite extends NoteHandlerPlugin | |||
* HTML rendering event that adds the favourite form as a note | |||
* action, if a user is logged in | |||
* | |||
* @throws \Doctrine\ORM\OptimisticLockException | |||
* @throws \Doctrine\ORM\ORMException | |||
* @throws \Doctrine\ORM\TransactionRequiredException | |||
* @param Note $note Current Note being rendered | |||
* @param array $actions Array containing all Note actions to be rendered | |||
* | |||
* @return bool Event hook | |||
* @return bool Event hook, Event::next (true) is returned to allow Event to be handled by other handlers | |||
*/ | |||
public function onAddNoteActions(Request $request, Note $note, array &$actions): bool | |||
{ | |||
@@ -134,9 +141,9 @@ class Favourite extends NoteHandlerPlugin | |||
$opts = ['note_id' => $note->getId(), 'actor_id' => $user->getId()]; | |||
$is_favourite = !\is_null( | |||
Cache::get( | |||
FavouriteEntity::cacheKeys($note->getId(), $user->getId())['favourite'], | |||
fn () => DB::findOneBy('note_favourite', $opts, return_null: true), | |||
), | |||
FavouriteEntity::cacheKeys($note->getId(), $user->getId())['favourite'], | |||
static fn () => DB::findOneBy('note_favourite', $opts, return_null: true), | |||
), | |||
); | |||
// Generating URL for favourite action route | |||
@@ -162,7 +169,15 @@ class Favourite extends NoteHandlerPlugin | |||
return Event::next; | |||
} | |||
public function onAppendCardNote(array $vars, array &$result) | |||
/** | |||
* Appends on Note currently being rendered complementary information regarding whom (subject) performed which Activity (verb) on aforementioned Note (object) | |||
* | |||
* @param array $vars Array containing necessary info to process event. In this case, contains the current Note being rendered | |||
* @param array $result Contains a hashmap for each Activity performed on Note (object) | |||
* | |||
* @return bool Event hook, Event::next (true) is returned to allow Event to be handled by other handlers | |||
*/ | |||
public function onAppendCardNote(array $vars, array &$result): bool | |||
{ | |||
// If note is the original and user isn't the one who repeated, append on end "user repeated this" | |||
// If user is the one who repeated, append on end "you repeated this, remove repeat?" | |||
@@ -198,6 +213,9 @@ class Favourite extends NoteHandlerPlugin | |||
return Event::next; | |||
} | |||
/** | |||
* Maps Routes to their respective Controllers | |||
*/ | |||
public function onAddRoute(RouteLoader $r): bool | |||
{ | |||
// Add/remove note to/from favourites | |||
@@ -214,7 +232,14 @@ class Favourite extends NoteHandlerPlugin | |||
return Event::next; | |||
} | |||
public function onCreateDefaultFeeds(int $actor_id, LocalUser $user, int &$ordering) | |||
/** | |||
* Creates two feeds, including reverse favourites or favourites performed by given Actor | |||
* | |||
* @param int $actor_id Whom the favourites belong to | |||
* | |||
* @throws \App\Util\Exception\ServerException | |||
*/ | |||
public function onCreateDefaultFeeds(int $actor_id, LocalUser $user, int &$ordering): bool | |||
{ | |||
DB::persist(Feed::create([ | |||
'actor_id' => $actor_id, | |||
@@ -243,6 +268,15 @@ class Favourite extends NoteHandlerPlugin | |||
* @param mixed $type_object Activity's Object | |||
* @param null|\Plugin\ActivityPub\Entity\ActivitypubActivity $ap_act Resulting ActivitypubActivity | |||
* | |||
* @throws \App\Util\Exception\ClientException | |||
* @throws \App\Util\Exception\DuplicateFoundException | |||
* @throws \App\Util\Exception\NoSuchActorException | |||
* @throws \App\Util\Exception\ServerException | |||
* @throws \Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface | |||
* @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface | |||
* @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface | |||
* @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface | |||
* | |||
* @return bool Returns `Event::stop` if handled, `Event::next` otherwise | |||
*/ | |||
private function activitypub_handler(Actor $actor, \ActivityPhp\Type\AbstractObject $type_activity, mixed $type_object, ?\Plugin\ActivityPub\Entity\ActivitypubActivity &$ap_act): bool | |||
@@ -262,24 +296,22 @@ class Favourite extends NoteHandlerPlugin | |||
} else { | |||
return Event::next; | |||
} | |||
} else { // Undo Favourite | |||
if ($type_object instanceof \ActivityPhp\Type\AbstractObject) { | |||
$ap_prev_favourite_act = \Plugin\ActivityPub\Util\Model\Activity::fromJson($type_object); | |||
$prev_favourite_act = $ap_prev_favourite_act->getActivity(); | |||
if ($prev_favourite_act->getVerb() === 'favourite' && $prev_favourite_act->getObjectType() === 'note') { | |||
$note_id = $prev_favourite_act->getObjectId(); | |||
} else { | |||
return Event::next; | |||
} | |||
} elseif ($type_object instanceof Activity) { | |||
if ($type_object->getVerb() === 'favourite' && $type_object->getObjectType() === 'note') { | |||
$note_id = $type_object->getObjectId(); | |||
} else { | |||
return Event::next; | |||
} | |||
} elseif ($type_object instanceof \ActivityPhp\Type\AbstractObject) { | |||
$ap_prev_favourite_act = \Plugin\ActivityPub\Util\Model\Activity::fromJson($type_object); | |||
$prev_favourite_act = $ap_prev_favourite_act->getActivity(); | |||
if ($prev_favourite_act->getVerb() === 'favourite' && $prev_favourite_act->getObjectType() === 'note') { | |||
$note_id = $prev_favourite_act->getObjectId(); | |||
} else { | |||
return Event::next; | |||
} | |||
} elseif ($type_object instanceof Activity) { | |||
if ($type_object->getVerb() === 'favourite' && $type_object->getObjectType() === 'note') { | |||
$note_id = $type_object->getObjectId(); | |||
} else { | |||
return Event::next; | |||
} | |||
} else { | |||
return Event::next; | |||
} | |||
if ($type_activity->get('type') === 'Like') { | |||
@@ -308,6 +340,15 @@ class Favourite extends NoteHandlerPlugin | |||
* @param \ActivityPhp\Type\AbstractObject $type_object Activity Streams 2.0 Object | |||
* @param null|\Plugin\ActivityPub\Entity\ActivitypubActivity $ap_act Resulting ActivitypubActivity | |||
* | |||
* @throws \App\Util\Exception\ClientException | |||
* @throws \App\Util\Exception\DuplicateFoundException | |||
* @throws \App\Util\Exception\NoSuchActorException | |||
* @throws \App\Util\Exception\ServerException | |||
* @throws \Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface | |||
* @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface | |||
* @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface | |||
* @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface | |||
* | |||
* @return bool Returns `Event::stop` if handled, `Event::next` otherwise | |||
*/ | |||
public function onNewActivityPubActivity(Actor $actor, \ActivityPhp\Type\AbstractObject $type_activity, \ActivityPhp\Type\AbstractObject $type_object, ?\Plugin\ActivityPub\Entity\ActivitypubActivity &$ap_act): bool | |||
@@ -323,6 +364,15 @@ class Favourite extends NoteHandlerPlugin | |||
* @param mixed $type_object Object | |||
* @param null|\Plugin\ActivityPub\Entity\ActivitypubActivity $ap_act Resulting ActivitypubActivity | |||
* | |||
* @throws \App\Util\Exception\ClientException | |||
* @throws \App\Util\Exception\DuplicateFoundException | |||
* @throws \App\Util\Exception\NoSuchActorException | |||
* @throws \App\Util\Exception\ServerException | |||
* @throws \Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface | |||
* @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface | |||
* @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface | |||
* @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface | |||
* | |||
* @return bool Returns `Event::stop` if handled, `Event::next` otherwise | |||
*/ | |||
public function onNewActivityPubActivityWithObject(Actor $actor, \ActivityPhp\Type\AbstractObject $type_activity, mixed $type_object, ?\Plugin\ActivityPub\Entity\ActivitypubActivity &$ap_act): bool | |||
@@ -49,7 +49,7 @@ use Symfony\Component\HttpFoundation\Request; | |||
class Oomox extends Plugin | |||
{ | |||
/** | |||
* Map URLs to actions | |||
* Maps Routes to their respective Controllers | |||
*/ | |||
public function onAddRoute(RouteLoader $r): bool | |||
{ | |||
@@ -108,10 +108,8 @@ class Oomox extends Plugin | |||
/** | |||
* Adds to array $styles the generated CSS according to user settings, if any are present. | |||
* | |||
* @return bool | |||
*/ | |||
public function onEndShowStyles(array &$styles, string $route) | |||
public function onEndShowStyles(array &$styles, string $route): bool | |||
{ | |||
$user = Common::user(); | |||
if (!\is_null($user) && !\is_null(Cache::get(self::cacheKey($user), fn () => self::getEntity($user)))) { | |||
@@ -33,7 +33,6 @@ use App\Entity\Note; | |||
use App\Util\Common; | |||
use App\Util\Exception\ClientException; | |||
use App\Util\Exception\NoLoggedInUser; | |||
use App\Util\Exception\NoSuchNoteException; | |||
use App\Util\Exception\RedirectException; | |||
use App\Util\Exception\ServerException; | |||
use Symfony\Component\Form\Extension\Core\Type\SubmitType; | |||
@@ -44,9 +43,11 @@ class Repeat extends Controller | |||
/** | |||
* Controller for the note repeat non-JS page | |||
* | |||
* @param int $note_id Note being repeated | |||
* | |||
* @throws \App\Util\Exception\DuplicateFoundException | |||
* @throws ClientException | |||
* @throws NoLoggedInUser | |||
* @throws NoSuchNoteException | |||
* @throws RedirectException | |||
* @throws ServerException | |||
*/ | |||
@@ -79,14 +80,13 @@ class Repeat extends Controller | |||
if (Router::isAbsolute($from)) { | |||
Log::warning("Actor {$actor_id} attempted to reply to a note and then get redirected to another host, or the URL was invalid ({$from})"); | |||
throw new ClientException(_m('Can not redirect to outside the website from here'), 400); // 400 Bad request (deceptive) | |||
} else { | |||
// TODO anchor on element id | |||
throw new RedirectException(url: $from); | |||
} | |||
} else { | |||
// If we don't have a URL to return to, go to the instance root | |||
throw new RedirectException('root'); | |||
// TODO anchor on element id | |||
throw new RedirectException(url: $from); | |||
} | |||
// If we don't have a URL to return to, go to the instance root | |||
throw new RedirectException('root'); | |||
} | |||
return [ | |||
@@ -97,9 +97,14 @@ class Repeat extends Controller | |||
} | |||
/** | |||
* Controller for the note unrepeat non-JS page | |||
* | |||
* @param int $note_id Note being unrepeated | |||
* | |||
* @throws \App\Util\Exception\DuplicateFoundException | |||
* @throws \App\Util\Exception\NotFoundException | |||
* @throws ClientException | |||
* @throws NoLoggedInUser | |||
* @throws NoSuchNoteException | |||
* @throws RedirectException | |||
* @throws ServerException | |||
*/ | |||
@@ -134,14 +139,13 @@ class Repeat extends Controller | |||
if (Router::isAbsolute($from)) { | |||
Log::warning("Actor {$actor_id} attempted to reply to a note and then get redirected to another host, or the URL was invalid ({$from})"); | |||
throw new ClientException(_m('Can not redirect to outside the website from here'), 400); // 400 Bad request (deceptive) | |||
} else { | |||
// TODO anchor on element id | |||
throw new RedirectException(url: $from); | |||
} | |||
} else { | |||
// If we don't have a URL to return to, go to the instance root | |||
throw new RedirectException('root'); | |||
// TODO anchor on element id | |||
throw new RedirectException(url: $from); | |||
} | |||
// If we don't have a URL to return to, go to the instance root | |||
throw new RedirectException('root'); | |||
} | |||
return [ | |||
@@ -19,6 +19,13 @@ declare(strict_types = 1); | |||
// along with GNU social. If not, see <http://www.gnu.org/licenses/>. | |||
// }}} | |||
/** | |||
* @author Eliseu Amaro <mail@eliseuama.ro> | |||
* @author Diogo Peralta Cordeiro <@diogo.site> | |||
* @copyright 2021-2022 Free Software Foundation, Inc http://www.fsf.org | |||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later | |||
*/ | |||
namespace Plugin\RepeatNote; | |||
use App\Core\Cache; | |||
@@ -32,7 +39,6 @@ use App\Entity\Activity; | |||
use App\Entity\Actor; | |||
use App\Entity\Note; | |||
use App\Util\Common; | |||
use App\Util\Exception\BugFoundException; | |||
use App\Util\Exception\ClientException; | |||
use App\Util\Exception\DuplicateFoundException; | |||
use App\Util\Exception\ServerException; | |||
@@ -57,7 +63,9 @@ class RepeatNote extends NoteHandlerPlugin | |||
* In the end, the Activity is created, and a new notification for the | |||
* repeat Activity created | |||
* | |||
* @throws BugFoundException | |||
* @param Note $note Note being repeated | |||
* @param int $actor_id Actor id of whom is performing the repeat Activity | |||
* | |||
* @throws ClientException | |||
* @throws DuplicateFoundException | |||
* @throws ServerException | |||
@@ -116,6 +124,11 @@ class RepeatNote extends NoteHandlerPlugin | |||
* Finally, creates a new Activity, undoing the repeat, and the respective | |||
* Notification is handled. | |||
* | |||
* @param int $note_id Note id being unrepeated | |||
* @param int $actor_id Actor undoing repeat Activity | |||
* | |||
* @throws \App\Util\Exception\NotFoundException | |||
* @throws DuplicateFoundException | |||
* @throws ServerException | |||
*/ | |||
public static function unrepeatNote(int $note_id, int $actor_id, string $source = 'web'): ?Activity | |||
@@ -176,6 +189,10 @@ class RepeatNote extends NoteHandlerPlugin | |||
/** | |||
* Filters repeats out of Conversations, and replaces a repeat with the | |||
* original Note on Actor feed | |||
* | |||
* @param array $notes List of Notes to be filtered | |||
* | |||
* @return bool Event hook, Event::next (true) is returned to allow Event to be handled by other handlers | |||
*/ | |||
public function onFilterNoteList(?Actor $actor, array &$notes, Request $request): bool | |||
{ | |||
@@ -183,7 +200,7 @@ class RepeatNote extends NoteHandlerPlugin | |||
// it's pretty cool | |||
if (str_starts_with($request->get('_route'), 'actor_view_')) { | |||
$notes = array_map( | |||
fn (Note $note) => RepeatEntity::isNoteRepeat($note) | |||
static fn (Note $note) => RepeatEntity::isNoteRepeat($note) | |||
? Note::getById(RepeatEntity::getByPK($note->getId())->getRepeatOf()) | |||
: $note, | |||
$notes, | |||
@@ -192,7 +209,7 @@ class RepeatNote extends NoteHandlerPlugin | |||
} | |||
// Filter out repeats altogether | |||
$notes = array_filter($notes, fn (Note $note) => !RepeatEntity::isNoteRepeat($note)); | |||
$notes = array_filter($notes, static fn (Note $note) => !RepeatEntity::isNoteRepeat($note)); | |||
return Event::next; | |||
} | |||
@@ -200,7 +217,7 @@ class RepeatNote extends NoteHandlerPlugin | |||
* HTML rendering event that adds the repeat form as a note | |||
* action, if a user is logged in | |||
* | |||
* @return bool Event hook | |||
* @return bool Event hook, Event::next (true) is returned to allow Event to be handled by other handlers | |||
*/ | |||
public function onAddNoteActions(Request $request, Note $note, array &$actions): bool | |||
{ | |||
@@ -244,7 +261,7 @@ class RepeatNote extends NoteHandlerPlugin | |||
* @param array $result Rendered String containing anchors for Actors that | |||
* repeated the Note | |||
* | |||
* @return bool | |||
* @return bool Event hook, Event::next (true) is returned to allow Event to be handled by other handlers | |||
*/ | |||
public function onAppendCardNote(array $vars, array &$result) | |||
{ | |||
@@ -278,6 +295,11 @@ class RepeatNote extends NoteHandlerPlugin | |||
/** | |||
* Deletes every repeat entity that is related to a deleted Note in its | |||
* respective table | |||
* | |||
* @param Note $note Note to be deleted | |||
* @param Actor $actor Who performed the Delete action | |||
* | |||
* @return bool Event hook, Event::next (true) is returned to allow Event to be handled by other handlers | |||
*/ | |||
public function onNoteDeleteRelated(Note &$note, Actor $actor): bool | |||
{ | |||
@@ -299,7 +321,7 @@ class RepeatNote extends NoteHandlerPlugin | |||
* - **repeat_remove** | |||
* same as above, except that it undoes the aforementioned action | |||
* | |||
* @return bool Event hook | |||
* @return bool Event hook, Event::next (true) is returned to allow Event to be handled by other handlers | |||
*/ | |||
public function onAddRoute(RouteLoader $r): bool | |||
{ | |||
@@ -320,6 +342,16 @@ class RepeatNote extends NoteHandlerPlugin | |||
* @param mixed $type_object Activity's Object | |||
* @param null|\Plugin\ActivityPub\Entity\ActivitypubActivity $ap_act Resulting ActivitypubActivity | |||
* | |||
* @throws \App\Util\Exception\NoSuchActorException | |||
* @throws \App\Util\Exception\NotFoundException | |||
* @throws \Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface | |||
* @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface | |||
* @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface | |||
* @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface | |||
* @throws ClientException | |||
* @throws DuplicateFoundException | |||
* @throws ServerException | |||
* | |||
* @return bool Returns `Event::stop` if handled, `Event::next` otherwise | |||
*/ | |||
private function activitypub_handler(Actor $actor, \ActivityPhp\Type\AbstractObject $type_activity, mixed $type_object, ?\Plugin\ActivityPub\Entity\ActivitypubActivity &$ap_act): bool | |||
@@ -387,6 +419,16 @@ class RepeatNote extends NoteHandlerPlugin | |||
* @param \ActivityPhp\Type\AbstractObject $type_object Activity Streams 2.0 Object | |||
* @param null|\Plugin\ActivityPub\Entity\ActivitypubActivity $ap_act Resulting ActivitypubActivity | |||
* | |||
* @throws \App\Util\Exception\NoSuchActorException | |||
* @throws \App\Util\Exception\NotFoundException | |||
* @throws \Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface | |||
* @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface | |||
* @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface | |||
* @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface | |||
* @throws ClientException | |||
* @throws DuplicateFoundException | |||
* @throws ServerException | |||
* | |||
* @return bool Returns `Event::stop` if handled, `Event::next` otherwise | |||
*/ | |||
public function onNewActivityPubActivity(Actor $actor, \ActivityPhp\Type\AbstractObject $type_activity, \ActivityPhp\Type\AbstractObject $type_object, ?\Plugin\ActivityPub\Entity\ActivitypubActivity &$ap_act): bool | |||
@@ -402,6 +444,16 @@ class RepeatNote extends NoteHandlerPlugin | |||
* @param mixed $type_object Object | |||
* @param null|\Plugin\ActivityPub\Entity\ActivitypubActivity $ap_act Resulting ActivitypubActivity | |||
* | |||
* @throws \App\Util\Exception\NoSuchActorException | |||
* @throws \App\Util\Exception\NotFoundException | |||
* @throws \Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface | |||
* @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface | |||
* @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface | |||
* @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface | |||
* @throws ClientException | |||
* @throws DuplicateFoundException | |||
* @throws ServerException | |||
* | |||
* @return bool Returns `Event::stop` if handled, `Event::next` otherwise | |||
*/ | |||
public function onNewActivityPubActivityWithObject(Actor $actor, \ActivityPhp\Type\AbstractObject $type_activity, mixed $type_object, ?\Plugin\ActivityPub\Entity\ActivitypubActivity &$ap_act): bool | |||
@@ -98,10 +98,11 @@ | |||
.feed-actions-details[open] > .feed-actions-details-dropdown, .note-actions-extra-details[open] > summary + * { | |||
background: var(--background-card); | |||
border: 2px solid var(--border); | |||
border: 1px solid var(--border); | |||
border-radius: var(--s); | |||
box-shadow: var(--shadow); | |||
display: flex; | |||
flex-direction: column; | |||
padding: 4px 8px; | |||
position: absolute; | |||
right: 0; | |||
@@ -167,14 +168,16 @@ | |||
text-decoration: underline; | |||
} | |||
.h-entry figure { | |||
.h-entry figure, | |||
.note-attachments-unit figure { | |||
background: var(--gradient); | |||
border-radius: var(--s); | |||
margin: unset; | |||
padding: var(--s); | |||
} | |||
.h-entry img { | |||
.h-entry img, | |||
.note-attachments-unit figure img { | |||
background: repeating-conic-gradient(#ffffff66 0deg 90deg, #ffffff33 0deg 180deg) 0 0/40px 40px round; | |||
} | |||
@@ -279,7 +282,7 @@ embed header { | |||
.note-actions-extra-details summary { | |||
opacity: 0.5; | |||
} | |||
.note-actions-extra-details[open] > summary + * > li { | |||
.note-actions-extra-details[open] > summary + * > a { | |||
font-size: 0.937rem; | |||
line-height: 2; | |||
} | |||
@@ -1,10 +1,9 @@ | |||
{% extends 'stdgrid.html.twig' %} | |||
{% block body %} | |||
<div class="page"> | |||
<section class="frame-section frame-section-padding"> | |||
{% include '/cards/attachments/view.html.twig' with {'attachment': attachment, 'note': note, 'title': title} only %} | |||
<a class="frame-section-button-like" href="{{ download }}"> {{ 'Download link' | trans }}</a> | |||
</section> | |||
</div> | |||
<section class="frame-section frame-section-padding"> | |||
<h1>{{ 'Attachment' | trans }} {{ title }}</h1> | |||
{% include '/cards/attachments/view.html.twig' with {'attachment': attachment, 'note': note, 'title': title} only %} | |||
<a class="frame-section-button-like" href="{{ download }}"> {{ 'Original attachment link' | trans }}</a> | |||
</section> | |||
{% endblock body %} |
@@ -4,29 +4,20 @@ | |||
<li class="note-actions-extra"> | |||
<details class="note-actions-extra-details"> | |||
<summary> | |||
{{ icon('kebab', 'icon icon-note-actions-extra') | raw }} {# button-container #} | |||
{{ icon('kebab', 'icon icon-note-actions-extra') | raw }} | |||
</summary> | |||
<ul> | |||
<li> | |||
<a href="{{ note.getUrl() }}">{{ 'Permalink' | trans }}</a> | |||
</li> | |||
<menu> | |||
<a href="{{ note.getUrl() }}">{{ 'Permalink' | trans }}</a> | |||
<hr> | |||
{% for current_action in get_extra_note_actions(note) %} | |||
<li> | |||
<a class="{{ current_action["classes"] }}" | |||
href="{{ current_action["url"] }}">{{ current_action['title'] }}</a> | |||
</li> | |||
<a class="{{ current_action["classes"] }}" href="{{ current_action["url"] }}">{{ current_action['title'] }}</a> | |||
{% endfor %} | |||
</ul> | |||
</menu> | |||
</details> | |||
</li> | |||
<span class="note-actions-separator"></span> | |||
{% for current_action in get_note_actions(note) %} | |||
<li> | |||
<a title="{{ current_action["title"] | trans }}" | |||
class="{{ current_action["classes"] }}" | |||
href="{{ current_action["url"] }}"></a> | |||
</li> | |||
<li><a title="{{ current_action["title"] | trans }}" class="{{ current_action["classes"] }}" href="{{ current_action["url"] }}"></a></li> | |||
{% endfor %} | |||
</menu> | |||
{% endif %} | |||
@@ -79,7 +70,8 @@ | |||
{% block note_author %} | |||
{# Microformat's h-card properties indicates a face icon is a "u-logo" #} | |||
<a href="{{ actor_url }}" class="note-author-url u-url" tabindex="0" title="{{ 'Begin a note by the user: ' | trans }} {{ nickname }}"> | |||
<a href="{{ actor_url }}" class="note-author-url u-url" tabindex="0" | |||
title="{{ 'Begin a note by the user: ' | trans }} {{ nickname }}"> | |||
{% if fullname is not null %} | |||
{{ fullname }} | |||
{% else %} | |||