initial support for articles from CN

- fixed how To and CC field (recipients) are handled in general
- fixed posts in database
- improved some error exceptions and prevented early breaks through try-catch blocks
- we now support CN-articles on our newcontent endpoint, with create and update calls
This commit is contained in:
Yannis Vogel 2025-05-22 20:44:37 +02:00
parent ba88adcebd
commit 62cfd6ef0d
No known key found for this signature in database
12 changed files with 398 additions and 150 deletions

View file

@ -43,7 +43,7 @@ class Inbox implements \Federator\Api\FedUsers\FedUsersInterface
/** /**
* handle post call * handle post call
* *
* @param string|null $_user user to add data to inbox * @param string|null $_user user to add data to inbox @unused-param
* @return string|false response * @return string|false response
*/ */
public function post($_user) public function post($_user)
@ -59,6 +59,12 @@ class Inbox implements \Federator\Api\FedUsers\FedUsersInterface
$activity = is_string($_rawInput) ? json_decode($_rawInput, true) : null; $activity = is_string($_rawInput) ? json_decode($_rawInput, true) : null;
$dbh = $this->main->getDatabase();
$cache = $this->main->getCache();
$connector = $this->main->getConnector();
$config = $this->main->getConfig();
if (!is_array($activity)) { if (!is_array($activity)) {
throw new \Federator\Exceptions\ServerError("Inbox::post Input wasn't of type array"); throw new \Federator\Exceptions\ServerError("Inbox::post Input wasn't of type array");
} }
@ -68,33 +74,33 @@ class Inbox implements \Federator\Api\FedUsers\FedUsersInterface
if ($inboxActivity === false) { if ($inboxActivity === false) {
throw new \Federator\Exceptions\ServerError("Inbox::post couldn't create inboxActivity"); throw new \Federator\Exceptions\ServerError("Inbox::post couldn't create inboxActivity");
} }
$user = $inboxActivity->getAActor(); // url of the sender https://contentnation.net/username
$rootDir = PROJECT_ROOT . '/'; $username = basename((string) (parse_url($user, PHP_URL_PATH) ?? ''));
$domain = parse_url($user, PHP_URL_HOST);
// Shared inbox
if (!isset($_user)) {
// Save the raw input and parsed JSON to a file for inspection
file_put_contents(
$rootDir . 'logs/inbox.log',
date('Y-m-d H:i:s') . ": ==== POST Inbox Activity ====\n" . json_encode($inboxActivity, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT) . "\n\n",
FILE_APPEND
);
}
$sendTo = $inboxActivity->getCC();
if ($inboxActivity->getType() === 'Undo') { // for undo the object holds the proper cc
$object = $inboxActivity->getObject();
if ($object !== null && is_object($object)) {
$sendTo = $object->getCC();
}
}
$users = []; $users = [];
$dbh = $this->main->getDatabase();
$cache = $this->main->getCache();
$connector = $this->main->getConnector();
foreach ($sendTo as $receiver) { $receivers = array_merge($inboxActivity->getTo(), $inboxActivity->getCC());
// For Undo, the object may hold the proper to/cc
if ($inboxActivity->getType() === 'Undo') {
$object = $inboxActivity->getObject();
if ($object !== null && is_object($object)) {
$receivers = array_merge($object->getTo(), $object->getCC());
}
}
// Filter out the public address and keep only actual URLs
$receivers = array_filter($receivers, static function (mixed $receiver): bool {
return is_string($receiver)
&& $receiver !== 'https://www.w3.org/ns/activitystreams#Public'
&& (filter_var($receiver, FILTER_VALIDATE_URL) !== false);
});
if (!in_array($username, $receivers, true)) {
$receivers[] = $username;
}
foreach ($receivers as $receiver) {
if ($receiver === '' || !is_string($receiver)) { if ($receiver === '' || !is_string($receiver)) {
continue; continue;
} }
@ -110,25 +116,68 @@ class Inbox implements \Federator\Api\FedUsers\FedUsersInterface
$username = basename((string) (parse_url($actor, PHP_URL_PATH) ?? '')); $username = basename((string) (parse_url($actor, PHP_URL_PATH) ?? ''));
$domain = parse_url($actor, PHP_URL_HOST); $domain = parse_url($actor, PHP_URL_HOST);
if ($username === null || $domain === null) { if ($username === null || $domain === null) {
error_log("Inbox::post no username or domain found"); error_log("Inbox::post no username or domain found for recipient: $receiver");
continue; continue;
} }
try {
$followers = \Federator\DIO\Followers::getFollowersByFedUser($dbh, $connector, $cache, $username . '@' . $domain); $followers = \Federator\DIO\Followers::getFollowersByFedUser($dbh, $connector, $cache, $username . '@' . $domain);
} catch (\Throwable $e) {
error_log("Inbox::post get followers for user: " . $username . '@' . $domain . ". Exception: " . $e->getMessage());
continue;
}
if (is_array($followers)) { if (is_array($followers)) {
$users = array_merge($users, array_column($followers, 'id')); $users = array_merge($users, array_column($followers, 'id'));
} }
} else {
$ourDomain = $config['generic']['externaldomain'];
// check if receiver is an actor url from our domain
if (!str_contains($receiver, $ourDomain)) {
continue;
}
$receiverName = basename((string) (parse_url($receiver, PHP_URL_PATH) ?? ''));
$domain = parse_url($receiver, PHP_URL_HOST);
if ($receiverName === null || $domain === null) {
error_log("Inbox::post no receiverName or domain found for receiver: " . $receiver);
continue;
}
$receiver = $receiverName . '@' . $domain;
try {
$user = \Federator\DIO\User::getUserByName(
$dbh,
$receiver,
$connector,
$cache
);
} catch (\Throwable $e) {
error_log("Inbox::post get user by name: " . $receiver . ". Exception: " . $e->getMessage());
continue;
}
if ($user === null || $user->id === null) {
error_log("Inbox::post couldn't find user: $receiver");
continue;
}
$users[] = $user->id;
} }
} }
if ($_user !== false && !in_array($_user, $users, true)) {
$users[] = $_user; if (empty($users)) { // todo remove after proper implementation, debugging for now
$rootDir = PROJECT_ROOT . '/';
// Save the raw input and parsed JSON to a file for inspection
file_put_contents(
$rootDir . 'logs/inbox.log',
date('Y-m-d H:i:s') . ": ==== POST Inbox Activity ====\n" . json_encode($inboxActivity, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT) . "\n\n",
FILE_APPEND
);
} }
foreach ($users as $user) {
foreach ($users as $receiver) {
if (!isset($user)) { if (!isset($user)) {
continue; continue;
} }
$token = \Resque::enqueue('inbox', 'Federator\\Jobs\\InboxJob', [ $token = \Resque::enqueue('inbox', 'Federator\\Jobs\\InboxJob', [
'user' => $user, 'user' => $username . '@' . $domain,
'recipientId' => $receiver,
'activity' => $inboxActivity->toObject(), 'activity' => $inboxActivity->toObject(),
]); ]);
error_log("Inbox::post enqueued job for user: $user with token: $token"); error_log("Inbox::post enqueued job for user: $user with token: $token");
@ -142,32 +191,46 @@ class Inbox implements \Federator\Api\FedUsers\FedUsersInterface
* @param \mysqli $dbh database handle * @param \mysqli $dbh database handle
* @param \Federator\Connector\Connector $connector connector to use * @param \Federator\Connector\Connector $connector connector to use
* @param \Federator\Cache\Cache|null $cache optional caching service * @param \Federator\Cache\Cache|null $cache optional caching service
* @param string $_user user to add data to inbox * @param string $_user user that triggered the post
* @param string $_recipientId recipient of the post
* @param \Federator\Data\ActivityPub\Common\Activity $inboxActivity the activity that we received * @param \Federator\Data\ActivityPub\Common\Activity $inboxActivity the activity that we received
* @return boolean response * @return boolean response
*/ */
public static function postForUser($dbh, $connector, $cache, $_user, $inboxActivity) public static function postForUser($dbh, $connector, $cache, $_user, $_recipientId, $inboxActivity)
{ {
if (!isset($_user)) { if (!isset($_user)) {
error_log("Inbox::postForUser no user given"); error_log("Inbox::postForUser no user given");
return false; return false;
} }
$user = \Federator\DIO\User::getUserByName( // get sender
$user = \Federator\DIO\FedUser::getUserByName(
$dbh, $dbh,
$_user, $_user,
$connector,
$cache $cache
); );
if ($user === null || $user->id === null) { if ($user === null || $user->id === null) {
throw new \Federator\Exceptions\ServerError("Inbox::postForUser couldn't find user: $_user"); error_log("Inbox::postForUser couldn't find user: $_user");
return false;
}
// get recipient
$recipient = \Federator\DIO\User::getUserByName(
$dbh,
$_recipientId,
$connector,
$cache
);
if ($recipient === null || $recipient->id === null) {
error_log("Inbox::postForUser couldn't find user: $_recipientId");
return false;
} }
$rootDir = PROJECT_ROOT . '/'; $rootDir = PROJECT_ROOT . '/';
// Save the raw input and parsed JSON to a file for inspection // Save the raw input and parsed JSON to a file for inspection
file_put_contents( file_put_contents(
$rootDir . 'logs/inbox_' . $_user . '.log', $rootDir . 'logs/inbox_' . $recipient->id . '.log',
date('Y-m-d H:i:s') . ": ==== POST " . $_user . " Inbox Activity ====\n" . json_encode($inboxActivity, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT) . "\n\n", date('Y-m-d H:i:s') . ": ==== POST " . $recipient->id . " Inbox Activity ====\n" . json_encode($inboxActivity, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT) . "\n\n",
FILE_APPEND FILE_APPEND
); );
@ -195,12 +258,15 @@ class Inbox implements \Federator\Api\FedUsers\FedUsersInterface
$object = $inboxActivity->getObject(); $object = $inboxActivity->getObject();
if (is_string($object)) { if (is_string($object)) {
\Federator\DIO\Posts::deletePost($dbh, $object); \Federator\DIO\Posts::deletePost($dbh, $object);
} elseif (is_object($object)) {
$objectId = $object->getID();
\Federator\DIO\Posts::deletePost($dbh, $objectId);
} }
break; break;
case 'Undo': case 'Undo':
$object = $inboxActivity->getObject(); $object = $inboxActivity->getObject();
if (is_object($object) && method_exists($object, 'getType')) { if (is_object($object)) {
switch ($object->getType()) { switch ($object->getType()) {
case 'Follow': case 'Follow':
$success = false; $success = false;

View file

@ -77,7 +77,6 @@ class NewContent implements \Federator\Api\APIInterface
public function post($_user) public function post($_user)
{ {
$_rawInput = file_get_contents('php://input'); $_rawInput = file_get_contents('php://input');
$allHeaders = getallheaders(); $allHeaders = getallheaders();
try { try {
$this->main->checkSignature($allHeaders); $this->main->checkSignature($allHeaders);
@ -111,27 +110,94 @@ class NewContent implements \Federator\Api\APIInterface
error_log("NewContent::post couldn't create newActivity"); error_log("NewContent::post couldn't create newActivity");
return false; return false;
} }
if (!isset($_user)) { if (!isset($_user)) {
$user = $newActivity->getAActor(); // url of the sender https://contentnation.net/username $user = $newActivity->getAActor(); // url of the sender https://contentnation.net/username
$user = str_replace( $posterName = str_replace(
$domain, $domain,
'', '',
$user $user
); // retrieve only the last part of the url ); // retrieve only the last part of the url
} else { } else {
$user = $dbh->real_escape_string($_user); $posterName = $dbh->real_escape_string($_user);
} }
$users = []; $users = [];
if ($newActivity->getType() === 'Create') { $receivers = array_merge($newActivity->getTo(), $newActivity->getCC());
$followers = $this->fetchAllFollowers($dbh, $connector, $cache, $user);
// For Undo, the object may hold the proper to/cc
if ($newActivity->getType() === 'Undo') {
$object = $newActivity->getObject();
if ($object !== null && is_object($object)) {
$receivers = array_merge($object->getTo(), $object->getCC());
} }
if (!empty($followers)) {
$users = array_merge($users, $followers);
} }
// Filter out the public address and keep only actual URLs
$receivers = array_filter($receivers, static function (mixed $receiver): bool {
return is_string($receiver)
&& $receiver !== 'https://www.w3.org/ns/activitystreams#Public'
&& (filter_var($receiver, FILTER_VALIDATE_URL) !== false);
});
if (!in_array($posterName, $receivers, true)) {
$receivers[] = $posterName;
}
foreach ($receivers as $receiver) {
if ($receiver === '' || !is_string($receiver)) {
continue;
}
if (str_ends_with($receiver, '/followers')) {
$actor = $newActivity->getAActor();
if ($actor === null || !is_string($actor)) {
error_log("NewContent::post no actor found");
continue;
}
if ($posterName === null) {
error_log("NewContent::post no username found");
continue;
}
try {
$followers = \Federator\DIO\Followers::getFollowersByUser($dbh, $posterName, $connector, $cache);
} catch (\Throwable $e) {
error_log("NewContent::post get followers for user: " . $posterName . ". Exception: " . $e->getMessage());
continue;
}
if (is_array($followers)) {
$users = array_merge($users, array_column($followers, 'id'));
}
} else {
// check if receiver is an actor url and not from our domain
if (str_contains($receiver, $domain)) {
continue;
}
$receiverName = basename((string) (parse_url($receiver, PHP_URL_PATH) ?? ''));
$domain = parse_url($receiver, PHP_URL_HOST);
if ($receiverName === null || $domain === null) {
error_log("NewContent::post no receiverName or domain found for receiver: " . $receiver);
continue;
}
$receiver = $receiverName . '@' . $domain;
try {
$user = \Federator\DIO\FedUser::getUserByName(
$dbh,
$receiver,
$cache
);
} catch (\Throwable $e) {
error_log("NewContent::post get user by name: " . $receiver . ". Exception: " . $e->getMessage());
continue;
}
if ($user === null || $user->id === null) {
error_log("NewContent::post couldn't find user: $receiver");
continue;
}
$users[] = $user->id;
}
}
if (empty($users)) { // todo remove after proper implementation, debugging for now if (empty($users)) { // todo remove after proper implementation, debugging for now
$rootDir = PROJECT_ROOT . '/'; $rootDir = PROJECT_ROOT . '/';
// Save the raw input and parsed JSON to a file for inspection // Save the raw input and parsed JSON to a file for inspection
@ -141,20 +207,18 @@ class NewContent implements \Federator\Api\APIInterface
FILE_APPEND FILE_APPEND
); );
} }
if ($_user !== false && !in_array($_user, $users, true)) {
$users[] = $_user;
}
foreach ($users as $user) { foreach ($users as $receiver) {
if (!isset($user)) { if (!isset($receiver)) {
continue; continue;
} }
$token = \Resque::enqueue('inbox', 'Federator\\Jobs\\NewContentJob', [ $token = \Resque::enqueue('inbox', 'Federator\\Jobs\\NewContentJob', [
'user' => $user, 'user' => $posterName,
'recipientId' => $receiver,
'activity' => $newActivity->toObject(), 'activity' => $newActivity->toObject(),
]); ]);
error_log("Inbox::post enqueued job for user: $user with token: $token"); error_log("Inbox::post enqueued job for receiver: $receiver with token: $token");
} }
return json_encode($newActivity, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT); return json_encode($newActivity, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
@ -170,17 +234,18 @@ class NewContent implements \Federator\Api\APIInterface
* @param \Federator\Cache\Cache|null $cache * @param \Federator\Cache\Cache|null $cache
* optional caching service * optional caching service
* @param string $_user user that triggered the post * @param string $_user user that triggered the post
* @param string $_recipientId recipient of the post
* @param \Federator\Data\ActivityPub\Common\Activity $newActivity the activity that we received * @param \Federator\Data\ActivityPub\Common\Activity $newActivity the activity that we received
* @return boolean response * @return boolean response
*/ */
public static function postForUser($dbh, $connector, $cache, $_user, $newActivity) public static function postForUser($dbh, $connector, $cache, $_user, $_recipientId, $newActivity)
{ {
if (!isset($_user)) { if (!isset($_user)) {
error_log("NewContent::postForUser no user given"); error_log("NewContent::postForUser no user given");
return false; return false;
} }
// get user // get sender
$user = \Federator\DIO\User::getUserByName( $user = \Federator\DIO\User::getUserByName(
$dbh, $dbh,
$_user, $_user,
@ -192,11 +257,22 @@ class NewContent implements \Federator\Api\APIInterface
return false; return false;
} }
// get recipient
$recipient = \Federator\DIO\FedUser::getUserByName(
$dbh,
$_recipientId,
$cache
);
if ($recipient === null || $recipient->id === null) {
error_log("NewContent::postForUser couldn't find user: $_recipientId");
return false;
}
$rootDir = PROJECT_ROOT . '/'; $rootDir = PROJECT_ROOT . '/';
// Save the raw input and parsed JSON to a file for inspection // Save the raw input and parsed JSON to a file for inspection
file_put_contents( file_put_contents(
$rootDir . 'logs/newcontent_' . $_user . '.log', $rootDir . 'logs/newcontent_' . $recipient->id . '.log',
date('Y-m-d H:i:s') . ": ==== POST " . $_user . " NewContent Activity ====\n" . json_encode($newActivity, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT) . "\n\n", date('Y-m-d H:i:s') . ": ==== POST " . $recipient->id . " NewContent Activity ====\n" . json_encode($newActivity, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT) . "\n\n",
FILE_APPEND FILE_APPEND
); );
@ -303,6 +379,7 @@ class NewContent implements \Federator\Api\APIInterface
break; break;
case 'Create': case 'Create':
case 'Update':
$object = $newActivity->getObject(); $object = $newActivity->getObject();
if (is_object($object)) { if (is_object($object)) {
switch ($object->getType()) { switch ($object->getType()) {
@ -324,40 +401,6 @@ class NewContent implements \Federator\Api\APIInterface
return true; return true;
} }
/**
* fetch all followers from url and return the ones that belong to our server
*
* @param \mysqli $dbh
* database handle
* @param \Federator\Connector\Connector $connector
* connector to fetch use with
* @param \Federator\Cache\Cache|null $cache
* optional caching service
* @param string $userId The id of the user
* @return string[] the names of the followers that are hosted on our server
*/
private static function fetchAllFollowers($dbh, $connector, $cache, string $userId): array
{
if (empty($userId)) {
return [];
}
$users = [];
$apFollowers = \Federator\DIO\Followers::getFollowersByUser(
$dbh,
$userId,
$connector,
cache: $cache,
);
foreach ($apFollowers as $follower) {
$users[] = $follower->id;
}
return $users;
}
/** /**
* get internal represenation as json string * get internal represenation as json string
* @return string json string or html * @return string json string or html

View file

@ -0,0 +1,45 @@
<?php
/**
* SPDX-FileCopyrightText: 2024 Sascha Nitsch (grumpydeveloper) https://contentnation.net/@grumpydevelop
* SPDX-License-Identifier: GPL-3.0-or-later
*
* @author Yannis Vogel (vogeldevelopment)
**/
namespace Federator\Data\ActivityPub\Common;
class Update extends Activity
{
public function __construct()
{
parent::__construct('Update');
parent::addContext('https://www.w3.org/ns/activitystreams');
}
/**
* convert internal state to php array
*
* @return array<string,mixed>
*/
public function toObject()
{
$return = parent::toObject();
$return['type'] = 'Update';
// overwrite id from url
if ($this->getURL() !== '') {
$return['id'] = $this->getURL();
}
return $return;
}
/**
* create object from json
*
* @param array<string,mixed> $json input json
* @return bool true on success
*/
public function fromJson($json)
{
return parent::fromJson($json);
}
}

View file

@ -647,7 +647,7 @@ class APObject implements \JsonSerializable
if (array_key_exists('duration', $json)) { if (array_key_exists('duration', $json)) {
try { try {
$this->duration = new \DateInterval($json['duration']); $this->duration = new \DateInterval($json['duration']);
} catch (\Exception $unused_e) { } catch (\Throwable $unused_e) {
error_log("error parsing duration ". $json['duration']); error_log("error parsing duration ". $json['duration']);
} }
} }
@ -875,7 +875,7 @@ class APObject implements \JsonSerializable
$return['tag'] = $tags; $return['tag'] = $tags;
} }
if ($this->updated > 0) { if ($this->updated > 0) {
$return['updated'] = gmdate("Y-m-d\TH:i:S\Z", $this->updated); $return['updated'] = gmdate("Y-m-d\TH:i:s\Z", $this->updated);
} }
if ($this->url !== '') { if ($this->url !== '') {
$return['url'] = $this->url; $return['url'] = $this->url;

View file

@ -116,6 +116,9 @@ class Factory
case 'Undo': case 'Undo':
$return = new Common\Undo(); $return = new Common\Undo();
break; break;
case 'Update':
$return = new Common\Update();
break;
default: default:
error_log("newActivityFromJson unsupported type: " . print_r($json, true)); error_log("newActivityFromJson unsupported type: " . print_r($json, true));
} }

View file

@ -26,7 +26,7 @@ class FedUser
$sql = 'select unix_timestamp(`validuntil`) from fedusers where id=?'; $sql = 'select unix_timestamp(`validuntil`) from fedusers where id=?';
$stmt = $dbh->prepare($sql); $stmt = $dbh->prepare($sql);
if ($stmt === false) { if ($stmt === false) {
throw new \Federator\Exceptions\ServerError(); throw new \Federator\Exceptions\ServerError("FedUser::addLocalUser Failed to prepare statement");
} }
$stmt->bind_param("s", $_user); $stmt->bind_param("s", $_user);
$validuntil = 0; $validuntil = 0;
@ -42,7 +42,7 @@ class FedUser
$sql .= ' values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, now() + interval 1 day)'; $sql .= ' values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, now() + interval 1 day)';
$stmt = $dbh->prepare($sql); $stmt = $dbh->prepare($sql);
if ($stmt === false) { if ($stmt === false) {
throw new \Federator\Exceptions\ServerError(); throw new \Federator\Exceptions\ServerError("FedUser::addLocalUser Failed to prepare create statement");
} }
$stmt->bind_param( $stmt->bind_param(
"ssssssssssss", "ssssssssssss",
@ -66,7 +66,7 @@ class FedUser
$sql .= ' where id=?'; $sql .= ' where id=?';
$stmt = $dbh->prepare($sql); $stmt = $dbh->prepare($sql);
if ($stmt === false) { if ($stmt === false) {
throw new \Federator\Exceptions\ServerError(); throw new \Federator\Exceptions\ServerError("FedUser::extendUser Failed to prepare update statement");
} }
$stmt->bind_param( $stmt->bind_param(
"ssssssssssss", "ssssssssssss",
@ -106,7 +106,7 @@ class FedUser
$sql = 'select id,unix_timestamp(`validuntil`) from fedusers where id=?'; $sql = 'select id,unix_timestamp(`validuntil`) from fedusers where id=?';
$stmt = $dbh->prepare($sql); $stmt = $dbh->prepare($sql);
if ($stmt === false) { if ($stmt === false) {
throw new \Federator\Exceptions\ServerError(); throw new \Federator\Exceptions\ServerError("FedUser::extendUser Failed to prepare statement");
} }
$stmt->bind_param("s", $_user); $stmt->bind_param("s", $_user);
$validuntil = 0; $validuntil = 0;
@ -147,12 +147,12 @@ class FedUser
return $user; return $user;
} }
// check our db // check our db
$sql = 'select id, url, name, publickey, summary, type, inboxurl, sharedinboxurl, followersurl,'; $sql = 'select `id`, `url`, `name`, `publickey`, `summary`, `type`, `inboxurl`, `sharedinboxurl`, `followersurl`,';
$sql .= ' followingurl,publickeyid,outboxurl'; $sql .= ' `followingurl`, `publickeyid`, `outboxurl`';
$sql .= ' from fedusers where id=? and validuntil>=now()'; $sql .= ' from fedusers where `id`=? and `validuntil`>=now()';
$stmt = $dbh->prepare($sql); $stmt = $dbh->prepare($sql);
if ($stmt === false) { if ($stmt === false) {
throw new \Federator\Exceptions\ServerError(); throw new \Federator\Exceptions\ServerError("FedUser::getUserByName Failed to prepare statement");
} }
$stmt->bind_param("s", $_name); $stmt->bind_param("s", $_name);
$user = new \Federator\Data\FedUser(); $user = new \Federator\Data\FedUser();
@ -184,11 +184,11 @@ class FedUser
$headers = ['Accept: application/activity+json']; $headers = ['Accept: application/activity+json'];
[$response, $info] = \Federator\Main::getFromRemote($remoteURL, $headers); [$response, $info] = \Federator\Main::getFromRemote($remoteURL, $headers);
if ($info['http_code'] != 200) { if ($info['http_code'] != 200) {
throw new \Federator\Exceptions\ServerError(); throw new \Federator\Exceptions\ServerError("FedUser::getUserByName Failed to fetch webfinger for " . $_name);
} }
$r = json_decode($response, true); $r = json_decode($response, true);
if ($r === false || $r === null || !is_array($r)) { if ($r === false || $r === null || !is_array($r)) {
throw new \Federator\Exceptions\ServerError(); throw new \Federator\Exceptions\ServerError("FedUser::getUserByName Failed to decode webfinger for " . $_name);
} }
// get the webwinger user url and fetch the user // get the webwinger user url and fetch the user
if (isset($r['links'])) { if (isset($r['links'])) {
@ -200,17 +200,17 @@ class FedUser
} }
} }
if (!isset($remoteURL)) { if (!isset($remoteURL)) {
throw new \Federator\Exceptions\ServerError(); throw new \Federator\Exceptions\ServerError("FedUser::getUserByName Failed to find self link in webfinger for " . $_name);
} }
// fetch the user // fetch the user
$headers = ['Accept: application/activity+json']; $headers = ['Accept: application/activity+json'];
[$response, $info] = \Federator\Main::getFromRemote($remoteURL, $headers); [$response, $info] = \Federator\Main::getFromRemote($remoteURL, $headers);
if ($info['http_code'] != 200) { if ($info['http_code'] != 200) {
throw new \Federator\Exceptions\ServerError(); throw new \Federator\Exceptions\ServerError("FedUser::getUserByName Failed to fetch user from remoteUrl for " . $_name);
} }
$r = json_decode($response, true); $r = json_decode($response, true);
if ($r === false || $r === null || !is_array($r)) { if ($r === false || $r === null || !is_array($r)) {
throw new \Federator\Exceptions\ServerError(); throw new \Federator\Exceptions\ServerError("FedUser::getUserByName Failed to decode user for " . $_name);
} }
$r['publicKeyId'] = $r['publicKey']['id']; $r['publicKeyId'] = $r['publicKey']['id'];
$r['publicKey'] = $r['publicKey']['publicKeyPem']; $r['publicKey'] = $r['publicKey']['publicKeyPem'];
@ -222,20 +222,20 @@ class FedUser
$r['actorURL'] = $remoteURL; $r['actorURL'] = $remoteURL;
$data = json_encode($r); $data = json_encode($r);
if ($data === false) { if ($data === false) {
throw new \Federator\Exceptions\ServerError(); throw new \Federator\Exceptions\ServerError("FedUser::getUserByName Failed to encode userdata " . $_name);
} }
$user = \Federator\Data\FedUser::createFromJson($data); $user = \Federator\Data\FedUser::createFromJson($data);
} }
} }
if ($cache !== null && $user !== false) { if ($cache !== null && $user !== false) {
if ($user->id === null && $user->actorURL !== null) { if ($user->id !== null && $user->actorURL !== null) {
self::addLocalUser($dbh, $user, $_name); self::addLocalUser($dbh, $user, $_name);
} }
$cache->saveRemoteFedUserByName($_name, $user); $cache->saveRemoteFedUserByName($_name, $user);
} }
if ($user === false) { if ($user === false) {
throw new \Federator\Exceptions\ServerError(); throw new \Federator\Exceptions\ServerError("FedUser::getUserByName User not found");
} }
return $user; return $user;
} }

View file

@ -39,7 +39,7 @@ class Followers
$sql = 'select source_user from follows where target_user = ?'; $sql = 'select source_user from follows where target_user = ?';
$stmt = $dbh->prepare($sql); $stmt = $dbh->prepare($sql);
if ($stmt === false) { if ($stmt === false) {
throw new \Federator\Exceptions\ServerError(); throw new \Federator\Exceptions\ServerError("Followers::getFollowersByUser Failed to prepare statement");
} }
$stmt->bind_param("s", $id); $stmt->bind_param("s", $id);
$stmt->execute(); $stmt->execute();
@ -50,11 +50,16 @@ class Followers
} }
$stmt->close(); $stmt->close();
foreach ($followerIds as $followerId) { foreach ($followerIds as $followerId) {
try {
$user = \Federator\DIO\FedUser::getUserByName( $user = \Federator\DIO\FedUser::getUserByName(
$dbh, $dbh,
$followerId, $followerId,
$cache, $cache,
); );
} catch (\Throwable $e) {
error_log("Followers::getFollowersByUser Exception: " . $e->getMessage());
continue; // Skip this user if an exception occurs
}
if ($user !== false && $user->id !== null) { if ($user !== false && $user->id !== null) {
$followers[] = $user; $followers[] = $user;
} }
@ -67,7 +72,7 @@ class Followers
$followers = []; $followers = [];
} }
} }
// save posts to DB // save followers to cache
if ($cache !== null) { if ($cache !== null) {
$cache->saveRemoteFollowersOfUser($id, $followers); $cache->saveRemoteFollowersOfUser($id, $followers);
} }
@ -100,7 +105,7 @@ class Followers
$sql = 'select target_user from follows where source_user = ?'; $sql = 'select target_user from follows where source_user = ?';
$stmt = $dbh->prepare($sql); $stmt = $dbh->prepare($sql);
if ($stmt === false) { if ($stmt === false) {
throw new \Federator\Exceptions\ServerError(); throw new \Federator\Exceptions\ServerError("Followers::getFollowingForUser Failed to prepare statement");
} }
$stmt->bind_param("s", $id); $stmt->bind_param("s", $id);
$stmt->execute(); $stmt->execute();
@ -111,11 +116,16 @@ class Followers
} }
$stmt->close(); $stmt->close();
foreach ($followingIds as $followingId) { foreach ($followingIds as $followingId) {
try {
$user = \Federator\DIO\FedUser::getUserByName( $user = \Federator\DIO\FedUser::getUserByName(
$dbh, $dbh,
$followingId, $followingId,
$cache, $cache,
); );
} catch (\Throwable $e) {
error_log("Followers::getFollowingForUser Exception: " . $e->getMessage());
continue; // Skip this user if an exception occurs
}
if ($user !== false && $user->id !== null) { if ($user !== false && $user->id !== null) {
$following[] = $user; $following[] = $user;
} }
@ -156,7 +166,7 @@ class Followers
$sql = 'select source_user from follows where target_user = ?'; $sql = 'select source_user from follows where target_user = ?';
$stmt = $dbh->prepare($sql); $stmt = $dbh->prepare($sql);
if ($stmt === false) { if ($stmt === false) {
throw new \Federator\Exceptions\ServerError(); throw new \Federator\Exceptions\ServerError("Followers::getFollowersByFedUser Failed to prepare statement");
} }
$stmt->bind_param("s", $id); $stmt->bind_param("s", $id);
$stmt->execute(); $stmt->execute();
@ -166,12 +176,17 @@ class Followers
$followerIds[] = $sourceUser; $followerIds[] = $sourceUser;
} }
foreach ($followerIds as $followerId) { foreach ($followerIds as $followerId) {
try {
$user = \Federator\DIO\User::getUserByName( $user = \Federator\DIO\User::getUserByName(
$dbh, $dbh,
$followerId, $followerId,
$connector, $connector,
$cache $cache
); );
} catch (\Throwable $e) {
error_log("Followers::getFollowersByFedUser Exception: " . $e->getMessage());
continue; // Skip this user if an exception occurs
}
if ($user !== false && $user->id !== null) { if ($user !== false && $user->id !== null) {
$followers[] = $user; $followers[] = $user;
} }
@ -193,7 +208,7 @@ class Followers
public static function sendFollowRequest($dbh, $connector, $cache, $_user, $_targetUser, $host) public static function sendFollowRequest($dbh, $connector, $cache, $_user, $_targetUser, $host)
{ {
if ($dbh === false) { if ($dbh === false) {
throw new \Federator\Exceptions\ServerError(); throw new \Federator\Exceptions\ServerError("Followers::sendFollowRequest Failed to get database handle");
} }
$user = \Federator\DIO\User::getUserByName( $user = \Federator\DIO\User::getUserByName(
$dbh, $dbh,
@ -326,7 +341,7 @@ class Followers
$sql = 'select id from follows where source_user = ? and target_user = ?'; $sql = 'select id from follows where source_user = ? and target_user = ?';
$stmt = $dbh->prepare($sql); $stmt = $dbh->prepare($sql);
if ($stmt === false) { if ($stmt === false) {
throw new \Federator\Exceptions\ServerError(); throw new \Federator\Exceptions\ServerError("Followers::addFollow Failed to prepare statement");
} }
$stmt->bind_param("ss", $sourceUser, $targetUserId); $stmt->bind_param("ss", $sourceUser, $targetUserId);
$foundId = 0; $foundId = 0;
@ -349,7 +364,7 @@ class Followers
$sql = 'select id from follows where id = ?'; $sql = 'select id from follows where id = ?';
$stmt = $dbh->prepare($sql); $stmt = $dbh->prepare($sql);
if ($stmt === false) { if ($stmt === false) {
throw new \Federator\Exceptions\ServerError(); throw new \Federator\Exceptions\ServerError("Followers::addFollow Failed to prepare id-check statement");
} }
$stmt->bind_param("s", $idurl); $stmt->bind_param("s", $idurl);
$foundId = 0; $foundId = 0;
@ -365,7 +380,7 @@ class Followers
$sql = 'insert into follows (id, source_user, target_user, created_at) values (?, ?, ?, NOW())'; $sql = 'insert into follows (id, source_user, target_user, created_at) values (?, ?, ?, NOW())';
$stmt = $dbh->prepare($sql); $stmt = $dbh->prepare($sql);
if ($stmt === false) { if ($stmt === false) {
throw new \Federator\Exceptions\ServerError(); throw new \Federator\Exceptions\ServerError("Followers::addFollow Failed to prepare insert statement");
} }
$stmt->bind_param("sss", $idurl, $sourceUser, $targetUserId); $stmt->bind_param("sss", $idurl, $sourceUser, $targetUserId);
$stmt->execute(); $stmt->execute();
@ -386,7 +401,7 @@ class Followers
$sql = 'delete from follows where source_user = ? and target_user = ?'; $sql = 'delete from follows where source_user = ? and target_user = ?';
$stmt = $dbh->prepare($sql); $stmt = $dbh->prepare($sql);
if ($stmt === false) { if ($stmt === false) {
throw new \Federator\Exceptions\ServerError(); throw new \Federator\Exceptions\ServerError("Followers::removeFollow Failed to prepare statement");
} }
$stmt->bind_param("ss", $sourceUser, $targetUserId); $stmt->bind_param("ss", $sourceUser, $targetUserId);
$stmt->execute(); $stmt->execute();

View file

@ -43,7 +43,6 @@ class Posts
if ($posts === false) { if ($posts === false) {
$posts = []; $posts = [];
} }
echo "Found " . count($posts) . " posts in DB\n";
// Only override $min if we found posts in our DB // Only override $min if we found posts in our DB
$remoteMin = $min; $remoteMin = $min;

View file

@ -26,7 +26,7 @@ class User
$sql = 'select unix_timestamp(`validuntil`) from users where id=?'; $sql = 'select unix_timestamp(`validuntil`) from users where id=?';
$stmt = $dbh->prepare($sql); $stmt = $dbh->prepare($sql);
if ($stmt === false) { if ($stmt === false) {
throw new \Federator\Exceptions\ServerError(); throw new \Federator\Exceptions\ServerError("User::addLocalUser Failed to prepare statement");
} }
$stmt->bind_param("s", $_user); $stmt->bind_param("s", $_user);
$validuntil = 0; $validuntil = 0;
@ -50,7 +50,7 @@ class User
$sql .= ' values (?, ?, ?, ?, now() + interval 1 day, ?, ?, ?, ?, ?, ?, ?, ?)'; $sql .= ' values (?, ?, ?, ?, now() + interval 1 day, ?, ?, ?, ?, ?, ?, ?, ?)';
$stmt = $dbh->prepare($sql); $stmt = $dbh->prepare($sql);
if ($stmt === false) { if ($stmt === false) {
throw new \Federator\Exceptions\ServerError(); throw new \Federator\Exceptions\ServerError("User::addLocalUser Failed to prepare create statement");
} }
$registered = gmdate('Y-m-d H:i:s', $user->registered); $registered = gmdate('Y-m-d H:i:s', $user->registered);
$stmt->bind_param( $stmt->bind_param(
@ -74,7 +74,7 @@ class User
$sql .= ' iconmediatype=?, iconurl=?, imagemediatype=?, imageurl=? where id=?'; $sql .= ' iconmediatype=?, iconurl=?, imagemediatype=?, imageurl=? where id=?';
$stmt = $dbh->prepare($sql); $stmt = $dbh->prepare($sql);
if ($stmt === false) { if ($stmt === false) {
throw new \Federator\Exceptions\ServerError(); throw new \Federator\Exceptions\ServerError("User::addLocalUser Failed to prepare update statement");
} }
$registered = gmdate('Y-m-d H:i:s', $user->registered); $registered = gmdate('Y-m-d H:i:s', $user->registered);
$stmt->bind_param( $stmt->bind_param(
@ -110,7 +110,7 @@ class User
$sql = "select rsaprivate from users where id=?"; $sql = "select rsaprivate from users where id=?";
$stmt = $dbh->prepare($sql); $stmt = $dbh->prepare($sql);
if ($stmt === false) { if ($stmt === false) {
throw new \Federator\Exceptions\ServerError(); throw new \Federator\Exceptions\ServerError("User::getrsaprivate Failed to prepare statement");
} }
$stmt->bind_param("s", $_user); $stmt->bind_param("s", $_user);
$ret = $stmt->bind_result($rsaPrivateKey); $ret = $stmt->bind_result($rsaPrivateKey);
@ -136,7 +136,7 @@ class User
$sql = 'select id,unix_timestamp(`validuntil`) from users where id=?'; $sql = 'select id,unix_timestamp(`validuntil`) from users where id=?';
$stmt = $dbh->prepare($sql); $stmt = $dbh->prepare($sql);
if ($stmt === false) { if ($stmt === false) {
throw new \Federator\Exceptions\ServerError(); throw new \Federator\Exceptions\ServerError("User::extendUser Failed to prepare statement");
} }
$stmt->bind_param("s", $_user); $stmt->bind_param("s", $_user);
$validuntil = 0; $validuntil = 0;
@ -183,7 +183,7 @@ class User
$sql .= 'iconmediatype,iconurl,imagemediatype,imageurl from users where id=? and validuntil>=now()'; $sql .= 'iconmediatype,iconurl,imagemediatype,imageurl from users where id=? and validuntil>=now()';
$stmt = $dbh->prepare($sql); $stmt = $dbh->prepare($sql);
if ($stmt === false) { if ($stmt === false) {
throw new \Federator\Exceptions\ServerError(); throw new \Federator\Exceptions\ServerError("User::getUserByName Failed to prepare statement");
} }
$stmt->bind_param("s", $_name); $stmt->bind_param("s", $_name);
$user = new \Federator\Data\User(); $user = new \Federator\Data\User();

View file

@ -54,6 +54,7 @@ class InboxJob extends \Federator\Api
{ {
error_log("InboxJob: Starting inbox job"); error_log("InboxJob: Starting inbox job");
$user = $this->args['user']; $user = $this->args['user'];
$recipientId = $this->args['recipientId'];
$activity = $this->args['activity']; $activity = $this->args['activity'];
$inboxActivity = \Federator\Data\ActivityPub\Factory::newActivityFromJson($activity); $inboxActivity = \Federator\Data\ActivityPub\Factory::newActivityFromJson($activity);
@ -63,7 +64,7 @@ class InboxJob extends \Federator\Api
return false; return false;
} }
\Federator\Api\FedUsers\Inbox::postForUser($this->dbh, $this->connector, $this->cache, $user, $inboxActivity); \Federator\Api\FedUsers\Inbox::postForUser($this->dbh, $this->connector, $this->cache, $user, $recipientId, $inboxActivity);
return true; return true;
} }
} }

View file

@ -54,6 +54,7 @@ class NewContentJob extends \Federator\Api
{ {
error_log("NewContentJob: Starting inbox job"); error_log("NewContentJob: Starting inbox job");
$user = $this->args['user']; $user = $this->args['user'];
$recipientId = $this->args['recipientId'];
$activity = $this->args['activity']; $activity = $this->args['activity'];
$activity = \Federator\Data\ActivityPub\Factory::newActivityFromJson($activity); $activity = \Federator\Data\ActivityPub\Factory::newActivityFromJson($activity);
@ -63,7 +64,7 @@ class NewContentJob extends \Federator\Api
return false; return false;
} }
\Federator\Api\V1\NewContent::postForUser($this->dbh, $this->connector, $this->cache, $user, $activity); \Federator\Api\V1\NewContent::postForUser($this->dbh, $this->connector, $this->cache, $user, $recipientId, $activity);
return true; return true;
} }
} }

View file

@ -148,8 +148,8 @@ class ContentNation implements Connector
$create->setAActor('https://' . $domain . '/' . $userId); $create->setAActor('https://' . $domain . '/' . $userId);
$create->setID($activity['id']) $create->setID($activity['id'])
->setPublished($activity['published'] ?? $activity['timestamp']) ->setPublished($activity['published'] ?? $activity['timestamp'])
->addTo("https://www.w3.org/ns/activitystreams#Public") ->addTo('https://' . $domain . '/' . $userId . '/followers')
->addCC('https://' . $domain . '/' . $userId . '/followers'); ->addCC("https://www.w3.org/ns/activitystreams#Public");
$create->setURL('https://' . $domain . '/' . $activity['profilename'] . '/' . $activity['name']); $create->setURL('https://' . $domain . '/' . $activity['profilename'] . '/' . $activity['name']);
$create->setID('https://' . $domain . '/' . $activity['profilename'] . '/' . $activity['id']); $create->setID('https://' . $domain . '/' . $activity['profilename'] . '/' . $activity['id']);
$apArticle = new \Federator\Data\ActivityPub\Common\Article(); $apArticle = new \Federator\Data\ActivityPub\Common\Article();
@ -203,8 +203,8 @@ class ContentNation implements Connector
$create->setAActor('https://' . $domain . '/' . $userId); $create->setAActor('https://' . $domain . '/' . $userId);
$create->setID($activity['id']) $create->setID($activity['id'])
->setPublished($activity['published'] ?? $activity['timestamp']) ->setPublished($activity['published'] ?? $activity['timestamp'])
->addTo("https://www.w3.org/ns/activitystreams#Public") ->addTo('https://' . $domain . '/' . $userId . '/followers')
->addCC('https://' . $domain . '/' . $userId . '/followers'); ->addCC("https://www.w3.org/ns/activitystreams#Public");
$commentJson = $activity; $commentJson = $activity;
$commentJson['type'] = 'Note'; $commentJson['type'] = 'Note';
$commentJson['summary'] = $activity['subject']; $commentJson['summary'] = $activity['subject'];
@ -219,7 +219,7 @@ class ContentNation implements Connector
$note->setID($commentJson['id']); $note->setID($commentJson['id']);
if (!isset($commentJson['parent']) || $commentJson['parent'] === null) { if (!isset($commentJson['parent']) || $commentJson['parent'] === null) {
$note->setInReplyTo('https://' . $domain . '/' . $activity['articleOwnerName'] . '/' . $activity['articleName']); $note->setInReplyTo('https://' . $domain . '/' . $activity['articleOwnerName'] . '/' . $activity['articleName']);
} elseif ($replyType === "comment") { } else {
$note->setInReplyTo('https://' . $domain . '/' . $activity['articleOwnerName'] . '/' . $activity['articleName'] . "#" . $commentJson['parent']); $note->setInReplyTo('https://' . $domain . '/' . $activity['articleOwnerName'] . '/' . $activity['articleName'] . "#" . $commentJson['parent']);
} }
$url = 'https://' . $domain . '/' . $activity['articleOwnerName'] . '/' . $activity['articleName'] . '#' . $activity['id']; $url = 'https://' . $domain . '/' . $activity['articleOwnerName'] . '/' . $activity['articleName'] . '#' . $activity['id'];
@ -392,6 +392,65 @@ class ContentNation implements Connector
// Handle specific fields based on the type // Handle specific fields based on the type
switch ($jsonData['type']) { switch ($jsonData['type']) {
case 'article':
$articleName = $jsonData['object']['name'] ?? null;
$articleOwnerName = $jsonData['object']['ownerName'] ?? null;
// Set Create-level fields
$updatedOn = $jsonData['object']['modified'] ?? null;
$originalPublished = $jsonData['object']['published'] ?? null;
$update = $updatedOn !== $originalPublished;
$ap['published'] = $updatedOn ?? $originalPublished;
$ap['id'] = $ourUrl . "/" . $articleOwnerName . "/" . $articleName;
$ap['url'] = $ourUrl . "/" . $articleOwnerName . "/" . $articleName;
$ap['type'] = $update ? 'Update' : 'Create';
$ap['actor'] = $ourUrl . '/' . $actorName;
// Set Article-level fields
$ap['object'] = [
'type' => 'Article',
'id' => $ourUrl . "/" . $articleOwnerName . "/" . $articleName,
'name' => $jsonData['object']['name'] ?? null,
'published' => $originalPublished,
'summary' => $jsonData['object']['summary'] ?? null,
'content' => $jsonData['object']['content'] ?? null,
'attributedTo' => $ap['actor'],
'url' => $ap['url'],
'cc' => ['https://www.w3.org/ns/activitystreams#Public'],
];
if ($update) {
$ap['id'] .= '#update';
$ap['url'] .= '#update';
$ap['object']['updated'] = $updatedOn;
}
$ap['cc'] = ['https://www.w3.org/ns/activitystreams#Public'];
if (isset($jsonData['object']['tags'])) {
if (is_array($jsonData['object']['tags'])) {
foreach ($jsonData['object']['tags'] as $tag) {
$ap['object']['tags'][] = $tag;
}
} elseif (is_string($jsonData['object']['tags']) && $jsonData['object']['tags'] !== '') {
// If it's a single tag as a string, add it as a one-element array
$ap['object']['tags'][] = $jsonData['object']['tags'];
}
}
if (isset($jsonData['options'])) {
if (isset($jsonData['options']['informFollowers'])) {
if ($jsonData['options']['informFollowers'] === true) {
$ap['to'][] = $ourUrl . '/' . $actorName . '/followers';
$ap['object']['to'][] = $ourUrl . '/' . $actorName . '/followers';
}
}
}
$returnActivity = \Federator\Data\ActivityPub\Factory::newActivityFromJson($ap);
if ($returnActivity === false) {
error_log("ContentNation::jsonToActivity couldn't create article");
$returnActivity = new \Federator\Data\ActivityPub\Common\Activity('Create');
} else {
$returnActivity->setID($ap['id']);
$returnActivity->setURL($ap['url']);
}
break;
case 'comment': case 'comment':
$commentId = $jsonData['object']['id'] ?? null; $commentId = $jsonData['object']['id'] ?? null;
$articleName = $jsonData['object']['articleName'] ?? null; $articleName = $jsonData['object']['articleName'] ?? null;
@ -411,7 +470,7 @@ class ContentNation implements Connector
if (isset($jsonData['options'])) { if (isset($jsonData['options'])) {
if (isset($jsonData['options']['informFollowers'])) { if (isset($jsonData['options']['informFollowers'])) {
if ($jsonData['options']['informFollowers'] === true) { if ($jsonData['options']['informFollowers'] === true) {
if ($actorName != $articleOwnerName) { if ($actorName !== $articleOwnerName) {
$ap['to'][] = $ourUrl . '/' . $articleOwnerName; $ap['to'][] = $ourUrl . '/' . $articleOwnerName;
} }
$ap['to'][] = $ourUrl . '/' . $actorName . '/followers'; $ap['to'][] = $ourUrl . '/' . $actorName . '/followers';
@ -427,8 +486,13 @@ class ContentNation implements Connector
error_log("ContentNation::jsonToActivity unknown inReplyTo type: {$replyType}"); error_log("ContentNation::jsonToActivity unknown inReplyTo type: {$replyType}");
} }
$returnActivity = \Federator\Data\ActivityPub\Factory::newActivityFromJson($ap); $returnActivity = \Federator\Data\ActivityPub\Factory::newActivityFromJson($ap);
if ($returnActivity === false) {
error_log("ContentNation::jsonToActivity couldn't create comment");
$returnActivity = new \Federator\Data\ActivityPub\Common\Activity('Create');
} else {
$returnActivity->setID($ap['id']); $returnActivity->setID($ap['id']);
$returnActivity->setURL($ap['url']); $returnActivity->setURL($ap['url']);
}
break; break;
case 'vote': case 'vote':
@ -487,8 +551,19 @@ class ContentNation implements Connector
} */ } */
$returnActivity = \Federator\Data\ActivityPub\Factory::newActivityFromJson($ap); $returnActivity = \Federator\Data\ActivityPub\Factory::newActivityFromJson($ap);
if ($returnActivity === false) {
error_log("ContentNation::jsonToActivity couldn't create vote");
if ($ap['type'] === "Like") {
$returnActivity = new \Federator\Data\ActivityPub\Common\Like();
} elseif ($ap['type'] === "Dislike") {
$returnActivity = new \Federator\Data\ActivityPub\Common\Dislike();
} else {
$returnActivity = new \Federator\Data\ActivityPub\Common\Undo();
}
} else {
$returnActivity->setID($ap['id']); $returnActivity->setID($ap['id']);
$returnActivity->setURL($ap['url']); $returnActivity->setURL($ap['url']);
}
break; break;
default: default: