71 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
13f22c911c [COMPONENT][Notification] Feed: Fix typo in query 2022-03-23 16:09:13 +00:00
56b8710b26 [PLUGIN][ActivityPub][Notification] Fix some issues with targetting 2022-03-23 13:23:44 +00:00
e63c310d70 [COMPONENT][Notification] Always pre-add Actor subscribers when notifying 2022-03-23 13:23:44 +00:00
03f449035a [PLUGIN][ActivityPub][Model][Activity] Sometimes we don't have a local, move on with encapsulated 2022-03-23 13:23:44 +00:00
8808195a80 [PLUGIN][ActivityPub][Test] Test @language handling 2022-03-23 13:23:44 +00:00
45344c80d1 [PLUGIN][ActivityPub][Model][Note] Fix @language handling 2022-03-23 13:23:43 +00:00
7eddbd343d [PLUGIN][ActivityPub][Test] Add Like{Note} fixture 2022-03-23 13:23:43 +00:00
259d2da05a [CORE][Controller] Add default handler for when using http methods 2022-03-23 13:23:43 +00:00
2f7fdf6ee4 [PLUGIN][ActivityPub][Test] Activity: Create Page
Fixed a couple of bugs
2022-03-19 22:21:35 +00:00
6955872e05 [PLUGIN][ActivityPub][Model][Activity] toJson: When in activity context, use object's context if available 2022-03-19 22:20:32 +00:00
23e88b30a6 [COMPONENT][Blog] This is not used for replies 2022-03-19 22:18:33 +00:00
60713878f0 [TESTS] Load languages prior to remaining fixtures 2022-03-19 22:18:00 +00:00
06c67b31c2 [PLUGIN][ActivityPub][Model][Note] toJson: Respect source attribute and @language from context 2022-03-19 18:01:25 +00:00
a08b661779 [COMPONENT][Group] Cast integer string to int when getting group from context 2022-03-19 18:01:25 +00:00
0649a5154c [PLUGIN][ActivityPub][Test][Model][Note] fromJson 2022-03-19 18:01:24 +00:00
91fecd77ba [TOOLS][DOCKER] Use a more robust way to check for database availability 2022-03-19 17:20:12 +00:00
e22fe55bbe [TOOLS] Add .well-known/acme-challenge/ root certbot to nginx container, to allow certbot certificate renewals 2022-03-19 07:32:01 +00:00
dd62825169 [PLUGIN][ActivityPub][Model][Note] fromJson: Respect source attribute and @language from context 2022-03-15 17:49:09 +00:00
27706d63f4 [PLUGIN][OAuth] Fix login for OAuth 2022-03-14 21:41:22 +00:00
20f690c532 [TESTS] Fix a couple of issues from last changes 2022-03-14 18:37:39 +00:00
888c3798b7 [COMPONENT][Notification] Make logic more generic and robust
Fixed various bugs

Some important concepts to bear in mind:

* Notification: Associated with activities, won't be reconstructed
together with objects, can be thought of as transient

* Attention: Associated with objects, will be reconstructed with them, can
be thought as persistent

* Notifications and Attentions have no direct implications.

* Mentions are a specific form of attentions in notes, leads to the creation of Attentions.

Finally,

Potential PHP issue detected and reported: https://github.com/php/php-src/issues/8199
`static::method()` from a non static context (such as a class method) calls `__call`, rather than
the expected `__callStatic`. Can be fixed by using `(static fn() => static::method())()`, but the
usage of the magic method is strictly unnecessary in this case.
2022-03-14 11:37:09 +00:00
e1cceac150 [CORE][Form][TESTS] Fix FormTest::handle 2022-03-13 18:53:53 +00:00
63ef9292f3 [DEPENDENCIES] Update dependencies 2022-03-13 18:17:32 +00:00
cbae649991 [PLUGIN][ActivityPub][TESTS] Move ActivityPub test fixtures to new facility 2022-03-13 18:11:11 +00:00
1d8bba3949 [TESTS][MODULES] Move Test Fixtures to tests/fixtures folder and add support for loading fixtures from components and plugins 2022-03-13 18:00:21 +00:00
18864ca9fa [CONTROLLER][Security] Override the _next form field in Security->register to redirect to login page 2022-03-13 16:01:51 +00:00
390c532456 [PLUGIN][ActivityPub][Tests] Create Actor Tests 2022-03-13 16:00:35 +00:00
636cb681d6 [PLUGIN][ActivityPub][Tests] Create a TestCase for the plugin 2022-03-13 15:54:14 +00:00
7d84323df4 [PLUGIN][ActivityPub][Tests] Add some fixtures for GNU social's 2022-03-13 15:53:21 +00:00
2d7850ccfb [PLUGIN][ActivityPub][Tests] Borrow test fixtures from Lemmy 2022-03-13 15:52:48 +00:00
d8108dbc32 [COMPONENT][Posting] Fix request handling issues that resulted from splitting creation and controller 2022-03-13 15:52:48 +00:00
cf05d3dbb0 [ENTITY][TESTS] Fix Note->isVisibleTo with and associated test 2022-03-13 15:03:03 +00:00
eb3c848fc8 [TOOLS][TESTS] Ensure database schema is up to date in tests 2022-03-13 14:22:18 +00:00
5c708af272 [CORE][Form] Remove unweildy return of form errors from Form::handle 2022-03-13 14:19:56 +00:00
301 changed files with 3871 additions and 1627 deletions

View File

@@ -3,4 +3,4 @@ KERNEL_CLASS='App\Kernel'
APP_SECRET='$ecretf0rt3st'
SYMFONY_DEPRECATIONS_HELPER=999999
PANTHER_APP_ENV=panther
DATABASE_URL=postgresql://postgres:password@db:5432/social
DATABASE_URL=postgresql://postgres:password@db:5432/test

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']);
@@ -51,12 +50,17 @@ class Group extends Component
* Enqueues a notification for an Actor (such as person or group) which means
* it shows up in their home feed and such.
*/
public function onNewNotificationWithTargets(Actor $sender, Activity $activity, array $targets = [], ?string $reason = null): bool
public function onNewNotificationStart(Actor $sender, Activity $activity, array $targets = [], ?string $reason = null): bool
{
foreach ($targets as $target) {
if ($target->isGroup()) {
// The Group announces to its subscribers
Notification::notify($target, $activity, $target->getSubscribers(), $reason);
Notification::notify(
sender: $target,
activity: $activity,
targets: $target->getSubscribers(),
reason: $reason,
);
}
}
@@ -75,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;
}
@@ -99,7 +102,7 @@ class Group extends Component
case 'group_actor_view_nickname':
return LocalGroup::getActorByNickname($identifier);
case 'group_actor_view_id':
return Actor::getById($identifier);
return Actor::getById((int) $identifier);
}
}
return null;

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;
@@ -116,7 +116,7 @@ class Language extends Entity
return Cache::getHashMapKey(
map_key: 'languages-id',
key: (string) $id,
calculate_map: fn () => F\reindex(DB::dql('select l from language l'), fn (self $l) => (string) $l->getId()),
calculate_map: fn () => F\reindex(DB::dql('SELECT l FROM \Component\Language\Entity\Language AS l'), fn (self $l) => (string) $l->getId()),
);
}
@@ -125,7 +125,7 @@ class Language extends Entity
return Cache::getHashMapKey(
'languages',
$locale,
calculate_map: fn () => F\reindex(DB::dql('select l from language l'), fn (self $l) => $l->getLocale()),
calculate_map: fn () => F\reindex(DB::dql('SELECT l FROM \Component\Language\Entity\Language AS l'), fn (self $l) => $l->getLocale()),
);
}
@@ -138,7 +138,7 @@ class Language extends Entity
{
$langs = Cache::getHashMap(
'languages',
fn () => F\reindex(DB::dql('select l from language l'), fn (self $l) => $l->getLocale()),
fn () => F\reindex(DB::dql('SELECT l FROM \Component\Language\Entity\Language AS l'), fn (self $l) => $l->getLocale()),
);
return array_merge(...F\map(array_values($langs), fn ($l) => $l->toChoiceFormat()));

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;
@@ -53,9 +53,9 @@ class Feed extends Controller
WHERE n.id IN (
SELECT act.object_id FROM \App\Entity\Activity AS act
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)
(SELECT att.activity_id FROM \Component\Notification\Entity\Notification AS att WHERE att.target_id = :target_id)
)
EOF, ['id' => $user->getId()]);
EOF, [':target_id' => $user->getId()]);
return [
'_template' => 'collection/notes.html.twig',
'page_title' => _m('Notifications'),

View File

@@ -24,31 +24,51 @@ namespace Component\Notification\Entity;
use App\Core\Entity;
/**
* Entity for note attentions
* Entity for object attentions
*
* An attention is a form of persistent notification.
* It exists together and for as long as the object it belongs to.
* Creating an attention requires creating a Notification.
*
* @category DB
* @package GNUsocial
*
* @author Zach Copley <zach@status.net>
* @copyright 2010 StatusNet Inc.
* @author Mikael Nordfeldth <mmn@hethane.se>
* @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
* @author Diogo Peralta Cordeiro <@diogo.site>
* @copyright 2022 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 Attention extends Entity
{
// {{{ Autocode
// @codeCoverageIgnoreStart
private int $note_id;
private string $object_type;
private int $object_id;
private int $target_id;
public function setNoteId(int $note_id): self
public function setObjectType(string $object_type): self
{
$this->note_id = $note_id;
$this->object_type = mb_substr($object_type, 0, 32);
return $this;
}
public function getNoteId(): int
public function getObjectType(): string
{
return $this->note_id;
return $this->object_type;
}
public function setObjectId(int $object_id): self
{
$this->object_id = $object_id;
return $this;
}
public function getObjectId(): int
{
return $this->object_id;
}
public function setTargetId(int $target_id): self
@@ -68,15 +88,16 @@ class Attention extends Entity
public static function schemaDef(): array
{
return [
'name' => 'note_attention',
'description' => 'Note attentions to actors (that are not a mention)',
'name' => 'attention',
'description' => 'Attentions to actors (these are not mentions)',
'fields' => [
'note_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Note.id', 'multiplicity' => 'one to one', 'not null' => true, 'description' => 'note_id to give attention'],
'target_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Actor.id', 'multiplicity' => 'one to one', 'not null' => true, 'description' => 'actor_id for feed receiver'],
'object_type' => ['type' => 'varchar', 'length' => 32, 'not null' => true, 'description' => 'the name of the table this object refers to'],
'object_id' => ['type' => 'int', 'not null' => true, 'description' => 'id in the referenced table'],
'target_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Actor.id', 'multiplicity' => 'one to one', 'not null' => true, 'description' => 'actor_id for feed receiver'],
],
'primary key' => ['note_id', 'target_id'],
'primary key' => ['object_type', 'object_id', 'target_id'],
'indexes' => [
'attention_note_id_idx' => ['note_id'],
'attention_object_id_idx' => ['object_id'],
'attention_target_id_idx' => ['target_id'],
],
];

View File

@@ -21,24 +21,24 @@ 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;
use DateTimeInterface;
/**
* Entity for attentions
* Entity for Notifications
*
* A Notification when isolated is a form of transient notification.
* When together with a persistent form of notification such as attentions or mentions,
* it records that the target was notified - which avoids re-notifying upon objects reconstructions.
*
* @category DB
* @package GNUsocial
*
* @author Zach Copley <zach@status.net>
* @copyright 2010 StatusNet Inc.
* @author Mikael Nordfeldth <mmn@hethane.se>
* @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
* @author Hugo Sales <hugo@hsal.es>
* @copyright 2020-2021 Free Software Foundation, Inc http://www.fsf.org
* @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
*/
class Notification extends Entity

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;
@@ -64,14 +63,42 @@ 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.
*
* $targets should be of the shape:
* (int|Actor)[] // Prefer Actor whenever possible
* Example of $targets:
* [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. 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 $ids_already_known = [], ?string $reason = null): bool
public function onNewNotification(Actor $sender, Activity $activity, array $targets, ?string $reason = null): bool
{
$targets = $activity->getNotificationTargets(ids_already_known: $ids_already_known, sender_id: $sender->getId());
if (Event::handle('NewNotificationWithTargets', [$sender, $activity, $targets, $reason]) === Event::next) {
self::notify($sender, $activity, $targets, $reason);
// Ensure targets are all actor objects and unique
$effective_targets = [];
foreach ($targets as $target) {
if (\is_int($target)) {
$target_id = $target;
$target_object = null;
} else {
$target_id = $target->getId();
$target_object = $target;
}
if (!\array_key_exists(key: $target_id, array: $effective_targets)) {
$target_object ??= Actor::getById($target_id);
$effective_targets[$target_id] = $target_object;
}
}
unset($targets);
if (Event::handle('NewNotificationStart', [$sender, $activity, $effective_targets, $reason]) === Event::next) {
self::notify($sender, $activity, $effective_targets, $reason);
}
Event::handle('NewNotificationEnd', [$sender, $activity, $effective_targets, $reason]);
return Event::next;
}
@@ -91,7 +118,8 @@ class Notification extends Component
}
/**
* Bring given Activity to Targets's attention
* Bring given Activity to Targets' knowledge.
* This will flush a Notification to DB.
*
* @return true if successful, false otherwise
*/
@@ -100,8 +128,8 @@ class Notification extends Component
$remote_targets = [];
foreach ($targets as $target) {
if ($target->getIsLocal()) {
if ($target->hasBlocked($activity->getActor())) {
Log::info("Not saving reply to actor {$target->getId()} from sender {$sender->getId()} because of a block.");
if ($target->hasBlocked($author = $activity->getActor())) {
Log::info("Not saving notification to actor {$target->getId()} from sender {$sender->getId()} because receiver blocked author {$author->getId()}.");
continue;
}
if (Event::handle('NewNotificationShould', [$activity, $target]) === Event::next) {
@@ -113,7 +141,7 @@ class Notification extends Component
}
Queue::enqueue(
payload: [$sender, $activity, $target, $reason],
queue: 'notification_local',
queue: 'NotificationLocal',
priority: true,
);
} else {
@@ -124,7 +152,7 @@ class Notification extends Component
}
// XXX: Unideal as in failures the rollback will leave behind a false notification,
// but most notifications (all) require flushing the objects first
// Should be okay as long as implementors bear this in mind
// Should be okay as long as implementations bear this in mind
try {
DB::wrapInTransaction(fn () => DB::persist(Entity\Notification::create([
'activity_id' => $activity->getId(),
@@ -132,7 +160,7 @@ class Notification extends Component
'reason' => $reason,
])));
} catch (Exception|Throwable $e) {
// We do our best not to record duplicated notifications, but it's not insane that can happen
// We do our best not to record duplicate notifications, but it's not insane that can happen
Log::error('It was attempted to record an invalid notification!', [$e]);
}
}
@@ -140,7 +168,7 @@ class Notification extends Component
if ($remote_targets !== []) {
Queue::enqueue(
payload: [$sender, $activity, $remote_targets, $reason],
queue: 'notification_remote',
queue: 'NotificationRemote',
priority: false,
);
}

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,11 +38,12 @@ 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;
use App\Core\Log;
use App\Entity\Actor;
use App\Util\Common;
use App\Util\Exception\AuthenticationException;
use App\Util\Exception\NicknameEmptyException;
@@ -91,8 +92,9 @@ class PersonSettings extends Controller
public function allSettings(Request $request, LanguageController $language): array
{
// Ensure the user is logged in and retrieve Actor object for given user
$user = Common::ensureLoggedIn();
$actor = $user->getActor();
$user = Common::ensureLoggedIn();
// Must be persisted
$actor = DB::findOneBy(Actor::class, ['id' => $user->getId()]);
$personal_form = ActorForms::personalInfo(request: $request, scope: $actor, target: $actor);
$email_form = self::email($request);
@@ -101,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(),
@@ -284,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;
@@ -55,13 +55,13 @@ class PersonSettingsTest extends GNUsocialTestCase
]);
$changed_user = DB::findOneBy(LocalUser::class, ['id' => $user->getId()]);
$actor = $changed_user->getActor();
static::assertSame($changed_user->getNickname(), 'form_test_user_new_nickname');
static::assertSame($actor->getNickname(), 'form_test_user_new_nickname');
static::assertSame($actor->getFullName(), 'Form User');
static::assertSame($actor->getHomepage(), 'https://gnu.org');
static::assertSame($actor->getBio(), 'I was born at a very young age');
static::assertSame($actor->getLocation(), 'right here');
// static::assertSame($changed_user->getPhoneNumber()->getNationalNumber(), '908555842');
static::assertSame('form_test_user_new_nickname', $changed_user->getNickname());
static::assertSame('form_test_user_new_nickname', $actor->getNickname());
static::assertSame('Form User', $actor->getFullName());
static::assertSame('https://gnu.org', $actor->getHomepage());
static::assertSame('I was born at a very young age', $actor->getBio());
static::assertSame('right here', $actor->getLocation());
// static::assertSame('908555842', $changed_user->getPhoneNumber()->getNationalNumber());
}
/**

View File

@@ -52,7 +52,7 @@ class Posting extends Controller
content_type: $data['content_type'],
locale: $data['language'],
scope: VisibilityScope::from($data['visibility']),
targets: isset($target) ? [$target] : [],
attentions: isset($target) ? [$target] : [],
reply_to: \array_key_exists('reply_to_id', $data) ? $data['reply_to_id'] : null,
attachments: $data['attachments'],
process_note_content_extra_args: $extra_args,
@@ -61,9 +61,9 @@ class Posting extends Controller
return Core\Form::forceRedirect($form, $request);
}
} catch (FormSizeFileException $e) {
throw new ClientException(_m('Invalid file size given'), previous: $e);
throw new ClientException(_m('Invalid file size given.'), previous: $e);
}
}
throw new ClientException(_m('Invalid form submission'));
throw new ClientException(_m('Invalid form submission.'));
}
}

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;
@@ -48,7 +47,6 @@ use Component\Attachment\Entity\AttachmentToNote;
use Component\Conversation\Conversation;
use Component\Language\Entity\Language;
use Component\Notification\Entity\Attention;
use Functional as F;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request;
@@ -56,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;
@@ -88,13 +86,13 @@ class Posting extends Component
* @throws DuplicateFoundException
* @throws ServerException
*/
public static function storeLocalPage(
public static function storeLocalArticle(
Actor $actor,
?string $content,
string $content_type,
?string $locale = null,
?VisibilityScope $scope = null,
array $targets = [],
array $attentions = [],
null|int|Note $reply_to = null,
array $attachments = [],
array $processed_attachments = [],
@@ -104,13 +102,13 @@ class Posting extends Component
string $source = 'web',
?string $title = null,
): array {
[$activity, $note, $attention_ids] = self::storeLocalNote(
[$activity, $note, $effective_attentions] = self::storeLocalNote(
actor: $actor,
content: $content,
content_type: $content_type,
locale: $locale,
scope: $scope,
targets: $targets,
attentions: $attentions,
reply_to: $reply_to,
attachments: $attachments,
processed_attachments: $processed_attachments,
@@ -119,16 +117,24 @@ class Posting extends Component
rendered: $rendered,
source: $source,
);
$note->setType('page');
$note->setType('article');
$note->setTitle($title);
if ($flush_and_notify) {
// Flush before notification
DB::flush();
Event::handle('NewNotification', [$actor, $activity, ['object' => $attention_ids], _m('{nickname} created a page {note_id}.', ['{nickname}' => $actor->getNickname(), '{note_id}' => $activity->getObjectId()])]);
Event::handle('NewNotification', [
$actor,
$activity,
$effective_attentions,
_m('Actor {actor_id} created article {note_id}.', [
'{actor_id}' => $actor->getId(),
'{note_id}' => $activity->getObjectId(),
]),
]);
}
return [$activity, $note, $attention_ids];
return [$activity, $note, $effective_attentions];
}
/**
@@ -141,7 +147,7 @@ class Posting extends Component
* @param string $content_type Indicating one of the various supported content format (Plain Text, Markdown, LaTeX...)
* @param null|string $locale Note's written text language, set by the default Actor language or upon filling
* @param null|VisibilityScope $scope The visibility of this Note
* @param array $targets Actor|int[]: In Group/To Person or Bot, registers an attention between note and target
* @param array $attentions Actor|int[]: In Group/To Person or Bot, registers an attention between note and target
* @param null|int|Note $reply_to The soon-to-be Note parent's id, if it's a Reply itself
* @param array $attachments UploadedFile[] to be stored as GSFiles associated to this note
* @param array $processed_attachments Array of [Attachment, Attachment's name][] to be associated to this $actor and Note
@@ -154,7 +160,7 @@ class Posting extends Component
* @throws DuplicateFoundException
* @throws ServerException
*
* @return array [Activity, Note, int[]] Activity, Note, Attention Ids
* @return array [Activity, Note, Effective Attentions]
*/
public static function storeLocalNote(
Actor $actor,
@@ -162,7 +168,7 @@ class Posting extends Component
string $content_type,
?string $locale = null,
?VisibilityScope $scope = null,
array $targets = [],
array $attentions = [],
null|int|Note $reply_to = null,
array $attachments = [],
array $processed_attachments = [],
@@ -209,7 +215,7 @@ class Posting extends Component
if (!\is_null($reply_to_id)) {
Cache::incr(Note::cacheKeys($reply_to_id)['replies-count']);
// Not having them cached doesn't mean replies don't exist, but don't push it to the
// list, as that means they need to be refetched, or some would be missed
// list, as that means they need to be re-fetched, or some would be missed
if (Cache::exists(Note::cacheKeys($reply_to_id)['replies'])) {
Cache::listPushRight(Note::cacheKeys($reply_to_id)['replies'], $note);
}
@@ -221,12 +227,12 @@ class Posting extends Component
Event::handle('ProcessNoteContent', [$note, $content, $content_type, $process_note_content_extra_args]);
}
// These are note attachments now, and not just attachments, ensure these relations are ensured
// These are note attachments now, and not just attachments, ensure these relations are respected
if ($processed_attachments !== []) {
foreach ($processed_attachments as [$a, $fname]) {
// Most attachments should already be associated with its author, but maybe it didn't make sense
//for this attachment, or it's simply a repost of an attachment by a different actor
if (DB::count('actor_to_attachment', $args = ['attachment_id' => $a->getId(), 'actor_id' => $actor->getId()]) === 0) {
if (DB::count(ActorToAttachment::class, $args = ['attachment_id' => $a->getId(), 'actor_id' => $actor->getId()]) === 0) {
DB::persist(ActorToAttachment::create($args));
}
DB::persist(AttachmentToNote::create(['attachment_id' => $a->getId(), 'note_id' => $note->getId(), 'title' => $fname]));
@@ -242,13 +248,38 @@ class Posting extends Component
]);
DB::persist($activity);
$attention_ids = [];
foreach ($targets as $target) {
$target_id = \is_int($target) ? $target : $target->getId();
DB::persist(Attention::create(['note_id' => $note->getId(), 'target_id' => $target_id]));
$attention_ids[$target_id] = true;
$effective_attentions = [];
foreach ($attentions as $target) {
if (\is_int($target)) {
$target_id = $target;
$add = !\array_key_exists($target_id, $effective_attentions);
$effective_attentions[$target_id] = $target;
} else {
$target_id = $target->getId();
if ($add = !\array_key_exists($target_id, $effective_attentions)) {
$effective_attentions[$target_id] = $target_id;
}
}
if ($add) {
DB::persist(Attention::create(['object_type' => Note::schemaName(), 'object_id' => $note->getId(), 'target_id' => $target_id]));
}
}
foreach ($mentions as $m) {
foreach ($m['mentioned'] ?? [] as $mentioned) {
$target_id = $mentioned->getId();
if (!\array_key_exists($target_id, $effective_attentions)) {
DB::persist(Attention::create(['object_type' => Note::schemaName(), 'object_id' => $note->getId(), 'target_id' => $target_id]));
}
$effective_attentions[$target_id] = $mentioned;
}
}
foreach ($actor->getSubscribers() as $subscriber) {
$target_id = $subscriber->getId();
DB::persist(Attention::create(['object_type' => Activity::schemaName(), 'object_id' => $activity->getId(), 'target_id' => $target_id]));
$effective_attentions[$target_id] = $subscriber;
}
$attention_ids = array_keys($attention_ids);
if ($flush_and_notify) {
// Flush before notification
@@ -256,18 +287,15 @@ class Posting extends Component
Event::handle('NewNotification', [
$actor,
$activity,
[
'note-attention' => $attention_ids,
'object' => F\unique(F\flat_map($mentions, fn (array $m) => F\map($m['mentioned'] ?? [], fn (Actor $a) => $a->getId()))),
],
_m('{nickname} created a note {note_id}.', [
'{nickname}' => $actor->getNickname(),
$effective_attentions,
_m('Actor {actor_id} created note {note_id}.', [
'{actor_id}' => $actor->getId(),
'{note_id}' => $activity->getObjectId(),
]),
]);
}
return [$activity, $note, $attention_ids];
return [$activity, $note, $effective_attentions];
}
public function onRenderNoteContent(string $content, string $content_type, ?string &$rendered, Actor $author, ?string $language = null, array &$mentions = [])

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

@@ -114,27 +114,6 @@ class ActorSubscription extends Entity
];
}
/**
* @see Entity->getNotificationTargetIds
*/
public function getNotificationTargetIds(array $ids_already_known = [], ?int $sender_id = null, bool $include_additional = true): array
{
if (!\array_key_exists('object', $ids_already_known)) {
$target_ids = [$this->getSubscribedId()]; // The object of any subscription is the one subscribed (or unsubscribed)
} else {
$target_ids = $ids_already_known['object'];
}
// Additional actors that should know about this
if ($include_additional && \array_key_exists('additional', $ids_already_known)) {
array_push($target_ids, ...$ids_already_known['additional']);
} else {
return $target_ids;
}
return array_unique($target_ids);
}
public static function schemaDef(): array
{
return [

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;
@@ -37,14 +36,14 @@ use App\Util\Common;
use App\Util\Exception\DuplicateFoundException;
use App\Util\Exception\NotFoundException;
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']);
@@ -97,22 +96,24 @@ class Subscription extends Component
$subscription = DB::findOneBy(table: Entity\ActorSubscription::class, criteria: $opts, return_null: true);
$activity = null;
if (\is_null($subscription)) {
DB::persist(Entity\ActorSubscription::create($opts));
$activity = Activity::create([
DB::persist($subscription = Entity\ActorSubscription::create($opts));
$activity = Activity::create([
'actor_id' => $subscriber_id,
'verb' => 'subscribe',
'object_type' => 'actor',
'object_type' => Actor::schemaName(),
'object_id' => $subscribed_id,
'source' => $source,
]);
DB::persist($activity);
DB::persist(Attention::create(['object_type' => Activity::schemaName(), 'object_id' => $activity->getId(), 'target_id' => $subscribed_id]));
Event::handle('NewNotification', [
\is_int($subject) ? $subject : Actor::getById($subscriber_id),
$activity,
['object' => [$activity->getObjectId()]],
_m('{subject} subscribed to {object}.', ['{subject}' => $activity->getActorId(), '{object}' => $activity->getObjectId()]),
[$subscribed_id],
$reason = _m('{subject} subscribed to {object}.', ['{subject}' => $activity->getActorId(), '{object}' => $activity->getObjectId()]),
]);
Event::handle('NewSubscriptionEnd', [$subject, $activity, $object, $reason]);
}
return $activity;
}
@@ -146,21 +147,22 @@ class Subscription extends Component
if (!\is_null($subscription)) {
// Remove Subscription
DB::remove($subscription);
$previous_follow_activity = DB::findBy('activity', ['verb' => 'subscribe', 'object_type' => 'actor', 'object_id' => $subscribed_id], order_by: ['created' => 'DESC'])[0];
$previous_follow_activity = DB::findBy(Activity::class, ['verb' => 'subscribe', 'object_type' => Actor::schemaName(), 'object_id' => $subscribed_id], order_by: ['created' => 'DESC'])[0];
// Store Activity
$activity = Activity::create([
'actor_id' => $subscriber_id,
'verb' => 'undo',
'object_type' => 'activity',
'object_type' => Activity::schemaName(),
'object_id' => $previous_follow_activity->getId(),
'source' => $source,
]);
DB::persist($activity);
DB::persist(Attention::create(['object_type' => Activity::schemaName(), 'object_id' => $activity->getId(), 'target_id' => $subscribed_id]));
Event::handle('NewNotification', [
\is_int($subject) ? $subject : Actor::getById($subscriber_id),
$activity,
['object' => [$previous_follow_activity->getObjectId()]],
[$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;
}

View File

@@ -112,7 +112,8 @@
},
"autoload-dev": {
"psr-4": {
"App\\Tests\\": "tests/"
"App\\Test\\Fixtures\\": "tests/fixtures/",
"App\\Test\\": "tests/"
}
},
"replace": {

231
composer.lock generated
View File

@@ -553,16 +553,16 @@
},
{
"name": "doctrine/dbal",
"version": "3.3.2",
"version": "3.3.4",
"source": {
"type": "git",
"url": "https://github.com/doctrine/dbal.git",
"reference": "35eae239ef515d55ebb24e9d4715cad09a4f58ed"
"reference": "83f779beaea1893c0bece093ab2104c6d15a7f26"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/dbal/zipball/35eae239ef515d55ebb24e9d4715cad09a4f58ed",
"reference": "35eae239ef515d55ebb24e9d4715cad09a4f58ed",
"url": "https://api.github.com/repos/doctrine/dbal/zipball/83f779beaea1893c0bece093ab2104c6d15a7f26",
"reference": "83f779beaea1893c0bece093ab2104c6d15a7f26",
"shasum": ""
},
"require": {
@@ -577,14 +577,14 @@
"require-dev": {
"doctrine/coding-standard": "9.0.0",
"jetbrains/phpstorm-stubs": "2021.1",
"phpstan/phpstan": "1.4.0",
"phpstan/phpstan": "1.4.6",
"phpstan/phpstan-strict-rules": "^1.1",
"phpunit/phpunit": "9.5.11",
"phpunit/phpunit": "9.5.16",
"psalm/plugin-phpunit": "0.16.1",
"squizlabs/php_codesniffer": "3.6.2",
"symfony/cache": "^5.2|^6.0",
"symfony/console": "^2.7|^3.0|^4.0|^5.0|^6.0",
"vimeo/psalm": "4.16.1"
"vimeo/psalm": "4.22.0"
},
"suggest": {
"symfony/console": "For helpful console commands such as SQL execution and import of files."
@@ -644,7 +644,7 @@
],
"support": {
"issues": "https://github.com/doctrine/dbal/issues",
"source": "https://github.com/doctrine/dbal/tree/3.3.2"
"source": "https://github.com/doctrine/dbal/tree/3.3.4"
},
"funding": [
{
@@ -660,7 +660,7 @@
"type": "tidelift"
}
],
"time": "2022-02-05T16:33:45+00:00"
"time": "2022-03-20T18:37:29+00:00"
},
{
"name": "doctrine/deprecations",
@@ -1342,16 +1342,16 @@
},
{
"name": "doctrine/orm",
"version": "2.11.1",
"version": "2.11.2",
"source": {
"type": "git",
"url": "https://github.com/doctrine/orm.git",
"reference": "4b88ce787d3916c8366abf52f6c658a7a27ed3a6"
"reference": "9c351e044478135aec1755e2c0c0493a4b6309db"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/orm/zipball/4b88ce787d3916c8366abf52f6c658a7a27ed3a6",
"reference": "4b88ce787d3916c8366abf52f6c658a7a27ed3a6",
"url": "https://api.github.com/repos/doctrine/orm/zipball/9c351e044478135aec1755e2c0c0493a4b6309db",
"reference": "9c351e044478135aec1755e2c0c0493a4b6309db",
"shasum": ""
},
"require": {
@@ -1367,7 +1367,6 @@
"doctrine/lexer": "^1.0",
"doctrine/persistence": "^2.2",
"ext-ctype": "*",
"ext-pdo": "*",
"php": "^7.1 || ^8.0",
"psr/cache": "^1 || ^2 || ^3",
"symfony/console": "^3.0 || ^4.0 || ^5.0 || ^6.0",
@@ -1381,12 +1380,12 @@
"doctrine/annotations": "^1.13",
"doctrine/coding-standard": "^9.0",
"phpbench/phpbench": "^0.16.10 || ^1.0",
"phpstan/phpstan": "1.4.3",
"phpstan/phpstan": "1.4.6",
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.4",
"squizlabs/php_codesniffer": "3.6.2",
"symfony/cache": "^4.4 || ^5.4 || ^6.0",
"symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0",
"vimeo/psalm": "4.19.0"
"vimeo/psalm": "4.22.0"
},
"suggest": {
"symfony/cache": "Provides cache support for Setup Tool with doctrine/cache 2.0",
@@ -1435,22 +1434,22 @@
],
"support": {
"issues": "https://github.com/doctrine/orm/issues",
"source": "https://github.com/doctrine/orm/tree/2.11.1"
"source": "https://github.com/doctrine/orm/tree/2.11.2"
},
"time": "2022-01-30T21:47:06+00:00"
"time": "2022-03-09T15:23:58+00:00"
},
{
"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": {
@@ -1463,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/",
@@ -1523,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",
@@ -1649,16 +1648,16 @@
},
{
"name": "embed/embed",
"version": "v4.4.2",
"version": "v4.4.3",
"source": {
"type": "git",
"url": "https://github.com/oscarotero/Embed.git",
"reference": "84631fa16f2a669de66218774a20b6c1c5f9ba03"
"reference": "2ac32581a8617c3bbe593e3d7799ca9db6974471"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/oscarotero/Embed/zipball/84631fa16f2a669de66218774a20b6c1c5f9ba03",
"reference": "84631fa16f2a669de66218774a20b6c1c5f9ba03",
"url": "https://api.github.com/repos/oscarotero/Embed/zipball/2ac32581a8617c3bbe593e3d7799ca9db6974471",
"reference": "2ac32581a8617c3bbe593e3d7799ca9db6974471",
"shasum": ""
},
"require": {
@@ -1718,7 +1717,7 @@
"support": {
"email": "oom@oscarotero.com",
"issues": "https://github.com/oscarotero/Embed/issues",
"source": "https://github.com/oscarotero/Embed/tree/v4.4.2"
"source": "https://github.com/oscarotero/Embed/tree/v4.4.3"
},
"funding": [
{
@@ -1734,7 +1733,7 @@
"type": "patreon"
}
],
"time": "2022-02-13T16:03:59+00:00"
"time": "2022-03-13T01:27:51+00:00"
},
{
"name": "erusev/parsedown",
@@ -1917,16 +1916,16 @@
},
{
"name": "giggsey/libphonenumber-for-php",
"version": "8.12.44",
"version": "8.12.45",
"source": {
"type": "git",
"url": "https://github.com/giggsey/libphonenumber-for-php.git",
"reference": "a726990faf05bfffdd826f75d9b41a1580dad4a7"
"reference": "7f494fd3b87c7d83472e8b6126eedc6b72dd3f3e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/giggsey/libphonenumber-for-php/zipball/a726990faf05bfffdd826f75d9b41a1580dad4a7",
"reference": "a726990faf05bfffdd826f75d9b41a1580dad4a7",
"url": "https://api.github.com/repos/giggsey/libphonenumber-for-php/zipball/7f494fd3b87c7d83472e8b6126eedc6b72dd3f3e",
"reference": "7f494fd3b87c7d83472e8b6126eedc6b72dd3f3e",
"shasum": ""
},
"require": {
@@ -1986,7 +1985,7 @@
"issues": "https://github.com/giggsey/libphonenumber-for-php/issues",
"source": "https://github.com/giggsey/libphonenumber-for-php"
},
"time": "2022-02-24T09:38:54+00:00"
"time": "2022-03-10T10:28:34+00:00"
},
{
"name": "giggsey/locale",
@@ -2044,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": {
@@ -2148,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": [
{
@@ -2164,7 +2163,7 @@
"type": "tidelift"
}
],
"time": "2021-12-06T18:43:05+00:00"
"time": "2022-03-20T14:16:28+00:00"
},
{
"name": "guzzlehttp/promises",
@@ -2252,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": {
@@ -2285,7 +2284,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.1-dev"
"dev-master": "2.2-dev"
}
},
"autoload": {
@@ -2347,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": [
{
@@ -2363,7 +2362,7 @@
"type": "tidelift"
}
],
"time": "2021-10-06T17:43:30+00:00"
"time": "2022-03-20T21:55:58+00:00"
},
{
"name": "jcupitt/vips",
@@ -3322,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": {
@@ -3353,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"
},
@@ -3405,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": [
{
@@ -3417,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",
@@ -3850,21 +3849,21 @@
},
{
"name": "php-ds/php-ds",
"version": "v1.4.0",
"version": "v1.4.1",
"source": {
"type": "git",
"url": "https://github.com/php-ds/polyfill.git",
"reference": "298cafa4e0e20aeba4d63644e3de694e7cf83ffd"
"reference": "43d2df301a9e2017f67b8c11d94a5222f9c00fd1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-ds/polyfill/zipball/298cafa4e0e20aeba4d63644e3de694e7cf83ffd",
"reference": "298cafa4e0e20aeba4d63644e3de694e7cf83ffd",
"url": "https://api.github.com/repos/php-ds/polyfill/zipball/43d2df301a9e2017f67b8c11d94a5222f9c00fd1",
"reference": "43d2df301a9e2017f67b8c11d94a5222f9c00fd1",
"shasum": ""
},
"require": {
"ext-json": "*",
"php": "^7.3 || ~8.0.0 || ~8.1.0"
"php": ">=7.0"
},
"provide": {
"ext-ds": "1.3.0"
@@ -3899,9 +3898,9 @@
],
"support": {
"issues": "https://github.com/php-ds/polyfill/issues",
"source": "https://github.com/php-ds/polyfill/tree/v1.4.0"
"source": "https://github.com/php-ds/polyfill/tree/v1.4.1"
},
"time": "2021-11-17T19:52:15+00:00"
"time": "2022-03-09T20:39:30+00:00"
},
{
"name": "php-ffmpeg/php-ffmpeg",
@@ -5021,16 +5020,16 @@
},
{
"name": "spatie/temporary-directory",
"version": "2.0.0",
"version": "2.1.0",
"source": {
"type": "git",
"url": "https://github.com/spatie/temporary-directory.git",
"reference": "06fe0f10d068fdf145c9b2235030e568c913bb61"
"reference": "79f138f2b81adae583d04d3727a4538dd394023f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/spatie/temporary-directory/zipball/06fe0f10d068fdf145c9b2235030e568c913bb61",
"reference": "06fe0f10d068fdf145c9b2235030e568c913bb61",
"url": "https://api.github.com/repos/spatie/temporary-directory/zipball/79f138f2b81adae583d04d3727a4538dd394023f",
"reference": "79f138f2b81adae583d04d3727a4538dd394023f",
"shasum": ""
},
"require": {
@@ -5066,7 +5065,7 @@
],
"support": {
"issues": "https://github.com/spatie/temporary-directory/issues",
"source": "https://github.com/spatie/temporary-directory/tree/2.0.0"
"source": "https://github.com/spatie/temporary-directory/tree/2.1.0"
},
"funding": [
{
@@ -5078,7 +5077,7 @@
"type": "github"
}
],
"time": "2021-03-30T19:46:13+00:00"
"time": "2022-03-11T08:16:01+00:00"
},
{
"name": "symfony/asset",
@@ -10716,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": {
@@ -10776,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": [
{
@@ -10788,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",
@@ -11243,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": {
@@ -11304,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": [
{
@@ -11320,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",
@@ -11555,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": {
@@ -11632,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": [
{
@@ -11640,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",
@@ -11999,12 +11998,12 @@
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "996a6c62b7e832903b4cf60e5c5744a4521ff2cc"
"reference": "a9503c3474dfca53db977ebd14987264e8e8d6bf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/996a6c62b7e832903b4cf60e5c5744a4521ff2cc",
"reference": "996a6c62b7e832903b4cf60e5c5744a4521ff2cc",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/a9503c3474dfca53db977ebd14987264e8e8d6bf",
"reference": "a9503c3474dfca53db977ebd14987264e8e8d6bf",
"shasum": ""
},
"require": {
@@ -12013,17 +12012,11 @@
"conflict": {
"phpstan/phpstan-shim": "*"
},
"default-branch": true,
"bin": [
"phpstan",
"phpstan.phar"
],
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.4-dev"
}
},
"autoload": {
"files": [
"bootstrap.php"
@@ -12056,7 +12049,7 @@
"type": "tidelift"
}
],
"time": "2022-03-08T14:11:13+00:00"
"time": "2022-03-12T12:05:50+00:00"
},
{
"name": "phpunit/php-code-coverage",
@@ -12378,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": {
@@ -12417,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": {
@@ -12465,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": [
{
@@ -12477,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",
@@ -13336,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": {
@@ -13380,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": [
{
@@ -13388,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

@@ -20,15 +20,13 @@ security:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
oauth:
pattern: ^/oauth
security: false
main:
lazy: true
provider: local_user
form_login:
login_path: security_login
check_path: security_login
default_target_path: root
logout:
path: security_logout
# where to redirect after logout

View File

@@ -15,13 +15,16 @@ services:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php,Routes}'
App\Test\Fixtures\:
resource: '../tests/fixtures/*'
# controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller class
App\Controller\:
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

@@ -1,3 +1,18 @@
server {
# Listen only on port 81 for localhost, and nothing else.
server_name 127.0.0.1;
listen 127.0.0.1:81 default_server;
charset utf-8;
# Certbot's folder used for the ACME challenge response.
location ^~ /.well-known/acme-challenge {
default_type text/plain;
root /var/www/certbot;
try_files $uri =404;
}
}
server {
listen [::]:80;
@@ -5,6 +20,10 @@ server {
server_name %hostname%;
location '/.well-known/acme-challenge' {
proxy_pass http://localhost:81;
}
# redirect all traffic to HTTPS
rewrite ^ https://$host$request_uri? permanent;
}
@@ -35,6 +54,13 @@ server {
root /var/www/social;
}
location /.well-known/acme-challenge/ {
allow all;
root /var/www/certbot;
try_files $uri =404;
break;
}
# PHP
location ~ ^/(index|install)\.php(/.*)?$ {
include fastcgi_params;

View File

@@ -2,8 +2,7 @@
case "${DBMS}" in
'postgres')
PGPASSWORD="${POSTGRES_PASSWORD}" psql -ltq -Upostgres -hdb | \
cut -d '|' -f1 | grep -Fwq "${SOCIAL_DB}"
test "$(PGPASSWORD="${POSTGRES_PASSWORD}" psql -Upostgres -hdb -tAc "select 1 from pg_database where datname='${SOCIAL_DB}'")" = "1"
DB_EXISTS=$?
;;
'mariadb')
@@ -28,7 +27,8 @@ if [ ${DB_EXISTS} -ne 0 ]; then
chmod g+w -R .
chown -R :www-data .
php bin/console doctrine:database:create || exit 1
php bin/console doctrine:database:drop -f
php bin/console doctrine:database:create
php bin/console doctrine:schema:create || exit 1
php bin/console app:populate_initial_values || exit 1

View File

@@ -3,8 +3,10 @@
cd /var/www/social || exit 1
printf "Cleaning Redis cache: " && echo "FLUSHALL" | nc redis 6379
yes yes | php bin/console doctrine:fixtures:load || exit 1
php bin/console app:populate_initial_values # since loading fixtures purges the DB
bin/console doctrine:database:drop --force || exit 1
bin/console doctrine:database:create || exit 1
bin/console doctrine:schema:update --force || exit 1
yes yes | bin/console doctrine:fixtures:load || exit 1
if [ "$#" -eq 0 ] || [ -z "$*" ]; then
vendor/bin/simple-phpunit -vvv --coverage-html .test_coverage_report

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';
}
@@ -141,14 +140,12 @@ class ActivityPub extends Plugin
$ap_actor->getActorId(),
Discovery::normalize($actor->getNickname() . '@' . parse_url($ap_actor->getInboxUri(), \PHP_URL_HOST)),
);
$already_known_ids = [];
if (!empty($ap_act->_object_mention_ids)) {
$already_known_ids = $ap_act->_object_mention_ids;
}
DB::flush();
if (Event::handle('ActivityPubNewNotification', [$actor, $ap_act->getActivity(), $already_known_ids, _m('{nickname} attentioned you.', ['{nickname}' => $actor->getNickname()])]) === Event::next) {
Event::handle('NewNotification', [$actor, $ap_act->getActivity(), $already_known_ids, _m('{nickname} attentioned you.', ['{nickname}' => $actor->getNickname()])]);
if (($att_targets = $ap_act->getAttentionTargets()) !== []) {
if (Event::handle('ActivityPubNewNotification', [$actor, ($act = $ap_act->getActivity()), $att_targets, _m('{actor_id} triggered a notification via ActivityPub.', ['{actor_id}' => $actor->getId()])]) === Event::next) {
Event::handle('NewNotification', [$actor, $act, $att_targets, _m('{actor_id} triggered a notification via ActivityPub.', ['{nickname}' => $actor->getId()])]);
}
}
return Event::stop;
@@ -158,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,15 +321,24 @@ class ActivityPub extends Plugin
string $inbox,
array $to_actors,
array &$retry_args,
): bool
{
): 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();
@@ -380,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;
@@ -391,7 +398,7 @@ class ActivityPub extends Plugin
foreach ($to_addr as $inbox => $to_actors) {
Queue::enqueue(
payload: [$sender, $activity, $inbox, $to_actors],
queue: 'activitypub_postman',
queue: 'ActivitypubPostman',
priority: false,
);
}
@@ -471,7 +478,7 @@ class ActivityPub extends Plugin
try {
if (FreeNetworkActorProtocol::canIAddr('activitypub', $addr = Discovery::normalize($target))) {
$ap_actor = DB::wrapInTransaction(fn () => ActivitypubActor::getByAddr($addr));
$actor = Actor::getById($ap_actor->getActorId());
$actor = Actor::getById($ap_actor->getActorId());
FreeNetworkActorProtocol::protocolSucceeded('activitypub', $actor->getId(), $addr);
return Event::stop;
} else {
@@ -530,7 +537,7 @@ class ActivityPub extends Plugin
*
* @return null|Actor|mixed|Note got from URI
*/
public static function getObjectByUri(string $resource, bool $try_online = true)
public static function getObjectByUri(string $resource, bool $try_online = true): mixed
{
// Try known object
$known_object = DB::findOneBy(ActivitypubObject::class, ['object_uri' => $resource], return_null: true);
@@ -544,18 +551,6 @@ class ActivityPub extends Plugin
return $known_activity->getActivity();
}
// Try local Note
if (Common::isValidHttpUrl($resource)) {
$resource_parts = parse_url($resource);
// TODO: Use URLMatcher
if ($resource_parts['host'] === Common::config('site', 'server')) {
$local_note = DB::findOneBy('note', ['url' => $resource], return_null: true);
if (!\is_null($local_note)) {
return $local_note;
}
}
}
// Try Actor
try {
return Explorer::getOneFromUri($resource, try_online: false);
@@ -563,20 +558,50 @@ class ActivityPub extends Plugin
// Ignore, this is brute forcing, it's okay not to find
}
// Try remote
if (!$try_online) {
return;
// Is it a HTTP URL?
if (Common::isValidHttpUrl($resource)) {
$resource_parts = parse_url($resource);
// If it is local
if ($resource_parts['host'] === Common::config('site', 'server')) {
// Try Local Note
$local_note = DB::findOneBy(Note::class, ['url' => $resource], return_null: true);
if (!\is_null($local_note)) {
return $local_note;
}
// Try local Activity
try {
$match = Router::match($resource_parts['path']);
$local_activity = DB::findOneBy(Activity::class, ['id' => $match['id']], return_null: true);
if (!\is_null($local_activity)) {
return $local_activity;
} else {
throw new InvalidArgumentException('Tried to retrieve a non-existent local activity.');
}
} catch (\Exception) {
// Ignore, this is brute forcing, it's okay not to find
}
throw new BugFoundException('ActivityPub failed to retrieve local resource: "' . $resource . '". This is a big issue.');
} else {
// Then it's remote
if (!$try_online) {
throw new Exception("Remote resource {$resource} not found without online resources.");
}
$response = HTTPClient::get($resource, ['headers' => self::HTTP_CLIENT_HEADERS]);
// If it was deleted
if ($response->getStatusCode() == 410) {
//$obj = Type::create('Tombstone', ['id' => $resource]);
return null;
} elseif (!HTTPClient::statusCodeIsOkay($response)) { // If it is unavailable
throw new Exception('Non Ok Status Code for given Object id.');
} else {
return Model::jsonToType($response->getContent());
}
}
}
$response = HTTPClient::get($resource, ['headers' => self::HTTP_CLIENT_HEADERS]);
// If it was deleted
if ($response->getStatusCode() == 410) {
//$obj = Type::create('Tombstone', ['id' => $resource]);
return;
} elseif (!HTTPClient::statusCodeIsOkay($response)) { // If it is unavailable
throw new Exception('Non Ok Status Code for given Object id.');
} else {
return Model::jsonToType($response->getContent());
}
return null;
}
}

View File

@@ -33,12 +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\Entity\Actor;
use App\Core\Queue;
use App\Core\Router;
use App\Util\Common;
use App\Util\Exception\ClientException;
use Exception;
@@ -99,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.');
@@ -164,7 +163,7 @@ class Inbox extends Controller
Queue::enqueue(
payload: [$ap_actor, $actor, $type],
queue: 'activitypub_inbox',
queue: 'ActivitypubInbox',
priority: false,
);

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;
@@ -101,27 +101,17 @@ class ActivitypubActivity extends Entity
public function getActivity(): Activity
{
return DB::findOneBy('activity', ['id' => $this->getActivityId()]);
return DB::findOneBy(Activity::class, ['id' => $this->getActivityId()]);
}
public array $_object_mention_ids = [];
public function setObjectMentionIds(array $mentions): self
public function getAttentionTargetIds(): array
{
$this->_object_mention_ids = $mentions;
return $this;
return $this->getActivity()->getAttentionTargetIds();
}
/**
* @see Entity->getNotificationTargetIds
*/
public function getNotificationTargetIds(array $ids_already_known = [], ?int $sender_id = null, bool $include_additional = true): array
public function getAttentionTargets(): array
{
// Additional actors that should know about this
if (\array_key_exists('additional', $ids_already_known)) {
return $ids_already_known['additional'];
} else {
return $this->_object_mention_ids;
}
return $this->getActivity()->getAttentionTargets();
}
public static function schemaDef(): array

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;
@@ -115,6 +115,16 @@ class ActivitypubObject extends Entity
return DB::findOneBy($this->getObjectType(), ['id' => $this->getObjectId()]);
}
public function getAttentionTargetIds(): array
{
return $this->getObject()->getAttentionTargetIds();
}
public function getAttentionTargets(): array
{
return $this->getObject()->getAttentionTargets();
}
public static function schemaDef(): array
{
return [

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

@@ -0,0 +1,70 @@
<?php
declare(strict_types = 1);
namespace Plugin\ActivityPub\Test\Fixtures;
use App\Core\DB;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Persistence\ObjectManager;
use Plugin\ActivityPub\Util\Model\Activity;
use Plugin\ActivityPub\Util\Model\Actor;
use Plugin\ActivityPub\Util\Model\Note;
class ActivityPubFixtures extends Fixture
{
private static string $fixtures_path = __DIR__ . \DIRECTORY_SEPARATOR;
public static function fixturesPath(string $path, string $ontology = 'gnusocial'): string
{
return self::$fixtures_path . $ontology . \DIRECTORY_SEPARATOR . $path;
}
public function load(ObjectManager $manager)
{
/*
* Beware that it's important to Load Actors, Objects, Activities in this sequence
* because we're running offline tests here.
*/
$ontology = 'gnusocial';
// Load Actors
$person_path = self::fixturesPath('objects/person.jsonld', $ontology);
$person = Actor::fromJson(fread(fopen($person_path, 'r'), filesize($person_path)));
DB::flush();
$another_person_path = self::fixturesPath('objects/another_person.jsonld', $ontology);
$another_person = Actor::fromJson(fread(fopen($another_person_path, 'r'), filesize($another_person_path)));
DB::flush();
$group_path = self::fixturesPath('objects/group.jsonld', $ontology);
$group = Actor::fromJson(fread(fopen($group_path, 'r'), filesize($group_path)));
DB::flush();
// Load Objects
$note_path = self::fixturesPath('objects/note.jsonld', $ontology);
$note = Note::fromJson(fread(fopen($note_path, 'r'), filesize($note_path)));
DB::flush();
$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)));
DB::flush();
$note_with_mention_path = self::fixturesPath('objects/note_with_mention.jsonld', $ontology);
$note_with_mention = Note::fromJson(fread(fopen($note_with_mention_path, 'r'), filesize($note_with_mention_path)));
DB::flush();
// Load Activities
$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_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)));
DB::flush();
$like_note_path = self::fixturesPath('activities/like_note.jsonld', $ontology);
$like_note = Activity::fromJson(fread(fopen($like_note_path, 'r'), filesize($like_note_path)));
DB::flush();
}
}

View File

@@ -0,0 +1,53 @@
{
"type": "Create",
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"gs": "https://www.gnu.org/software/social/ns#"
},
{
"litepub": "http://litepub.social/ns#"
},
{
"chatMessage": "litepub:chatMessage"
},
{
"inConversation": {
"@id": "gs:inConversation",
"@type": "@id"
}
}
],
"id": "https://instance.gnusocial.test/activity/1338",
"published": "2022-03-17T23:30:26+00:00",
"actor": "https://instance.gnusocial.test/actor/42",
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"cc": [
"https://instance.gnusocial.test/actor/21"
],
"object": {
"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 article.</p>",
"mediaType": "text/html",
"source": {
"content": "This is an interesting article.",
"mediaType": "text/markdown"
},
"attachment": [],
"tag": [],
"inConversation": "https://instance.gnusocial.test/conversation/1338",
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"cc": [
"https://instance.gnusocial.test/actor/21"
]
}
}

View File

@@ -17,16 +17,19 @@
"@id": "gs:inConversation",
"@type": "@id"
}
},
{
"@language": "en"
}
],
"id": "https://testv3.gnusocial.rocks/activity/1343",
"id": "https://instance.gnusocial.test/activity/1337",
"published": "2022-03-01T20:58:48+00:00",
"actor": "https://testv3.gnusocial.rocks/actor/42",
"actor": "https://instance.gnusocial.test/actor/42",
"object": {
"type": "Note",
"id": "https://testv3.gnusocial.rocks/object/note/1337",
"id": "https://instance.gnusocial.test/object/note/1337",
"published": "2022-03-10T23:07:50+00:00",
"attributedTo": "https://testv3.gnusocial.rocks/actor/42",
"attributedTo": "https://instance.gnusocial.test/actor/42",
"content": "<p>hello, world.</p>",
"mediaType": "text/html",
"source": {
@@ -35,18 +38,18 @@
},
"attachment": [],
"tag": [],
"inConversation": "https://testv3.gnusocial.rocks/conversation/1337",
"inConversation": "https://instance.gnusocial.test/conversation/1337",
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"cc": [
"https://testv3.gnusocial.rocks/actor/42/subscribers"
"https://instance.gnusocial.test/actor/42/subscribers"
]
},
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"cc": [
"https://testv3.gnusocial.rocks/actor/42/subscribers"
"https://instance.gnusocial.test/actor/42/subscribers"
]
}

View File

@@ -0,0 +1,53 @@
{
"type": "Create",
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"gs": "https://www.gnu.org/software/social/ns#"
},
{
"litepub": "http://litepub.social/ns#"
},
{
"chatMessage": "litepub:chatMessage"
},
{
"inConversation": {
"@id": "gs:inConversation",
"@type": "@id"
}
}
],
"id": "https://instance.gnusocial.test/activity/1339",
"published": "2022-03-01T20:58:48+00:00",
"actor": "https://instance.gnusocial.test/actor/42",
"object": {
"type": "Note",
"id": "https://instance.gnusocial.test/object/note/1339",
"published": "2022-03-01T21:00:16+00:00",
"attributedTo": "https://instance.gnusocial.test/actor/42",
"content": "<p>yay ^^</p>",
"mediaType": "text/html",
"source": {
"content": "yay ^^",
"mediaType": "text/plain"
},
"attachment": [],
"tag": [],
"inReplyTo": "https://instance.gnusocial.test/object/note/1338",
"inConversation": "https://instance.gnusocial.test/conversation/1338",
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"cc": [
"https://instance.gnusocial.test/actor/42/subscribers"
]
},
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"cc": [
"https://instance.gnusocial.test/actor/42/subscribers"
]
}

View File

@@ -0,0 +1,16 @@
{
"type": "Like",
"@context": [
"https://www.w3.org/ns/activitystreams"
],
"id": "https://another_instance.gnusocial.test/activity/41362",
"published": "2022-03-20T17:54:15+00:00",
"actor": "https://another_instance.gnusocial.test/actor/43",
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"cc": [
"https://instance.gnusocial.test/actor/42"
],
"object": "https://instance.gnusocial.test/object/note/1337"
}

View File

@@ -0,0 +1,42 @@
{
"type": "Person",
"streams": [],
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"gs": "https://www.gnu.org/software/social/ns#"
},
{
"litepub": "http://litepub.social/ns#"
},
{
"chatMessage": "litepub:chatMessage"
},
{
"inConversation": {
"@id": "gs:inConversation",
"@type": "@id"
}
}
],
"id": "https://another_instance.gnusocial.test/actor/43",
"inbox": "https://another_instance.gnusocial.test/actor/43/inbox.json",
"outbox": "https://another_instance.gnusocial.test/actor/43/outbox.json",
"following": "https://another_instance.gnusocial.test/actor/43/subscriptions",
"followers": "https://another_instance.gnusocial.test/actor/43/subscribers",
"liked": "https://another_instance.gnusocial.test/actor/43/favourites",
"preferredUsername": "alice",
"publicKey": {
"id": "https://another_instance.gnusocial.test/actor/43#public-key",
"owner": "https://another_instance.gnusocial.test/actor/43",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArRHP8lxqj1HqFL4q7CKh\noDyBvuhoaoWo/AGdjiWu5AEODL6utaQX+bCJApH9+XNICCkWvayKupgOvLPqBxkh\nl4TPUjlb1iCt+iZeMB8ftude4epaUNUDdqK1zG3g8z8AdF3nx9/cHI+8UY7+JAh6\naZ5EBi+wNYtl4UoDfizmLeRmmGIam5UQ6x2RseYCevIm1BBCZZHLdIaoPJphyjLW\n8sRJtHL4D3m28NkGG8GizctXHbMl7+RlVJ8HyQSr5taRMF+CmZ9ZDFqF2ewc9Pmw\nOMG4o/6m50Q2ELz23O8idjGxKgG7iGdEa3c5cQZTCQ0+2N77L+iS029AV9AKyZMi\nCwIDAQAB\n-----END PUBLIC KEY-----\n"
},
"name": "Alice P. Hacker",
"published": "2022-02-23T17:20:30+00:00",
"updated": "2022-02-25T02:12:48+00:00",
"url": "https://another_instance.gnusocial.test/@alice",
"endpoints": {
"sharedInbox": "https://another_instance.gnusocial.test/inbox.json"
}
}

View File

@@ -0,0 +1,41 @@
{
"type": "Article",
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"gs": "https://www.gnu.org/software/social/ns#"
},
{
"litepub": "http://litepub.social/ns#"
},
{
"chatMessage": "litepub:chatMessage"
},
{
"inConversation": {
"@id": "gs:inConversation",
"@type": "@id"
}
}
],
"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 article.</p>",
"mediaType": "text/html",
"source": {
"content": "This is an interesting article.",
"mediaType": "text/markdown"
},
"attachment": [],
"tag": [],
"inConversation": "https://instance.gnusocial.test/conversation/1338",
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"cc": [
"https://instance.gnusocial.test/actor/21"
]
}

View File

@@ -20,23 +20,23 @@
}
}
],
"id": "https://testv3.gnusocial.rocks/actor/2",
"inbox": "https://testv3.gnusocial.rocks/actor/2/inbox.json",
"outbox": "https://testv3.gnusocial.rocks/actor/2/outbox.json",
"following": "https://testv3.gnusocial.rocks/actor/2/subscriptions",
"followers": "https://testv3.gnusocial.rocks/actor/2/subscribers",
"liked": "https://testv3.gnusocial.rocks/actor/2/favourites",
"id": "https://instance.gnusocial.test/actor/21",
"inbox": "https://instance.gnusocial.test/actor/21/inbox.json",
"outbox": "https://instance.gnusocial.test/actor/21/outbox.json",
"following": "https://instance.gnusocial.test/actor/21/subscriptions",
"followers": "https://instance.gnusocial.test/actor/21/subscribers",
"liked": "https://instance.gnusocial.test/actor/21/favourites",
"preferredUsername": "hackers",
"publicKey": {
"id": "https://testv3.gnusocial.rocks/actor/2#public-key",
"owner": "https://testv3.gnusocial.rocks/actor/2",
"id": "https://instance.gnusocial.test/actor/2#public-key",
"owner": "https://instance.gnusocial.test/actor/2",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoZyKL+GyJbTV/ilVBlzz\n8OL/UwNi3KpfV5kQwXU0pPcBbw6y2JOfWnKUT1CfiHG3ntiOFnc+wQfHZk4hRSE8\n9Xe/G5Y215xW+gqx/kjt2GOENqzSzYXdEZ5Qsx6yumZD/yb6VZK9Og0HjX2mpRs9\nbactY76w4BQVntjZ17gSkMhYcyPFZTAIe7QDkeSPk5lkXfTwtaB3YcJSbQ3+s7La\npeEgukQDkrLUIP6cxayKrgUl4fhHdpx1Yk4Bzd/1XkZCjeBca94lP1p2M12amI+Z\nOLSTuLyEiCcku8aN+Ms9plwATmIDaGvKFVk0YVtBHdIJlYXV0yIscab3bqyhsLBK\njwIDAQAB\n-----END PUBLIC KEY-----\n"
},
"name": "Hackers!",
"published": "2022-02-23T21:54:52+00:00",
"updated": "2022-02-23T21:55:16+00:00",
"url": "https://testv3.gnusocial.rocks/!hackers",
"url": "https://instance.gnusocial.test/!hackers",
"endpoints": {
"sharedInbox": "https://testv3.gnusocial.rocks/inbox.json"
"sharedInbox": "https://instance.gnusocial.test/inbox.json"
}
}

View File

@@ -19,24 +19,12 @@
}
},
{
"toot": "http://joinmastodon.org/ns#"
},
{
"featured": {
"@id": "toot:featured",
"@type": "@id"
}
},
{
"webmonetizationWallet": {
"@id": "gs:webmonetizationWallet",
"@type": "@id"
}
"@language": "en"
}
],
"id": "https://testv3.gnusocial.rocks/object/note/1337",
"id": "https://instance.gnusocial.test/object/note/1337",
"published": "2022-03-10T23:07:50+00:00",
"attributedTo": "https://testv3.gnusocial.rocks/actor/42",
"attributedTo": "https://instance.gnusocial.test/actor/42",
"content": "<p>hello, world.</p>",
"mediaType": "text/html",
"source": {
@@ -45,11 +33,11 @@
},
"attachment": [],
"tag": [],
"inConversation": "https://testv3.gnusocial.rocks/conversation/1337",
"inConversation": "https://instance.gnusocial.test/conversation/1337",
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"cc": [
"https://testv3.gnusocial.rocks/actor/42/subscribers"
"https://instance.gnusocial.test/actor/42/subscribers"
]
}

View File

@@ -0,0 +1,50 @@
{
"type": "Note",
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{
"gs": "https://www.gnu.org/software/social/ns#"
},
{
"litepub": "http://litepub.social/ns#"
},
{
"chatMessage": "litepub:chatMessage"
},
{
"inConversation": {
"@id": "gs:inConversation",
"@type": "@id"
}
},
{
"@language": "en"
}
],
"id": "https://instance.gnusocial.test/object/note/1340",
"published": "2022-03-16T21:54:43+00:00",
"attributedTo": "https://instance.gnusocial.test/actor/42",
"content": "<p>This is a public root note with a mention to @<span class=\"h-card\"><a href=\"https://another_instance.gnusocial.test/actor/43\" class=\"h-card u-url p-nickname mention\">alice@another_instance.gnusocial.test</a></span>.</p>",
"mediaType": "text/html",
"source": {
"content": "This is a public root note with a mention to @alice@another_instance.gnusocial.test.",
"mediaType": "text/plain"
},
"attachment": [],
"tag": [
{
"type": "Mention",
"href": "https://another_instance.gnusocial.test/actor/43",
"name": "@alice@another_instance.gnusocial.test"
}
],
"inConversation": "https://instance.gnusocial.test/conversation/1340",
"to": [
"https://www.w3.org/ns/activitystreams#Public",
"https://testv3.gnusocial.rocks/actor/43"
],
"cc": [
"https://testv3.gnusocial.rocks/actor/43"
]
}

View File

@@ -20,23 +20,23 @@
}
}
],
"id": "https://testv3.gnusocial.rocks/actor/42",
"inbox": "https://testv3.gnusocial.rocks/actor/42/inbox.json",
"outbox": "https://testv3.gnusocial.rocks/actor/42/outbox.json",
"following": "https://testv3.gnusocial.rocks/actor/42/subscriptions",
"followers": "https://testv3.gnusocial.rocks/actor/42/subscribers",
"liked": "https://testv3.gnusocial.rocks/actor/42/favourites",
"id": "https://instance.gnusocial.test/actor/42",
"inbox": "https://instance.gnusocial.test/actor/42/inbox.json",
"outbox": "https://instance.gnusocial.test/actor/42/outbox.json",
"following": "https://instance.gnusocial.test/actor/42/subscriptions",
"followers": "https://instance.gnusocial.test/actor/42/subscribers",
"liked": "https://instance.gnusocial.test/actor/42/favourites",
"preferredUsername": "diogo",
"publicKey": {
"id": "https://testv3.gnusocial.rocks/actor/42#public-key",
"owner": "https://testv3.gnusocial.rocks/actor/42",
"id": "https://instance.gnusocial.test/actor/42#public-key",
"owner": "https://instance.gnusocial.test/actor/42",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArBB+3ldwA2qC1hQTtIho\n9KYhvvMlPdydn8dA6OlyIQ3Jy57ADt2e144jDSY5RQ3esmzWm2QqsI8rAsZsAraO\nl2+855y7Fw35WH4GBc7PJ6MLAEvMk1YWeS/rttXaDzh2i4n/AXkMuxDjS1IBqw2w\nn0qTz2sdGcBJ+mop6AB9Qt2lseBc5IW040jSnfLEDDIaYgoc5m2yRsjGKItOh3BG\njGHDb6JB9FySToSMGIt0/tE5k06wfvAxtkxX5dfGeKtciBpC2MGT169iyMIOM8DN\nFhSl8mowtV1NJQ7nN692USrmNvSJjqe9ugPCDPPvwQ5A6A61Qrgpz5pav/o5Sz69\nzQIDAQAB\n-----END PUBLIC KEY-----\n"
},
"name": "Diogo Cordeiro",
"name": "Diogo Peralta Cordeiro",
"published": "2022-02-23T17:20:30+00:00",
"updated": "2022-02-25T02:12:48+00:00",
"url": "https://testv3.gnusocial.rocks/@diogo",
"url": "https://instance.gnusocial.test/@diogo",
"endpoints": {
"sharedInbox": "https://testv3.gnusocial.rocks/inbox.json"
"sharedInbox": "https://instance.gnusocial.test/inbox.json"
}
}

View File

@@ -17,11 +17,14 @@
"@id": "gs:inConversation",
"@type": "@id"
}
},
{
"@language": "en"
}
],
"id": "https://testv3.gnusocial.rocks/object/note/1338",
"id": "https://instance.gnusocial.test/object/note/1339",
"published": "2022-03-01T21:00:16+00:00",
"attributedTo": "https://testv3.gnusocial.rocks/actor/42",
"attributedTo": "https://instance.gnusocial.test/actor/42",
"content": "<p>yay ^^</p>",
"mediaType": "text/html",
"source": {
@@ -30,12 +33,12 @@
},
"attachment": [],
"tag": [],
"inReplyTo": "https://testv3.gnusocial.rocks/object/note/31337",
"inConversation": "https://testv3.gnusocial.rocks/conversation/21",
"inReplyTo": "https://instance.gnusocial.test/object/note/1338",
"inConversation": "https://instance.gnusocial.test/conversation/1338",
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
"cc": [
"https://testv3.gnusocial.rocks/actor/42/subscribers"
"https://instance.gnusocial.test/actor/42/subscribers"
]
}

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