[PLUGIN][ActivityPub] Finish base ActivityStreams 2.0 interface

Instructions below

To extend an Activity properties do:

public function onActivityPubValidateActivityStreamsTwoData(string $type_name, array &$validators): bool {
    if ($type_name === '{Type}') {
        $validators['attribute'] = myValidator::class;
    }
    return Event::next;
}

The Validator should be of the form:

use ActivityPhp\Type;
use ActivityPhp\Type\Util;
use Plugin\ActivityPub\Util\ModelValidator;

class myValidator extends ModelValidator
{
    /**
     * Validate Attribute's value
     *
     * @param mixed $value from JSON's attribute
     * @param mixed $container A {Type}
     * @return bool
     * @throws Exception
     */
    public function validate($value, $container): bool
    {
        // Validate that container is a {Type}
        Util::subclassOf($container, Type\Extended\Object\{Type}::class, true);

        return {Validation Result};

To act on received activities do:

public function onActivityPubNew{Type}(&$obj): bool {

To add information to Activities being federated by ActivityPub do:

public function ActivityPubAddActivityStreamsTwoData(string $type_name, &$type): bool {

To implement an ActivityStreams 2.0 representation do:

public function onActivityPubActivityStreamsTwoResponse(string $route, arrray $vars, ?TypeResponse &$response = null): bool {
        if ($route === '{Object route}') {
                $response = ModelResponse::handle($vars[{Object}]);
                return Event::stop;
        }
        return Event::next;
}
This commit is contained in:
Diogo Peralta Cordeiro 2021-12-04 04:07:08 +00:00
parent 044649c745
commit 778cb57d83
Signed by: diogo
GPG Key ID: 18D2D35001FBFAB0
153 changed files with 2194 additions and 7768 deletions

790
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,33 @@
declare(strict_types=1);
// {{{ License
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU social is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
// }}}
/**
* ActivityPub implementation for GNU social
*
* @package GNUsocial
* @category ActivityPub
* @author Diogo Peralta Cordeiro <@diogo.site>
* @copyright 2018-2019, 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
namespace Plugin\ActivityPub;
use App\Core\Event;
@ -22,18 +49,30 @@ use Exception;
use Plugin\ActivityPub\Controller\Inbox;
use Plugin\ActivityPub\Entity\ActivitypubActor;
use Plugin\ActivityPub\Util\HTTPSignature;
use Plugin\ActivityPub\Util\Model\EntityToType\EntityToType;
use Plugin\ActivityPub\Util\Model;
use Plugin\ActivityPub\Util\Response\ActorResponse;
use Plugin\ActivityPub\Util\Response\NoteResponse;
use Plugin\ActivityPub\Util\Response\TypeResponse;
use Plugin\ActivityPub\Util\Type;
use Plugin\ActivityPub\Util\TypeResponse;
use Plugin\ActivityPub\Util\Validator\contentLangModelValidator;
use Plugin\ActivityPub\Util\Validator\manuallyApprovesFollowersModelValidator;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;
use XML_XRD;
use XML_XRD_Element_Link;
use function count;
use function Psy\debug;
use function is_null;
use const PHP_URL_HOST;
use const PREG_SET_ORDER;
/**
* Adds ActivityPub support to GNU social when enabled
*
* @copyright 2018-2019, 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
class ActivityPub extends Plugin
{
// ActivityStreams 2.0 Accept Headers
@ -89,6 +128,14 @@ class ActivityPub extends Plugin
return Event::next;
}
/**
* Fill Actor->getUrl() calls with correct URL coming from ActivityPub
*
* @param Actor $actor
* @param int $type
* @param string|null $url
* @return bool
*/
public function onStartGetActorUrl(Actor $actor, int $type, ?string &$url): bool
{
if (
@ -106,12 +153,80 @@ class ActivityPub extends Plugin
return Event::next;
}
/**
* Overload core endpoints to make resources available in ActivityStreams 2.0
*
* @throws Exception
*/
public function onControllerResponseInFormat(string $route, array $accept_header, array $vars, ?TypeResponse &$response = null): bool
{
if (count(array_intersect(self::$accept_headers, $accept_header)) === 0) {
return Event::next;
}
switch ($route) {
case 'actor_view_id':
case 'actor_view_nickname':
$response = ActorResponse::handle($vars['actor']);
return Event::stop;
case 'note_view':
$response = NoteResponse::handle($vars['note']);
return Event::stop;
default:
if (Event::handle('ActivityPubActivityStreamsTwoResponse', [$route, $vars, &$response]) === Event::stop) {
return Event::stop;
}
return Event::next;
}
}
/**
* Add ActivityStreams 2 Extensions
*
* @param string $type_name
* @param array $validators
* @return bool
*/
public function onActivityPubValidateActivityStreamsTwoData(string $type_name, array &$validators): bool
{
switch ($type_name) {
case 'Person':
$validators['manuallyApprovesFollowers'] = manuallyApprovesFollowersModelValidator::class;
break;
case 'Note':
$validators['contentLang'] = contentLangModelValidator::class;
break;
}
return Event::next;
}
// FreeNetworkComponent Events
/**
* Let FreeNetwork Component know we exist and which class to use to call the freeNetworkDistribute method
*
* @param array $protocols
* @return bool
*/
public function onAddFreeNetworkProtocol(array &$protocols): bool
{
$protocols[] = '\Plugin\ActivityPub\ActivityPub';
return Event::next;
}
/**
* The FreeNetwork component will call this function to distribute this instance's activities
*
* @param Actor $sender
* @param Activity $activity
* @param array $targets
* @param string|null $reason
* @param array $delivered
* @return bool
* @throws ClientExceptionInterface
* @throws RedirectionExceptionInterface
* @throws ServerExceptionInterface
* @throws TransportExceptionInterface
*/
public static function freeNetworkDistribute(Actor $sender, Activity $activity, array $targets, ?string $reason = null, array &$delivered = []): bool
{
$to_addr = [];
@ -130,14 +245,13 @@ class ActivityPub extends Plugin
//$to_failed = [];
foreach ($to_addr as $inbox => $dummy) {
try {
$res = self::postman($sender, EntityToType::translate($activity), $inbox);
$res = self::postman($sender, Model::toJson($activity), $inbox);
// accumulate errors for later use, if needed
$status_code = $res->getStatusCode();
if (!($status_code === 200 || $status_code === 202 || $status_code === 409)) {
$res_body = json_decode($res->getContent(), true);
$errors[] = isset($res_body['error']) ?
$res_body['error'] : "An unknown error occurred.";
$errors[] = $res_body['error'] ?? 'An unknown error occurred.';
//$to_failed[$inbox] = $activity;
} else {
array_push($delivered, ...$dummy);
@ -145,7 +259,7 @@ class ActivityPub extends Plugin
FreeNetworkActorProtocol::protocolSucceeded(
'activitypub',
$actor,
Discovery::normalize($actor->getNickname() . '@' . parse_url($inbox, PHP_URL_HOST))
Discovery::normalize($actor->getNickname() . '@' . parse_url($inbox, PHP_URL_HOST)),
);
}
}
@ -153,7 +267,6 @@ class ActivityPub extends Plugin
Log::error('ActivityPub @ freeNetworkDistribute: ' . $e->getMessage());
//$to_failed[$inbox] = $activity;
}
}
if (!empty($errors)) {
@ -165,27 +278,117 @@ class ActivityPub extends Plugin
}
/**
* Internal tool to sign and send activities out
*
* @param Actor $sender
* @param Type $activity
* @param string $json_activity
* @param string $inbox
* @param string $method
* @return ResponseInterface
* @throws Exception
*/
public static function postman(Actor $sender, mixed $activity, string $inbox, string $method = 'post'): ResponseInterface
private static function postman(Actor $sender, string $json_activity, string $inbox, string $method = 'post'): ResponseInterface
{
$data = $activity->toJson();
Log::debug('ActivityPub Postman: Delivering ' . $data . ' to ' . $inbox);
Log::debug('ActivityPub Postman: Delivering ' . $json_activity . ' to ' . $inbox);
$headers = HTTPSignature::sign($sender, $inbox, $data);
$headers = HTTPSignature::sign($sender, $inbox, $json_activity);
Log::debug('ActivityPub Postman: Delivery headers were: ' . print_r($headers, true));
$response = HTTPClient::$method($inbox, ['headers' => $headers, 'body' => $data]);
$response = HTTPClient::$method($inbox, ['headers' => $headers, 'body' => $json_activity]);
Log::debug('ActivityPub Postman: Delivery result with status code ' . $response->getStatusCode() . ': ' . $response->getContent());
return $response;
}
// WebFinger Events
public static function getActorByUri(string $resource, ?bool $attempt_fetch = true): Actor
/**
* Add activity+json mimetype to WebFinger
*
* @param XML_XRD $xrd
* @param Actor $object
* @return bool
*/
public function onEndWebFingerProfileLinks(XML_XRD $xrd, Actor $object): bool
{
if ($object->isPerson()) {
$link = new XML_XRD_Element_Link(
rel: 'self',
href: $object->getUri(Router::ABSOLUTE_URL),//Router::url('actor_view_id', ['id' => $object->getId()], Router::ABSOLUTE_URL),
type: 'application/activity+json',
);
$xrd->links[] = clone $link;
}
return Event::next;
}
/**
* When FreeNetwork component asks us to help with identifying Actors from XRDs
*
* @param XML_XRD $xrd
* @param Actor|null $actor
* @return bool
*/
public function onFreeNetworkFoundXrd(XML_XRD $xrd, ?Actor &$actor = null): bool
{
$addr = null;
foreach ($xrd->aliases as $alias) {
if (Discovery::isAcct($alias)) {
$addr = Discovery::normalize($alias);
}
}
if (is_null($addr)) {
return Event::next;
} else {
if (!FreeNetworkActorProtocol::canIAddr('activitypub', $addr)) {
return Event::next;
}
}
try {
$ap_actor = ActivitypubActor::fromXrd($addr, $xrd);
$actor = Actor::getById($ap_actor->getActorId());
FreeNetworkActorProtocol::protocolSucceeded('activitypub', $actor, $addr);
return Event::stop;
} catch (Exception $e) {
Log::error('ActivityPub Actor from URL Mention check failed: ' . $e->getMessage());
return Event::next;
}
}
// Discovery Events
/**
* When FreeNetwork component asks us to help with identifying Actors from URIs
*
* @param string $target
* @param Actor|null $actor
* @return bool
*/
public function onFreeNetworkFindMentions(string $target, ?Actor &$actor = null): bool
{
try {
if (FreeNetworkActorProtocol::canIAddr('activitypub', $addr = Discovery::normalize($target))) {
$ap_actor = ActivitypubActor::getByAddr($addr);
$actor = Actor::getById($ap_actor->getActorId());
FreeNetworkActorProtocol::protocolSucceeded('activitypub', $actor->getId(), $addr);
return Event::stop;
} else {
return Event::next;
}
} catch (Exception $e) {
Log::error('ActivityPub Webfinger Mention check failed: ' . $e->getMessage());
return Event::next;
}
}
/**
* Get an Actor from ActivityPub URI, if it doesn't exist, attempt to fetch it
* This should only be necessary internally.
*
* @param string $resource
* @return Actor got from URI
* @throws NoSuchActorException
*/
public static function getActorByUri(string $resource): Actor
{
// Try local
if (Common::isValidHttpUrl($resource)) {
@ -213,168 +416,4 @@ class ActivityPub extends Plugin
throw new NoSuchActorException("From URI: {$resource}");
}
}
/**
* @throws Exception
*/
public function onControllerResponseInFormat(string $route, array $accept_header, array $vars, ?TypeResponse &$response = null): bool
{
if (count(array_intersect(self::$accept_headers, $accept_header)) === 0) {
return Event::next;
}
switch ($route) {
case 'actor_view_id':
case 'actor_view_nickname':
$response = ActorResponse::handle($vars['actor']);
return Event::stop;
case 'note_view':
$response = NoteResponse::handle($vars['note']);
return Event::stop;
/*case 'actor_favourites_id':
case 'actor_favourites_nickname':
$response = LikeResponse::handle($vars['actor']);
return Event::stop;
case 'actor_subscriptions_id':
case 'actor_subscriptions_nickname':
$response = FollowingResponse::handle($vars['actor']);
return Event::stop;
case 'actor_subscribers_id':
case 'actor_subscribers_nickname':
$response = FollowersResponse::handle($vars['actor']);
return Event::stop;*/
default:
if (Event::handle('ActivityStreamsTwoResponse', [$route, &$response]) == Event::stop) {
return Event::stop;
}
return Event::next;
}
}
/********************************************************
* WebFinger Events *
********************************************************/
/**
* Add activity+json mimetype on WebFinger
*
* @throws Exception
*/
public function onEndWebFingerProfileLinks(XML_XRD $xrd, Actor $object): bool
{
if ($object->isPerson()) {
$link = new XML_XRD_Element_Link(
rel: 'self',
href: $object->getUri(Router::ABSOLUTE_URL),//Router::url('actor_view_id', ['id' => $object->getId()], Router::ABSOLUTE_URL),
type: 'application/activity+json',
);
$xrd->links[] = clone $link;
}
return Event::next;
}
public function onFreeNetworkFindMentions(string $target, ?Actor &$actor = null): bool
{
try {
if (FreeNetworkActorProtocol::canIAddr('activitypub', $addr = Discovery::normalize($target))) {
$ap_actor = ActivitypubActor::getByAddr($addr);
$actor = Actor::getById($ap_actor->getActorId());
FreeNetworkActorProtocol::protocolSucceeded('activitypub', $actor->getId(), $addr);
return Event::stop;
} else {
return Event::next;
}
} catch (Exception $e) {
Log::error("ActivityPub Webfinger Mention check failed: " . $e->getMessage());
return Event::next;
}
}
public function onFreeNetworkFoundXrd(XML_XRD $xrd, ?Actor &$actor = null): bool
{
$addr = null;
foreach ($xrd->aliases as $alias) {
if (Discovery::isAcct($alias)) {
$addr = Discovery::normalize($alias);
}
}
if (is_null($addr)) {
return Event::next;
} else {
if (!FreeNetworkActorProtocol::canIAddr('activitypub', $addr)) {
return Event::next;
}
}
try {
$ap_actor = ActivitypubActor::fromXrd($addr, $xrd);
$actor = Actor::getById($ap_actor->getActorId());
FreeNetworkActorProtocol::protocolSucceeded('activitypub', $actor, $addr);
return Event::stop;
} catch (Exception $e) {
Log::error("ActivityPub Actor from URL Mention check failed: " . $e->getMessage());
return Event::next;
}
}
/**
* Allow remote profile references to be used in commands:
* sub update@status.net
* whois evan@identi.ca
* reply http://identi.ca/evan hey what's up
*
* @param Command $command
* @param string $arg
* @param Actor &$profile
* @return bool hook return code
* @author GNU social
*/
//public function onStartCommandGetProfile($command, $arg, &$profile)
//{
// $aprofile = ActivitypubActor::fromUri($arg);
// if (!($aprofile instanceof ActivitypubActor)) {
// // No remote ActivityPub profile found
// return Event::next;
// }
//
// return Event::stop;
//}
/********************************************************
* Discovery Events *
********************************************************/
/**
* Profile from URI.
*
* @param string $uri
* @param Actor &$profile in/out param: Profile got from URI
* @return mixed hook return code
* @author GNU social
*/
//public function onStartGetProfileFromURI($uri, &$profile)
//{
// try {
// $profile = Explorer::get_profile_from_url($uri);
// return Event::stop;
// } catch (Exception) {
// return Event::next; // It's not an ActivityPub profile as far as we know, continue event handling
// }
//}
/**
* Try to grab and store the remote profile by the given uri
*
* @param string $uri
* @param Actor|null &$profile
* @return bool
*/
//public function onRemoteFollowPullProfile(string $uri, ?Actor &$profile): bool
//{
// $aprofile = ActivitypubActor::fromUri($uri);
// if (!($aprofile instanceof ActivitypubActor)) {
// // No remote ActivityPub profile found
// return Event::next;
// }
//
// return is_null($profile) ? Event::next : Event::stop;
//}
}

View File

@ -1,9 +1,8 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
// {{{ License
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
@ -18,9 +17,18 @@ declare(strict_types = 1);
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
// }}}
/**
* ActivityPub implementation for GNU social
*
* @package GNUsocial
* @category ActivityPub
* @author Diogo Peralta Cordeiro <@diogo.site>
* @copyright 2018-2019, 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
namespace Plugin\ActivityPub\Controller;
use App\Core\Controller;
@ -28,30 +36,37 @@ use App\Core\DB\DB;
use App\Core\Log;
use App\Core\Router\Router;
use App\Entity\Actor;
use App\Util\Exception\ClientException;
use Component\FreeNetwork\Entity\FreeNetworkActorProtocol;
use Component\FreeNetwork\Util\Discovery;
use Exception;
use Plugin\ActivityPub\Entity\ActivitypubActor;
use Plugin\ActivityPub\Entity\ActivitypubRsa;
use Plugin\ActivityPub\Util\Explorer;
use Plugin\ActivityPub\Util\HTTPSignature;
use Plugin\ActivityPub\Util\Model;
use Plugin\ActivityPub\Util\TypeResponse;
use function App\Core\I18n\_m;
use App\Util\Exception\ClientException;
use Plugin\ActivityPub\ActivityPub;
use Plugin\ActivityPub\Util\Model\AS2ToEntity\AS2ToEntity;
use Plugin\ActivityPub\Util\Response\TypeResponse;
use Plugin\ActivityPub\Util\Type;
use Plugin\ActivityPub\Util\Type\Util;
use function is_null;
use const PHP_URL_HOST;
/**
* ActivityPub Inbox Handler
*
* @copyright 2018-2019, 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
class Inbox extends Controller
{
/**
* Inbox handler
* Create an Inbox Handler to receive something from someone.
*/
public function handle(?int $gsactor_id = null): TypeResponse
{
$error = fn(string $m): TypeResponse => new TypeResponse(json_encode(['error' => $m]));
$path = Router::url('activitypub_inbox', type: Router::ABSOLUTE_PATH);
if (!\is_null($gsactor_id)) {
if (!is_null($gsactor_id)) {
try {
$user = DB::findOneBy('local_user', ['id' => $gsactor_id]);
$path = Router::url('activitypub_actor_inbox', ['gsactor_id' => $user->getId()], type: Router::ABSOLUTE_PATH);
@ -61,19 +76,19 @@ class Inbox extends Controller
}
Log::debug('ActivityPub Inbox: Received a POST request.');
$body = (string) $this->request->getContent();
$type = Type::fromJson($body);
$body = (string)$this->request->getContent();
$type = Model::jsonToType($body);
if ($type->has('actor') === false) {
ActivityPubReturn::error('Actor not found in the request.');
$error('Actor not found in the request.');
}
try {
$ap_actor = ActivitypubActor::fromUri($type->get('actor'));
$actor = Actor::getById($ap_actor->getActorId());
DB::flush();
} catch (Exception) {
ActivityPubReturn::error('Invalid actor.');
} catch (Exception $e) {
$error('Invalid actor.');
}
$actor_public_key = ActivitypubRsa::getByActor($actor)->getPublicKey();
@ -86,7 +101,7 @@ class Inbox extends Controller
if (!isset($headers['signature'])) {
Log::debug('ActivityPub Inbox: HTTP Signature: Missing Signature header.');
ActivityPubReturn::error('Missing Signature header.', 400);
$error('Missing Signature header.', 400);
// TODO: support other methods beyond HTTP Signatures
}
@ -95,7 +110,7 @@ class Inbox extends Controller
Log::debug('ActivityPub Inbox: HTTP Signature Data: ' . print_r($signatureData, true));
if (isset($signatureData['error'])) {
Log::debug('ActivityPub Inbox: HTTP Signature: ' . json_encode($signatureData, JSON_PRETTY_PRINT));
ActivityPubReturn::error(json_encode($signatureData, JSON_PRETTY_PRINT), 400);
$error(json_encode($signatureData, JSON_PRETTY_PRINT), 400);
}
list($verified, /*$headers*/) = HTTPSignature::verify($actor_public_key, $signatureData, $headers, $path, $body);
@ -105,12 +120,12 @@ class Inbox extends Controller
try {
$res = Explorer::get_remote_user_activity($ap_actor->getUri());
} catch (Exception) {
ActivityPubReturn::error('Invalid remote actor.');
$error('Invalid remote actor.');
}
try {
$actor = ActivitypubActor::update_profile($ap_actor, $res);
} catch (Exception) {
ActivityPubReturn::error('Failed to updated remote actor information.');
$error('Failed to updated remote actor information.');
}
[$verified, /*$headers*/] = HTTPSignature::verify($actor_public_key, $signatureData, $headers, $path, $body);
@ -119,7 +134,7 @@ class Inbox extends Controller
// If it still failed despite profile update
if ($verified !== 1) {
Log::debug('ActivityPub Inbox: HTTP Signature: Invalid signature.');
ActivityPubReturn::error('Invalid signature.');
$error('Invalid signature.');
}
// HTTP signature checked out, make sure the "actor" of the activity matches that of the signature
@ -128,8 +143,12 @@ class Inbox extends Controller
// TODO: Check if Actor has authority over payload
// Store Activity
$ap_act = AS2ToEntity::store(activity: $type->toArray(), source: 'ActivityPub');
FreeNetworkActorProtocol::protocolSucceeded('activitypub', $actor->getId());
$ap_act = Model\Activity::fromJson($type, ['source' => 'ActivityPub']);
FreeNetworkActorProtocol::protocolSucceeded(
'activitypub',
$ap_actor->getActorId(),
Discovery::normalize($actor->getNickname() . '@' . parse_url($ap_actor->getInboxUri(), PHP_URL_HOST))
);
DB::flush();
dd($ap_act, $act = $ap_act->getActivity(), $act->getActor(), $act->getObject());

View File

@ -1,9 +1,8 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
// {{{ License
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
@ -18,9 +17,18 @@ declare(strict_types = 1);
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
// }}}
/**
* ActivityPub implementation for GNU social
*
* @package GNUsocial
* @category ActivityPub
* @author Diogo Peralta Cordeiro <@diogo.site>
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
namespace Plugin\ActivityPub\Entity;
use App\Core\DB\DB;
@ -29,14 +37,9 @@ use App\Entity\Activity;
use DateTimeInterface;
/**
* Entity for all activities we know about
* Table Definition for activitypub_activity
*
* @category DB
* @package GNUsocial
*
* @author Hugo Sales <hugo@hsal.es>
* @author Diogo Peralta Cordeiro <mail@diogo.site>
* @copyright 2020-2021 Free Software Foundation, Inc http://www.fsf.org
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
class ActivitypubActivity extends Entity
@ -127,19 +130,19 @@ class ActivitypubActivity extends Entity
public static function schemaDef(): array
{
return [
'name' => 'activitypub_activity',
'name' => 'activitypub_activity',
'fields' => [
'activity_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Activity.id', 'multiplicity' => 'one to one', 'not null' => true, 'description' => 'activity_id to give attention'],
'activity_uri' => ['type' => 'text', 'not null' => true, 'description' => 'Activity\'s URI'],
'object_uri' => ['type' => 'text', 'not null' => true, 'description' => 'Object\'s URI'],
'is_local' => ['type' => 'bool', 'not null' => true, 'description' => 'whether this was a locally generated or an imported activity'],
'created' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'],
'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
'activity_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Activity.id', 'multiplicity' => 'one to one', 'not null' => true, 'description' => 'activity_id to give attention'],
'activity_uri' => ['type' => 'text', 'not null' => true, 'description' => 'Activity\'s URI'],
'object_uri' => ['type' => 'text', 'not null' => true, 'description' => 'Object\'s URI'],
'is_local' => ['type' => 'bool', 'not null' => true, 'description' => 'whether this was a locally generated or an imported activity'],
'created' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'],
'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
],
'primary key' => ['activity_uri'],
'indexes' => [
'indexes' => [
'activity_activity_uri_idx' => ['activity_uri'],
'activity_object_uri_idx' => ['object_uri'],
'activity_object_uri_idx' => ['object_uri'],
],
];
}

View File

@ -1,8 +1,8 @@
<?php
declare(strict_types = 1);
// {{{ License
declare(strict_types=1);
// {{{ License
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
@ -17,16 +17,15 @@ declare(strict_types = 1);
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
// }}}
/**
* ActivityPub implementation for GNU social
*
* @package GNUsocial
*
* @author Diogo Peralta Cordeiro <mail@diogo.site
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
* @category ActivityPub
* @author Diogo Peralta Cordeiro <@diogo.site>
* @copyright 2018-2019, 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
@ -34,20 +33,21 @@ namespace Plugin\ActivityPub\Entity;
use App\Core\Cache;
use App\Core\Entity;
use function App\Core\I18n\_m;
use App\Core\Log;
use App\Entity\Actor;
use Component\FreeNetwork\Util\Discovery;
use DateTimeInterface;
use Exception;
use Plugin\ActivityPub\Util\DiscoveryHints;
use Plugin\ActivityPub\Util\Explorer;
use XML_XRD;
use function App\Core\I18n\_m;
use function array_key_exists;
use function is_null;
/**
* Table Definition for activitypub_actor
*
* @author Diogo Peralta Cordeiro <mail@diogo.site
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
* @copyright 2018-2019, 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
class ActivitypubActor extends Entity
@ -157,10 +157,10 @@ class ActivitypubActor extends Entity
$addr = Discovery::normalize($addr);
// Try the cache
$uri = Cache::get(sprintf('ActivitypubActor-webfinger-%s', urlencode($addr)), fn () => false);
$uri = Cache::get(sprintf('ActivitypubActor-webfinger-%s', urlencode($addr)), fn() => false);
if ($uri !== false) {
if (\is_null($uri)) {
if (is_null($uri)) {
// TRANS: Exception.
throw new Exception(_m('Not a valid WebFinger address (via cache).'));
}
@ -189,14 +189,14 @@ class ActivitypubActor extends Entity
return self::fromXrd($addr, $xrd);
}
public static function fromXrd(string $addr, \XML_XRD $xrd): self
public static function fromXrd(string $addr, XML_XRD $xrd): self
{
$hints = array_merge(
['webfinger' => $addr],
DiscoveryHints::fromXRD($xrd),
);
if (\array_key_exists('activitypub', $hints)) {
if (array_key_exists('activitypub', $hints)) {
$uri = $hints['activitypub'];
try {
LOG::info("Discovery on acct:{$addr} with URI:{$uri}");
@ -240,17 +240,17 @@ class ActivitypubActor extends Entity
public static function schemaDef(): array
{
return [
'name' => 'activitypub_actor',
'name' => 'activitypub_actor',
'fields' => [
'uri' => ['type' => 'text', 'not null' => true],
'actor_id' => ['type' => 'int', 'not null' => true],
'inbox_uri' => ['type' => 'text', 'not null' => true],
'uri' => ['type' => 'text', 'not null' => true],
'actor_id' => ['type' => 'int', 'not null' => true],
'inbox_uri' => ['type' => 'text', 'not null' => true],
'inbox_shared_uri' => ['type' => 'text'],
'url' => ['type' => 'text'],
'created' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'],
'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
'url' => ['type' => 'text'],
'created' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'],
'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
],
'primary key' => ['actor_id'],
'primary key' => ['actor_id'],
'foreign keys' => [
'activitypub_actor_actor_id_fkey' => ['actor', ['actor_id' => 'id']],
],

View File

@ -1,4 +1,8 @@
<?php
declare(strict_types=1);
// {{{ License
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
@ -13,6 +17,17 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
// }}}
/**
* ActivityPub implementation for GNU social
*
* @package GNUsocial
* @category ActivityPub
* @author Diogo Peralta Cordeiro <@diogo.site>
* @copyright 2018-2019, 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
namespace Plugin\ActivityPub\Entity;
@ -22,25 +37,11 @@ use App\Core\Log;
use App\Entity\Actor;
use App\Util\Exception\ServerException;
use DateTimeInterface;
use Doctrine\ORM\UnitOfWork;
use Exception;
/**
* ActivityPub implementation for GNU social
*
* @package GNUsocial
* @author Diogo Peralta Cordeiro <@diogo.site>
* @copyright 2018-2019, 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
* @link http://www.gnu.org/software/social/
*/
/**
* ActivityPub Keys System
*
* @category Plugin
* @package GNUsocial
* @author Diogo Peralta Cordeiro <@diogo.site>
* @copyright 2018-2019, 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
class ActivitypubRsa extends Entity
@ -137,7 +138,7 @@ class ActivitypubRsa extends Entity
* Guarantees RSA keys for a given actor.
*
* @param Actor $gsactor
* @param bool $fetch=true Should attempt to fetch keys from a remote profile?
* @param bool $fetch =true Should attempt to fetch keys from a remote profile?
* @return ActivitypubRsa The keys (private key is null for remote actors)
* @throws ServerException It should never occur, but if so, we break everything!
*/

View File

@ -1,6 +1,33 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
// {{{ License
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU social is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
// }}}
/**
* ActivityPub implementation for GNU social
*
* @package GNUsocial
* @category ActivityPub
* @author Diogo Peralta Cordeiro <@diogo.site>
* @copyright 2018-2019, 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
namespace Plugin\ActivityPub\Util;
@ -19,31 +46,24 @@ namespace Plugin\ActivityPub\Util;
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
use App\Core\Event;
use App\Core\HTTPClient;
use Component\FreeNetwork\Util\Discovery;
use Component\FreeNetwork\Util\WebfingerResource\WebfingerResourceActor;
use Mf2 as Mf2;
use XML_XRD;
/**
* ActivityPub implementation for GNU social
* DiscoveryHints implementation for GNU social
*
* @package GNUsocial
*
* @author Evan Prodromou
* @author Brion Vibber
* @author James Walker
* @author Siebrand Mazeland
* @author Mikael Nordfeldth
* @author Diogo Cordeiro
* @copyright 2010, 2019, 2021 Free Software Foundation, Inc http://www.fsf.org
* @copyright 2018-2019, 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*
* @see http://www.gnu.org/software/social/
*/
class DiscoveryHints
{
public static function fromXRD(XML_XRD $xrd)
/**
* Search the WebFinger XRD after an ActivityPub URI
*
* @param XML_XRD $xrd
* @return array
*/
public static function fromXRD(XML_XRD $xrd): array
{
$hints = [];
@ -59,121 +79,4 @@ class DiscoveryHints
return $hints;
}
}
//class DiscoveryHints
//{
// public static function fromXRD(XML_XRD $xrd)
// {
// $hints = [];
//
// if (Event::handle('StartDiscoveryHintsFromXRD', [$xrd, &$hints])) {
// foreach ($xrd->links as $link) {
// switch ($link->rel) {
// case WebfingerResourceActor::PROFILEPAGE:
// $hints['profileurl'] = $link->href;
// break;
// case Discovery::UPDATESFROM:
// if (empty($link->type) || $link->type == 'application/atom+xml') {
// $hints['feedurl'] = $link->href;
// }
// break;
// case Discovery::HCARD:
// case Discovery::MF2_HCARD:
// $hints['hcard'] = $link->href;
// break;
// default:
// break;
// }
// }
// Event::handle('EndDiscoveryHintsFromXRD', [$xrd, &$hints]);
// }
//
// return $hints;
// }
//
// public static function fromHcardUrl($url)
// {
// $response = HTTPClient::get($url, ['headers' => ['Accept' => 'text/html,application/xhtml+xml']]);
//
// if (!HTTPClient::statusCodeIsOkay($response)) {
// return null;
// }
//
// return self::hcardHints(
// $response->getContent(),
// HTTPClient::getEffectiveUrl($response)
// );
// }
//
// public static function hcardHints($body, $url)
// {
// $hcard = self::hcard($body, $url);
//
// if (empty($hcard)) {
// return [];
// }
//
// $hints = [];
//
// // XXX: don't copy stuff into an array and then copy it again
//
// if (array_key_exists('nickname', $hcard) && !empty($hcard['nickname'][0])) {
// $hints['nickname'] = $hcard['nickname'][0];
// }
//
// if (array_key_exists('name', $hcard) && !empty($hcard['name'][0])) {
// $hints['fullname'] = $hcard['name'][0];
// }
//
// if (array_key_exists('photo', $hcard) && count($hcard['photo'])) {
// $hints['avatar'] = $hcard['photo'][0];
// }
//
// if (array_key_exists('note', $hcard) && !empty($hcard['note'][0])) {
// $hints['bio'] = $hcard['note'][0];
// }
//
// if (array_key_exists('adr', $hcard) && !empty($hcard['adr'][0])) {
// $hints['location'] = $hcard['adr'][0]['value'];
// }
//
// if (array_key_exists('url', $hcard) && !empty($hcard['url'][0])) {
// $hints['homepage'] = $hcard['url'][0];
// }
//
// return $hints;
// }
//
// private static function hcard($body, $url)
// {
// $mf2 = new Mf2\Parser($body, $url);
// $mf2 = $mf2->parse();
//
// if (empty($mf2['items'])) {
// return null;
// }
//
// $hcards = [];
//
// foreach ($mf2['items'] as $item) {
// if (!in_array('h-card', $item['type'])) {
// continue;
// }
//
// // We found a match, return it immediately
// if (isset($item['properties']['url']) && in_array($url, $item['properties']['url'])) {
// return $item['properties'];
// }
//
// // Let's keep all the hcards for later, to return one of them at least
// $hcards[] = $item['properties'];
// }
//
// // No match immediately for the url we expected, but there were h-cards found
// if (count($hcards) > 0) {
// return $hcards[0];
// }
//
// return null;
// }
//}
}

View File

@ -1,7 +1,8 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
// {{{ License
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
@ -16,65 +17,58 @@ declare(strict_types = 1);
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
namespace Plugin\ActivityPub\Util;
use App\Core\DB\DB;
use App\Core\HTTPClient;
use App\Core\Log;
use App\Core\Security;
use App\Entity\Actor;
use App\Util\Exception\NoSuchActorException;
use App\Util\Formatting;
use DateTime;
use Exception;
use Plugin\ActivityPub\ActivityPub;
use Plugin\ActivityPub\Entity\ActivitypubActor;
use Plugin\ActivityPub\Entity\ActivitypubRsa;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
// }}}
/**
* ActivityPub implementation for GNU social
*
* @package GNUsocial
*
* @category ActivityPub
* @author Diogo Peralta Cordeiro <@diogo.site>
* @copyright 2018-2019, 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*
* @see http://www.gnu.org/software/social/
*/
namespace Plugin\ActivityPub\Util;
use App\Core\HTTPClient;
use App\Core\Log;
use App\Util\Exception\NoSuchActorException;
use Exception;
use Plugin\ActivityPub\ActivityPub;
use Plugin\ActivityPub\Entity\ActivitypubActor;
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use function in_array;
use function is_null;
use const JSON_UNESCAPED_SLASHES;
/**
* ActivityPub's own Explorer
*
* Allows to discovery new remote actors
*
* @author Diogo Peralta Cordeiro (@diogo.site)
*
* @category Plugin
* @package GNUsocial
*
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
class Explorer
{
private $discovered_actor_profiles = [];
private array $discovered_actor_profiles = [];
/**
* Shortcut function to get a single profile from its URL.
*
* @param string $url
* @param bool $grab_online whether to try online grabbing, defaults to true
*
* @return ActivitypubActor
* @throws ClientExceptionInterface
* @throws NoSuchActorException
* @throws RedirectionExceptionInterface
* @throws ServerExceptionInterface
* @throws TransportExceptionInterface
*
* @return Actor
*/
public static function get_profile_from_url(string $url, bool $grab_online = true): ActivitypubActor
{
@ -92,8 +86,8 @@ class Explorer
* This function cleans the $this->discovered_actor_profiles array
* so that there is no erroneous data
*
* @param string $url User's url
* @param bool $grab_online whether to try online grabbing, defaults to true
* @param string $url User's url
* @param bool $grab_online whether to try online grabbing, defaults to true
*
* @throws ClientExceptionInterface
* @throws NoSuchActorException
@ -101,11 +95,11 @@ class Explorer
* @throws ServerExceptionInterface
* @throws TransportExceptionInterface
*
* @return array of Profile objects
* @return array of Actor objects
*/
public function lookup(string $url, bool $grab_online = true)
{
if (\in_array($url, ActivityPub::PUBLIC_TO)) {
if (in_array($url, ActivityPub::PUBLIC_TO)) {
return [];
}
@ -120,8 +114,8 @@ class Explorer
* This is a recursive function that will accumulate the results on
* $discovered_actor_profiles array
*
* @param string $url User's url
* @param bool $grab_online whether to try online grabbing, defaults to true
* @param string $url User's url
* @param bool $grab_online whether to try online grabbing, defaults to true
*
* @throws ClientExceptionInterface
* @throws NoSuchActorException
@ -193,95 +187,38 @@ class Explorer
{
Log::debug('ActivityPub Explorer: Trying to grab a remote actor for ' . $url);
$response = HTTPClient::get($url, ['headers' => ACTIVITYPUB::HTTP_CLIENT_HEADERS]);
$res = json_decode($response->getContent(), true);
$res = json_decode($response->getContent(), true);
if ($response->getStatusCode() == 410) { // If it was deleted
return true; // Nothing to add.
} elseif (!HTTPClient::statusCodeIsOkay($response)) { // If it is unavailable
return false; // Try to add at another time.
}
if (\is_null($res)) {
Log::debug('ActivityPub Explorer: Invalid JSON returned from given Actor URL: ' . $response->getContent());
if (is_null($res)) {
Log::debug('ActivityPub Explorer: Invalid response returned from given Actor URL: ' . $res);
return true; // Nothing to add.
}
if (isset($res['type']) && $res['type'] === 'OrderedCollection' && isset($res['first'])) { // It's a potential collection of actors!!!
if ($res['type'] === 'OrderedCollection') { // It's a potential collection of actors!!!
Log::debug('ActivityPub Explorer: Found a collection of actors for ' . $url);
$this->travel_collection($res['first']);
return true;
} elseif (self::validate_remote_response($res)) {
Log::debug('ActivityPub Explorer: Found a valid remote actor for ' . $url);
$this->discovered_actor_profiles[] = $this->store_profile($res);
return true;
} else {
Log::debug('ActivityPub Explorer: Invalid potential remote actor while grabbing remotely: ' . $url . '. He returned the following: ' . json_encode($res, \JSON_UNESCAPED_SLASHES));
return false;
try {
$this->discovered_actor_profiles[] = Model\Actor::fromJson(json_encode($res));
return true;
} catch (Exception $e) {
Log::debug(
'ActivityPub Explorer: Invalid potential remote actor while grabbing remotely: ' . $url
. '. He returned the following: ' . json_encode($res, JSON_UNESCAPED_SLASHES)
. ' and the following exception: ' . $e->getMessage()
);
return false;
}
}
return false;
}
/**
* Save remote user profile in known instance
*
* @param array $res remote response
*
* @throws Exception
* @throws NoSuchActorException
*
* @return Actor remote Profile object
*/
private function store_profile(array $res): ActivitypubActor
{
// Actor
$actor_map = [
'nickname' => $res['preferredUsername'],
'fullname' => $res['name'] ?? null,
'created' => new DateTime($res['published'] ?? 'now'),
'bio' => isset($res['summary']) ? mb_substr(Security::sanitize($res['summary']), 0, 1000) : null,
'is_local' => false,
'modified' => new DateTime(),
];
$actor = new Actor();
foreach ($actor_map as $prop => $val) {
$set = Formatting::snakeCaseToCamelCase("set_{$prop}");
$actor->{$set}($val);
}
DB::persist($actor);
// ActivityPub Actor
$aprofile = ActivitypubActor::create([
'inbox_uri' => $res['inbox'],
'inbox_shared_uri' => $res['endpoints']['sharedInbox'],
'uri' => $res['id'],
'actor_id' => $actor->getId(),
'url' => $res['url'] ?? null,
]);
DB::persist($aprofile);
// Public Key
$apRSA = ActivitypubRsa::create([
'actor_id' => $actor->getID(),
'public_key' => $res['publicKey']['publicKeyPem'],
]);
DB::persist($apRSA);
// Avatar
//if (isset($res['icon']['url'])) {
// try {
// $this->update_avatar($profile, $res['icon']['url']);
// } catch (Exception $e) {
// // Let the exception go, it isn't a serious issue
// Log::debug('ActivityPub Explorer: An error ocurred while grabbing remote avatar: ' . $e->getMessage());
// }
//}
return $aprofile;
}
/**
* Validates a remote response in order to determine whether this
* response is a valid profile or not
@ -305,18 +242,24 @@ class Explorer
public static function get_aprofile_by_url(string $v): ActivitypubActor|bool
{
$aprofile = ActivitypubActor::getWithPK(['uri' => $v]);
return \is_null($aprofile) ? false : ActivitypubActor::getWithPK(['uri' => $v]);
return is_null($aprofile) ? false : ActivitypubActor::getWithPK(['uri' => $v]);
}
/**
* Allows the Explorer to transverse a collection of persons.
*
* @param string $url
* @return bool
* @throws ClientExceptionInterface
* @throws NoSuchActorException
* @throws RedirectionExceptionInterface
* @throws ServerExceptionInterface
* @throws TransportExceptionInterface
*/
private function travel_collection(string $url): bool
{
$response = HTTPClient::get($url, ['headers' => ACTIVITYPUB::HTTP_CLIENT_HEADERS]);
$res = json_decode($response->getContent(), true);
$res = json_decode($response->getContent(), true);
if (!isset($res['orderedItems'])) {
return false;
@ -328,7 +271,7 @@ class Explorer
}
}
// Go through entire collection
if (!\is_null($res['next'])) {
if (!is_null($res['next'])) {
$this->travel_collection($res['next']);
}
@ -359,7 +302,7 @@ class Explorer
throw new Exception('Non Ok Status Code for given Actor URL.');
}
$res = json_decode($response->getContent(), true);
if (\is_null($res)) {
if (is_null($res)) {
Log::debug('ActivityPub Explorer: Invalid JSON returned from given Actor URL: ' . $response->getContent());
throw new Exception('Given Actor URL didn\'t return a valid JSON.');
}

View File

@ -64,7 +64,7 @@ class HTTPSignature
}
/**
* @param array|string array or json string $body
* @param array|string $body array or json string $body
* @return string
*/
private static function _digest(array|string $body): string

View File

@ -0,0 +1,143 @@
<?php
declare(strict_types=1);
// {{{ License
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU social is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
// }}}
/**
* ActivityPub implementation for GNU social
*
* @package GNUsocial
* @category ActivityPub
* @author Diogo Peralta Cordeiro <@diogo.site>
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
namespace Plugin\ActivityPub\Util;
use ActivityPhp\Type;
use ActivityPhp\Type\TypeConfiguration;
use ActivityPhp\Type\TypeResolver;
use App\Core\Entity;
use App\Core\Event;
use App\Util\Exception\ClientException;
use Exception;
use InvalidArgumentException;
use Plugin\ActivityPub\Util\Model\Activity;
use Plugin\ActivityPub\Util\Model\Note;
/**
* This class handles translation between JSON and GS Entities
*
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
abstract class Model
{
/**
* Create a Type from an ActivityStreams 2.0 JSON string
*
* @param string|array $data
* @return Type\AbstractObject
* @throws Exception
*/
public static function jsonToType(string|array $data): Type\AbstractObject
{
if (is_string($data)) {
$attributes = json_decode($data, true);
if (json_last_error() !== JSON_ERROR_NONE
|| !is_array($attributes)
) {
throw new Exception(
sprintf(
"An error occurred during the JSON decoding.\n '%s'",
$data
)
);
}
} else {
$attributes = $data;
}
if (!array_key_exists('type', $attributes)) {
throw new InvalidArgumentException('Missing "type" attribute in $data: ' . var_export($data, true));
}
unset($data);
try {
$type = TypeResolver::getClass($attributes['type']);
} catch (Exception $e) {
$message = json_encode($attributes, JSON_PRETTY_PRINT);
throw new Exception(
$e->getMessage() . "\n$message"
);
}
if (is_string($type)) {
$type = new $type();
}
// Add our own extensions
$validators = [];
if (Event::handle('ActivityPubValidateActivityStreamsTwoData', [$attributes['type'], &$validators]) === Event::next) {
foreach ($validators as $name => $class) {
Type::addValidator($name, $class);
$type->extend($name);
}
}
TypeConfiguration::set('undefined_properties', 'include');
foreach ($attributes as $name => $value) {
$type->set($name, $value);
}
return $type;
}
/**
* Create an Entity from an ActivityStreams 2.0 JSON string
*
* @param string|Type\AbstractObject $json
* @param array $options
* @return Entity
*/
abstract public static function fromJson(string|Type\AbstractObject $json, array $options = []): Entity;
/**
* Get a JSON
*
* @param mixed $object
* @param ?int $options PHP JSON options
* @return string
* @throws ClientException
*/
public static function toJson(mixed $object, int $options = null): string
{
switch ($object::class) {
case 'App\Entity\Activity':
return Activity::toJson($object, $options);
case 'App\Entity\Note':
return Note::toJson($object, $options);
default:
$type = self::jsonToType($object);
Event::handle('ActivityPubAddActivityStreamsTwoData', [$type->get('type'), &$type]);
return $type->toJson($options);
}
}
}

View File

@ -1,82 +0,0 @@
<?php
declare(strict_types = 1);
namespace Plugin\ActivityPub\Util\Model\AS2ToEntity;
use App\Core\DB\DB;
use App\Core\Event;
use App\Entity\Activity;
use App\Entity\Actor;
use App\Entity\Note;
use App\Util\Exception\ClientException;
use App\Util\Formatting;
use DateTime;
use Plugin\ActivityPub\ActivityPub;
use Plugin\ActivityPub\Entity\ActivitypubActivity;
abstract class AS2ToEntity
{
public static function activity_stream_two_verb_to_gs_verb(string $verb): string
{
return match ($verb) {
'Create' => 'create',
default => throw new ClientException('Invalid verb'),
};
}
public static function activity_stream_two_object_type_to_gs_table(string $object): string
{
return match ($object) {
'Note' => 'note',
default => throw new ClientException('Invalid verb'),
};
}
/**
* @throws ClientException
*/
public static function store(array $activity, ?string $source = null): ActivitypubActivity
{
$ap_act = ActivitypubActivity::getWithPK(['activity_uri' => $activity['id']]);
if (\is_null($ap_act)) {
$actor = ActivityPub::getActorByUri($activity['actor']);
// Store Object
$obj = null;
switch ($activity['object']['type']) {
case 'Note':
$obj = AS2ToNote::translate($activity['object'], $source, $activity['actor'], $actor->getId());
break;
default:
if (!Event::handle('ActivityPubObject', [$activity['object']['type'], $activity['object'], &$obj])) {
throw new ClientException('Unsupported Object type.');
}
break;
}
DB::persist($obj);
// Store Activity
$act = Activity::create([
'actor_id' => $actor->getId(),
'verb' => self::activity_stream_two_verb_to_gs_verb($activity['type']),
'object_type' => self::activity_stream_two_object_type_to_gs_table($activity['object']['type']),
'object_id' => $obj->getId(),
'is_local' => false,
'created' => new DateTime($activity['published'] ?? 'now'),
'source' => $source,
]);
DB::persist($act);
// Store ActivityPub Activity
$ap_act = ActivitypubActivity::create([
'activity_id' => $act->getId(),
'activity_uri' => $activity['id'],
'object_uri' => $activity['object']['id'],
'is_local' => false,
'created' => new DateTime($activity['published'] ?? 'now'),
'modified' => new DateTime(),
]);
DB::persist($ap_act);
}
return $ap_act;
}
}

View File

@ -1,67 +0,0 @@
<?php
declare(strict_types = 1);
namespace Plugin\ActivityPub\Util\Model\AS2ToEntity;
use App\Core\Event;
use App\Entity\Actor;
use App\Entity\Language;
use App\Entity\Note;
use App\Util\Formatting;
use DateTime;
use Exception;
use Plugin\ActivityPub\ActivityPub;
use Plugin\ActivityPub\Entity\ActivitypubActivity;
abstract class AS2ToNote
{
/**
*@throws Exception
*/
public static function translate(array $object, ?string $source, ?string $actor_uri = null, ?int $actor_id = null): Note
{
if (is_null($actor_uri) || $actor_uri !== $object['attributedTo']) {
$actor_id = ActivityPub::getActorByUri($object['attributedTo'])->getId();
}
$map = [
'is_local' => false,
'created' => new DateTime($object['published'] ?? 'now'),
'content' => $object['content'] ?? null,
'content_type' => 'text/html',
'language_id' => $object['contentLang'] ?? null,
'url' => \array_key_exists('url', $object) ? $object['url'] : $object['id'],
'actor_id' => $actor_id,
'modified' => new DateTime(),
'source' => $source,
];
if ($map['content'] !== null) {
$mentions = [];
Event::handle('RenderNoteContent', [
$map['content'],
$map['content_type'],
&$map['rendered'],
Actor::getById($actor_id),
$map['language_id'],
&$mentions,
]);
}
$obj = new Note();
if (!is_null($map['language_id'])) {
$map['language_id'] = Language::getFromLocale($map['language_id'])->getId();
} else {
$map['language_id'] = null;
}
foreach ($map as $prop => $val) {
$set = Formatting::snakeCaseToCamelCase("set_{$prop}");
$obj->{$set}($val);
}
Event::handle('NewNoteFromActivityStreamsTwo', [$source, $obj, $actor_id]);
return $obj;
}
}

View File

@ -0,0 +1,160 @@
<?php
declare(strict_types=1);
// {{{ License
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU social is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
// }}}
/**
* ActivityPub implementation for GNU social
*
* @package GNUsocial
* @category ActivityPub
* @author Diogo Peralta Cordeiro <@diogo.site>
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
namespace Plugin\ActivityPub\Util\Model;
use ActivityPhp\Type\AbstractObject;
use App\Core\DB\DB;
use App\Core\Event;
use App\Core\Router\Router;
use App\Entity\Activity as GSActivity;
use App\Util\Exception\ClientException;
use App\Util\Exception\NoSuchActorException;
use DateTime;
use DateTimeInterface;
use InvalidArgumentException;
use Plugin\ActivityPub\ActivityPub;
use Plugin\ActivityPub\Entity\ActivitypubActivity;
use Plugin\ActivityPub\Util\Model;
/**
* This class handles translation between JSON and ActivityPub Activities
*
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
class Activity extends Model
{
/**
* Create an Entity from an ActivityStreams 2.0 JSON string
* This will persist new GSActivities, GSObjects, and APActivity
*
* @param string|AbstractObject $json
* @param array $options
* @return ActivitypubActivity
* @throws ClientException
* @throws NoSuchActorException
*/
public static function fromJson(string|AbstractObject $json, array $options = []): ActivitypubActivity
{
$type_activity = is_string($json) ? self::jsonToType($json) : $json;
$activity_stream_two_verb_to_gs_verb = fn(string $verb): string => match ($verb) {
'Create' => 'create',
default => throw new ClientException('Invalid verb'),
};
$activity_stream_two_object_type_to_gs_table = fn(string $object): string => match ($object) {
'Note' => 'note',
default => throw new ClientException('Invalid verb'),
};
$ap_act = ActivitypubActivity::getWithPK(['activity_uri' => $type_activity->get('id')]);
if (is_null($ap_act)) {
$actor = ActivityPub::getActorByUri($type_activity->get('actor'));
// Store Object
$obj = null;
if (!$type_activity->has('object') || !isset($type_activity->get('object')['type'])) {
throw new InvalidArgumentException('Activity Object or Activity Object Type is missing.');
}
switch ($type_activity->get('object')['type']) {
case 'Note':
$obj = Note::toJson($type_activity->get('object'), ['source' => $source, 'actor_uri' => $type_activity->get('actor'), 'actor_id' => $actor->getId()]);
break;
default:
if (!Event::handle('ActivityPubObject', [$type_activity->get('object')['type'], $type_activity->get('object'), &$obj])) {
throw new ClientException('Unsupported Object type.');
}
break;
}
DB::persist($obj);
// Store Activity
$act = GSActivity::create([
'actor_id' => $actor->getId(),
'verb' => $activity_stream_two_verb_to_gs_verb($type_activity->get('type')),
'object_type' => $activity_stream_two_object_type_to_gs_table($type_activity->get('object')['type']),
'object_id' => $obj->getId(),
'is_local' => false,
'created' => new DateTime($activity['published'] ?? 'now'),
'source' => $source,
]);
DB::persist($act);
// Store ActivityPub Activity
$ap_act = ActivitypubActivity::create([
'activity_id' => $act->getId(),
'activity_uri' => $activity['id'],
'object_uri' => $activity['object']['id'],
'is_local' => false,
'created' => new DateTime($activity['published'] ?? 'now'),
'modified' => new DateTime(),
]);
DB::persist($ap_act);
}
Event::handle('ActivityPubNewActivity', [&$ap_act, &$act, &$obj]);
return $ap_act;
}
/**
* Get a JSON
*
* @param mixed $object
* @param int|null $options
* @return string
* @throws ClientException
*/
public static function toJson(mixed $object, ?int $options = null): string
{
if ($object::class !== 'App\Entity\Activity') {
throw new InvalidArgumentException('First argument type is Activity');
}
$gs_verb_to_activity_stream_two_verb = fn($verb): string => match ($verb) {
'create' => 'Create',
default => throw new ClientException('Invalid verb'),
};
$attr = [
'type' => $gs_verb_to_activity_stream_two_verb($object->getVerb()),
'@context' => 'https://www.w3.org/ns/activitystreams',
'id' => Router::url('activity_view', ['id' => $object->getId()], Router::ABSOLUTE_URL),
'published' => $object->getCreated()->format(DateTimeInterface::RFC3339),
'actor' => $object->getActor()->getUri(Router::ABSOLUTE_URL),
'to' => ['https://www.w3.org/ns/activitystreams#Public'], // TODO: implement proper scope address
'cc' => ['https://www.w3.org/ns/activitystreams#Public'],
'object' => self::jsonToType(self::toJson($object->getObject())),
];
$type = self::jsonToType($attr);
Event::handle('ActivityPubAddActivityStreamsTwoData', [$type->get('type'), &$type]);
return $type->toJson($options);
}
}

View File

@ -0,0 +1,174 @@
<?php
declare(strict_types=1);
// {{{ License
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU social is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
// }}}
/**
* ActivityPub implementation for GNU social
*
* @package GNUsocial
* @category ActivityPub
* @author Diogo Peralta Cordeiro <@diogo.site>
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
namespace Plugin\ActivityPub\Util\Model;
use ActivityPhp\Type\AbstractObject;
use App\Core\DB\DB;
use App\Core\Event;
use App\Core\Router\Router;
use App\Core\Security;
use App\Entity\Actor as GSActor;
use App\Util\Exception\ServerException;
use App\Util\Formatting;
use Component\Avatar\Avatar;
use Component\Avatar\Exception\NoAvatarException;
use DateTime;
use DateTimeInterface;
use Exception;
use InvalidArgumentException;
use Plugin\ActivityPub\Entity\ActivitypubActor;
use Plugin\ActivityPub\Entity\ActivitypubRsa;
use Plugin\ActivityPub\Util\Model;
/**
* This class handles translation between JSON and GSActors
*
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
class Actor extends Model
{
/**
* Create an Entity from an ActivityStreams 2.0 JSON string
* This will persist a new GSActor, ActivityPubRSA, and ActivityPubActor
*
* @param string|AbstractObject $json
* @param array $options
* @return GSActor
* @throws Exception
*/
public static function fromJson(string|AbstractObject $json, array $options = []): GSActor
{
$person = is_string($json) ? self::jsonToType($json) : $json;
// Actor
$actor_map = [
'nickname' => $person->get('preferredUsername'),
'fullname' => $person->get('name'),
'created' => new DateTime($person->get('published') ?? 'now'),
'bio' => $person->has('summary') ? mb_substr(Security::sanitize($person->get('summary')), 0, 1000) : null,
'is_local' => false,
'modified' => new DateTime(),
];
$actor = new GSActor();
foreach ($actor_map as $prop => $val) {
$set = Formatting::snakeCaseToCamelCase("set_{$prop}");
$actor->{$set}($val);
}
DB::persist($actor);
// ActivityPub Actor
$aprofile = ActivitypubActor::create([
'inbox_uri' => $person->get('inbox'),
'inbox_shared_uri' => ($person->has('endpoints') && isset($person->get('endpoints')['sharedInbox'])) ? $person->get('endpoints')['sharedInbox'] : null,
'uri' => $person->get('id'),
'actor_id' => $actor->getId(),
'url' => $person->get('url') ?? null,
]);
DB::persist($aprofile);
// Public Key
$apRSA = ActivitypubRsa::create([
'actor_id' => $actor->getID(),
'public_key' => ($person->has('publicKey') && isset($person->get('publicKey')['publicKeyPem'])) ? $person->get('publicKey')['publicKeyPem'] : null,
]);
DB::persist($apRSA);
// Avatar
//if (isset($res['icon']['url'])) {
// try {
// $this->update_avatar($profile, $res['icon']['url']);
// } catch (Exception $e) {
// // Let the exception go, it isn't a serious issue
// Log::debug('ActivityPub Explorer: An error occurred while grabbing remote avatar: ' . $e->getMessage());
// }
//}
Event::handle('ActivityPubNewActor', [&$aprofile, &$actor, &$apRSA]);
return $aprofile;
}
/**
* Get a JSON
*
* @param mixed $object
* @param int|null $options PHP JSON options
* @return string
* @throws ServerException
*/
public static function toJson(mixed $object, ?int $options = null): string
{
if ($object::class !== 'App\Entity\Actor') {
throw new InvalidArgumentException('First argument type is Actor');
}
$rsa = ActivitypubRsa::getByActor($object);
$public_key = $rsa->getPublicKey();
$uri = null;
$attr = [
'@context' => 'https://www.w3.org/ns/activitystreams',
'type' => 'Person',
'id' => $object->getUri(Router::ABSOLUTE_URL),
'inbox' => Router::url('activitypub_actor_inbox', ['gsactor_id' => $object->getId()], Router::ABSOLUTE_URL),
'outbox' => Router::url('activitypub_actor_outbox', ['gsactor_id' => $object->getId()], Router::ABSOLUTE_URL),
'following' => Router::url('actor_subscriptions_id', ['id' => $object->getId()], Router::ABSOLUTE_URL),
'followers' => Router::url('actor_subscribers_id', ['id' => $object->getId()], Router::ABSOLUTE_URL),
'liked' => Router::url('favourites_view_by_actor_id', ['id' => $object->getId()], Router::ABSOLUTE_URL),
//'streams' =>
'preferredUsername' => $object->getNickname(),
'publicKey' => [
'id' => $uri . "#public-key",
'owner' => $uri,
'publicKeyPem' => $public_key
],
'name' => $object->getFullname(),
'location' => $object->getLocation(),
'published' => $object->getCreated()->format(DateTimeInterface::RFC3339),
'summary' => $object->getBio(),
//'tag' => $object->getSelfTags(),
'updated' => $object->getModified()->format(DateTimeInterface::RFC3339),
'url' => $object->getUrl(Router::ABSOLUTE_URL),
];
try {
$attr['icon'] = Avatar::getAvatar($object->getId())->getUrl(type: Router::ABSOLUTE_URL);
} catch (NoAvatarException) {
// No icon for this actor
}
$type = self::jsonToType($attr);
return $type->toJson($options);
}
}

View File

@ -1,41 +0,0 @@
<?php
declare(strict_types=1);
namespace Plugin\ActivityPub\Util\Model\EntityToType;
use App\Core\Router\Router;
use App\Entity\Activity;
use App\Util\Exception\ClientException;
use DateTimeInterface;
use Exception;
use Plugin\ActivityPub\Util\Type;
class ActivityToType
{
public static function gs_verb_to_activity_stream_two_verb($verb)
{
return match ($verb) {
'create' => 'Create',
default => throw new ClientException('Invalid verb'),
};
}
/**
* @throws Exception
*/
public static function translate(Activity $activity): Type\Core\Activity
{
$attr = [
'type' => self::gs_verb_to_activity_stream_two_verb($activity->getVerb()),
'@context' => 'https://www.w3.org/ns/activitystreams',
'id' => Router::url('activity_view', ['id' => $activity->getId()], Router::ABSOLUTE_URL),
'published' => $activity->getCreated()->format(DateTimeInterface::RFC3339),
'actor' => $activity->getActor()->getUri(Router::ABSOLUTE_URL),
'to' => ['https://www.w3.org/ns/activitystreams#Public'], // TODO: implement proper scope address
'cc' => ['https://www.w3.org/ns/activitystreams#Public'],
'object' => EntityToType::translate($activity->getObject()),
];
return Type::create($attr);
}
}

View File

@ -1,31 +0,0 @@
<?php
declare(strict_types = 1);
namespace Plugin\ActivityPub\Util\Model\EntityToType;
use App\Core\Entity;
use Exception;
use Plugin\ActivityPub\Util\Type;
abstract class EntityToType
{
/**
* @return Type
* @throws Exception
*/
public static function translate(Entity $entity): mixed
{
switch ($entity::class) {
case 'App\Entity\Activity':
return ActivityToType::translate($entity);
case 'App\Entity\Note':
return NoteToType::translate($entity);
default:
$map = [
'type' => 'Object',
];
return Type::create($map);
}
}
}

View File

@ -1,58 +0,0 @@
<?php
declare(strict_types = 1);
namespace Plugin\ActivityPub\Util\Model\EntityToType;
use App\Core\Router\Router;
use App\Entity\Actor;
use Component\Avatar\Avatar;
use Component\Avatar\Exception\NoAvatarException;
use DateTimeInterface;
use Exception;
use Plugin\ActivityPub\Entity\ActivitypubRsa;
use Plugin\ActivityPub\Util\Type;
use Plugin\ActivityPub\Util\Type\Extended\Actor\Person;
class GSActorToType
{
/**
*@throws Exception
*/
public static function translate(Actor $gsactor): Person
{
$rsa = ActivitypubRsa::getByActor($gsactor);
$public_key = $rsa->getPublicKey();
$uri = null;
$attr = [
'@context' => 'https://www.w3.org/ns/activitystreams',
'id' => $gsactor->getUri(Router::ABSOLUTE_URL),
'inbox' => Router::url('activitypub_actor_inbox', ['gsactor_id' => $gsactor->getId()], Router::ABSOLUTE_URL),
'outbox' => Router::url('activitypub_actor_outbox', ['gsactor_id' => $gsactor->getId()], Router::ABSOLUTE_URL),
'following' => Router::url('actor_subscriptions_id', ['id' => $gsactor->getId()], Router::ABSOLUTE_URL),
'followers' => Router::url('actor_subscribers_id', ['id' => $gsactor->getId()], Router::ABSOLUTE_URL),
'liked' => Router::url('favourites_view_by_actor_id', ['id' => $gsactor->getId()], Router::ABSOLUTE_URL),
//'streams' =>
'preferredUsername' => $gsactor->getNickname(),
'publicKey' => [
'id' => $uri . "#public-key",
'owner' => $uri,
'publicKeyPem' => $public_key
],
'name' => $gsactor->getFullname(),
'location' => $gsactor->getLocation(),
'published' => $gsactor->getCreated()->format(DateTimeInterface::RFC3339),
'summary' => $gsactor->getBio(),
//'tag' => $gsactor->getSelfTags(),
'updated' => $gsactor->getModified()->format(DateTimeInterface::RFC3339),
'url' => $gsactor->getUrl(Router::ABSOLUTE_URL),
];
try {
$attr['icon'] = Avatar::getAvatar($gsactor->getId())->getUrl(type: Router::ABSOLUTE_URL);
} catch (NoAvatarException) {
// No icon for this actor
}
return Type::create(type: 'Person', attributes: $attr);
}
}

View File

@ -1,32 +0,0 @@
<?php
declare(strict_types=1);
namespace Plugin\ActivityPub\Util\Model\EntityToType;
use App\Core\Router\Router;
use App\Entity\Note;
use DateTimeInterface;
use Exception;
use Plugin\ActivityPub\Util\Type;
class NoteToType
{
/**
* @throws Exception
*/
public static function translate(Note $note): Type\Extended\Object\Note
{
$attr = [
'@context' => 'https://www.w3.org/ns/activitystreams',
'id' => Router::url('note_view', ['id' => $note->getId()], Router::ABSOLUTE_URL),
'published' => $note->getCreated()->format(DateTimeInterface::RFC3339),
'attributedTo' => $note->getActor()->getUri(Router::ABSOLUTE_URL),
'to' => ['https://www.w3.org/ns/activitystreams#Public'], // TODO: implement proper scope address
'cc' => ['https://www.w3.org/ns/activitystreams#Public'],
'content' => $note->getRendered(),
//'tag' => $tags
];
return Type::create(type: 'Note', attributes: $attr);
}
}

View File

@ -0,0 +1,145 @@
<?php
declare(strict_types=1);
// {{{ License
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU social is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
// }}}
/**
* ActivityPub implementation for GNU social
*
* @package GNUsocial
* @category ActivityPub
* @author Diogo Peralta Cordeiro <@diogo.site>
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
namespace Plugin\ActivityPub\Util\Model;
use ActivityPhp\Type\AbstractObject;
use App\Core\Event;
use App\Core\Router\Router;
use App\Entity\Actor;
use App\Entity\Language;
use App\Entity\Note as GSNote;
use App\Util\Formatting;
use DateTime;
use DateTimeInterface;
use Exception;
use InvalidArgumentException;
use Plugin\ActivityPub\ActivityPub;
use Plugin\ActivityPub\Util\Model;
/**
* This class handles translation between JSON and GSNotes
*
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
class Note extends Model
{
/**
* Create an Entity from an ActivityStreams 2.0 JSON string
* This will persist a new GSNote
*
* @param string|AbstractObject $json
* @param array $options
* @return GSNote
* @throws Exception
*/
public static function fromJson(string|AbstractObject $json, array $options = []): GSNote
{
$source = $options['source'];
$actor_uri = $options['actor_uri'];
$actor_id = $options['actor_id'];
$type_note = is_string($json) ? self::jsonToType($json) : $json;
if (is_null($actor_uri) || $actor_uri !== $type_note->get('attributedTo')) {
$actor_id = ActivityPub::getActorByUri($type_note->get('attributedTo'))->getId();
}
$map = [
'is_local' => false,
'created' => new DateTime($type_note->get('published') ?? 'now'),
'content' => $type_note->get('content') ?? null,
'content_type' => 'text/html',
'language_id' => $type_note->get('contentLang') ?? null,
'url' => $type_note->get('url') ?? $type_note->get('id'),
'actor_id' => $actor_id,
'modified' => new DateTime(),
'source' => $source,
];
if ($map['content'] !== null) {
$mentions = [];
Event::handle('RenderNoteContent', [
$map['content'],
$map['content_type'],
&$map['rendered'],
Actor::getById($actor_id),
$map['language_id'],
&$mentions,
]);
}
$obj = new GSNote();
if (!is_null($map['language_id'])) {
$map['language_id'] = Language::getFromLocale($map['language_id'])->getId();
} else {
$map['language_id'] = null;
}
foreach ($map as $prop => $val) {
$set = Formatting::snakeCaseToCamelCase("set_{$prop}");
$obj->{$set}($val);
}
Event::handle('ActivityPubNewNote', [&$obj]);
return $obj;
}
/**
* Get a JSON
*
* @param mixed $object
* @param int|null $options
* @return string
* @throws Exception
*/
public static function toJson(mixed $object, ?int $options = null): string
{
if ($object::class !== 'App\Entity\Note') {
throw new InvalidArgumentException('First argument type is Note');
}
$attr = [
'@context' => 'https://www.w3.org/ns/activitystreams',
'id' => Router::url('note_view', ['id' => $object->getId()], Router::ABSOLUTE_URL),
'published' => $object->getCreated()->format(DateTimeInterface::RFC3339),
'attributedTo' => $object->getActor()->getUri(Router::ABSOLUTE_URL),
'to' => ['https://www.w3.org/ns/activitystreams#Public'], // TODO: implement proper scope address
'cc' => ['https://www.w3.org/ns/activitystreams#Public'],
'content' => $object->getRendered(),
//'tag' => $tags
];
$type = self::jsonToType($attr);
Event::handle('ActivityPubAddActivityStreamsTwoData', [$type->get('type'), &$type]);
return $type->toJson($options);
}
}

View File

@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
// {{{ License
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU social is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
// }}}
/**
* ActivityPub implementation for GNU social
*
* @package GNUsocial
* @category ActivityPub
* @author Diogo Peralta Cordeiro <@diogo.site>
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
namespace Plugin\ActivityPub\Util;
/**
* Provides a response in application/ld+json to GS Entities
*
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
abstract class ModelResponse
{
/**
* Provides a response in application/ld+json for ActivityStreams 2.0 Types
*
* @param mixed $object (Entity)
* @param int $status The response status code
* @return TypeResponse
*/
public static function handle(mixed $object, int $status = 200): TypeResponse
{
return new TypeResponse(
json: Model::toJson($object),
status: $status,
);
}
}

View File

@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
// {{{ License
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU social is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
// }}}
/**
* ActivityPub implementation for GNU social
*
* @package GNUsocial
* @category ActivityPub
* @author Diogo Peralta Cordeiro <@diogo.site>
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
namespace Plugin\ActivityPub\Util;
use ActivityPhp\Type\ValidatorInterface;
use ActivityPhp\Type\ValidatorTools;
/**
* \Plugin\ActivityPub\Util\ModelValidator is an abstract class for
* attribute validation.
* Its purpose is to be extended by Plugin\ActivityPub\Util\Validator\*
* classes.
* It provides some methods to make some regular validations.
* It implements \ActivityPhp\Type\ValidatorInterface.
*
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
abstract class ModelValidator extends ValidatorTools implements ValidatorInterface
{
}

View File

@ -1,26 +0,0 @@
<?php
declare(strict_types = 1);
namespace Plugin\ActivityPub\Util\Response;
use Exception;
use Plugin\ActivityPub\Util\Model\EntityToType\EntityToType;
abstract class AbstractResponse
{
/**
* param Type $type // What is this `Type`
*
* @param int $status The response status code
*
* @throws Exception
*/
public static function handle($type, int $status = 200): TypeResponse
{
return new TypeResponse(
data: EntityToType::translate($type),
status: $status,
);
}
}

View File

@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
// {{{ License
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU social is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
// }}}
/**
* ActivityPub implementation for GNU social
*
* @package GNUsocial
* @category ActivityPub
* @author Diogo Peralta Cordeiro <@diogo.site>
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
namespace Plugin\ActivityPub\Util\Response;
use App\Entity\Activity as GSActivity;
use App\Util\Exception\ClientException;
use Plugin\ActivityPub\Util\Model\Activity as ModelActivity;
use Plugin\ActivityPub\Util\TypeResponse;
/**
* Provides a response in application/ld+json to GSActivity
*
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
abstract class ActivityResponse
{
/**
* Provides a response in application/ld+json to GSActivity
*
* @param GSActivity $activity
* @param int $status The response status code
* @return TypeResponse
* @throws ClientException
*/
public static function handle(GSActivity $activity, int $status = 200): TypeResponse
{
return new TypeResponse(json: ModelActivity::toJson($activity), status: $status);
}
}

View File

@ -1,25 +1,61 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
// {{{ License
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU social is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
// }}}
/**
* ActivityPub implementation for GNU social
*
* @package GNUsocial
* @category ActivityPub
* @author Diogo Peralta Cordeiro <@diogo.site>
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
namespace Plugin\ActivityPub\Util\Response;
use App\Entity\Actor;
use App\Entity\Actor as GSActor;
use App\Util\Exception\ClientException;
use Exception;
use Plugin\ActivityPub\Util\Model\EntityToType\GSActorToType;
use Plugin\ActivityPub\Util\Model\Actor as ModelActor;
use Plugin\ActivityPub\Util\TypeResponse;
/**
* Provides a response in application/ld+json to GSActors
*
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
abstract class ActorResponse
{
/**
* @param int $status The response status code
* Provides a response in application/ld+json to GSActors
*
*@throws Exception
* @param GSActor $gsactor
* @param int $status The response status code
* @return TypeResponse
* @throws ClientException
*/
public static function handle(Actor $gsactor, int $status = 200): TypeResponse
public static function handle(GSActor $gsactor, int $status = 200): TypeResponse
{
if ($gsactor->getIsLocal()) {
return new TypeResponse(data: GSActorToType::translate($gsactor), status: $status);
return new TypeResponse(json: ModelActor::toJson($gsactor), status: $status);
} else {
throw new ClientException('This is a remote actor, you should request it to its source of authority instead.');
}

View File

@ -1,25 +1,57 @@
<?php
declare(strict_types = 1);
declare(strict_types=1);
// {{{ License
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// GNU social is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with GNU social. If not, see <http://www.gnu.org/licenses/>.
// }}}
/**
* ActivityPub implementation for GNU social
*
* @package GNUsocial
* @category ActivityPub
* @author Diogo Peralta Cordeiro <@diogo.site>
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
namespace Plugin\ActivityPub\Util\Response;
use App\Entity\Note;
use Exception;
use Plugin\ActivityPub\Util\Model\EntityToType\NoteToType;
use App\Entity\Note as GSNote;
use Plugin\ActivityPub\Util\Model\Note as ModelNote;
use Plugin\ActivityPub\Util\TypeResponse;
/**
* Provides a response in application/ld+json to GSNotes
*
* @copyright 2021 Free Software Foundation, Inc http://www.fsf.org
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
abstract class NoteResponse
//class NoteResponse extends Controller
{
/**
* @param int $status The response status code
* Provides a response in application/ld+json to GSNotes
*
* @throws Exception
* @param GSNote $note
* @param int $status The response status code
* @return TypeResponse
*/
public static function handle(Note $note, int $status = 200): TypeResponse
// public function handle(Request $request, int $id): JsonResponse
public static function handle(GSNote $note, int $status = 200): TypeResponse
{
// $note = DB::findOneBy('note', ['id' => $id]);
return new TypeResponse(data: NoteToType::translate($note), status: $status);
return new TypeResponse(json: ModelNote::toJson($note), status: $status);
}
}

View File

@ -1,28 +0,0 @@
<?php
declare(strict_types = 1);
namespace Plugin\ActivityPub\Util\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
class TypeResponse extends JsonResponse
{
/**
* param Type $data // what `Type` is this?
*
* @param int $status The response status code
* @param null|mixed $data
*
* @return JsonResponse
*/
public function __construct($data = null, int $status = 202)
{
parent::__construct(
data: !\is_null($data) ? $data->toJson() : null,
status: $status,
headers: ['content-type' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'],
json: true,
);
}
}

View File

@ -1,125 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util;
use Exception;
use Plugin\ActivityPub\Util\Type\AbstractObject;
use Plugin\ActivityPub\Util\Type\TypeResolver;
use Plugin\ActivityPub\Util\Type\Validator;
/**
* \ActivityPhp\Type is a Factory for ActivityStreams 2.0 types.
*
* It provides shortcuts methods for type instantiation and more.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#types
* @see https://www.w3.org/TR/activitystreams-vocabulary/#activity-types
* @see https://www.w3.org/TR/activitystreams-vocabulary/#actor-types
* @see https://www.w3.org/TR/activitystreams-vocabulary/#object-types
*/
abstract class Type
{
/**
* Factory method to create type instance and set attributes values
*
* To see which default types are defined and their attributes:
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#types
* @see https://www.w3.org/TR/activitystreams-vocabulary/#activity-types
* @see https://www.w3.org/TR/activitystreams-vocabulary/#actor-types
* @see https://www.w3.org/TR/activitystreams-vocabulary/#object-types
*
* @param array<string,mixed>|string $type
* @param array<string,mixed> $attributes
*
* @throws Exception
*/
public static function create($type, array $attributes = []): mixed
{
if (!\is_string($type) && !\is_array($type)) {
throw new Exception(
'Type parameter must be a string or an array. Given='
. \gettype($type),
);
}
if (\is_array($type)) {
if (!isset($type['type'])) {
throw new Exception(
"Type parameter must have a 'type' key",
);
}
$attributes = $type;
}
try {
$class = \is_array($type)
? TypeResolver::getClass($type['type'])
: TypeResolver::getClass($type);
} catch (Exception $exception) {
$message = json_encode($attributes, \JSON_PRETTY_PRINT);
throw new Exception(
$exception->getMessage() . "\n{$message}",
);
}
if (\is_string($class)) {
$class = new $class();
}
foreach ($attributes as $name => $value) {
try {
$class->set($name, $value);
} catch (Exception) {
// Discard invalid properties
}
}
return $class;
}
/**
* Create an activitystream type from a JSON string
*/
public static function fromJson(string $json): AbstractObject
{
$data = json_decode($json, true);
if (json_last_error() === \JSON_ERROR_NONE
&& \is_array($data)
) {
return self::create($data);
}
throw new Exception(
sprintf(
"An error occurred during the JSON decoding.\n '%s'",
$json,
),
);
}
/**
* Add a custom validator for an attribute.
* It checks that it implements Validator\Interface
*
* @param string $name an attribute name to validate
* @param string $class A validator class name
*/
public static function addValidator(string $name, string $class): void
{
Validator::add($name, $class);
}
}

View File

@ -1,365 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type;
use Exception;
use Plugin\ActivityPub\Util\Type;
use ReflectionClass;
use ReflectionProperty;
/**
* \ActivityPhp\Type\ObjectAbstract is an abstract class for all
* Activity Streams Core Types.
*
* @see https://www.w3.org/TR/activitystreams-core/#model
*/
abstract class AbstractObject
{
/**
* Keep all properties values that have been set
*/
private array $_props = [];
protected string $type = 'AbstractObject';
/**
* Standard setter method
* - Perform content validation if a validator exists
*
* @throws Exception
*
* @return $this
*/
public function set(string $name, mixed $value): static
{
// Throws an exception when property is undefined
if ($name !== '@context') {
$this->has($name);
}
// Validate given value
if (!Validator::validate($name, $value, $this)) {
$message = "Rejected value. Type='%s', Property='%s', value='%s'";
throw new Exception(
sprintf(
$message,
static::class,
$name,
print_r($value, true),
)
. \PHP_EOL,
);
}
// @context has a special role
if ($name === '@context') {
$this->_props[$name] = $value;
// All modes and property defined
} elseif ($this->has($name)) {
$this->_props[$name] = $this->transform($value);
// Undefined property but it's valid as it was
// tested in the if clause above (no exception) so, let's include it
} else {
$this->_props[$name] = $this->transform($value);
}
return $this;
}
/**
* Affect a value to a property or an extended property
*
* @throws Exception
*/
private function transform(mixed $value): mixed
{
// Deep typing
if (\is_array($value)) {
if (isset($value['type'])) {
return Type::create($value);
} elseif (\is_int(key($value))) {
return array_map(
static function ($value) {
return \is_array($value) && isset($value['type'])
? Type::create($value)
: $value;
},
$value,
);
// Empty array, array that should not be cast as ActivityStreams types
} else {
return $value;
}
} else {
// Scalars
return $value;
}
}
/**
* Standard getter method
*
* @throws Exception
*/
public function get(string $name): mixed
{
// Throws an exception when property is undefined
$this->has($name);
return $this->_props[$name];
}
/**
* Checks that property exists
*
* @throws Exception
*/
public function has(string $name): bool
{
if (isset($this->{$name})) {
if (!\array_key_exists($name, $this->_props)) {
$this->_props[$name] = $this->{$name};
}
return true;
}
if (\array_key_exists($name, $this->_props)) {
return true;
}
$reflect = new ReflectionClass(Type::create($this->type));
$allowed_props = $reflect->getProperties(ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED);
$allowed = [];
foreach ($allowed_props as $prop) {
$allowed[] = $prop->getName();
}
if (!\in_array($name, $allowed)) {
sort($allowed);
throw new Exception(
sprintf(
'Property "%s" is not defined. Type="%s", '
. 'Class="%s"' . \PHP_EOL . 'Allowed properties: %s',
$name,
$this->get('type'),
static::class,
implode(', ', $allowed),
),
);
} else {
return false;
}
}
/**
* Get a list of all properties names
*/
public function getProperties(): array
{
return array_values(
array_unique(
array_merge(
array_keys($this->_props),
array_keys(
array_diff_key(
get_object_vars($this),
['_props' => '1'],
),
),
),
),
);
}
/**
* Get a list of all properties and their values
* as an associative array.
* Null values are not returned.
*/
public function toArray(): array
{
$keys = array_keys(
array_filter(
get_object_vars($this),
static fn ($value, $key): bool => !\is_null($value) && $key !== '_props',
\ARRAY_FILTER_USE_BOTH,
),
);
$stack = [];
// native properties
foreach ($keys as $key) {
if ($this->{$key} instanceof self) {
$stack[$key] = $this->{$key}->toArray();
} elseif (!\is_array($this->{$key})) {
$stack[$key] = $this->{$key};
} elseif (\is_array($this->{$key})) {
if (\is_int(key($this->{$key}))) {
$stack[$key] = array_map(
static function ($value) {
return $value instanceof self
? $value->toArray()
: $value;
},
$this->{$key},
);
} else {
$stack[$key] = $this->{$key};
}
}
}
// _props
foreach ($this->_props as $key => $value) {
if (\is_null($value)) {
continue;
}
if ($value instanceof self) {
$stack[$key] = $value->toArray();
} elseif (!\is_array($value)) {
$stack[$key] = $value;
} else {
if (\is_int(key($value))) {
$stack[$key] = array_map(
static function ($value) {
return $value instanceof self
? $value->toArray()
: $value;
},
$value,
);
} else {
$stack[$key] = $value;
}
}
}
return $stack;
}
/**
* Get a JSON
*
* @param null|int $options PHP JSON options
*/
public function toJson(?int $options = null): string
{
return json_encode(
value: $this->toArray(),
flags: (int) $options,
);
}
/**
* Get a copy of current object and return a new instance
*
* @throws Exception
*
* @return self A new instance of this object
*/
public function copy(): self
{
return Type::create(
$this->type,
$this->toArray(),
);
}
/**
* Extend current type properties
*
* @param mixed $default
*
* @throws Exception
*/
public function extend(string $property, mixed $default = null): void
{
if ($this->has($property)) {
return;
}
if (!\array_key_exists($property, $this->_props)) {
$this->_props[$property] = $default;
}
}
/**
* Magical isset method
*/
public function __isset(string $name): bool
{
return property_exists($this, $name)
|| \array_key_exists($name, $this->_props);
}
/**
* Magical setter method
*
* @throws Exception
*/
public function __set(string $name, mixed $value): void
{
$this->set($name, $value);
}
/**
* Magical getter method
*
* @throws Exception
*/
public function __get(string $name): mixed
{
return $this->get($name);
}
/**
* Overloading methods
*
* @throws Exception
*/
public function __call(string $name, ?array $arguments = []): mixed
{
// Getters
if (str_starts_with($name, 'get')) {
$attr = lcfirst(mb_substr($name, 3));
return $this->get($attr);
}
// Setters
if (str_starts_with($name, 'set')) {
if (\count($arguments) === 1) {
$attr = lcfirst(mb_substr($name, 3));
return $this->set($attr, $arguments[0]);
} else {
throw new Exception(
sprintf(
'Expected exactly one argument for method "%s()"',
$name,
),
);
}
}
throw new Exception(
sprintf(
'Method "%s" is not defined',
$name,
),
);
}
}

View File

@ -1,110 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Core;
/**
* \Plugin\ActivityPub\Util\Type\Core\AbstractActivity implements only common
* attributes between Activity and IntransitiveActivity.
*
* It SHOULD NOT be used as if.
*
* Please use IntransitiveActivity or Activity instead.
*
* @see https://www.w3.org/TR/activitystreams-core/#activities
* @see https://www.w3.org/TR/activitystreams-core/#intransitiveactivities
*/
abstract class AbstractActivity extends ObjectType
{
public string $id;
/**
* Describes one or more entities that either performed or are
* expected to perform the activity.
* Any single activity can have multiple actors.
* The actor MAY be specified using an indirect Link.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-actor
*
* @var string
* | \Plugin\ActivityPub\Util\Type\Extended\AbstractActor
* | array<Actor>
* | array<Link>
* | Link
*/
protected string $actor;
/**
* The indirect object, or target, of the activity.
* The precise meaning of the target is largely dependent on the
* type of action being described but will often be the object of
* the English preposition "to".
* For instance, in the activity "John added a movie to his
* wishlist", the target of the activity is John's wishlist.
* An activity can have more than one target.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-target
*
* @var string
* | ObjectType
* | array<ObjectType>
* | Link
* | array<Link>
*/
protected string $target;
/**
* Describes the result of the activity.
* For instance, if a particular action results in the creation of
* a new resource, the result property can be used to describe
* that new resource.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-result
*
* @var string
* | ObjectType
* | Link
* | null
*/
protected string $result;
/**
* An indirect object of the activity from which the
* activity is directed.
* The precise meaning of the origin is the object of the English
* preposition "from".
* For instance, in the activity "John moved an item to List B
* from List A", the origin of the activity is "List A".
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-origin
*
* @var string
* | ObjectType
* | Link
* | null
*/
protected string $origin;
/**
* One or more objects used (or to be used) in the completion of an
* Activity.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-instrument
*
* @var string
* | ObjectType
* | Link
* | null
*/
protected string $instrument;
}

View File

@ -1,45 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Core;
use Plugin\ActivityPub\Util\Type;
/**
* \Plugin\ActivityPub\Util\Type\Core\Activity is an implementation of one of the
* Activity Streams Core Types.
*
* Activity objects are specializations of the base Object type that
* provide information about actions that have either already occurred,
* are in the process of occurring, or may occur in the future.
*
* @see https://www.w3.org/TR/activitystreams-core/#activities
*/
class Activity extends AbstractActivity
{
protected string $type = 'Activity';
/**
* Describes the direct object of the activity.
* For instance, in the activity "John added a movie to his
* wishlist", the object of the activity is the movie added.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-object-term
*
* @var string
* | ObjectType
* | Link
* | null
*/
protected ObjectType $object;
}

View File

@ -1,103 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Core;
/**
* \Plugin\ActivityPub\Util\Type\Core\Collection is an implementation of one of the
* Activity Streams Core Types.
*
* Collection objects are a specialization of the base Object that serve
* as a container for other Objects or Links.
*
* @see https://www.w3.org/TR/activitystreams-core/#collections
*/
class Collection extends ObjectType
{
protected string $type = 'Collection';
public string $id;
/**
* A non-negative integer specifying the total number of objects
* contained by the logical view of the collection.
* This number might not reflect the actual number of items
* serialized within the Collection object instance.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-totalitems
*/
protected int $totalItems;
/**
* In a paged Collection, indicates the page that contains the most
* recently updated member items.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-current
*
* @var string
* | Link
* | CollectionPage
* | null
*/
protected string $current;
/**
* The furthest preceding page of items in the collection.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-last
*
* @var string
* | Link
* | CollectionPage
* | null
*/
protected string $first;
/**
* The furthest proceeding page of the collection.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-last
*
* @var string
* | Link
* | CollectionPage
* | null
*/
protected string $last;
/**
* The items contained in a collection.
* The items are considered as unordered.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-items
*
* @var array
* | Link
* | array<Link>
* | array<ObjectType>
*/
protected array $items = [];
/**
* The items contained in a collection.
* The items are considered as ordered.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-items
*
* @var array
* | Link
* | array<Link>
* | array<ObjectType>
*/
protected array $orderedItems = [];
}

View File

@ -1,66 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Core;
/**
* CollectionPage is an implementation of one
* of the Activity Streams Core Types.
*
* Used to represent distinct subsets of items from a Collection.
*
* @see https://www.w3.org/TR/activitystreams-core/#paging
*/
class CollectionPage extends Collection
{
protected string $type = 'CollectionPage';
public string $id;
/**
* Identifies the Collection to which CollectionPage objects items
* belong.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-partof
*
* @var string
* | Link
* | Collection
* | null
*/
protected string $partOf;
/**
* Indicates the next page of items.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-next
*
* @var string
* | Link
* | CollectionPage
* | null
*/
protected string $next;
/**
* Identifies the previous page of items.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-prev
*
* @var string
* | Link
* | CollectionPage
* | null
*/
protected string $prev;
}

View File

@ -1,29 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Core;
/**
* \Plugin\ActivityPub\Util\Type\Core\IntransitiveActivity is an implementation of
* one of the Activity Streams Core Types.
*
* IntransitiveActivity objects are specializations of the Activity type
* that represent intransitive actions. IntransitiveActivity objects do
* not have an object property.
*
* @see https://www.w3.org/TR/activitystreams-core/#intransitiveactivities
*/
class IntransitiveActivity extends AbstractActivity
{
protected string $type = 'IntransitiveActivity';
}

View File

@ -1,116 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Core;
use Plugin\ActivityPub\Util\Type\AbstractObject;
/**
* \Plugin\ActivityPub\Util\Type\Core\Link is an implementation of one of the
* Activity Streams Core Types.
*
* A Link describes a qualified, indirect reference to another resource.
* The properties of the Link object are not the properties of the
* referenced resource, but are provided as hints for rendering agents
* to understand how to make use of the resource.
*
* @see https://www.w3.org/TR/activitystreams-core/#link
*/
class Link extends AbstractObject
{
protected string $type = 'Link';
protected string $id;
/**
* A simple, human-readable, plain-text name for the object.
* HTML markup MUST NOT be included.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-name
*
* @var null|string xsd:string
*/
protected ?string $name;
/**
* The name MAY be expressed using multiple language-tagged values.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-name
*
* @var null|array<string,string> rdf:langString
*/
protected ?array $nameMap;
/**
* The target resource pointed to by a Link.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-href
*/
protected ?string $href;
/**
* Hints as to the language used by the target resource.
* Value MUST be a BCP47 Language-Tag.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-hreflang
*/
protected ?string $hreflang;
/**
* The MIME media type of the referenced resource.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-mediatype
*/
protected ?string $mediaType;
/**
* A link relation associated with a Link.
* The value MUST conform to both the HTML5
* and RFC5988 "link relation" definitions.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-rel
*/
protected string|array|null $rel;
/**
* Specifies a hint as to the rendering height
* in device-independentpixels of the linked resource
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-height
*
* @var null|int A non negative integer
*/
protected ?int $height;
/**
* An entity that provides a preview of this link.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-preview
*
* @var string
* | Object
* | Link
* | null
*/
protected string $preview;
/**
* On a Link, specifies a hint as to the rendering width in
* device-independent pixels of the linked resource.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-width
*
* @var null|int A non negative integer
*/
protected ?int $width;
}

View File

@ -1,423 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Core;
use Plugin\ActivityPub\Util\Type\AbstractObject;
/**
* ObjectType is an implementation of one of the
* Activity Streams Core Types.
*
* The Object is the primary base type for the Activity Streams
* vocabulary.
*
* Note: Object is a reserved keyword in PHP. It has been suffixed with
* 'Type' for this reason.
*
* @see https://www.w3.org/TR/activitystreams-core/#object
*/
class ObjectType extends AbstractObject
{
/**
* The object's unique global identifier
*
* @see https://www.w3.org/TR/activitypub/#obj-id
*/
public string $id;
protected string $type = 'Object';
/**
* A resource attached or related to an object that potentially
* requires special handling.
* The intent is to provide a model that is at least semantically
* similar to attachments in email.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-attachment
*
* @var string
* | ObjectType
* | Link
* | array<ObjectType>
* | array<Link>
* | null
*/
protected string $attachment;
/**
* One or more entities to which this object is attributed.
* The attributed entities might not be Actors. For instance, an
* object might be attributed to the completion of another activity.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-attributedto
*
* @var string
* | ObjectType
* | Link
* | array<ObjectType>
* | array<Link>
* | null
*/
protected string $attributedTo;
/**
* One or more entities that represent the total population of
* entities for which the object can considered to be relevant.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-audience
*
* @var string
* | ObjectType
* | Link
* | array<ObjectType>
* | array<Link>
* | null
*/
protected string $audience;
/**
* The content or textual representation of the Object encoded as a
* JSON string. By default, the value of content is HTML.
* The mediaType property can be used in the object to indicate a
* different content type.
*
* The content MAY be expressed using multiple language-tagged
* values.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-content
*/
protected ?string $content;
/**
* The context within which the object exists or an activity was
* performed.
* The notion of "context" used is intentionally vague.
* The intended function is to serve as a means of grouping objects
* and activities that share a common originating context or
* purpose. An example could be all activities relating to a common
* project or event.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-context
*
* @var string
* | ObjectType
* | Link
* | null
*/
protected string $context;
/**
* The content MAY be expressed using multiple language-tagged
* values.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-content
*/
protected ?array $contentMap;
/**
* A simple, human-readable, plain-text name for the object.
* HTML markup MUST NOT be included.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-name
*
* @var null|string xsd:string
*/
protected ?string $name;
/**
* The name MAY be expressed using multiple language-tagged values.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-name
*
* @var null|array rdf:langString
*/
protected ?array $nameMap;
/**
* The date and time describing the actual or expected ending time
* of the object.
* When used with an Activity object, for instance, the endTime
* property specifies the moment the activity concluded or
* is expected to conclude.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-endtime
*/
protected ?string $endTime;
/**
* The entity (e.g. an application) that generated the object.
*/
protected ?string $generator;
/**
* An entity that describes an icon for this object.
* The image should have an aspect ratio of one (horizontal)
* to one (vertical) and should be suitable for presentation
* at a small size.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-icon
*
* @var string
* | Image
* | Link
* | array<Image>
* | array<Link>
* | null
*/
protected string $icon;
/**
* An entity that describes an image for this object.
* Unlike the icon property, there are no aspect ratio
* or display size limitations assumed.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-image-term
*
* @var string
* | Image
* | Link
* | array<Image>
* | array<Link>
* | null
*/
protected string $image;
/**
* One or more entities for which this object is considered a
* response.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-inreplyto
*
* @var string
* | ObjectType
* | Link
* | array<ObjectType>
* | array<Link>
* | null
*/
protected string $inReplyTo;
/**
* One or more physical or logical locations associated with the
* object.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-location
*
* @var string
* | ObjectType
* | Link
* | array<ObjectType>
* | array<Link>
* | null
*/
protected string $location;
/**
* An entity that provides a preview of this object.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-preview
*
* @var string
* | ObjectType
* | Link
* | null
*/
protected string $preview;
/**
* The date and time at which the object was published
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-published
*
* @var null|string xsd:dateTime
*/
protected ?string $published;
/**
* A Collection containing objects considered to be responses to
* this object.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-replies
*
* @var string
* | Collection
* | Link
* | null
*/
protected string $replies;
/**
* The date and time describing the actual or expected starting time
* of the object.
* When used with an Activity object, for instance, the startTime
* property specifies the moment the activity began
* or is scheduled to begin.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-starttime
*
* @var null|string xsd:dateTime
*/
protected ?string $startTime;
/**
* A natural language summarization of the object encoded as HTML.
* Multiple language tagged summaries MAY be provided.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-summary
*
* @var string
* | ObjectType
* | Link
* | null
*/
protected string $summary;
/**
* The content MAY be expressed using multiple language-tagged
* values.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-summary
*
* @var null|array<string>
*/
protected mixed $summaryMap;
/**
* One or more "tags" that have been associated with an objects.
* A tag can be any kind of Object.
* The key difference between attachment and tag is that the former
* implies association by inclusion, while the latter implies
* associated by reference.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-tag
*
* @var string
* | ObjectType
* | Link
* | array<ObjectType>
* | array<Link>
* | null
*/
protected string $tag;
/**
* The date and time at which the object was updated
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-updated
*
* @var null|string xsd:dateTime
*/
protected ?string $updated;
/**
* One or more links to representations of the object.
*
* @var string
* | array<string>
* | Link
* | array<Link>
* | null
*/
protected string $url;
/**
* An entity considered to be part of the public primary audience
* of an Object
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-to
*
* @var string
* | ObjectType
* | Link
* | array<ObjectType>
* | array<Link>
* | null
*/
protected string $to;
/**
* An Object that is part of the private primary audience of this
* Object.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-bto
*
* @var string
* | ObjectType
* | Link
* | array<ObjectType>
* | array<Link>
* | null
*/
protected string $bto;
/**
* An Object that is part of the public secondary audience of this
* Object.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-cc
*
* @var string
* | ObjectType
* | Link
* | array<ObjectType>
* | array<Link>
* | null
*/
protected string $cc;
/**
* One or more Objects that are part of the private secondary
* audience of this Object.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-bcc
*
* @var string
* | ObjectType
* | Link
* | array<ObjectType>
* | array<Link>
* | null
*/
protected string $bcc;
/**
* The MIME media type of the value of the content property.
* If not specified, the content property is assumed to contain
* text/html content.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-mediatype
*/
protected ?string $mediaType;
/**
* When the object describes a time-bound resource, such as an audio
* or video, a meeting, etc, the duration property indicates the
* object's approximate duration.
* The value MUST be expressed as an xsd:duration as defined by
* xmlschema11-2, section 3.3.6 (e.g. a period of 5 seconds is
* represented as "PT5S").
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-duration
*/
protected ?string $duration;
/**
* Intended to convey some sort of source from which the content
* markup was derived, as a form of provenance, or to support
* future editing by clients.
*
* @see https://www.w3.org/TR/activitypub/#source-property
*/
protected ObjectType $source;
}

View File

@ -1,28 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Core;
/**
* \Plugin\ActivityPub\Util\Type\Core\OrderedCollection is an implementation of one
* of the Activity Streams Core Types.
*
* A subtype of Collection in which members of the logical collection
* are assumed to always be strictly ordered.
*
* @see https://www.w3.org/TR/activitystreams-core/#collections
*/
class OrderedCollection extends Collection
{
protected string $type = 'OrderedCollection';
}

View File

@ -1,38 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Core;
/**
* \Plugin\ActivityPub\Util\Type\Core\OrderedCollection is an implementation of one
* of the Activity Streams Core Types.
*
* The OrderedCollectionPage type extends from both CollectionPage and
* OrderedCollection. In addition to the properties inherited from each
* of those, the OrderedCollectionPage may contain an additional
* startIndex property whose value indicates the relative index position
* of the first item contained by the page within the OrderedCollection
* to which the page belongs.
*
* @see https://www.w3.org/TR/activitystreams-core/#paging
*/
class OrderedCollectionPage extends CollectionPage
{
protected string $type = 'OrderedCollectionPage';
/**
* A non-negative integer value identifying the relative position
* within the logical view of a strictly ordered collection.
*/
protected int $startIndex;
}

View File

@ -1,114 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended;
use Plugin\ActivityPub\Util\Type\Core\ObjectType;
use Plugin\ActivityPub\Util\Type\Core\OrderedCollection;
/**
* \ActivityPhp\Type\Extended\AbstractActor is an abstract class that
* provides dedicated Actor's properties
*/
abstract class AbstractActor extends ObjectType
{
/**
* A reference to an ActivityStreams OrderedCollection comprised of
* all the messages received by the actor.
*
* @see https://www.w3.org/TR/activitypub/#inbox
*
* @var OrderedCollection
* | \ActivityPhp\Type\Core\OrderedCollectionPage
* | null
*/
protected OrderedCollection $inbox;
/**
* A reference to an ActivityStreams OrderedCollection comprised of
* all the messages produced by the actor.
*
* @see https://www.w3.org/TR/activitypub/#outbox
*
* @var OrderedCollection
* | \ActivityPhp\Type\Core\OrderedCollectionPage
* | null
*/
protected OrderedCollection $outbox;
/**
* A link to an ActivityStreams collection of the actors that this
* actor is following.
*
* @see https://www.w3.org/TR/activitypub/#following
*/
protected string $following;
/**
* A link to an ActivityStreams collection of the actors that
* follow this actor.
*
* @see https://www.w3.org/TR/activitypub/#followers
*/
protected string $followers;
/**
* A link to an ActivityStreams collection of objects this actor has
* liked.
*
* @see https://www.w3.org/TR/activitypub/#liked
*/
protected string $liked;
/**
* A list of supplementary Collections which may be of interest.
*
* @see https://www.w3.org/TR/activitypub/#streams-property
*/
protected array $streams = [];
/**
* A short username which may be used to refer to the actor, with no
* uniqueness guarantees.
*
* @see https://www.w3.org/TR/activitypub/#preferredUsername
*/
protected ?string $preferredUsername;
/**
* A JSON object which maps additional typically server/domain-wide
* endpoints which may be useful either for this actor or someone
* referencing this actor. This mapping may be nested inside the
* actor document as the value or may be a link to a JSON-LD
* document with these properties.
*
* @see https://www.w3.org/TR/activitypub/#endpoints
*/
protected string|array|null $endpoints;
/**
* It's not part of the ActivityPub protocol, but it's a quite common
* practice handling an actor public key with a publicKey array:
* [
* 'id' => 'https://my-example.com/actor#main-key'
* 'owner' => 'https://my-example.com/actor',
* 'publicKeyPem' => '-----BEGIN PUBLIC KEY-----
* MIIBI [...]
* DQIDAQAB
* -----END PUBLIC KEY-----'
* ]
*
* @see https://www.w3.org/wiki/SocialCG/ActivityPub/Authentication_Authorization#Signing_requests_using_HTTP_Signatures
*/
protected string|array|null $publicKey;
}

View File

@ -1,31 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Activity;
use Plugin\ActivityPub\Util\Type\Core\Activity;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Activity\Accept is an implementation of
* one of the Activity Streams Extended Types.
*
* Indicates that the actor accepts the object. The target property can
* be used in certain circumstances to indicate the context into which
* the object has been accepted.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-accept
*/
class Accept extends Activity
{
protected string $type = 'Accept';
}

View File

@ -1,30 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Activity;
use Plugin\ActivityPub\Util\Type\Core\Activity;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Activity\Announce is an implementation of
* one of the Activity Streams Extended Types.
*
* Indicates that the actor is calling the target's attention the
* object.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-announce
*/
class Announce extends Activity
{
protected string $type = 'Announce';
}

View File

@ -1,30 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Activity;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Activity\Block is an implementation of
* one of the Activity Streams Extended Types.
*
* Indicates that the actor is blocking the object. Blocking is a
* stronger form of Ignore. The typical use is to support social systems
* that allow one user to block activities or content of other users.
* The target and origin typically have no defined meaning.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-block
*/
class Block extends Ignore
{
protected string $type = 'Block';
}

View File

@ -1,29 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Activity;
use Plugin\ActivityPub\Util\Type\Core\Activity;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Activity\Create is an implementation of
* one of the Activity Streams Extended Types.
*
* Indicates that the actor has created the object.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-create
*/
class Create extends Activity
{
protected string $type = 'Create';
}

View File

@ -1,30 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Activity;
use Plugin\ActivityPub\Util\Type\Core\Activity;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Activity\Delete is an implementation of
* one of the Activity Streams Extended Types.
*
* Indicates that the actor has deleted the object. If specified, the
* origin indicates the context from which the object was deleted.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-delete
*/
class Delete extends Activity
{
protected string $type = 'Delete';
}

View File

@ -1,33 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Activity;
use Plugin\ActivityPub\Util\Type\Core\Activity;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Activity\Follow is an implementation of
* one of the Activity Streams Extended Types.
*
* Indicates that the actor is "following" the object. Following is
* defined in the sense typically used within Social systems in which
* the actor is interested in any activity performed by or on the
* object.
* The target and origin typically have no defined meaning.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-follow
*/
class Follow extends Activity
{
protected string $type = 'Follow';
}

View File

@ -1,30 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Activity;
use Plugin\ActivityPub\Util\Type\Core\Activity;
/**
* \ActivityPhp\Type\Extended\Activity\Ignore is an implementation of
* one of the Activity Streams Extended Types.
*
* Indicates that the actor is ignoring the object.
* The target and origin typically have no defined meaning.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-ignore
*/
class Ignore extends Activity
{
protected string $type = 'Ignore';
}

View File

@ -1,30 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Activity;
use Plugin\ActivityPub\Util\Type\Core\Activity;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Activity\Join is an implementation of
* one of the Activity Streams Extended Types.
*
* Indicates that the actor has joined the object.
* The target and origin typically have no defined meaning.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-join
*/
class Join extends Activity
{
protected string $type = 'Join';
}

View File

@ -1,30 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Activity;
use Plugin\ActivityPub\Util\Type\Core\Activity;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Activity\Leave is an implementation of
* one of the Activity Streams Extended Types.
*
* Indicates that the actor has left the object.
* The target and origin typically have no meaning.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-leave
*/
class Leave extends Activity
{
protected string $type = 'Leave';
}

View File

@ -1,30 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Activity;
use Plugin\ActivityPub\Util\Type\Core\Activity;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Activity\Like is an implementation of
* one of the Activity Streams Extended Types.
*
* Indicates that the actor likes, recommends or endorses the object.
* The target and origin typically have no defined meaning.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-like
*/
class Like extends Activity
{
protected string $type = 'Like';
}

View File

@ -1,75 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Activity;
use Plugin\ActivityPub\Util\Type\Core\IntransitiveActivity;
use Plugin\ActivityPub\Util\Type\Core\ObjectType;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Activity\Question is an implementation of
* one of the Activity Streams Extended Types.
*
* Represents a question being asked. Question objects are an extension
* of IntransitiveActivity. That is, the Question object is an Activity,
* but the direct object is the question itself, and therefore it would
* not contain an object property.
*
* Either of the anyOf and oneOf properties MAY be used to express
* possible answers, but a Question object MUST NOT have both properties
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-move
*/
class Question extends IntransitiveActivity
{
protected string $type = 'Question';
/**
* An exclusive option for a Question
* Use of oneOf implies that the Question can have only a
* single answer.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-oneof
*
* @var array<ObjectType>
* | array<\Plugin\ActivityPub\Util\Type\Core\Link>
* | null
*/
protected array $oneOf;
/**
* An inclusive option for a Question.
* Use of anyOf implies that the Question can have multiple answers.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-anyof
*
* @var array<ObjectType>
* | array<\Plugin\ActivityPub\Util\Type\Core\Link>
* | null
*/
protected array $anyOf;
/**
* Indicates that a question has been closed, and answers are no
* longer accepted.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-closed
*
* @var ObjectType
* | \Plugin\ActivityPub\Util\Type\Core\Link
* | \DateTime
* | bool
* | null
*/
protected ObjectType $closed;
}

View File

@ -1,30 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Activity;
use Plugin\ActivityPub\Util\Type\Core\Activity;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Activity\Reject is an implementation of
* one of the Activity Streams Extended Types.
*
* Indicates that the actor is rejecting the object.
* The target and origin typically have no defined meaning.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-reject
*/
class Reject extends Activity
{
protected string $type = 'Reject';
}

View File

@ -1,31 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Activity;
use Plugin\ActivityPub\Util\Type\Core\Activity;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Activity\Remove is an implementation of
* one of the Activity Streams Extended Types.
*
* Indicates that the actor is removing the object.
* If specified, the origin indicates the context from which the object
* is being removed.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-remove
*/
class Remove extends Activity
{
protected string $type = 'Remove';
}

View File

@ -1,35 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Activity;
use Plugin\ActivityPub\Util\Type\Core\Activity;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Activity\Undo is an implementation of
* one of the Activity Streams Extended Types.
*
* Indicates that the actor is undoing the object. In most cases, the
* object will be an Activity describing some previously performed
* action (for instance, a person may have previously "liked" an article
* but, for whatever reason, might choose to undo that like at some
* later point in time).
*
* The target and origin typically have no defined meaning.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-undo
*/
class Undo extends Activity
{
protected string $type = 'Undo';
}

View File

@ -1,33 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Activity;
use Plugin\ActivityPub\Util\Type\Core\Activity;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Activity\Update is an implementation of
* one of the Activity Streams Extended Types.
*
* Indicates that the actor has updated the object. Note, however, that
* this vocabulary does not define a mechanism for describing the actual
* set of modifications made to object.
*
* The target and origin typically have no defined meaning.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-update
*/
class Update extends Activity
{
protected string $type = 'Update';
}

View File

@ -1,29 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Actor;
use Plugin\ActivityPub\Util\Type\Extended\AbstractActor;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Actor\Application is an implementation of
* one of the Activity Streams Extended Types.
*
* Describes a software application.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-application
*/
class Application extends AbstractActor
{
protected string $type = 'Application';
}

View File

@ -1,29 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Actor;
use Plugin\ActivityPub\Util\Type\Extended\AbstractActor;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Actor\Group is an implementation of
* one of the Activity Streams Extended Types.
*
* Represents a formal or informal collective of Actors.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-group
*/
class Group extends AbstractActor
{
protected string $type = 'Group';
}

View File

@ -1,29 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Actor;
use Plugin\ActivityPub\Util\Type\Extended\AbstractActor;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Actor\Organization is an implementation of
* one of the Activity Streams Extended Types.
*
* Represents a formal or informal collective of Actors.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-organization
*/
class Organization extends AbstractActor
{
protected string $type = 'Organization';
}

View File

@ -1,29 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Actor;
use Plugin\ActivityPub\Util\Type\Extended\AbstractActor;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Actor\Person is an implementation of
* one of the Activity Streams Extended Types.
*
* Represents an individual person.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-person
*/
class Person extends AbstractActor
{
protected string $type = 'Person';
}

View File

@ -1,29 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Actor;
use Plugin\ActivityPub\Util\Type\Extended\AbstractActor;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Actor\Service is an implementation of
* one of the Activity Streams Extended Types.
*
* Represents a service of any kind.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-service
*/
class Service extends AbstractActor
{
protected string $type = 'Service';
}

View File

@ -1,29 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Object;
use Plugin\ActivityPub\Util\Type\Core\ObjectType;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Object\Article is an implementation of
* one of the Activity Streams Extended Types.
*
* Represents any kind of multi-paragraph written work.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-article
*/
class Article extends ObjectType
{
protected string $type = 'Article';
}

View File

@ -1,27 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Object;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Object\Audio is an implementation of
* one of the Activity Streams Extended Types.
*
* Represents a document of any kind.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-audio
*/
class Audio extends Document
{
protected string $type = 'Audio';
}

View File

@ -1,29 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Object;
use Plugin\ActivityPub\Util\Type\Core\ObjectType;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Object\Document is an implementation of
* one of the Activity Streams Extended Types.
*
* Represents an audio document of any kind.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-document
*/
class Document extends ObjectType
{
protected string $type = 'Document';
}

View File

@ -1,29 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Object;
use Plugin\ActivityPub\Util\Type\Core\ObjectType;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Object\Event is an implementation of
* one of the Activity Streams Extended Types.
*
* Represents any kind of event.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-event
*/
class Event extends ObjectType
{
protected string $type = 'Event';
}

View File

@ -1,27 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Object;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Object\Image is an implementation of
* one of the Activity Streams Extended Types.
*
* An image document of any kind.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-image
*/
class Image extends Document
{
protected string $type = 'Image';
}

View File

@ -1,29 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Object;
use Plugin\ActivityPub\Util\Type\Core\Link;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Object\Mention is an implementation of
* one of the Activity Streams Extended Types.
*
* A specialized Link that represents an @mention.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-mention
*/
class Mention extends Link
{
protected string $type = 'Mention';
}

View File

@ -1,30 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Object;
use Plugin\ActivityPub\Util\Type\Core\ObjectType;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Object\Note is an implementation of
* one of the Activity Streams Extended Types.
*
* Represents a short written work typically less than a single
* paragraph in length.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-note
*/
class Note extends ObjectType
{
protected string $type = 'Note';
}

View File

@ -1,27 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Object;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Object\Page is an implementation of
* one of the Activity Streams Extended Types.
*
* Represents a Web Page.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-page
*/
class Page extends Document
{
protected string $type = 'Page';
}

View File

@ -1,83 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Object;
use Plugin\ActivityPub\Util\Type\Core\ObjectType;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Object\Place is an implementation of
* one of the Activity Streams Extended Types.
*
* Represents a logical or physical location.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-place
*/
class Place extends ObjectType
{
protected string $type = 'Place';
/**
* Indicates the accuracy of position coordinates on a Place
* objects. Expressed in properties of percentage.
* e.g. "94.0" means "94.0% accurate".
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-accuracy
*/
protected ?float $accuracy;
/**
* The altitude of a place.
* The measurement units is indicated using the units' property.
* If units is not specified, the default is assumed to be "m"
* indicating meters.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-altitude
*/
protected ?float $altitude;
/**
* The latitude of a place.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-latitude
*/
protected int|null|float $latitude;
/**
* The longitude of a place.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-longitude
*/
protected int|null|float $longitude;
/**
* The radius from the given latitude and longitude for a Place.
* The units are expressed by the units' property.
* If units is not specified, the default is assumed to be "m"
* indicating "meters".
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-radius
*/
protected int|null|float $radius;
/**
* Specifies the measurement units for the radius and altitude
* properties on a Place object.
* If not specified, the default is assumed to be "m" for "meters".
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-units
*
* "cm" | " feet" | " inches" | " km" | " m" | " miles" | xsd:anyURI
*/
protected string $units;
}

View File

@ -1,37 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Object;
use Plugin\ActivityPub\Util\Type\Core\ObjectType;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Object\Profile is an implementation of
* one of the Activity Streams Extended Types.
*
* A Profile is a content object that describes another Object,
* typically used to describe Actor Type objects.
* The describes property is used to reference the object being
* described by the profile.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-profile
*/
class Profile extends ObjectType
{
protected string $type = 'Profile';
/**
* Identify the object described by the Profile.
*/
protected ObjectType $describes;
}

View File

@ -1,47 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Object;
use Plugin\ActivityPub\Util\Type\Core\ObjectType;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Object\Tombstone is an implementation of
* one of the Activity Streams Extended Types.
*
* A Tombstone represents a content object that has been deleted. It can
* be used in Collections to signify that there used to be an object at
* this position, but it has been deleted.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-tombstone
*/
class Tombstone extends ObjectType
{
protected string $type = 'Tombstone';
/**
* The type of the object that was deleted.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-formertype
*/
protected ?string $formerType;
/**
* A timestamp for when the object was deleted.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-deleted
*
* @var null|string xsd:dateTime formatted
*/
protected ?string $deleted;
}

View File

@ -1,27 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Extended\Object;
/**
* \Plugin\ActivityPub\Util\Type\Extended\Object\Video is an implementation of
* one of the Activity Streams Extended Types.
*
* Represents a video document of any kind.
*
* @see https://www.w3.org/TR/activitystreams-vocabulary/#dfn-video
*/
class Video extends Document
{
protected string $type = 'Video';
}

View File

@ -1,134 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type;
use Exception;
/**
* \Plugin\ActivityPub\Util\Type\TypeResolver is an abstract class for
* resolving class names called by their short names (AS types).
*/
abstract class TypeResolver
{
/**
* A list of core types
*/
protected static array $coreTypes = [
'Activity', 'Collection', 'CollectionPage',
'IntransitiveActivity', 'Link', 'ObjectType',
'OrderedCollection', 'OrderedCollectionPage',
'Object',
];
/**
* A list of actor types
*/
protected static array $actorTypes = [
'Application', 'Group', 'Organization', 'Person', 'Service',
];
/**
* A list of activity types
*/
protected static array $activityTypes = [
'Accept', 'Add', 'Announce', 'Block',
'Create', 'Delete', 'Follow', 'Ignore',
'Invite', 'Join', 'Leave', 'Like',
'Question', 'Reject', 'Remove', 'Undo',
];
/**
* A list of object types
*/
protected static array $objectTypes = [
'Article', 'Audio', 'Document', 'Event', 'Image',
'Mention', 'Note', 'Page', 'Place', 'Profile',
'Tombstone', 'Video',
];
/**
* Get namespaced class for a given short type
*
* @throws Exception
*
* @return string Related namespace
* @throw \Exception if a namespace was not found.
*/
public static function getClass(string $type): string
{
$ns = __NAMESPACE__;
if ($type == 'Object') {
$type .= 'Type';
}
switch ($type) {
case \in_array($type, self::$coreTypes):
$ns .= '\Core';
break;
case \in_array($type, self::$activityTypes):
$ns .= '\Extended\Activity';
break;
case \in_array($type, self::$actorTypes):
$ns .= '\Extended\Actor';
break;
case \in_array($type, self::$objectTypes):
$ns .= '\Extended\Object';
break;
default:
throw new Exception(
"Undefined scope for type '{$type}'",
);
}
return $ns . '\\' . $type;
}
/**
* Validate an object pool type with type attribute
*
* @param string $poolname An expected pool name
*/
public static function isScope(object $item, string $poolname = 'all'): bool
{
if (!\is_object($item)
|| !isset($item->type)
|| !\is_string($item->type)
) {
return false;
}
return match (mb_strtolower($poolname)) {
'all' => self::exists($item->type),
'actor' => \in_array($item->type, self::$actorTypes),
default => false,
};
}
/**
* Verify that a type exists
*/
public static function exists(string $name): bool
{
return \in_array(
$name,
array_merge(
self::$coreTypes,
self::$activityTypes,
self::$actorTypes,
self::$objectTypes,
),
);
}
}

View File

@ -1,472 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type;
use DateInterval;
use DateTime;
use Exception;
use Plugin\ActivityPub\Util\Type;
/**
* \Plugin\ActivityPub\Util\Type\Util is an abstract class for
* supporting validators checks & transformations.
*/
abstract class Util
{
/**
* Allowed units
*
* @var array<string>
*/
protected static array $units = [
'cm', 'feet', 'inches', 'km', 'm', 'miles',
];
/**
* Transform an array into an ActivityStreams type
*
* @throws Exception
*
* @return AbstractObject|array an ActivityStreams
* type or given array if type key is not defined
*/
public static function arrayToType(array $item): AbstractObject|array
{
// Maybe an array representing an AS object
// It must have a type key
if (isset($item['type'])) {
return Type::create($item['type'], $item);
}
return $item;
}
/**
* Validate an URL
*/
public static function validateUrl(mixed $value): bool
{
return \is_string($value)
&& filter_var($value, \FILTER_VALIDATE_URL) !== false
&& \in_array(
parse_url($value, \PHP_URL_SCHEME),
['http', 'https', 'magnet'],
);
}
/**
* Validate a magnet link
*
* @see https://en.wikipedia.org/wiki/Magnet_URI_scheme
*
* @todo Make a better validation as xs is not the only parameter
*/
public static function validateMagnet(mixed $value): bool
{
return \is_string($value)
&& mb_strlen($value) < 262144
&& preg_match(
'#^magnet:\?xs=(https?)://.*$#iu',
urldecode($value),
);
}
/**
* Validate an OStatus tag string
*/
public static function validateOstatusTag(mixed $value): bool
{
return \is_string($value)
&& mb_strlen($value) < 262144
&& preg_match(
'#^tag:([\w\-\.]+),([\d]{4}-[\d]{2}-[\d]{2}):([\w])+Id=([\d]+):objectType=([\w]+)#iu',
$value,
);
}
/**
* Validate a rel attribute value.
*
* @see https://tools.ietf.org/html/rfc5988
*/
public static function validateRel(string $value): bool
{
return \is_string($value)
&& preg_match("/^[^\\s\r\n\\,]+\\z/i", $value);
}
/**
* Validate a non negative integer.
*/
public static function validateNonNegativeInteger(int $value): bool
{
return \is_int($value)
&& $value >= 0;
}
/**
* Validate a non negative number.
*/
public static function validateNonNegativeNumber(float|int $value): bool
{
return is_numeric($value)
&& $value >= 0;
}
/**
* Validate units format.
*/
public static function validateUnits(string $value): bool
{
if (\is_string($value)) {
if (\in_array($value, self::$units)
|| self::validateUrl($value)
) {
return true;
}
}
return false;
}
/**
* Validate an Object type
*
* @throws Exception
*/
public static function validateObject(object $item): bool
{
return self::hasProperties($item, ['type'])
&& \is_string($item->type)
&& $item->type === 'Object';
}
/**
* Decode a JSON string
*
* @throws Exception if JSON decoding process has failed
*/
public static function decodeJson(string $value): array
{
$json = json_decode($value, true);
if (json_last_error() !== \JSON_ERROR_NONE) {
throw new Exception(
'JSON decoding failed for string: ' . $value,
);
}
return $json;
}
/**
* Checks that all properties exist for a stdClass
*
* @param bool $strict If true throws an \Exception,
* otherwise, returns false
*
* @throws Exception if a property is not set
*/
public static function hasProperties(
object $item,
array $properties,
bool $strict = false,
): bool {
foreach ($properties as $property) {
if (!property_exists($item, $property)) {
if ($strict) {
throw new Exception(
sprintf(
'Attribute "%s" MUST be set for item: %s',
$property,
print_r($item, true),
),
);
}
return false;
}
}
return true;
}
/**
* Validate a reference with a Link or an Object with a URL
*
* @throws Exception
*/
public static function isLinkOrUrlObject(object $item): bool
{
self::hasProperties($item, ['type'], true);
// Validate Link type
if ($item->type === 'Link') {
return self::validateLink($item);
}
// Validate Object type
self::hasProperties($item, ['url'], true);
return self::validateUrl($item->url);
}
/**
* Validate a reference as Link
*
* @throws Exception
*/
public static function validateLink(object|array $item): bool
{
if (\is_array($item)) {
$item = (object) $item;
}
if (!\is_object($item)) {
return false;
}
self::hasProperties($item, ['type'], true);
// Validate Link type
if ($item->type !== 'Link') {
return false;
}
// Validate Object type
self::hasProperties($item, ['href'], true);
return self::validateUrl($item->href)
|| self::validateMagnet($item->href);
}
/**
* Validate a datetime
*/
public static function validateDatetime($value): bool
{
if (!\is_string($value)
|| !preg_match(
'/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(.*)$/',
$value,
)
) {
return false;
}
try {
$dt = new DateTime($value);
return true;
} catch (Exception $e) {
return false;
}
}
/**
* Check that container class is a subclass of a given class
*
* @param bool $strict If true, throws an exception
*
* @throws Exception
*/
public static function subclassOf(object $container, array|string $classes, bool $strict = false): bool
{
if (!\is_array($classes)) {
$classes = [$classes];
}
foreach ($classes as $class) {
if (\get_class($container) === $class
|| is_subclass_of($container, $class)
) {
return true;
}
}
if ($strict) {
throw new Exception(
sprintf(
'Class "%s" MUST be a subclass of "%s"',
\get_class($container),
implode(', ', $classes),
),
);
}
return false;
}
/**
* Checks that a numeric value is part of a range.
* If a minimal value is null, value has to be inferior to max value
* If a maximum value is null, value has to be superior to min value
*/
public static function between(float|int $value, float|int|null $min, float|int|null $max): bool
{
if (!is_numeric($value)) {
return false;
}
return match (true) {
\is_null($min) && \is_null($max) => false,
\is_null($min) => $value <= $max,
\is_null($max) => $value >= $min,
default => $value >= $min
&& $value <= $max,
};
}
/**
* Check that a given string is a valid XML Schema xsd:duration
*
* @param bool $strict If true, throws an exception
*
* @throws Exception
*/
public static function isDuration(string $duration, bool $strict = false): bool
{
try {
new DateInterval($duration);
return true;
} catch (Exception $e) {
if ($strict) {
throw new Exception(
sprintf(
'Duration "%s" MUST respect xsd:duration',
$duration,
),
);
}
}
return false;
}
/**
* Checks that it's an object type
*/
public static function isObjectType(object $item): bool
{
return TypeResolver::isScope($item);
}
/**
* Checks that it's an actor type
*/
public static function isActorType(object $item): bool
{
return TypeResolver::isScope($item, 'actor');
}
/**
* Validate an object type with type attribute
*
* @param string $type An expected type
*/
public static function isType(object $item, string $type): bool
{
// Validate that container is a certain type
if (!\is_object($item)) {
return false;
}
return (bool) (
property_exists($item, 'type')
&& \is_string($item->type)
&& $item->type === $type
);
}
/**
* Validate a BCP 47 language value
*/
public static function validateBcp47(string $value): bool
{
return \is_string($value)
&& preg_match(
'/^(((en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang))|((([A-Za-z]{2,3}(-([A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?)|[A-Za-z]{4}|[A-Za-z]{5,8})(-([A-Za-z]{4}))?(-([A-Za-z]{2}|[0-9]{3}))?(-([A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*(-([0-9A-WY-Za-wy-z](-[A-Za-z0-9]{2,8})+))*(-(x(-[A-Za-z0-9]{1,8})+))?)|(x(-[A-Za-z0-9]{1,8})+))$/',
$value,
);
}
/**
* Validate a plain text value
*/
public static function validatePlainText(string $value): bool
{
return \is_string($value)
&& preg_match(
'/^([^<]+)$/',
$value,
);
}
/**
* Validate mediaType format
*/
public static function validateMediaType(string $value): bool
{
return \is_string($value)
&& preg_match(
'#^(([\w]+[\w\-]+[\w+])/(([\w]+[\w\-\.\+]+[\w]+)|(\*));?)+$#',
$value,
);
}
/**
* Validate a Collection type
*
* @throws Exception
*/
public static function validateCollection(object $item): bool
{
if (is_scalar($item)) {
return false;
}
if (!\is_object($item)) {
$item = (object) $item;
}
self::hasProperties(
$item,
[/*totalItems', 'current', 'first', 'last', */ 'items'],
true,
);
return true;
}
/**
* Validate a CollectionPage type
*
* @throws Exception
*/
public static function validateCollectionPage(object $item): bool
{
// Must be a Collection
if (!self::validateCollection($item)) {
return false;
}
self::hasProperties(
$item,
['partOf'/*, 'next', 'prev'*/],
true,
);
return true;
}
}

View File

@ -1,98 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type;
use Exception;
/**
* \Plugin\ActivityPub\Util\Type\Validator is an abstract class for
* attribute validation.
*/
abstract class Validator
{
/**
* Contains all custom validators
*
* @var array<string, ValidatorInterface>
*
* [ 'attributeName' => CustomValidatorClassName::class ]
*/
protected static array $validators = [];
/**
* Validate an attribute value for given attribute name and
* container object.
*
* @param mixed $container An object
*
* @throws Exception if $container is not an object
*/
public static function validate(string $name, mixed $value, mixed $container): bool
{
if (!\is_object($container)) {
throw new Exception(
'Given container is not an object',
);
}
// Perform validation
if (isset(self::$validators[$name])) {
return self::$validators[$name]->validate(
$value,
$container,
);
}
// Try to load a default validator
$validatorName = sprintf(
'\Plugin\ActivityPub\Util\Type\Validator\%sValidator',
ucfirst($name),
);
if (class_exists($validatorName)) {
self::add($name, $validatorName);
return self::validate($name, $value, $container);
}
// There is no validator for this attribute
return true;
}
/**
* Add a new validator in the pool.
* It checks that it implements Validator\Interface
*
* @param string $name an attribute name to validate
* @param object|string $class A validator class name
*
* @throws Exception if validator class does not implement
* \Plugin\ActivityPub\Util\Type\Helper\ValidatorInterface
*/
public static function add(string $name, object|string $class): void
{
$validator = new $class();
if (!($validator instanceof ValidatorInterface)) {
throw new Exception(
sprintf(
'Validator "%s" MUST implement "%s" interface',
\get_class($validator),
ValidatorInterface::class,
),
);
}
self::$validators[$name] = $validator;
}
}

View File

@ -1,35 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Validator;
use Plugin\ActivityPub\Util\Type\ValidatorInterface;
/**
* \Plugin\ActivityPub\Util\Type\Validator\AccuracyValidator is a dedicated
* validator for accuracy attribute.
*/
class AccuracyValidator implements ValidatorInterface
{
/**
* Validate an ACCURACY attribute value
*
* @param mixed $container An object
*/
public function validate(mixed $value, mixed $container): bool
{
return is_numeric($value)
&& (float) $value >= 0
&& (float) $value <= 100.0;
}
}

View File

@ -1,112 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Validator;
use Exception;
use Plugin\ActivityPub\Util\Type\Core\Collection;
use Plugin\ActivityPub\Util\Type\Core\Link;
use Plugin\ActivityPub\Util\Type\Extended\AbstractActor;
use Plugin\ActivityPub\Util\Type\Util;
use Plugin\ActivityPub\Util\Type\ValidatorInterface;
/**
* \Plugin\ActivityPub\Util\Type\Validator\ActorValidator is a dedicated
* validator for actor attribute.
*/
class ActorValidator implements ValidatorInterface
{
/**
* Validate an ACTOR attribute value
*
* @param mixed $container An object
*
* @throws Exception
*/
public function validate(mixed $value, mixed $container): bool
{
// Can be an indirect link
if (\is_string($value) && Util::validateUrl($value)) {
return true;
}
if (\is_array($value)) {
$value = Util::arrayToType($value);
}
// A collection
if (\is_array($value)) {
return $this->validateObjectCollection($value);
}
// Must be an object
if (!\is_object($value)) {
return false;
}
// A single actor
return $this->validateObject($value);
}
/**
* Validate an Actor object type
*
* @throws Exception
*/
protected function validateObject(object|array $item): bool
{
if (\is_array($item)) {
$item = Util::arrayToType($item);
}
Util::subclassOf(
$item,
[
AbstractActor::class,
Link::class,
Collection::class,
],
true,
);
return true;
}
/**
* Validate a list of object
* Collection can contain:
* - Indirect URL
* - An actor object
*
* @throws Exception
* @throws Exception
*/
protected function validateObjectCollection(array $collection): bool
{
foreach ($collection as $item) {
if (\is_array($item) && $this->validateObject($item)) {
continue;
}
if (\is_object($item) && $this->validateObject($item)) {
continue;
}
if (\is_string($item) && Util::validateUrl($item)) {
continue;
}
return false;
}
return \count($collection) > 0;
}
}

View File

@ -1,33 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Validator;
use Plugin\ActivityPub\Util\Type\ValidatorInterface;
/**
* \Plugin\ActivityPub\Util\Type\Validator\AltitudeValidator is a dedicated
* validator for altitude attribute.
*/
class AltitudeValidator implements ValidatorInterface
{
/**
* Validate an ALTITUDE attribute value
*
* @param mixed $container An object
*/
public function validate(mixed $value, mixed $container): bool
{
return (bool) (\is_float($value) || \is_int($value));
}
}

View File

@ -1,56 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Validator;
use Exception;
use Plugin\ActivityPub\Util\Type\Extended\Activity\Question;
use Plugin\ActivityPub\Util\Type\Util;
use Plugin\ActivityPub\Util\Type\ValidatorTools;
/**
* \Plugin\ActivityPub\Util\Type\Validator\AnyOfValidator is a dedicated
* validator for anyOf attribute.
*/
class AnyOfValidator extends ValidatorTools
{
/**
* Validate an ANYOF attribute value
*
* @param mixed $container An object
*
* @throws Exception
*
* @todo Choices can contain Indirect references.
* This validation should validate this kind of usage.
*/
public function validate(mixed $value, mixed $container): bool
{
// Validate that container is a Question type
Util::subclassOf($container, Question::class, true);
// A collection
if (!\is_array($value)) {
return false;
}
if (!\count($value)) {
return false;
}
return $this->validateObjectCollection(
$value,
$this->getQuestionAnswerValidator(),
);
}
}

View File

@ -1,44 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Validator;
use Exception;
use Plugin\ActivityPub\Util\Type\ValidatorTools;
/**
* \Plugin\ActivityPub\Util\Type\Validator\AttachmentValidator is a dedicated
* validator for attachment attribute.
*/
class AttachmentValidator extends ValidatorTools
{
/**
* Validate an attachment value
*
* @param mixed $container An Object type
*
* @throws Exception
*/
public function validate(mixed $value, mixed $container): bool
{
if (\is_array($value) && !\count($value)) {
return true;
}
return $this->validateListOrObject(
$value,
$container,
$this->getAttachmentValidator(),
);
}
}

View File

@ -1,40 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Validator;
use Exception;
use Plugin\ActivityPub\Util\Type\ValidatorTools;
/**
* \Plugin\ActivityPub\Util\Type\Validator\AttributedToValidator is a dedicated
* validator for attributedTo attribute.
*/
class AttributedToValidator extends ValidatorTools
{
/**
* Validate an attributedTo value
*
* @param mixed $container An Object type
*
* @throws Exception
*/
public function validate(mixed $value, mixed $container): bool
{
return $this->validateListOrObject(
$value,
$container,
$this->getCollectionActorsValidator(),
);
}
}

View File

@ -1,40 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Validator;
use Exception;
use Plugin\ActivityPub\Util\Type\ValidatorTools;
/**
* \Plugin\ActivityPub\Util\Type\Validator\AudienceValidator is a dedicated
* validator for audience attribute.
*/
class AudienceValidator extends ValidatorTools
{
/**
* Validate an audience value
*
* @param mixed $container An Object type
*
* @throws Exception
*/
public function validate(mixed $value, mixed $container): bool
{
return $this->validateListOrObject(
$value,
$container,
$this->getLinkOrNamedObjectValidator(),
);
}
}

View File

@ -1,40 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Validator;
use Exception;
use Plugin\ActivityPub\Util\Type\ValidatorTools;
/**
* \Plugin\ActivityPub\Util\Type\Validator\BccValidator is a dedicated
* validator for bcc attribute.
*/
class BccValidator extends ValidatorTools
{
/**
* Validate a bcc value
*
* @param mixed $container An Object type
*
* @throws Exception
*/
public function validate(mixed $value, mixed $container): bool
{
return $this->validateListOrObject(
$value,
$container,
$this->getLinkOrUrlObjectValidator(),
);
}
}

View File

@ -1,40 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Validator;
use Exception;
use Plugin\ActivityPub\Util\Type\ValidatorTools;
/**
* \Plugin\ActivityPub\Util\Type\Validator\BtoValidator is a dedicated
* validator for bto attribute.
*/
class BtoValidator extends ValidatorTools
{
/**
* Validate a bto value
*
* @param mixed $container An Object type
*
* @throws Exception
*/
public function validate(mixed $value, mixed $container): bool
{
return $this->validateListOrObject(
$value,
$container,
$this->getLinkOrUrlObjectValidator(),
);
}
}

View File

@ -1,40 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Validator;
use Exception;
use Plugin\ActivityPub\Util\Type\ValidatorTools;
/**
* \Plugin\ActivityPub\Util\Type\Validator\CcValidator is a dedicated
* validator for cc attribute.
*/
class CcValidator extends ValidatorTools
{
/**
* Validate a cc value
*
* @param mixed $container An Object type
*
* @throws Exception
*/
public function validate(mixed $value, mixed $container): bool
{
return $this->validateListOrObject(
$value,
$container,
$this->getLinkOrUrlObjectValidator(),
);
}
}

View File

@ -1,68 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Validator;
use Exception;
use Plugin\ActivityPub\Util\Type\Extended\Activity\Question;
use Plugin\ActivityPub\Util\Type\Util;
use Plugin\ActivityPub\Util\Type\ValidatorInterface;
/**
* \Plugin\ActivityPub\Util\Type\Validator\ClosedValidator is a dedicated
* validator for closed attribute.
*/
class ClosedValidator implements ValidatorInterface
{
/**
* Validate an CLOSED attribute value
*
* @param mixed $container A Question type
*
* @throws Exception
*/
public function validate(mixed $value, mixed $container): bool
{
// Validate that container is a Question type
Util::subclassOf($container, Question::class, true);
// Can be a boolean
if (\is_bool($value)) {
return true;
}
if (\is_string($value)) {
// Can be a datetime
if (Util::validateDatetime($value)) {
return true;
}
// Can be a URL
if (Util::validateUrl($value)) {
return true;
}
}
if (\is_array($value)) {
$value = Util::arrayToType($value);
}
// An Object or a Link
if (\is_object($value)) {
return Util::validateLink($value)
|| Util::validateObject($value);
}
return false;
}
}

View File

@ -1,34 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Validator;
use Exception;
use Plugin\ActivityPub\Util\Type\ValidatorTools;
/**
* \Plugin\ActivityPub\Util\Type\Validator\ContentMapValidator is a dedicated
* validator for contentMap attribute.
*/
class ContentMapValidator extends ValidatorTools
{
/**
* Validate a contentMap value
*
* @throws Exception
*/
public function validate(mixed $value, mixed $container): bool
{
return $this->validateMap('content', $value, $container);
}
}

View File

@ -1,32 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Validator;
use Plugin\ActivityPub\Util\Type\ValidatorInterface;
/**
* \Plugin\ActivityPub\Util\Type\Validator\ContentValidator is a dedicated
* validator for content attribute.
*/
class ContentValidator implements ValidatorInterface
{
/**
* Validate a content attribute value
*/
public function validate(mixed $value, mixed $container): bool
{
// Must be a string or null
return (bool) (\is_null($value) || \is_string($value));
}
}

View File

@ -1,50 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Validator;
use Exception;
use Plugin\ActivityPub\Util\Type\Util;
use Plugin\ActivityPub\Util\Type\ValidatorInterface;
/**
* \Plugin\ActivityPub\Util\Type\Validator\ContextValidator is a dedicated
* validator for context attribute.
*/
class ContextValidator implements ValidatorInterface
{
/**
* Validate a context attribute value
*
* @throws Exception
*/
public function validate(mixed $value, mixed $container): bool
{
// URL
if (Util::validateUrl($value)) {
return true;
}
if (\is_array($value)) {
$value = Util::arrayToType($value);
}
// Link or Object
if (\is_object($value)) {
return Util::validateLink($value)
|| Util::validateObject($value);
}
return false;
}
}

View File

@ -1,46 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Validator;
use Exception;
use Plugin\ActivityPub\Util\Type\Core\Collection;
use Plugin\ActivityPub\Util\Type\Util;
use Plugin\ActivityPub\Util\Type\ValidatorInterface;
/**
* \Plugin\ActivityPub\Util\Type\Validator\CurrentValidator is a dedicated
* validator for current attribute.
*/
class CurrentValidator implements ValidatorInterface
{
/**
* Validate a current attribute value
*
* @throws Exception
*/
public function validate(mixed $value, mixed $container): bool
{
// Container must be a Collection
Util::subclassOf($container, Collection::class, true);
// URL
if (Util::validateUrl($value)) {
return true;
}
// Link or CollectionPage
return Util::validateLink($value)
|| Util::validateCollectionPage($value);
}
}

View File

@ -1,48 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Validator;
use Exception;
use Plugin\ActivityPub\Util\Type\Extended\Object\Tombstone;
use Plugin\ActivityPub\Util\Type\Util;
use Plugin\ActivityPub\Util\Type\ValidatorInterface;
/**
* \Plugin\ActivityPub\Util\Type\Validator\DeletedValidator is a dedicated
* validator for deleted attribute.
*/
class DeletedValidator implements ValidatorInterface
{
/**
* Validate a DELETED attribute value
*
* @param mixed $container A Tombstone type
*
* @throws Exception
*/
public function validate(mixed $value, mixed $container): bool
{
// Validate that container is a Tombstone type
Util::subclassOf($container, Tombstone::class, true);
if (\is_string($value)) {
// MUST be a datetime
if (Util::validateDatetime($value)) {
return true;
}
}
return false;
}
}

View File

@ -1,47 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Validator;
use Exception;
use Plugin\ActivityPub\Util\Type\Core\ObjectType;
use Plugin\ActivityPub\Util\Type\Extended\Object\Profile;
use Plugin\ActivityPub\Util\Type\Util;
use Plugin\ActivityPub\Util\Type\ValidatorInterface;
/**
* \Plugin\ActivityPub\Util\Type\Validator\DescribesValidator is a dedicated
* validator for describes attribute.
*/
class DescribesValidator implements ValidatorInterface
{
/**
* Validate an DESCRIBES attribute value
*
* @param mixed $container A Profile type
*
* @throws Exception
*/
public function validate(mixed $value, mixed $container): bool
{
// Validate that container is a Tombstone type
Util::subclassOf($container, Profile::class, true);
if (\is_object($value)) {
// MUST be an Object
return Util::subclassOf($value, ObjectType::class, true);
}
return false;
}
}

View File

@ -1,44 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Validator;
use Exception;
use Plugin\ActivityPub\Util\Type\Core\ObjectType;
use Plugin\ActivityPub\Util\Type\Util;
use Plugin\ActivityPub\Util\Type\ValidatorInterface;
/**
* \Plugin\ActivityPub\Util\Type\Validator\DurationValidator is a dedicated
* validator for duration attribute.
*/
class DurationValidator implements ValidatorInterface
{
/**
* Validate an DURATION attribute value
*
* @throws Exception
*/
public function validate(mixed $value, mixed $container): bool
{
// Validate that container has an ObjectType type
Util::subclassOf($container, ObjectType::class, true);
if (\is_string($value)) {
// MUST be an XML 8601 Duration formatted string
return Util::isDuration($value, true);
}
return false;
}
}

View File

@ -1,40 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Validator;
use Exception;
use Plugin\ActivityPub\Util\Type\Core\ObjectType;
use Plugin\ActivityPub\Util\Type\Util;
use Plugin\ActivityPub\Util\Type\ValidatorInterface;
/**
* \Plugin\ActivityPub\Util\Type\Validator\EndTimeValidator is a dedicated
* validator for endTime attribute.
*/
class EndTimeValidator implements ValidatorInterface
{
/**
* Validate an ENDTIME attribute value
*
* @throws Exception
*/
public function validate(mixed $value, mixed $container): bool
{
// Validate that container has an ObjectType type
Util::subclassOf($container, ObjectType::class, true);
// MUST be a valid xsd:dateTime
return Util::validateDatetime($value);
}
}

View File

@ -1,75 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Validator;
use Exception;
use Plugin\ActivityPub\Util\Type\Extended\AbstractActor;
use Plugin\ActivityPub\Util\Type\Util;
use Plugin\ActivityPub\Util\Type\ValidatorInterface;
/**
* \Plugin\ActivityPub\Util\Type\Validator\EndpointsValidator is a dedicated
* validator for endpoints attribute.
*/
class EndpointsValidator implements ValidatorInterface
{
/**
* Validate ENDPOINTS value
*
* @throws Exception
*/
public function validate(mixed $value, mixed $container): bool
{
// Validate that container is an AbstractActor type
Util::subclassOf($container, AbstractActor::class, true);
// A link to a JSON-LD document
if (Util::validateUrl($value)) {
return true;
}
// A map
return \is_array($value) && $this->validateObject($value);
}
/**
* Validate endpoints mapping
*/
protected function validateObject(array $item): bool
{
foreach ($item as $key => $value) {
switch ($key) {
case 'proxyUrl':
case 'oauthAuthorizationEndpoint':
case 'oauthTokenEndpoint':
case 'provideClientKey':
case 'signClientKey':
case 'sharedInbox':
if (!Util::validateUrl($value)) {
return false;
}
break;
// All other keys are not allowed
default:
return false;
}
if (is_numeric($key)) {
return false;
}
}
return true;
}
}

View File

@ -1,22 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Validator;
/**
* \Plugin\ActivityPub\Util\Type\Validator\FirstValidator is a dedicated
* validator for first attribute.
*/
class FirstValidator extends CurrentValidator
{
}

View File

@ -1,65 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Validator;
use Exception;
use Plugin\ActivityPub\Util\Type\Core\Collection;
use Plugin\ActivityPub\Util\Type\Core\OrderedCollection;
use Plugin\ActivityPub\Util\Type\Extended\AbstractActor;
use Plugin\ActivityPub\Util\Type\Util;
use Plugin\ActivityPub\Util\Type\ValidatorInterface;
/**
* \Plugin\ActivityPub\Util\Type\Validator\FollowersValidator is a dedicated
* validator for followers attribute.
*/
class FollowersValidator implements ValidatorInterface
{
/**
* Validate a FOLLOWERS attribute value
*
* @throws Exception
*
* @todo Support indirect reference for followers attribute?
*/
public function validate(mixed $value, mixed $container): bool
{
// Validate that container is an AbstractActor type
Util::subclassOf($container, AbstractActor::class, true);
if (\is_string($value)) {
return Util::validateUrl($value);
}
// A collection
return \is_object($value) && $this->validateObject($value);
}
/**
* Validate that it is an OrderedCollection or a Collection
*
* @throws Exception
* @throws Exception
*/
protected function validateObject(object $collection): bool
{
return Util::subclassOf(
$collection,
OrderedCollection::class,
) || Util::subclassOf(
$collection,
Collection::class,
);
}
}

View File

@ -1,22 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Validator;
/**
* \Plugin\ActivityPub\Util\Type\Validator\FollowingValidator is a dedicated
* validator for followers attribute.
*/
class FollowingValidator extends FollowersValidator
{
}

View File

@ -1,44 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Validator;
use Exception;
use Plugin\ActivityPub\Util\Type\Extended\Object\Tombstone;
use Plugin\ActivityPub\Util\Type\Util;
use Plugin\ActivityPub\Util\Type\ValidatorInterface;
/**
* \Plugin\ActivityPub\Util\Type\Validator\FormerTypeValidator is a dedicated
* validator for formerType attribute.
*/
class FormerTypeValidator implements ValidatorInterface
{
/**
* Validate a formerType attribute value
*
* @throws Exception
*/
public function validate(mixed $value, mixed $container): bool
{
// Validate that container has a Tombstone type
Util::subclassOf($container, Tombstone::class, true);
if (\is_array($value)) {
$value = Util::arrayToType($value);
}
// MUST be a valid Object type
return Util::isObjectType($value);
}
}

View File

@ -1,48 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Validator;
use Exception;
use Plugin\ActivityPub\Util\Type\Core\ObjectType;
use Plugin\ActivityPub\Util\Type\Util;
use Plugin\ActivityPub\Util\Type\ValidatorInterface;
/**
* \Plugin\ActivityPub\Util\Type\Validator\GeneratorValidator is a dedicated
* validator for generator attribute.
*/
class GeneratorValidator implements ValidatorInterface
{
/**
* Validate a generator attribute value
*
* @throws Exception
*/
public function validate(mixed $value, mixed $container): bool
{
// Validate that container has an ObjectType type
Util::subclassOf($container, ObjectType::class, true);
if (Util::validateUrl($value)) {
return true;
}
if (\is_array($value)) {
$value = Util::arrayToType($value);
}
// MUST be a valid Actor type
return Util::isActorType($value) || Util::validateLink($value);
}
}

View File

@ -1,43 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Validator;
use Exception;
use Plugin\ActivityPub\Util\Type\Core\Link;
use Plugin\ActivityPub\Util\Type\Extended\Object\Image;
use Plugin\ActivityPub\Util\Type\Util;
use Plugin\ActivityPub\Util\Type\ValidatorInterface;
/**
* \Plugin\ActivityPub\Util\Type\Validator\HeightValidator is a dedicated
* validator for height attribute.
*/
class HeightValidator implements ValidatorInterface
{
/**
* Validate height value
*
* @param mixed $container An object
*
* @throws Exception
*/
public function validate(mixed $value, mixed $container): bool
{
// Validate that container is a Link
Util::subclassOf($container, [Link::class, Image::class], true);
// Must be a non-negative integer
return Util::validateNonNegativeInteger($value);
}
}

View File

@ -1,48 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This file is part of the ActivityPhp package.
*
* Copyright (c) landrok at github.com/landrok
*
* For the full copyright and license information, please see
* <https://github.com/landrok/activitypub/blob/master/LICENSE>.
*/
namespace Plugin\ActivityPub\Util\Type\Validator;
use Exception;
use Plugin\ActivityPub\Util\Type\Core\Link;
use Plugin\ActivityPub\Util\Type\Core\ObjectType;
use Plugin\ActivityPub\Util\Type\Util;
use Plugin\ActivityPub\Util\Type\ValidatorInterface;
/**
* \Plugin\ActivityPub\Util\Type\Validator\HrefValidator is a dedicated
* validator for href attribute.
*/
class HrefValidator implements ValidatorInterface
{
/**
* Validate href value
*
* @param mixed $container An object
*
* @throws Exception
*/
public function validate(mixed $value, mixed $container): bool
{
// Validate that container is a Link or an Object
Util::subclassOf(
$container,
[Link::class, ObjectType::class],
true,
);
// Must be a valid URL or a valid magnet link
return Util::validateUrl($value)
|| Util::validateMagnet($value);
}
}

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