37 Commits

Author SHA1 Message Date
539104ec33 [PLUGIN][Pinboard] Refactor and cleanup code 2022-04-01 00:17:57 +01:00
74ffd261b8 [PLUGIN][Pinboard] Implement tag handling 2022-04-01 00:16:04 +01:00
ca9945a4be [ENTITY][Actor][COMPONENT][Tag] Add Actor->getNoteTags(?string $note_type) which gets a cached list of NoteTags for notes of type $note_type for the actor 2022-04-01 00:11:01 +01:00
08587b6942 [COMPONENT][Link][Tag] Refactor to make it easier to create links or tags from other places 2022-04-01 00:09:25 +01:00
1664293cf7 [PLUGIN][Pinboard] Change token to user user ID rather than nickname, to avoid complications with it possibly changing 2022-03-31 22:06:37 +01:00
94ab4ce8c4 [PLUGIN][Pinboard] Invalidate token and it's cache when actor information is changed via ActorForms 2022-03-31 03:47:14 +01:00
dd70de20da [PLUGIN][Pinboard] Implement token authentication and settings page, allowing the user to enable, disable, refresh or consult their token 2022-03-31 03:29:31 +01:00
ded9c86054 [CORE][DB] Add DB::refetch, which refetches an entity from the database, so it's managed and definitely up to date (use when wanting to update entities from cache) 2022-03-31 03:29:31 +01:00
20e07c9140 [CORE][DB] Make DB::dql return an object rather than an array if limit 1 is specified 2022-03-31 03:29:31 +01:00
4e2f6545ec [COMPONENT][Person][PLUGIN][WebHooks] Rename person settings section from 'others' to 'api' 2022-03-31 03:29:31 +01:00
f6a8f44420 [COMPONENT][Person][TEMPLATES] Move persosn settings template from core to the component 2022-03-31 03:29:31 +01:00
fd71d6ee7d [PLUGIN][UnboundGroup] Finish implementation 2022-03-29 00:57:41 +01:00
dfc5918c2c [PLUGIN][ActivityPub] Federate out Service information in Activities 2022-03-28 23:54:19 +01:00
83599ef866 [CORE][Modules][Plugin] version should be static 2022-03-28 23:54:18 +01:00
fa82306f6f [COMPONENT][Posting] Blog posts should be Articles by default 2022-03-28 23:54:18 +01:00
10f71e9fed [UI][TEMPLATES] Fix note text template. Use rendered content directly 2022-03-28 23:23:07 +01:00
e2501ee927 [PLUGIN][Pinboard] Implement remaining API endpoints, restructure, fix template 2022-03-28 23:23:07 +01:00
a9665177ea [PLUGIN][Blog] Move to plugins, mistakenly was in components 2022-03-28 20:59:16 +01:00
41861d284c [COMPONENT][Circle] Correct self tags settings text 2022-03-28 20:59:16 +01:00
bd868a2675 [PLUGINS][Pinboard] Add initial implementation of Pinboard API, lacking authentication, tags and feed endpoints 2022-03-28 20:59:16 +01:00
87e35716c1 [UTIL] Add Formatting::explode(array , string ) 2022-03-28 20:59:16 +01:00
dac94f53cd [CORE][Entity] Rename createOrUpdate to 'checkExistingAndCreateOrUpdate', remove update feature from 'create' and add 'createOrUpdate' and fix users 2022-03-28 20:59:15 +01:00
b10c359dec [DEPENDENCIES] Update dependencies 2022-03-28 20:59:15 +01:00
483983790a [CORE][Router] Rename \App\Core\Router\Router to \App\Core\Router and merge \App\Core\Router\RouteLoader with \App\Core\Router 2022-03-28 20:59:15 +01:00
60af9f5e9b [CORE][Queue] Rename App\Core\Queue\Queue to App\Core\Queue 2022-03-28 20:59:15 +01:00
abe35428da [CORE][DB] Rename App\Core\DB\DB to App\Core\DB 2022-03-28 20:59:14 +01:00
ca5520edbf [PLUGIN][WebHooks] Add hook for subscriptions 2022-03-28 20:59:14 +01:00
e3e14c53ef [PLUGIN][ActivityPub] Model/Note->toJson federate the url, even though it's the same as the id 2022-03-28 20:59:14 +01:00
be33c20614 [PLUGIN][ActivityPub] Improve flexibility of Type layer, accomodate more elaborate understanding of Group Announces after FEP-2100 development 2022-03-28 20:58:48 +01:00
7305a725cb [PLUGIN][UnboundGroup] First steps on implementing AP FEP-2100 2022-03-28 20:57:43 +01:00
fd4c3b0e68 [PLUGIN][Embed][Test] Move Test to correct location 2022-03-28 20:53:50 +01:00
16f51e5143 [COMPONENT][Notification] ->getSubscribers() should not be pre-included
Notification bug fix on Subscription component
Correct docblock
2022-03-28 20:53:19 +01:00
ba4230447e [COMPONENT][Group] Add orderBy to query, as otherwise the feed order is wrong 2022-03-28 20:49:28 +01:00
7463044971 [COMPONENT][Circle] Ensure strict typing on getter 2022-03-28 20:48:29 +01:00
7027633ed5 [PLUGIN][WebHooks] Make request method configurable
This way, PUT can be used, which doesn't seem to be the standard, so isn't the default, but which makes sense to me, as it doesn't have a response, which we don't care about anyway
2022-03-24 00:51:00 +00:00
48b42c539c [PLUGINS][WebHooks] Use ActivityPub to serialize the activity, so the object is included 2022-03-24 00:51:00 +00:00
d41a67a9f9 [PLUGIN][WebHooks] Add WebHooks plugin, which allows for sending a POST request to an external resource when a notification or a follow occurs 2022-03-24 00:51:00 +00:00
195 changed files with 2502 additions and 675 deletions

View File

@@ -22,10 +22,10 @@ declare(strict_types = 1);
namespace Component\Attachment;
use App\Core\Cache;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Event;
use App\Core\Modules\Component;
use App\Core\Router\RouteLoader;
use App\Core\Router;
use App\Entity\Actor;
use App\Entity\Note;
use App\Util\Formatting;
@@ -37,7 +37,7 @@ use Doctrine\ORM\QueryBuilder;
class Attachment extends Component
{
public function onAddRoute(RouteLoader $r): bool
public function onAddRoute(Router $r): bool
{
$r->connect('note_attachment_show', '/object/note/{note_id<\d+>}/attachment/{attachment_id<\d+>}', [C\Attachment::class, 'attachmentShowWithNote']);
$r->connect('note_attachment_view', '/object/note/{note_id<\d+>}/attachment/{attachment_id<\d+>}/view', [C\Attachment::class, 'attachmentViewWithNote']);

View File

@@ -24,7 +24,7 @@ declare(strict_types = 1);
namespace Component\Attachment\Controller;
use App\Core\Controller;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Event;
use App\Core\GSFile;
use function App\Core\I18n\_m;

View File

@@ -21,7 +21,7 @@ declare(strict_types = 1);
namespace Component\Attachment\Entity;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Entity;
use DateTimeInterface;

View File

@@ -24,13 +24,13 @@ declare(strict_types = 1);
namespace Component\Attachment\Entity;
use App\Core\Cache;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Entity;
use App\Core\Event;
use App\Core\GSFile;
use function App\Core\I18n\_m;
use App\Core\Log;
use App\Core\Router\Router;
use App\Core\Router;
use App\Entity\Note;
use App\Util\Common;
use App\Util\Exception\ClientException;

View File

@@ -24,12 +24,12 @@ declare(strict_types = 1);
namespace Component\Attachment\Entity;
use App\Core\Cache;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Entity;
use App\Core\Event;
use App\Core\GSFile;
use App\Core\Log;
use App\Core\Router\Router;
use App\Core\Router;
use App\Entity\Note;
use App\Util\Common;
use App\Util\Exception\ClientException;

View File

@@ -21,7 +21,7 @@ declare(strict_types = 1);
namespace Component\Attachment\Entity;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Entity;
use DateTimeInterface;

View File

@@ -21,7 +21,7 @@ declare(strict_types = 1);
namespace Component\Attachment\Entity;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Entity;
use DateTimeInterface;

View File

@@ -23,7 +23,7 @@ declare(strict_types = 1);
namespace Component\Attachment\tests\Controller;
use App\Core\DB\DB;
use App\Core\DB;
use App\Util\GNUsocialTestCase;
use Component\Attachment\Entity\Attachment;
use Component\Attachment\Entity\AttachmentToNote;

View File

@@ -21,10 +21,10 @@ declare(strict_types = 1);
namespace Component\Attachment\tests\Entity;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Event;
use App\Core\GSFile;
use App\Core\Router\Router;
use App\Core\Router;
use App\Entity\Note;
use App\Util\GNUsocialTestCase;
use App\Util\TemporaryFile;
@@ -107,7 +107,7 @@ class AttachmentTest extends GNUsocialTestCase
static::assertSame('Untitled attachment', $attachment->getBestTitle());
$attachment->setFilename($filename);
$actor = DB::findOneBy('actor', ['nickname' => 'taken_user']);
$actor = DB::findOneBy('actor', ['nickname' => 'taken_user']);
DB::persist($note = Note::create(['actor_id' => $actor->getId(), 'content' => 'attachment: some content', 'content_type' => 'text/plain', 'is_local' => true]));
Conversation::assignLocalConversation($note, null);
DB::persist(AttachmentToNote::create(['attachment_id' => $attachment->getId(), 'note_id' => $note->getId(), 'title' => 'A title']));

View File

@@ -21,7 +21,7 @@ declare(strict_types = 1);
namespace Component\Attachment\tests\Entity;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Event;
use App\Util\Exception\NotStoredLocallyException;
use App\Util\GNUsocialTestCase;

View File

@@ -22,12 +22,11 @@ declare(strict_types = 1);
namespace Component\Avatar;
use App\Core\Cache;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Event;
use App\Core\GSFile;
use App\Core\Modules\Component;
use App\Core\Router\RouteLoader;
use App\Core\Router\Router;
use App\Core\Router;
use App\Util\Common;
use Component\Attachment\Entity\Attachment;
use Component\Attachment\Entity\AttachmentThumbnail;
@@ -41,7 +40,7 @@ class Avatar extends Component
{
}
public function onAddRoute(RouteLoader $r): bool
public function onAddRoute(Router $r): bool
{
$r->connect('avatar_actor', '/actor/{actor_id<\d+>}/avatar/{size<full|big|medium|small>?medium}', [Controller\Avatar::class, 'avatar_view']);
$r->connect('avatar_default', '/avatar/default/{size<full|big|medium|small>?medium}', [Controller\Avatar::class, 'default_avatar_view']);

View File

@@ -24,7 +24,7 @@ declare(strict_types = 1);
namespace Component\Avatar\Controller;
use App\Core\Controller;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Event;
use App\Core\Form;
use App\Core\GSFile;

View File

@@ -24,10 +24,10 @@ declare(strict_types = 1);
namespace Component\Avatar\Entity;
use App\Core\Cache;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Entity;
use App\Core\Event;
use App\Core\Router\Router;
use App\Core\Router;
use App\Util\Common;
use Component\Attachment\Entity\Attachment;
use Component\Attachment\Entity\AttachmentThumbnail;

View File

@@ -24,16 +24,14 @@ declare(strict_types = 1);
namespace Component\Circle;
use App\Core\Cache;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Event;
use function App\Core\I18n\_m;
use App\Core\Modules\Component;
use App\Core\Router\RouteLoader;
use App\Core\Router\Router;
use App\Core\Router;
use App\Entity\Actor;
use App\Entity\Feed;
use App\Entity\LocalUser;
use App\Util\Common;
use App\Util\Nickname;
use Component\Circle\Controller as CircleController;
use Component\Circle\Entity\ActorCircle;
@@ -60,7 +58,7 @@ class Circle extends Component
protected const SLUG = 'circle';
protected const PLURAL_SLUG = 'circles';
public function onAddRoute(RouteLoader $r): bool
public function onAddRoute(Router $r): bool
{
$r->connect('actor_circle_view_by_circle_id', '/circle/{circle_id<\d+>}', [CircleController\Circle::class, 'circleById']);
// View circle members by (tagger id or nickname) and tag
@@ -99,10 +97,10 @@ class Circle extends Component
{
if ($section === 'profile' && \in_array($request->get('_route'), ['person_actor_settings', 'group_actor_settings'])) {
$tabs[] = [
'title' => 'Self tags',
'desc' => 'Add or remove tags on yourself',
'title' => _m('Self tags'),
'desc' => _m('Add or remove tags to this actor'),
'id' => 'settings-self-tags',
'controller' => CircleController\SelfTagsSettings::settingsSelfTags($request, Common::actor(), 'settings-self-tags-details'),
'controller' => CircleController\SelfTagsSettings::settingsSelfTags($request, Actor::getById((int) $request->get('id')), 'settings-self-tags-details'),
];
}
return Event::next;

View File

@@ -24,8 +24,8 @@ declare(strict_types = 1);
namespace Component\Circle\Controller;
use App\Core\Cache;
use App\Core\DB\DB;
use App\Core\Router\Router;
use App\Core\DB;
use App\Core\Router;
use App\Entity\Actor;
use App\Entity\LocalUser;
use Component\Circle\Entity\ActorCircle;

View File

@@ -6,7 +6,7 @@ namespace Component\Circle\Controller;
use App\Core\Cache;
use App\Core\Controller;
use App\Core\DB\DB;
use App\Core\DB;
use function App\Core\I18n\_m;
use App\Entity as E;
use App\Util\Common;
@@ -45,7 +45,7 @@ class SelfTagsSettings extends Controller
foreach ($tags as $tag) {
$tag = CompTag::sanitize($tag);
[$actor_tag, $actor_tag_existed] = ActorTag::createOrUpdate([
[$actor_tag, $actor_tag_existed] = ActorTag::checkExistingAndCreateOrUpdate([
'tagger' => $target->getId(), // self tag means tagger = tagger in ActorTag
'tagged' => $target->getId(),
'tag' => $tag,

View File

@@ -22,9 +22,9 @@ declare(strict_types = 1);
namespace Component\Circle\Entity;
use App\Core\Cache;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Entity;
use App\Core\Router\Router;
use App\Core\Router;
use DateTimeInterface;
/**

View File

@@ -21,9 +21,9 @@ declare(strict_types = 1);
namespace Component\Circle\Entity;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Entity;
use App\Core\Router\Router;
use App\Core\Router;
use App\Entity\Actor;
use Component\Tag\Tag;
use DateTimeInterface;

View File

@@ -34,7 +34,7 @@ abstract class SelfTagsForm
$existing_form = !empty($form_definition) ? Form::create($form_definition) : null;
$add_form = Form::create([
['new-tags', TextType::class, ['label' => ' ', 'data' => [], 'required' => false, 'help' => _m('Tags for yourself (letters, numbers, -, ., and _), comma- or space-separated.'), 'transformer' => ArrayTransformer::class]],
['new-tags', TextType::class, ['label' => ' ', 'data' => [], 'required' => false, 'help' => _m('Tags for this actor (letters, numbers, -, ., and _), comma- or space-separated.'), 'transformer' => ArrayTransformer::class]],
[$add_form_name = 'new-tags-add', SubmitType::class, ['label' => $add_label]],
]);

View File

@@ -4,7 +4,7 @@ declare(strict_types = 1);
namespace Component\Collection;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Event;
use App\Core\Modules\Component;
use App\Entity\Actor;

View File

@@ -31,7 +31,7 @@ declare(strict_types = 1);
namespace Component\Collection\Util\Controller;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Form;
use function App\Core\I18n\_m;
use App\Entity\LocalUser;

View File

@@ -31,7 +31,7 @@ declare(strict_types = 1);
namespace Component\Collection\Util;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Event;
use App\Core\Form;
use function App\Core\I18n\_m;
@@ -137,7 +137,7 @@ trait MetaCollectionTrait
if ($add_form->isSubmitted() && $add_form->isValid()) {
$selected = $add_form->getData()['collections'];
$removed = array_filter($already_selected, fn ($x) => !\in_array($x, $selected));
$added = array_filter($selected, fn ($x) => !\in_array($x, $already_selected));
$added = array_filter($selected, fn ($x) => !\in_array($x, $already_selected));
if (\count($removed) > 0) {
$this->removeItem($user, $vars, $removed, $collections);
}

View File

@@ -28,11 +28,11 @@ declare(strict_types = 1);
namespace Component\Conversation\Controller;
use App\Core\Cache;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Form;
use function App\Core\I18n\_m;
use App\Core\Log;
use App\Core\Router\Router;
use App\Core\Router;
use App\Entity\Note;
use App\Util\Common;
use App\Util\Exception\ClientException;

View File

@@ -28,12 +28,11 @@ declare(strict_types = 1);
namespace Component\Conversation;
use App\Core\Cache;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Event;
use function App\Core\I18n\_m;
use App\Core\Modules\Component;
use App\Core\Router\RouteLoader;
use App\Core\Router\Router;
use App\Core\Router;
use App\Entity\Activity;
use App\Entity\Actor;
use App\Entity\Note;
@@ -46,7 +45,7 @@ use Symfony\Component\HttpFoundation\Request;
class Conversation extends Component
{
public function onAddRoute(RouteLoader $r): bool
public function onAddRoute(Router $r): bool
{
$r->connect('conversation', '/conversation/{conversation_id<\d+>}', [Controller\Conversation::class, 'showConversation']);
$r->connect('conversation_mute', '/conversation/{conversation_id<\d+>}/mute', [Controller\Conversation::class, 'muteConversation']);

View File

@@ -23,9 +23,9 @@ declare(strict_types = 1);
namespace Component\Conversation\Entity;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Entity;
use App\Core\Router\Router;
use App\Core\Router;
/**
* Entity class for Conversations

View File

@@ -24,7 +24,7 @@ declare(strict_types = 1);
namespace Component\Conversation\Entity;
use App\Core\Cache;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Entity;
use App\Entity\Activity;
use App\Entity\Actor;

View File

@@ -25,12 +25,12 @@ namespace Component\Feed;
use App\Core\Event;
use App\Core\Modules\Component;
use App\Core\Router\RouteLoader;
use App\Core\Router;
use Component\Feed\Controller as C;
class Feed extends Component
{
public function onAddRoute(RouteLoader $r): bool
public function onAddRoute(Router $r): bool
{
$r->connect('feed_public', '/feed/public', [C\Feeds::class, 'public']);
$r->connect('feed_home', '/feed/home', [C\Feeds::class, 'home']);

View File

@@ -23,7 +23,7 @@ declare(strict_types = 1);
namespace Component\Feed\tests\Controller;
use App\Core\Router\Router;
use App\Core\Router;
use App\Util\GNUsocialTestCase;
use Component\Feed\Controller\Feeds;
use Jchook\AssertThrows\AssertThrows;

View File

@@ -34,7 +34,7 @@ declare(strict_types = 1);
namespace Component\FreeNetwork\Controller;
use App\Core\DB\DB;
use App\Core\DB;
use function App\Core\I18n\_m;
use App\Util\Common;
use Component\Collection\Util\Controller\FeedController;

View File

@@ -32,7 +32,7 @@ declare(strict_types = 1);
namespace Component\FreeNetwork\Entity;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Entity;
use App\Entity\Actor;
use Component\FreeNetwork\Util\Discovery;

View File

@@ -21,15 +21,14 @@ declare(strict_types = 1);
namespace Component\FreeNetwork;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Event;
use App\Core\GSFile;
use App\Core\HTTPClient;
use function App\Core\I18n\_m;
use App\Core\Log;
use App\Core\Modules\Component;
use App\Core\Router\RouteLoader;
use App\Core\Router\Router;
use App\Core\Router;
use App\Entity\Activity;
use App\Entity\Actor;
use App\Entity\LocalUser;
@@ -86,7 +85,7 @@ class FreeNetwork extends Component
return Event::next;
}
public function onAddRoute(RouteLoader $m): bool
public function onAddRoute(Router $m): bool
{
// Feeds
$m->connect('feed_network', '/feed/network', [Feeds::class, 'network']);

View File

@@ -6,7 +6,7 @@ namespace Component\FreeNetwork\Util\WebfingerResource;
use App\Core\Event;
use App\Core\Log;
use App\Core\Router\Router;
use App\Core\Router;
use App\Entity\Actor;
use App\Util\Common;
use Component\FreeNetwork\Exception\WebfingerReconstructionException;

View File

@@ -26,7 +26,7 @@ namespace Component\Group\Controller;
use App\Core\ActorLocalRoles;
use App\Core\Cache;
use App\Core\Controller;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Form;
use function App\Core\I18n\_m;
use App\Core\Log;

View File

@@ -24,10 +24,10 @@ declare(strict_types = 1);
namespace Component\Group\Controller;
use App\Core\Cache;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Form;
use function App\Core\I18n\_m;
use App\Core\Router\Router;
use App\Core\Router;
use App\Entity\Actor;
use App\Entity as E;
use App\Util\Common;
@@ -80,6 +80,7 @@ class GroupFeed extends FeedController
WHERE act.object_type = 'note' AND act.id IN
(SELECT att.activity_id FROM \Component\Notification\Entity\Notification AS att WHERE att.target_id = :id)
)
ORDER BY n.created DESC
EOF, ['id' => $group->getId()]);
return [

View File

@@ -21,7 +21,7 @@ declare(strict_types = 1);
namespace Component\Group\Entity;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Entity;
use App\Entity\Actor;
use App\Util\Exception\NicknameEmptyException;

View File

@@ -24,8 +24,7 @@ namespace Component\Group;
use App\Core\Event;
use function App\Core\I18n\_m;
use App\Core\Modules\Component;
use App\Core\Router\RouteLoader;
use App\Core\Router\Router;
use App\Core\Router;
use App\Entity\Activity;
use App\Entity\Actor;
use App\Util\Common;
@@ -38,7 +37,7 @@ use Symfony\Component\HttpFoundation\Request;
class Group extends Component
{
public function onAddRoute(RouteLoader $r): bool
public function onAddRoute(Router $r): bool
{
$r->connect(id: 'group_actor_view_id', uri_path: '/group/{id<\d+>}', target: [C\GroupFeed::class, 'groupViewId']);
$r->connect(id: 'group_actor_view_nickname', uri_path: '/!{nickname<' . Nickname::DISPLAY_FMT . '>}', target: [C\GroupFeed::class, 'groupViewNickname']);
@@ -80,7 +79,6 @@ class Group extends Component
$url = Router::url('group_actor_settings', ['id' => $group->getId()]);
$res[] = HTML::html(['a' => ['attrs' => ['href' => $url, 'title' => _m('Edit group settings'), 'class' => 'profile-extra-actions'], _m('Group settings')]]);
}
$res[] = HTML::html(['a' => ['attrs' => ['href' => Router::url('blog_post', ['in' => $group->getId()]), 'title' => _m('Make a new blog post'), 'class' => 'profile-extra-actions'], _m('Post in blog')]]);
}
return Event::next;
}

View File

@@ -7,7 +7,7 @@
<h1>Settings</h1>
<ul>
<li>
{% set profile_tabs = [{'title': 'Personal Info', 'desc': 'Nickname, Homepage, Bio, Self Tags and more.', 'id': 'settings-personal-info', 'form': personal_info_form}] %}
{% set profile_tabs = [{'title': 'Personal Info', 'desc': 'Nickname, Homepage, Bio and more.', 'id': 'settings-personal-info', 'form': personal_info_form}] %}
{% set profile_tabs = profile_tabs|merge(handle_event('PopulateSettingsTabs', app.request, 'profile')) %}
{{ macros.settings_details_container('Profile', 'Personal Information, Avatar and Profile', 'settings-profile-details', profile_tabs, _context) }}
</li>

View File

@@ -21,7 +21,7 @@ declare(strict_types = 1);
namespace Component\Group\tests\Entity;
use App\Core\DB\DB;
use App\Core\DB;
use App\Entity\Actor;
use App\Util\GNUsocialTestCase;
use Component\Group\Entity\LocalGroup;

View File

@@ -25,7 +25,7 @@ namespace Component\Language\Controller;
use App\Core\Cache;
use App\Core\Controller;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Form;
use function App\Core\I18n\_m;
use App\Util\Common;
@@ -70,7 +70,7 @@ class Language extends Controller
['actor_id' => $user->getId()],
);
$new_langs = array_udiff($selected_langs, $existing_langs, fn ($l, $r) => $l->getId() <=> $r->getId());
$new_langs = array_udiff($selected_langs, $existing_langs, fn ($l, $r) => $l->getId() <=> $r->getId());
$removing_langs = array_udiff($existing_langs, $selected_langs, fn ($l, $r) => $l->getId() <=> $r->getId());
foreach ($new_langs as $l) {
DB::persist(ActorLanguage::create(['actor_id' => $user->getId(), 'language_id' => $l->getId(), 'ordering' => 0]));

View File

@@ -24,7 +24,7 @@ declare(strict_types = 1);
namespace Component\Language\Entity;
use App\Core\Cache;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Entity;
use App\Entity\Actor;
use App\Entity\LocalUser;

View File

@@ -24,7 +24,7 @@ declare(strict_types = 1);
namespace Component\Language\Entity;
use App\Core\Cache;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Entity;
use function App\Core\I18n\_m;
use App\Entity\Actor;

View File

@@ -23,7 +23,7 @@ namespace Component\Language;
use App\Core\Event;
use App\Core\Modules\Component;
use App\Core\Router\RouteLoader;
use App\Core\Router;
use App\Entity\Actor;
use App\Entity\Note;
use App\Util\Formatting;
@@ -38,7 +38,7 @@ use Symfony\Component\HttpFoundation\Request;
class Language extends Component
{
public function onAddRoute(RouteLoader $r): bool
public function onAddRoute(Router $r): bool
{
$r->connect('settings_sort_languages', '/settings/sort_languages', [C\Language::class, 'sortLanguages']);
return Event::next;

View File

@@ -25,10 +25,10 @@ namespace Component\LeftPanel\Controller;
use App\Core\Cache;
use App\Core\Controller;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Form;
use function App\Core\I18n\_m;
use App\Core\Router\Router;
use App\Core\Router;
use App\Entity\Feed;
use App\Util\Common;
use App\Util\Exception\ClientException;

View File

@@ -22,12 +22,11 @@ declare(strict_types = 1);
namespace Component\LeftPanel;
use App\Core\Cache;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Event;
use function App\Core\I18n\_m;
use App\Core\Modules\Component;
use App\Core\Router\RouteLoader;
use App\Core\Router\Router;
use App\Core\Router;
use App\Entity\Actor;
use App\Entity\Feed;
use App\Util\Exception\ClientException;
@@ -36,7 +35,7 @@ use Component\LeftPanel\Controller as C;
class LeftPanel extends Component
{
public function onAddRoute(RouteLoader $r): bool
public function onAddRoute(Router $r): bool
{
$r->connect('edit_feeds', '/edit-feeds', C\EditFeeds::class);
return Event::next;

View File

@@ -21,7 +21,7 @@ declare(strict_types = 1);
namespace Component\Link\Entity;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Entity;
use App\Core\Event;
use App\Core\GSFile;

View File

@@ -21,7 +21,7 @@ declare(strict_types = 1);
namespace Component\Link\Entity;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Entity;
use App\Core\Event;
use DateTimeInterface;
@@ -84,16 +84,14 @@ class NoteToLink extends Entity
* Create an instance of NoteToLink or fill in the
* properties of $obj with the associative array $args. Doesn't
* persist the result
*
* @param null|mixed $obj
*/
public static function create(array $args, $obj = null)
public static function create(array $args, bool $_delegated_call = false): static
{
$link = DB::find('link', ['id' => $args['link_id']]);
$note = DB::find('note', ['id' => $args['note_id']]);
Event::handle('NewLinkFromNote', [$link, $note]);
$obj = new self();
return parent::create($args, $obj);
return parent::createOrUpdate(obj: $obj, args: $args);
}
public static function removeWhereNoteId(int $note_id): mixed

View File

@@ -23,7 +23,7 @@ declare(strict_types = 1);
namespace Component\Link;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Event;
use App\Core\Modules\Component;
use App\Entity\Actor;
@@ -35,6 +35,22 @@ use InvalidArgumentException;
class Link extends Component
{
/**
* Note that this persists both a Link and a NoteToLink
*
* @return [Entity\Link, NoteToLink]
*/
public static function maybeCreateLink(string $url, int $note_id): array
{
try {
$link = Entity\Link::getOrCreate($url);
DB::persist($note_link = NoteToLink::create(['link_id' => $link->getId(), 'note_id' => $note_id]));
return ['link' => $link, 'note_to_link' => $note_link];
} catch (InvalidArgumentException) {
return ['link' => null, 'note_to_link' => null];
}
}
/**
* Extract URLs from $content and create the appropriate Link and NoteToLink entities
*/
@@ -49,12 +65,7 @@ class Link extends Component
if (\in_array($match, $ignore)) {
continue;
}
try {
$link_id = Entity\Link::getOrCreate($match)->getId();
DB::persist(NoteToLink::create(['link_id' => $link_id, 'note_id' => $note->getId()]));
} catch (InvalidArgumentException) {
continue;
}
self::maybeCreateLink($match, $note_id);
}
}
return Event::next;

View File

@@ -35,7 +35,7 @@ declare(strict_types = 1);
namespace Component\Notification\Controller;
use App\Core\Controller;
use App\Core\DB\DB;
use App\Core\DB;
use function App\Core\I18n\_m;
use App\Util\Common;
use Symfony\Component\HttpFoundation\Request;

View File

@@ -21,7 +21,7 @@ declare(strict_types = 1);
namespace Component\Notification\Entity;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Entity;
use App\Entity\Activity;
use App\Entity\Actor;

View File

@@ -21,14 +21,13 @@ declare(strict_types = 1);
namespace Component\Notification;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Event;
use function App\Core\I18n\_m;
use App\Core\Log;
use App\Core\Modules\Component;
use App\Core\Queue\Queue;
use App\Core\Router\RouteLoader;
use App\Core\Router\Router;
use App\Core\Queue;
use App\Core\Router;
use App\Entity\Activity;
use App\Entity\Actor;
use App\Entity\LocalUser;
@@ -40,7 +39,7 @@ use Throwable;
class Notification extends Component
{
public function onAddRoute(RouteLoader $m): bool
public function onAddRoute(Router $m): bool
{
$m->connect('feed_notifications', '/feed/notifications', [Feed::class, 'notifications']);
return Event::next;
@@ -65,25 +64,21 @@ class Notification extends Component
* Enqueues a notification for an Actor (such as person or group) which means
* it shows up in their home feed and such.
* WARNING: It's highly advisable to have flushed any relevant objects before triggering this event.
* OBSERVATION: $sender->getSubscribers() will always be pre-included, thus why $targets=[] is normal
*
* $targets should be of the shape:
* ['source' => (int|Actor)[]] // Prefer Actor whenever possible
* (int|Actor)[] // Prefer Actor whenever possible
* Example of $targets:
* [[42, $actor_alice, $actor_bob]] // Avoid repeating actors or ids
* [42, $actor_alice, $actor_bob] // Avoid repeating actors or ids
*
* @param Actor $sender The one responsible for this activity, take care not to include it in targets
* @param Activity $activity The activity responsible for the object being given to known to targets
* @param array $targets attentions, Mentions, any other source
* @param array $targets Attentions, Mentions, any other source. Should never be empty, you usually want to register an attention to every $sender->getSubscribers()
* @param null|string $reason An optional reason explaining why this notification exists
*/
public function onNewNotification(Actor $sender, Activity $activity, array $targets = [], ?string $reason = null): bool
public function onNewNotification(Actor $sender, Activity $activity, array $targets, ?string $reason = null): bool
{
// Ensure targets are all actor objects and unique
$effective_targets = [];
foreach ($sender->getSubscribers() as $subscriber) {
$effective_targets[$subscriber->getId()] = $subscriber;
}
foreach ($targets as $target) {
if (\is_int($target)) {
$target_id = $target;

View File

@@ -24,7 +24,7 @@ declare(strict_types = 1);
namespace Component\Person\Controller;
use function App\Core\I18n\_m;
use App\Core\Router\Router;
use App\Core\Router;
use App\Entity\Actor;
use App\Entity as E;
use App\Entity\LocalUser;

View File

@@ -38,7 +38,7 @@ namespace Component\Person\Controller;
// {{{ Imports
use App\Core\Controller;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Event;
use App\Core\Form;
use function App\Core\I18n\_m;
@@ -103,7 +103,7 @@ class PersonSettings extends Controller
$language_form = $language->settings($request);
return [
'_template' => 'settings/base.html.twig',
'_template' => 'person/settings.html.twig',
'personal_info_form' => $personal_form->createView(),
'email_form' => $email_form->createView(),
'password_form' => $password_form->createView(),
@@ -286,7 +286,7 @@ class PersonSettings extends Controller
$data = $form->getData();
unset($data['translation_domain']);
try {
[$entity, $is_update] = UserNotificationPrefs::createOrUpdate(
[$entity, $is_update] = UserNotificationPrefs::checkExistingAndCreateOrUpdate(
array_merge(['user_id' => $user->getId(), 'transport' => $transport_name], $data),
find_by_keys: ['user_id', 'transport'],
);

View File

@@ -23,13 +23,13 @@ namespace Component\Person;
use App\Core\Event;
use App\Core\Modules\Component;
use App\Core\Router\RouteLoader;
use App\Core\Router;
use App\Util\Nickname;
use Component\Person\Controller as C;
class Person extends Component
{
public function onAddRoute(RouteLoader $r): bool
public function onAddRoute(Router $r): bool
{
$r->connect(id: 'person_actor_view_id', uri_path: '/person/{id<\d+>}', target: [C\PersonFeed::class, 'personViewId']);
$r->connect(id: 'person_actor_view_nickname', uri_path: '/@{nickname<' . Nickname::DISPLAY_FMT . '>}', target: [C\PersonFeed::class, 'personViewNickname'], options: ['is_system_path' => false]);

View File

@@ -33,6 +33,10 @@
<li>
{{ macros.settings_details_container('Notifications', 'Enable/disable notifications (Email, XMPP, Replies...)', 'notifications', tabbed_forms_notify, _context) }}
</li>
<li>
{% set other_tabs = handle_event('PopulateSettingsTabs', app.request, 'api') %}
{{ macros.settings_details_container('API', 'API settings', 'settings-other-details', other_tabs, _context) }}
</li>
</ul>
</nav>
{% endblock body %}

View File

@@ -23,8 +23,8 @@ declare(strict_types = 1);
namespace Component\Person\tests\Controller;
use App\Core\DB\DB;
use App\Core\Router\Router;
use App\Core\DB;
use App\Core\Router;
use App\Entity\LocalUser;
use App\Util\GNUsocialTestCase;
use Jchook\AssertThrows\AssertThrows;

View File

@@ -8,7 +8,7 @@ use App\Core\ActorLocalRoles;
use App\Core\Event;
use App\Core\Form;
use function App\Core\I18n\_m;
use App\Core\Router\Router;
use App\Core\Router;
use App\Core\VisibilityScope;
use App\Entity\Actor;
use App\Util\Common;

View File

@@ -24,13 +24,12 @@ declare(strict_types = 1);
namespace Component\Posting;
use App\Core\Cache;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Event;
use App\Core\GSFile;
use function App\Core\I18n\_m;
use App\Core\Modules\Component;
use App\Core\Router\RouteLoader;
use App\Core\Router\Router;
use App\Core\Router;
use App\Core\VisibilityScope;
use App\Entity\Activity;
use App\Entity\Actor;
@@ -55,7 +54,7 @@ class Posting extends Component
{
public const route = 'posting_form_action';
public function onAddRoute(RouteLoader $r): bool
public function onAddRoute(Router $r): bool
{
$r->connect(self::route, '/form/posting', Controller\Posting::class);
return Event::next;
@@ -87,7 +86,7 @@ class Posting extends Component
* @throws DuplicateFoundException
* @throws ServerException
*/
public static function storeLocalPage(
public static function storeLocalArticle(
Actor $actor,
?string $content,
string $content_type,
@@ -118,7 +117,7 @@ class Posting extends Component
rendered: $rendered,
source: $source,
);
$note->setType('page');
$note->setType('article');
$note->setTitle($title);
if ($flush_and_notify) {
@@ -128,7 +127,7 @@ class Posting extends Component
$actor,
$activity,
$effective_attentions,
_m('Actor {actor_id} created page {note_id}.', [
_m('Actor {actor_id} created article {note_id}.', [
'{actor_id}' => $actor->getId(),
'{note_id}' => $activity->getObjectId(),
]),

View File

@@ -23,11 +23,11 @@ declare(strict_types = 1);
namespace Component\Subscription\Controller;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Form;
use function App\Core\I18n\_m;
use App\Core\Log;
use App\Core\Router\Router;
use App\Core\Router;
use App\Entity\Actor;
use App\Util\Common;
use App\Util\Exception\ClientException;

View File

@@ -24,12 +24,11 @@ declare(strict_types = 1);
namespace Component\Subscription;
use App\Core\Cache;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Event;
use function App\Core\I18n\_m;
use App\Core\Modules\Component;
use App\Core\Router\RouteLoader;
use App\Core\Router\Router;
use App\Core\Router;
use App\Entity\Activity;
use App\Entity\Actor;
use App\Entity\LocalUser;
@@ -40,12 +39,11 @@ use App\Util\Exception\ServerException;
use Component\Notification\Entity\Attention;
use Component\Subscription\Controller\Subscribers as SubscribersController;
use Component\Subscription\Controller\Subscriptions as SubscriptionsController;
use Symfony\Component\HttpFoundation\Request;
class Subscription extends Component
{
public function onAddRoute(RouteLoader $r): bool
public function onAddRoute(Router $r): bool
{
$r->connect(id: 'actor_subscribe_add', uri_path: '/actor/subscribe/{object_id<\d+>}', target: [SubscribersController::class, 'subscribersAdd']);
$r->connect(id: 'actor_subscribe_remove', uri_path: '/actor/unsubscribe/{object_id<\d+>}', target: [SubscribersController::class, 'subscribersRemove']);
@@ -99,7 +97,7 @@ class Subscription extends Component
$activity = null;
if (\is_null($subscription)) {
DB::persist($subscription = Entity\ActorSubscription::create($opts));
$activity = Activity::create([
$activity = Activity::create([
'actor_id' => $subscriber_id,
'verb' => 'subscribe',
'object_type' => Actor::schemaName(),
@@ -113,8 +111,9 @@ class Subscription extends Component
\is_int($subject) ? $subject : Actor::getById($subscriber_id),
$activity,
[$subscribed_id],
_m('{subject} subscribed to {object}.', ['{subject}' => $activity->getActorId(), '{object}' => $activity->getObjectId()]),
$reason = _m('{subject} subscribed to {object}.', ['{subject}' => $activity->getActorId(), '{object}' => $activity->getObjectId()]),
]);
Event::handle('NewSubscriptionEnd', [$subject, $activity, $object, $reason]);
}
return $activity;
}
@@ -163,7 +162,7 @@ class Subscription extends Component
Event::handle('NewNotification', [
\is_int($subject) ? $subject : Actor::getById($subscriber_id),
$activity,
[],
[$subscribed_id],
_m('{subject} unsubscribed from {object}.', ['{subject}' => $activity->getActorId(), '{object}' => $previous_follow_activity->getObjectId()]),
]);
}

View File

@@ -22,9 +22,9 @@ declare(strict_types = 1);
namespace Component\Tag\Entity;
use App\Core\Cache;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Entity;
use App\Core\Router\Router;
use App\Core\Router;
use App\Entity\Actor;
use App\Entity\Note;
use Component\Language\Entity\Language;

View File

@@ -22,7 +22,7 @@ declare(strict_types = 1);
namespace Component\Tag\Entity;
use App\Core\Cache;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Entity;
use Component\Tag\Tag;
use DateTimeInterface;

View File

@@ -24,11 +24,11 @@ declare(strict_types = 1);
namespace Component\Tag;
use App\Core\Cache;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Event;
use function App\Core\I18n\_m;
use App\Core\Modules\Component;
use App\Core\Router\Router;
use App\Core\Router;
use App\Entity\Actor;
use App\Entity\Note;
use App\Util\Common;
@@ -67,6 +67,54 @@ class Tag extends Component
return Event::next;
}
public static function maybeCreateTag(string $tag, int $note_id, ?int $lang_id): ?NoteTag
{
if (!self::validate($tag)) {
return null; // Ignore invalid tag candidates
}
$canonical_tag = self::canonicalTag($tag, \is_null($lang_id) ? null : Language::getById($lang_id)->getLocale());
DB::persist($note_tag = NoteTag::create([
'tag' => $tag,
'canonical' => $canonical_tag,
'note_id' => $note_id,
'use_canonical' => $extra_args['tag_use_canonical'] ?? false,
'language_id' => $lang_id,
]));
foreach (self::cacheKeys($canonical_tag) as $key) {
Cache::delete($key);
}
return $note_tag;
}
/**
* @return NoteTag[]
*/
public static function getNoteTags(int $actor_id, ?string $note_type): array
{
$query = <<<'EOF'
select nt from \App\Entity\Note n
join \Component\Tag\Entity\NoteTag nt with n.id = nt.note_id
where n.actor_id = :id
EOF;
if (\is_null($note_type)) {
return Cache::getList(
Actor::cacheKeys($actor_id, 'any')['note-tags'],
fn () => DB::dql(
$query,
['id' => $actor_id],
),
);
} else {
return Cache::getList(
Actor::cacheKeys($actor_id, $note_type)['note-tags'],
fn () => DB::dql(
$query . ' and n.type = :type',
['id' => $actor_id, 'type' => $note_type],
),
);
}
}
/**
* Process note by extracting any tags present
*/
@@ -82,21 +130,7 @@ class Tag extends Component
$matched_tags = array_unique(F\map($matched_tags, fn ($m) => $m[2]));
foreach ($matched_tags as $match) {
$tag = self::extract($match);
if (!self::validate($tag)) {
continue; // Ignore invalid tag candidates
}
$canonical_tag = self::canonicalTag($tag, \is_null($lang_id = $note->getLanguageId()) ? null : Language::getById($lang_id)->getLocale());
DB::persist(NoteTag::create([
'tag' => $tag,
'canonical' => $canonical_tag,
'note_id' => $note->getId(),
'use_canonical' => $extra_args['tag_use_canonical'] ?? false,
'language_id' => $lang_id,
]));
Cache::listPushLeft("tag-{$canonical_tag}", $note);
foreach (self::cacheKeys($canonical_tag) as $key) {
Cache::delete($key);
}
self::maybeCreateTag(tag: $tag, note_id: $note->getId(), lang_id: $note->getLanguageId());
}
return Event::next;
}

147
composer.lock generated
View File

@@ -553,16 +553,16 @@
},
{
"name": "doctrine/dbal",
"version": "3.3.3",
"version": "3.3.4",
"source": {
"type": "git",
"url": "https://github.com/doctrine/dbal.git",
"reference": "82331b861727c15b1f457ef05a8729e508e7ead5"
"reference": "83f779beaea1893c0bece093ab2104c6d15a7f26"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/dbal/zipball/82331b861727c15b1f457ef05a8729e508e7ead5",
"reference": "82331b861727c15b1f457ef05a8729e508e7ead5",
"url": "https://api.github.com/repos/doctrine/dbal/zipball/83f779beaea1893c0bece093ab2104c6d15a7f26",
"reference": "83f779beaea1893c0bece093ab2104c6d15a7f26",
"shasum": ""
},
"require": {
@@ -644,7 +644,7 @@
],
"support": {
"issues": "https://github.com/doctrine/dbal/issues",
"source": "https://github.com/doctrine/dbal/tree/3.3.3"
"source": "https://github.com/doctrine/dbal/tree/3.3.4"
},
"funding": [
{
@@ -660,7 +660,7 @@
"type": "tidelift"
}
],
"time": "2022-03-09T15:39:50+00:00"
"time": "2022-03-20T18:37:29+00:00"
},
{
"name": "doctrine/deprecations",
@@ -1440,16 +1440,16 @@
},
{
"name": "doctrine/persistence",
"version": "2.3.0",
"version": "2.4.1",
"source": {
"type": "git",
"url": "https://github.com/doctrine/persistence.git",
"reference": "f8af155c1e7963f3d2b4415097d55757bbaa53d8"
"reference": "092a52b71410ac1795287bb5135704ef07d18dd0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/persistence/zipball/f8af155c1e7963f3d2b4415097d55757bbaa53d8",
"reference": "f8af155c1e7963f3d2b4415097d55757bbaa53d8",
"url": "https://api.github.com/repos/doctrine/persistence/zipball/092a52b71410ac1795287bb5135704ef07d18dd0",
"reference": "092a52b71410ac1795287bb5135704ef07d18dd0",
"shasum": ""
},
"require": {
@@ -1462,23 +1462,23 @@
},
"conflict": {
"doctrine/annotations": "<1.0 || >=2.0",
"doctrine/common": "<2.10@dev"
"doctrine/common": "<2.10"
},
"require-dev": {
"composer/package-versions-deprecated": "^1.11",
"doctrine/annotations": "^1.0",
"doctrine/coding-standard": "^6.0 || ^9.0",
"doctrine/coding-standard": "^9.0",
"doctrine/common": "^3.0",
"phpstan/phpstan": "1.2.0",
"phpunit/phpunit": "^7.5.20 || ^8.0 || ^9.0",
"symfony/cache": "^4.4 || ^5.0 || ^6.0",
"vimeo/psalm": "4.13.1"
"phpstan/phpstan": "1.4.6",
"phpunit/phpunit": "^7.5.20 || ^8.5 || ^9.5",
"symfony/cache": "^4.4 || ^5.4 || ^6.0",
"vimeo/psalm": "4.21.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Doctrine\\Common\\": "lib/Doctrine/Common",
"Doctrine\\Persistence\\": "lib/Doctrine/Persistence"
"Doctrine\\Common\\": "src/Common",
"Doctrine\\Persistence\\": "src/Persistence"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -1522,9 +1522,9 @@
],
"support": {
"issues": "https://github.com/doctrine/persistence/issues",
"source": "https://github.com/doctrine/persistence/tree/2.3.0"
"source": "https://github.com/doctrine/persistence/tree/2.4.1"
},
"time": "2022-01-09T19:58:46+00:00"
"time": "2022-03-22T06:44:40+00:00"
},
{
"name": "doctrine/sql-formatter",
@@ -2043,16 +2043,16 @@
},
{
"name": "guzzlehttp/guzzle",
"version": "7.4.1",
"version": "7.4.2",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "ee0a041b1760e6a53d2a39c8c34115adc2af2c79"
"reference": "ac1ec1cd9b5624694c3a40be801d94137afb12b4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/ee0a041b1760e6a53d2a39c8c34115adc2af2c79",
"reference": "ee0a041b1760e6a53d2a39c8c34115adc2af2c79",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/ac1ec1cd9b5624694c3a40be801d94137afb12b4",
"reference": "ac1ec1cd9b5624694c3a40be801d94137afb12b4",
"shasum": ""
},
"require": {
@@ -2147,7 +2147,7 @@
],
"support": {
"issues": "https://github.com/guzzle/guzzle/issues",
"source": "https://github.com/guzzle/guzzle/tree/7.4.1"
"source": "https://github.com/guzzle/guzzle/tree/7.4.2"
},
"funding": [
{
@@ -2163,7 +2163,7 @@
"type": "tidelift"
}
],
"time": "2021-12-06T18:43:05+00:00"
"time": "2022-03-20T14:16:28+00:00"
},
{
"name": "guzzlehttp/promises",
@@ -2251,16 +2251,16 @@
},
{
"name": "guzzlehttp/psr7",
"version": "2.1.0",
"version": "2.2.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "089edd38f5b8abba6cb01567c2a8aaa47cec4c72"
"reference": "c94a94f120803a18554c1805ef2e539f8285f9a2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/089edd38f5b8abba6cb01567c2a8aaa47cec4c72",
"reference": "089edd38f5b8abba6cb01567c2a8aaa47cec4c72",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/c94a94f120803a18554c1805ef2e539f8285f9a2",
"reference": "c94a94f120803a18554c1805ef2e539f8285f9a2",
"shasum": ""
},
"require": {
@@ -2284,7 +2284,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.1-dev"
"dev-master": "2.2-dev"
}
},
"autoload": {
@@ -2346,7 +2346,7 @@
],
"support": {
"issues": "https://github.com/guzzle/psr7/issues",
"source": "https://github.com/guzzle/psr7/tree/2.1.0"
"source": "https://github.com/guzzle/psr7/tree/2.2.1"
},
"funding": [
{
@@ -2362,7 +2362,7 @@
"type": "tidelift"
}
],
"time": "2021-10-06T17:43:30+00:00"
"time": "2022-03-20T21:55:58+00:00"
},
{
"name": "jcupitt/vips",
@@ -3321,16 +3321,16 @@
},
{
"name": "monolog/monolog",
"version": "2.3.5",
"version": "2.4.0",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/monolog.git",
"reference": "fd4380d6fc37626e2f799f29d91195040137eba9"
"reference": "d7fd7450628561ba697b7097d86db72662f54aef"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/fd4380d6fc37626e2f799f29d91195040137eba9",
"reference": "fd4380d6fc37626e2f799f29d91195040137eba9",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/d7fd7450628561ba697b7097d86db72662f54aef",
"reference": "d7fd7450628561ba697b7097d86db72662f54aef",
"shasum": ""
},
"require": {
@@ -3352,7 +3352,7 @@
"phpstan/phpstan": "^0.12.91",
"phpunit/phpunit": "^8.5",
"predis/predis": "^1.1",
"rollbar/rollbar": "^1.3",
"rollbar/rollbar": "^1.3 || ^2 || ^3",
"ruflin/elastica": ">=0.90@dev",
"swiftmailer/swiftmailer": "^5.3|^6.0"
},
@@ -3404,7 +3404,7 @@
],
"support": {
"issues": "https://github.com/Seldaek/monolog/issues",
"source": "https://github.com/Seldaek/monolog/tree/2.3.5"
"source": "https://github.com/Seldaek/monolog/tree/2.4.0"
},
"funding": [
{
@@ -3416,7 +3416,7 @@
"type": "tidelift"
}
],
"time": "2021-10-01T21:08:31+00:00"
"time": "2022-03-14T12:44:37+00:00"
},
{
"name": "nikic/php-parser",
@@ -10715,16 +10715,16 @@
},
{
"name": "twig/twig",
"version": "v3.3.8",
"version": "v3.3.9",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "972d8604a92b7054828b539f2febb0211dd5945c"
"reference": "6ff9b0e440fa66f97f207e181c41340ddfa5683d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/972d8604a92b7054828b539f2febb0211dd5945c",
"reference": "972d8604a92b7054828b539f2febb0211dd5945c",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/6ff9b0e440fa66f97f207e181c41340ddfa5683d",
"reference": "6ff9b0e440fa66f97f207e181c41340ddfa5683d",
"shasum": ""
},
"require": {
@@ -10775,7 +10775,7 @@
],
"support": {
"issues": "https://github.com/twigphp/Twig/issues",
"source": "https://github.com/twigphp/Twig/tree/v3.3.8"
"source": "https://github.com/twigphp/Twig/tree/v3.3.9"
},
"funding": [
{
@@ -10787,7 +10787,7 @@
"type": "tidelift"
}
],
"time": "2022-02-04T06:59:48+00:00"
"time": "2022-03-25T09:37:52+00:00"
},
{
"name": "voku/portable-ascii",
@@ -11242,16 +11242,16 @@
},
{
"name": "composer/semver",
"version": "3.2.9",
"version": "3.3.1",
"source": {
"type": "git",
"url": "https://github.com/composer/semver.git",
"reference": "a951f614bd64dcd26137bc9b7b2637ddcfc57649"
"reference": "5d8e574bb0e69188786b8ef77d43341222a41a71"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/semver/zipball/a951f614bd64dcd26137bc9b7b2637ddcfc57649",
"reference": "a951f614bd64dcd26137bc9b7b2637ddcfc57649",
"url": "https://api.github.com/repos/composer/semver/zipball/5d8e574bb0e69188786b8ef77d43341222a41a71",
"reference": "5d8e574bb0e69188786b8ef77d43341222a41a71",
"shasum": ""
},
"require": {
@@ -11303,7 +11303,7 @@
"support": {
"irc": "irc://irc.freenode.org/composer",
"issues": "https://github.com/composer/semver/issues",
"source": "https://github.com/composer/semver/tree/3.2.9"
"source": "https://github.com/composer/semver/tree/3.3.1"
},
"funding": [
{
@@ -11319,7 +11319,7 @@
"type": "tidelift"
}
],
"time": "2022-02-04T13:58:43+00:00"
"time": "2022-03-16T11:22:07+00:00"
},
{
"name": "composer/xdebug-handler",
@@ -11554,16 +11554,16 @@
},
{
"name": "friendsofphp/php-cs-fixer",
"version": "v3.7.0",
"version": "v3.8.0",
"source": {
"type": "git",
"url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git",
"reference": "7705d5a985132a40282d18a176eb9a4a0497747c"
"reference": "cbad1115aac4b5c3c5540e7210d3c9fba2f81fa3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/7705d5a985132a40282d18a176eb9a4a0497747c",
"reference": "7705d5a985132a40282d18a176eb9a4a0497747c",
"url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/cbad1115aac4b5c3c5540e7210d3c9fba2f81fa3",
"reference": "cbad1115aac4b5c3c5540e7210d3c9fba2f81fa3",
"shasum": ""
},
"require": {
@@ -11631,7 +11631,7 @@
"description": "A tool to automatically fix PHP code style",
"support": {
"issues": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues",
"source": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/tree/v3.7.0"
"source": "https://github.com/FriendsOfPHP/PHP-CS-Fixer/tree/v3.8.0"
},
"funding": [
{
@@ -11639,7 +11639,7 @@
"type": "github"
}
],
"time": "2022-03-07T16:59:59+00:00"
"time": "2022-03-18T17:20:59+00:00"
},
{
"name": "jchook/phpunit-assert-throws",
@@ -12012,7 +12012,6 @@
"conflict": {
"phpstan/phpstan-shim": "*"
},
"default-branch": true,
"bin": [
"phpstan",
"phpstan.phar"
@@ -12372,16 +12371,16 @@
},
{
"name": "phpunit/phpunit",
"version": "9.5.18",
"version": "9.5.19",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "1b5856028273bfd855e60a887278857d872ec67a"
"reference": "35ea4b7f3acabb26f4bb640f8c30866c401da807"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1b5856028273bfd855e60a887278857d872ec67a",
"reference": "1b5856028273bfd855e60a887278857d872ec67a",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/35ea4b7f3acabb26f4bb640f8c30866c401da807",
"reference": "35ea4b7f3acabb26f4bb640f8c30866c401da807",
"shasum": ""
},
"require": {
@@ -12411,7 +12410,7 @@
"sebastian/global-state": "^5.0.1",
"sebastian/object-enumerator": "^4.0.3",
"sebastian/resource-operations": "^3.0.3",
"sebastian/type": "^2.3.4",
"sebastian/type": "^3.0",
"sebastian/version": "^3.0.2"
},
"require-dev": {
@@ -12459,7 +12458,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.18"
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.19"
},
"funding": [
{
@@ -12471,7 +12470,7 @@
"type": "github"
}
],
"time": "2022-03-08T06:52:28+00:00"
"time": "2022-03-15T09:57:31+00:00"
},
{
"name": "sebastian/cli-parser",
@@ -13330,28 +13329,28 @@
},
{
"name": "sebastian/type",
"version": "2.3.4",
"version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/type.git",
"reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914"
"reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b8cd8a1c753c90bc1a0f5372170e3e489136f914",
"reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914",
"url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b233b84bc4465aff7b57cf1c4bc75c86d00d6dad",
"reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad",
"shasum": ""
},
"require": {
"php": ">=7.3"
},
"require-dev": {
"phpunit/phpunit": "^9.3"
"phpunit/phpunit": "^9.5"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
"dev-master": "3.0-dev"
}
},
"autoload": {
@@ -13374,7 +13373,7 @@
"homepage": "https://github.com/sebastianbergmann/type",
"support": {
"issues": "https://github.com/sebastianbergmann/type/issues",
"source": "https://github.com/sebastianbergmann/type/tree/2.3.4"
"source": "https://github.com/sebastianbergmann/type/tree/3.0.0"
},
"funding": [
{
@@ -13382,7 +13381,7 @@
"type": "github"
}
],
"time": "2021-06-15T12:49:02+00:00"
"time": "2022-03-15T09:54:48+00:00"
},
{
"name": "sebastian/version",

View File

@@ -24,7 +24,7 @@ services:
resource: '../src/Controller'
tags: ['controller.service_arguments']
App\Core\Router\RouteLoader:
App\Core\Router:
tags: ['routing.loader']
# Wrapper around Doctrine's StaticPHP metadata driver

View File

@@ -34,15 +34,14 @@ namespace Plugin\ActivityPub;
use ActivityPhp\Type;
use ActivityPhp\Type\AbstractObject;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Event;
use App\Core\HTTPClient;
use function App\Core\I18n\_m;
use App\Core\Log;
use App\Core\Modules\Plugin;
use App\Core\Queue\Queue;
use App\Core\Router\RouteLoader;
use App\Core\Router\Router;
use App\Core\Queue;
use App\Core\Router;
use App\Entity\Activity;
use App\Entity\Actor;
use App\Entity\Note;
@@ -104,7 +103,7 @@ class ActivityPub extends Plugin
'User-Agent' => 'GNUsocialBot ' . GNUSOCIAL_VERSION . ' - ' . GNUSOCIAL_PROJECT_URL,
];
public function version(): string
public static function version(): string
{
return '3.0.0';
}
@@ -156,9 +155,9 @@ class ActivityPub extends Plugin
* This code executes when GNU social creates the page routing, and we hook
* on this event to add our Inbox and Outbox handler for ActivityPub.
*
* @param RouteLoader $r the router that was initialized
* @param Router $r the router that was initialized
*/
public function onAddRoute(RouteLoader $r): bool
public function onAddRoute(Router $r): bool
{
$r->connect(
'activitypub_inbox',
@@ -324,12 +323,22 @@ class ActivityPub extends Plugin
array &$retry_args,
): bool {
try {
$data = Model::toJson($activity);
if ($sender->isGroup()) {
// When the sender is a group, we have to wrap it in an Announce activity
$data = Type::create('Announce', ['object' => $data])->toJson();
$data = Model::toType($activity);
if ($sender->isGroup()) { // When the sender is a group,
if ($activity->getVerb() === 'subscribe') {
// Regular postman happens
} elseif ($activity->getVerb() === 'undo' && $data->get('object')->get('type') === 'Follow') {
// Regular postman happens
} else {
// For every other activity sent by a Group, we have to wrap it in a transient Announce activity
$data = Type::create('Announce', [
'@context' => 'https:\/\/www.w3.org\/ns\/activitystreams',
'actor' => $sender->getUri(type: Router::ABSOLUTE_URL),
'object' => $data,
]);
}
}
$res = self::postman($sender, $data, $inbox);
$res = self::postman($sender, $data->toJson(), $inbox);
// accumulate errors for later use, if needed
$status_code = $res->getStatusCode();
@@ -377,6 +386,7 @@ class ActivityPub extends Plugin
// the actor, that could for example mean that OStatus handled this actor while we were deactivated
// On next interaction this should be resolved, for now continue
if (\is_null($ap_target = DB::findOneBy(ActivitypubActor::class, ['actor_id' => $actor->getId()], return_null: true))) {
Log::info('FreeNetwork wrongly told ActivityPub that it can handle actor id: ' . $actor->getId() . ' you might want to keep an eye on it.');
continue;
}
$to_addr[$ap_target->getInboxSharedUri() ?? $ap_target->getInboxUri()][] = $actor;

View File

@@ -33,11 +33,11 @@ declare(strict_types = 1);
namespace Plugin\ActivityPub\Controller;
use App\Core\Controller;
use App\Core\DB\DB;
use App\Core\DB;
use function App\Core\I18n\_m;
use App\Core\Log;
use App\Core\Queue\Queue;
use App\Core\Router\Router;
use App\Core\Queue;
use App\Core\Router;
use App\Util\Common;
use App\Util\Exception\ClientException;
use Exception;
@@ -98,7 +98,7 @@ class Inbox extends Controller
try {
$resource_parts = parse_url($type->get('actor'));
if ($resource_parts['host'] !== Common::config('site', 'server')) {
$actor = DB::wrapInTransaction(fn () => Explorer::getOneFromUri($type->get('actor')));
$actor = DB::wrapInTransaction(fn () => Explorer::getOneFromUri($type->get('actor')));
$ap_actor = DB::findOneBy(ActivitypubActor::class, ['actor_id' => $actor->getId()]);
} else {
throw new Exception('Only remote actors can use this endpoint.');

View File

@@ -34,7 +34,7 @@ namespace Plugin\ActivityPub\Controller;
use function App\Core\I18n\_m;
use App\Core\Log;
use App\Core\Router\Router;
use App\Core\Router;
use App\Entity\Activity;
use App\Entity\Actor;
use App\Util\Exception\ClientException;

View File

@@ -32,7 +32,7 @@ declare(strict_types = 1);
namespace Plugin\ActivityPub\Entity;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Entity;
use App\Entity\Activity;
use DateTimeInterface;

View File

@@ -33,7 +33,7 @@ declare(strict_types = 1);
namespace Plugin\ActivityPub\Entity;
use App\Core\Cache;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Entity;
use function App\Core\I18n\_m;
use App\Core\Log;

View File

@@ -32,7 +32,7 @@ declare(strict_types = 1);
namespace Plugin\ActivityPub\Entity;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Entity;
use DateTimeInterface;

View File

@@ -32,7 +32,7 @@ declare(strict_types = 1);
namespace Plugin\ActivityPub\Entity;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Entity;
use App\Core\Log;
use App\Entity\Actor;

View File

@@ -4,7 +4,7 @@ declare(strict_types = 1);
namespace Plugin\ActivityPub\Test\Fixtures;
use App\Core\DB\DB;
use App\Core\DB;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;
use Plugin\ActivityPub\Util\Model\Activity;
@@ -43,8 +43,8 @@ class ActivityPubFixtures extends Fixture
$note_path = self::fixturesPath('objects/note.jsonld', $ontology);
$note = Note::fromJson(fread(fopen($note_path, 'r'), filesize($note_path)));
DB::flush();
$page_path = self::fixturesPath('objects/page.jsonld', $ontology);
$page = Note::fromJson(fread(fopen($page_path, 'r'), filesize($page_path)));
$article_path = self::fixturesPath('objects/article.jsonld', $ontology);
$article = Note::fromJson(fread(fopen($article_path, 'r'), filesize($article_path)));
DB::flush();
$reply_path = self::fixturesPath('objects/reply.jsonld', $ontology);
$reply = Note::fromJson(fread(fopen($reply_path, 'r'), filesize($reply_path)));
@@ -57,8 +57,8 @@ class ActivityPubFixtures extends Fixture
$create_note_path = self::fixturesPath('activities/create_note.jsonld', $ontology);
$create_note = Activity::fromJson(fread(fopen($create_note_path, 'r'), filesize($create_note_path)));
DB::flush();
$create_page_path = self::fixturesPath('activities/create_page.jsonld', $ontology);
$create_page = Activity::fromJson(fread(fopen($create_page_path, 'r'), filesize($create_page_path)));
$create_article_path = self::fixturesPath('activities/create_article.jsonld', $ontology);
$create_article = Activity::fromJson(fread(fopen($create_article_path, 'r'), filesize($create_article_path)));
DB::flush();
$create_reply_path = self::fixturesPath('activities/create_reply.jsonld', $ontology);
$create_reply = Activity::fromJson(fread(fopen($create_reply_path, 'r'), filesize($create_reply_path)));

View File

@@ -29,15 +29,15 @@
"https://instance.gnusocial.test/actor/21"
],
"object": {
"type": "Page",
"type": "Article",
"id": "https://instance.gnusocial.test/object/note/1338",
"published": "2022-03-17T23:30:26+00:00",
"attributedTo": "https://instance.gnusocial.test/actor/42",
"name": "hello, world.",
"content": "<p>This is an interesting page.</p>",
"content": "<p>This is an interesting article.</p>",
"mediaType": "text/html",
"source": {
"content": "This is an interesting page.",
"content": "This is an interesting article.",
"mediaType": "text/markdown"
},
"attachment": [],

View File

@@ -1,5 +1,5 @@
{
"type": "Page",
"type": "Article",
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
@@ -23,10 +23,10 @@
"published": "2022-03-17T23:30:26+00:00",
"attributedTo": "https://instance.gnusocial.test/actor/42",
"name": "hello, world.",
"content": "<p>This is an interesting page.</p>",
"content": "<p>This is an interesting article.</p>",
"mediaType": "text/html",
"source": {
"content": "This is an interesting page.",
"content": "This is an interesting article.",
"mediaType": "text/markdown"
},
"attachment": [],

View File

@@ -30,7 +30,7 @@ use Plugin\ActivityPub\ActivityPub;
use Plugin\ActivityPub\Entity\ActivitypubObject;
use Plugin\ActivityPub\Util\Explorer;
class GSObjectPageTest extends GNUsocialTestCase
class GSObjectArticleTest extends GNUsocialTestCase
{
public function testNoteFromJson()
{
@@ -39,29 +39,29 @@ class GSObjectPageTest extends GNUsocialTestCase
$actor_uri = 'https://instance.gnusocial.test/actor/42';
$object_uri = 'https://instance.gnusocial.test/object/note/1338';
$group_uri = 'https://instance.gnusocial.test/actor/21';
$page = ActivityPub::getObjectByUri($object_uri, try_online: false);
static::assertInstanceOf(Note::class, $page);
$article = ActivityPub::getObjectByUri($object_uri, try_online: false);
static::assertInstanceOf(Note::class, $article);
static::assertSame(Explorer::getOneFromUri($actor_uri)->getId(), $page->getActorId());
static::assertSame('text/markdown', $page->getContentType());
static::assertSame('This is an interesting page.', $page->getContent());
static::assertSame('<p>This is an interesting page.</p>', $page->getRendered());
static::assertSame('ActivityPub', $page->getSource());
static::assertNull($page->getReplyTo());
static::assertFalse($page->getIsLocal());
static::assertSame(VisibilityScope::EVERYWHERE, $page->getScope());
static::assertSame($object_uri, $page->getUrl());
static::assertNull($page->getLanguageLocale());
static::assertSame('page', $page->getType());
static::assertSame('hello, world.', $page->getTitle());
static::assertSame(Explorer::getOneFromUri($actor_uri)->getId(), $article->getActorId());
static::assertSame('text/markdown', $article->getContentType());
static::assertSame('This is an interesting article.', $article->getContent());
static::assertSame('<p>This is an interesting article.</p>', $article->getRendered());
static::assertSame('ActivityPub', $article->getSource());
static::assertNull($article->getReplyTo());
static::assertFalse($article->getIsLocal());
static::assertSame(VisibilityScope::EVERYWHERE, $article->getScope());
static::assertSame($object_uri, $article->getUrl());
static::assertNull($article->getLanguageLocale());
static::assertSame('article', $article->getType());
static::assertSame('hello, world.', $article->getTitle());
$ap_object = ActivitypubObject::getByPK(['object_uri' => $object_uri]);
static::assertSame(Note::schemaName(), $ap_object->getObjectType());
static::assertSame($object_uri, $ap_object->getObjectUri());
static::assertSame($page->getId(), $ap_object->getObjectId());
static::assertSame($article->getId(), $ap_object->getObjectId());
static::assertCount(1, $attT = $page->getAttentionTargets());
static::assertCount(1, $attT = $article->getAttentionTargets());
static::assertObjectEquals(Explorer::getOneFromUri($group_uri, try_online: false), $attT[0]);
static::assertSame([], $page->getMentionTargets());
static::assertSame([], $article->getMentionTargets());
}
}

View File

@@ -32,7 +32,7 @@ declare(strict_types = 1);
namespace Plugin\ActivityPub\Util;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\HTTPClient;
use App\Core\Log;
use App\Entity\Actor;

View File

@@ -30,6 +30,7 @@ namespace Plugin\ActivityPub\Util;
use App\Entity\Actor;
use DateTime;
use Exception;
use Plugin\ActivityPub\ActivityPub;
use Plugin\ActivityPub\Entity\ActivitypubRsa;
class HTTPSignature
@@ -91,7 +92,7 @@ class HTTPSignature
'Date' => $date->format('D, d M Y H:i:s \G\M\T'),
'Host' => parse_url($url, \PHP_URL_HOST),
'Accept' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams", application/activity+json, application/json',
'User-Agent' => 'GNU social ActivityPub Plugin - ' . GNUSOCIAL_ENGINE_URL,
'User-Agent' => 'GNU social ActivityPub Plugin - v' . ActivityPub::version() . ' - ' . GNUSOCIAL_ENGINE_URL,
'Content-Type' => 'application/activity+json',
];

View File

@@ -114,24 +114,36 @@ abstract class Model
*/
abstract public static function fromJson(string|Type\AbstractObject $json, array $options = []): Entity;
/**
* Get a Type
*
* @throws \App\Util\Exception\ServerException
* @throws ClientException
*/
public static function toType(mixed $object): Type\AbstractObject
{
switch ($object::class) {
case \App\Entity\Activity::class:
return Activity::toType($object);
case \App\Entity\Note::class:
return Note::toType($object);
default:
$type = self::jsonToType($object);
Event::handle('ActivityPubAddActivityStreamsTwoData', [$type->get('type'), &$type]);
return $type;
}
}
/**
* Get a JSON
*
* @param ?int $options PHP JSON options
* @param int $options PHP JSON options
*
* @throws \App\Util\Exception\ServerException
* @throws ClientException
*/
public static function toJson(mixed $object, int $options = \JSON_UNESCAPED_SLASHES): string
{
switch ($object::class) {
case \App\Entity\Activity::class:
return Activity::toJson($object, $options);
case \App\Entity\Note::class:
return Note::toJson($object, $options);
default:
$type = self::jsonToType($object);
Event::handle('ActivityPubAddActivityStreamsTwoData', [$type->get('type'), &$type]);
return $type->toJson($options);
}
return self::toType($object)->toJson($options);
}
}

View File

@@ -34,11 +34,12 @@ namespace Plugin\ActivityPub\Util\Model;
use ActivityPhp\Type;
use ActivityPhp\Type\AbstractObject;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Event;
use App\Core\Log;
use App\Core\Router\Router;
use App\Core\Router;
use App\Entity\Activity as GSActivity;
use App\Util\Common;
use App\Util\Exception\ClientException;
use App\Util\Exception\NoSuchActorException;
use App\Util\Exception\NotFoundException;
@@ -46,7 +47,6 @@ use App\Util\Exception\NotImplementedException;
use DateTimeInterface;
use Exception;
use InvalidArgumentException;
use const JSON_UNESCAPED_SLASHES;
use Plugin\ActivityPub\ActivityPub;
use Plugin\ActivityPub\Entity\ActivitypubActivity;
use Plugin\ActivityPub\Util\Explorer;
@@ -90,9 +90,14 @@ class Activity extends Model
// Find Actor and Object
$actor = Explorer::getOneFromUri($type_activity->get('actor'));
$type_object = $type_activity->get('object');
if (\is_string($type_object)) { // Retrieve it
$type_object = ActivityPub::getObjectByUri($type_object, try_online: true);
} else { // Encapsulated, if we have it locally, prefer it
if (\is_string($type_object)) {
if (Common::isValidHttpUrl($type_object)) { // Retrieve it
$type_object = ActivityPub::getObjectByUri($type_object, try_online: true);
} else {
$type_object = Type::fromJson($type_object);
}
}
if ($type_object instanceof AbstractObject) { // Encapsulated, if we have it locally, prefer it
// TODO: Test authority of activity over object
try {
$type_object = ActivityPub::getObjectByUri($type_object->get('id'), try_online: false);
@@ -153,7 +158,7 @@ class Activity extends Model
*
* @throws ClientException
*/
public static function toJson(mixed $object, int $options = JSON_UNESCAPED_SLASHES): string
public static function toType(mixed $object): AbstractObject
{
if ($object::class !== GSActivity::class) {
throw new InvalidArgumentException('First argument type must be an Activity.');
@@ -170,8 +175,12 @@ class Activity extends Model
}
$attr = [
'type' => $gs_verb_to_activity_streams_two_verb,
'@context' => ActivityPub::$activity_streams_two_context,
'type' => $gs_verb_to_activity_streams_two_verb,
'@context' => ActivityPub::$activity_streams_two_context,
'instrument' => Type::create('Service', [
'name' => 'GNU social ActivityPub Plugin - v' . ActivityPub::version(),
'url' => GNUSOCIAL_ENGINE_URL,
]),
'id' => Router::url('activity_view', ['id' => $object->getId()], Router::ABSOLUTE_URL),
'published' => $object->getCreated()->format(DateTimeInterface::RFC3339),
'actor' => $object->getActor()->getUri(Router::ABSOLUTE_URL),
@@ -186,7 +195,8 @@ class Activity extends Model
// Get object or Tombstone
try {
$child = $object->getObject(); // Throws NotFoundException
$attr['object'] = ($attr['type'] === 'Create') ? self::jsonToType(Model::toJson($child)) : ActivityPub::getUriByObject($child);
$prefer_embed = ['Create', 'Undo'];
$attr['object'] = \in_array($attr['type'], $prefer_embed) ? self::jsonToType(Model::toJson($child)) : ActivityPub::getUriByObject($child);
} catch (NotFoundException) {
// It seems this object was deleted, refer to it as a Tombstone
$uri = match ($object->getObjectType()) {
@@ -203,6 +213,6 @@ class Activity extends Model
}
$type = self::jsonToType($attr);
Event::handle('ActivityPubAddActivityStreamsTwoData', [$type->get('type'), &$type]);
return $type->toJson($options);
return $type;
}
}

View File

@@ -33,6 +33,8 @@ declare(strict_types = 1);
namespace Plugin\ActivityPub\Util\Model;
use ActivityPhp\Type\AbstractObject;
use Exception;
use InvalidArgumentException;
use Plugin\ActivityPub\Entity\ActivitypubActivity;
/**
@@ -45,27 +47,15 @@ class ActivityAnnounce extends Activity
{
protected static function handle_core_activity(\App\Entity\Actor $actor, AbstractObject $type_activity, mixed $type_object, ?ActivitypubActivity &$ap_act): ActivitypubActivity
{
// The only core Announce we recognise is for (transitive) activities coming from Group actors
// The only core Announce we recognise is for (transient) activities coming from Group actors
if ($actor->isGroup()) {
if ($type_object instanceof AbstractObject) {
$actual_to = array_flip(\is_string($type_object->get('to')) ? [$type_object->get('to')] : $type_object->get('to'));
$actual_cc = array_flip(\is_string($type_object->get('cc')) ? [$type_object->get('cc')] : $type_object->get('cc'));
$actual_cc[$type_activity->get('actor')] = true; // Add group to targets
foreach (\is_string($type_activity->get('to')) ? [$type_activity->get('to')] : $type_activity->get('to') as $to) {
if ($to !== 'https://www.w3.org/ns/activitystreams#Public') {
$actual_to[$to] = true;
}
}
foreach (\is_string($type_activity->get('cc')) ? [$type_activity->get('cc')] : $type_activity->get('cc') as $cc) {
if ($cc !== 'https://www.w3.org/ns/activitystreams#Public') {
$actual_cc[$cc] = true;
}
}
$type_object->set('to', array_keys($actual_to));
$type_object->set('cc', array_keys($actual_cc));
$ap_act = self::fromJson($type_object);
return $ap_act = Activity::fromJson($type_object);
} else {
throw new Exception('Already handled.');
}
} else {
throw new InvalidArgumentException('Unsupported Announce Activity.');
}
return $ap_act ?? ($ap_act = $type_object);
}
}

View File

@@ -33,7 +33,7 @@ declare(strict_types = 1);
namespace Plugin\ActivityPub\Util\Model;
use ActivityPhp\Type\AbstractObject;
use App\Core\DB\DB;
use App\Core\DB;
use App\Entity\Activity as GSActivity;
use App\Util\Exception\NotImplementedException;
use DateTime;

View File

@@ -33,10 +33,11 @@ declare(strict_types = 1);
namespace Plugin\ActivityPub\Util\Model;
use ActivityPhp\Type\AbstractObject;
use App\Core\DB\DB;
use App\Core\DB;
use App\Entity\Activity as GSActivity;
use App\Util\Exception\ClientException;
use Component\Subscription\Subscription;
use Component\Subscription\Subscription as SubscriptionComponent;
use DateTime;
use InvalidArgumentException;
use Plugin\ActivityPub\Entity\ActivitypubActivity;
@@ -63,6 +64,8 @@ class ActivityFollow extends Activity
if (\is_null($act)) {
throw new ClientException('You are already subscribed to this actor.');
}
SubscriptionComponent::refreshSubscriptionCount($actor, $subscribed);
// Store ActivityPub Activity
$ap_act = ActivitypubActivity::create([
'activity_id' => $act->getId(),

View File

@@ -34,12 +34,12 @@ namespace Plugin\ActivityPub\Util\Model;
use ActivityPhp\Type\AbstractObject;
use App\Core\ActorLocalRoles;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Event;
use App\Core\GSFile;
use App\Core\HTTPClient;
use App\Core\Log;
use App\Core\Router\Router;
use App\Core\Router;
use App\Entity\Actor as GSActor;
use App\Util\Exception\ServerException;
use App\Util\TemporaryFile;
@@ -198,6 +198,7 @@ class Actor extends Model
}
}
Event::handle('ActivityPubCreateOrUpdateActor', [$object, &$actor, &$ap_actor]);
return $ap_actor;
}

View File

@@ -35,13 +35,13 @@ namespace Plugin\ActivityPub\Util\Model;
use ActivityPhp\Type;
use ActivityPhp\Type\AbstractObject;
use App\Core\Cache;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Event;
use App\Core\GSFile;
use App\Core\HTTPClient;
use function App\Core\I18n\_m;
use App\Core\Log;
use App\Core\Router\Router;
use App\Core\Router;
use App\Core\VisibilityScope;
use App\Entity\Note as GSNote;
use App\Util\Common;
@@ -166,8 +166,9 @@ class Note extends Model
'reply_to' => $reply_to = $handleInReplyTo($type_note),
'modified' => new DateTime(),
'type' => match ($type_note->get('type')) {
'Article' => 'article',
'Page' => 'page',
default => 'note'
default => 'note' // graceful degradation
},
'source' => $source,
];
@@ -361,20 +362,22 @@ class Note extends Model
* @throws InvalidArgumentException
* @throws ServerException
*/
public static function toJson(mixed $object, int $options = \JSON_UNESCAPED_SLASHES): string
public static function toType(mixed $object): AbstractObject
{
if ($object::class !== GSNote::class) {
throw new InvalidArgumentException('First argument type must be a Note.');
}
$attr = [
'@context' => ActivityPub::$activity_streams_two_context,
'type' => $object->getScope() === VisibilityScope::MESSAGE ? 'ChatMessage' : (match ($object->getType()) {
'note' => 'Note',
'page' => 'Page',
default => throw new Exception('Unsupported note type.')
'@context' => ActivityPub::$activity_streams_two_context,
'type' => $object->getScope() === VisibilityScope::MESSAGE ? 'ChatMessage' : (match ($object->getType()) {
'note' => 'Note',
'article' => 'Article',
'page' => 'Page',
default => throw new Exception('Unsupported note type.')
}),
'id' => $object->getUrl(),
'url' => $object->getUrl(),
'published' => $object->getCreated()->format(DateTimeInterface::RFC3339),
'attributedTo' => $object->getActor()->getUri(Router::ABSOLUTE_URL),
'name' => $object->getTitle(),
@@ -469,6 +472,6 @@ class Note extends Model
$type = self::jsonToType($attr);
Event::handle('ActivityPubAddActivityStreamsTwoData', [$type->get('type'), &$type]);
return $type->toJson($options);
return $type;
}
}

View File

@@ -34,7 +34,7 @@ namespace Plugin\ActivityPub\Util;
use ActivityPhp\Type\Core\OrderedCollection;
use ActivityPhp\Type\Core\OrderedCollectionPage;
use App\Core\Router\Router;
use App\Core\Router;
use Component\Collection\Util\Controller\CircleController;
use Component\Collection\Util\Controller\FeedController;
use Component\Collection\Util\Controller\OrderedCollection as GSOrderedCollection;

View File

@@ -31,12 +31,11 @@ declare(strict_types = 1);
namespace Plugin\AttachmentCollections;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Event;
use function App\Core\I18n\_m;
use App\Core\Modules\Plugin;
use App\Core\Router\RouteLoader;
use App\Core\Router\Router;
use App\Core\Router;
use App\Entity\Actor;
use App\Entity\Feed;
use App\Entity\LocalUser;
@@ -123,7 +122,7 @@ class AttachmentCollections extends Plugin
return array_map(fn ($x) => $x['attachment_collection_id'], $res);
}
public function onAddRoute(RouteLoader $r): bool
public function onAddRoute(Router $r): bool
{
// View all collections by actor id and nickname
$r->connect(

View File

@@ -23,8 +23,8 @@ declare(strict_types = 1);
namespace Plugin\AttachmentCollections\Controller;
use App\Core\DB\DB;
use App\Core\Router\Router;
use App\Core\DB;
use App\Core\Router;
use Component\Collection\Util\Controller\MetaCollectionController;
use Plugin\AttachmentCollections\Entity\AttachmentCollection;

View File

@@ -23,7 +23,7 @@ declare(strict_types = 1);
namespace Plugin\AttachmentShowRelated;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Event;
use App\Core\Modules\Plugin;
use App\Util\Common;

View File

@@ -43,7 +43,7 @@ use SplFileInfo;
class AudioEncoder extends Plugin
{
public function version(): string
public static function version(): string
{
return '0.1.0';
}

View File

@@ -20,18 +20,31 @@ declare(strict_types = 1);
// }}}
namespace Component\Blog;
namespace Plugin\Blog;
use App\Core\Event;
use App\Core\Modules\Plugin;
use App\Core\Router\RouteLoader;
use Component\Blog\Controller as C;
use App\Core\Router;
use App\Util\Common;
use App\Util\HTML;
use Plugin\Blog\Controller as C;
use function App\Core\I18n\_m;
class Blog extends Plugin
{
public function onAddRoute(RouteLoader $r): bool
public function onAddRoute(Router $r): bool
{
$r->connect(id: 'blog_post', uri_path: '/blog/post', target: [C\Post::class, 'makePost']);
return Event::next;
}
public function onAppendCardProfile(array $vars, array &$res): bool
{
$actor = Common::actor();
$group = $vars['actor'];
if (!\is_null($actor) && $group->isGroup()) {
$res[] = HTML::html(['a' => ['attrs' => ['href' => Router::url('blog_post', ['in' => $group->getId()]), 'title' => _m('Make a new blog post'), 'class' => 'profile-extra-actions'], _m('Post in blog')]]);
}
return Event::next;
}
}

View File

@@ -21,7 +21,7 @@ declare(strict_types = 1);
// }}}
namespace Component\Blog\Controller;
namespace Plugin\Blog\Controller;
use App\Core\ActorLocalRoles;
use App\Core\Controller;
@@ -139,7 +139,7 @@ class Post extends Controller
$extra_args = [];
Event::handle('AddExtraArgsToNoteContent', [$request, $actor, $data, &$extra_args, $form_params, $form]);
[,$note,] = Posting::storeLocalPage(
[,$note,] = Posting::storeLocalArticle(
actor: $actor,
content: $data['content'],
content_type: $content_type,

View File

@@ -22,7 +22,7 @@ declare(strict_types = 1);
namespace Plugin\Bundles;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Modules\Plugin;
use App\Entity\Actor;
use Component\Collection\Util\MetaCollectionTrait;

View File

@@ -23,8 +23,8 @@ declare(strict_types = 1);
namespace Plugin\Bundles\Controller;
use App\Core\DB\DB;
use App\Core\Router\Router;
use App\Core\DB;
use App\Core\Router;
use Component\Collection\Util\Controller\MetaCollectionController;
use Plugin\Bundles\Entity\BundleCollection;

View File

@@ -23,7 +23,7 @@ declare(strict_types = 1);
namespace Plugin\Cover\Controller;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Form;
use App\Core\GSFile;
use function App\Core\I18n\_m;

View File

@@ -22,10 +22,10 @@ declare(strict_types = 1);
namespace Plugin\Cover;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Event;
use App\Core\Modules\Plugin;
use App\Core\Router\RouteLoader;
use App\Core\Router;
use App\Util\Common;
use Plugin\Cover\Controller as C;
use Symfony\Component\HttpFoundation\Request;
@@ -47,7 +47,7 @@ class Cover extends Plugin
*
* @return bool hook value; true means continue processing, false means stop
*/
public function onAddRoute(RouteLoader $r): bool
public function onAddRoute(Router $r): bool
{
$r->connect('settings_profile_cover', 'settings/cover', [Controller\Cover::class, 'coversettings']);
$r->connect('cover', '/cover', [Controller\Cover::class, 'cover']);

View File

@@ -23,7 +23,7 @@ declare(strict_types = 1);
namespace Plugin\Cover\Entity;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Entity;
use App\Util\Common;
use Component\Attachment\Entity\Attachment;

View File

@@ -24,11 +24,11 @@ declare(strict_types = 1);
namespace Plugin\DeleteNote\Controller;
use App\Core\Controller;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Form;
use function App\Core\I18n\_m;
use App\Core\Log;
use App\Core\Router\Router;
use App\Core\Router;
use App\Entity\Note;
use App\Util\Common;
use App\Util\Exception\ClientException;

View File

@@ -23,12 +23,11 @@ namespace Plugin\DeleteNote;
use ActivityPhp\Type\AbstractObject;
use App\Core\Cache;
use App\Core\DB\DB;
use App\Core\DB;
use App\Core\Event;
use function App\Core\I18n\_m;
use App\Core\Modules\NoteHandlerPlugin;
use App\Core\Router\RouteLoader;
use App\Core\Router\Router;
use App\Core\Router;
use App\Entity\Activity;
use App\Entity\Actor;
use App\Entity\Note;
@@ -47,7 +46,7 @@ use Symfony\Component\HttpFoundation\Request;
* @category DeleteNote
*
* @author Eliseu Amaro <mail@eliseuama.ro>
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
* @copyright 2021-2022 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
class DeleteNote extends NoteHandlerPlugin
@@ -136,7 +135,7 @@ class DeleteNote extends NoteHandlerPlugin
*
* @return bool Event hook
*/
public function onAddRoute(RouteLoader $r)
public function onAddRoute(Router $r)
{
$r->connect(id: 'delete_note_action', uri_path: '/object/note/{note_id<\d+>}/delete', target: Controller\DeleteNote::class);

View File

@@ -23,7 +23,7 @@ declare(strict_types = 1);
namespace Plugin\Directory\Controller;
use App\Core\DB\DB;
use App\Core\DB;
use function App\Core\I18n\_m;
use App\Entity\Actor;
use App\Util\Common;
@@ -110,7 +110,7 @@ class Directory extends CircleController
// -------- *** --------
// -------- Start setting up the queries --------
$actor_query_fn = fn (int $actor_type) => DB::findBy(Actor::class, ['type' => $actor_type], order_by: $order_by, limit: $limit, offset: $offset);
$actor_query_fn = fn (int $actor_type) => DB::findBy(Actor::class, ['type' => $actor_type], order_by: $order_by, limit: $limit, offset: $offset);
$minmax_query_fn = $general_query_fn_fn(func: $order_by_op === 'ASC' ? 'MAX' : 'MIN', order: $order_by_op);
$count_query_fn = $general_query_fn_fn(func: 'COUNT', order: $order_by_op);
// -------- *** --------

Some files were not shown because too many files have changed in this diff Show More