forked from grumpydevelop/federator
		
	initial support for actually sending NewContent
- integrated functionality to actually send new content to federated recipients and followers (IT WORKS!!) - changed the way we remove a follow to return the removed followId (used in order to build the undo follow activity)
This commit is contained in:
		
							parent
							
								
									7a5870de95
								
							
						
					
					
						commit
						5c90b4cfc9
					
				
					 5 changed files with 229 additions and 41 deletions
				
			
		| 
						 | 
				
			
			@ -140,9 +140,6 @@ class NewContent implements \Federator\Api\APIInterface
 | 
			
		|||
                && (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;
 | 
			
		||||
| 
						 | 
				
			
			@ -173,7 +170,6 @@ class NewContent implements \Federator\Api\APIInterface
 | 
			
		|||
                $domain = parse_url($receiver, PHP_URL_HOST);
 | 
			
		||||
                if ($receiverName === null || $domain === null) {
 | 
			
		||||
                    if ($receiver === $posterName) {
 | 
			
		||||
                        $users[] = $receiver;
 | 
			
		||||
                        continue;
 | 
			
		||||
                    }
 | 
			
		||||
                    error_log("NewContent::post no receiverName or domain found for receiver: " . $receiver);
 | 
			
		||||
| 
						 | 
				
			
			@ -232,12 +228,13 @@ class NewContent implements \Federator\Api\APIInterface
 | 
			
		|||
     *          connector to fetch use with
 | 
			
		||||
     * @param \Federator\Cache\Cache|null $cache
 | 
			
		||||
     *          optional caching service
 | 
			
		||||
     * @param string $host host url of our server (e.g. https://federator.com)
 | 
			
		||||
     * @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
 | 
			
		||||
     * @return boolean response
 | 
			
		||||
     */
 | 
			
		||||
    public static function postForUser($dbh, $connector, $cache, $_user, $_recipientId, $newActivity)
 | 
			
		||||
    public static function postForUser($dbh, $connector, $cache, $host, $_user, $_recipientId, $newActivity)
 | 
			
		||||
    {
 | 
			
		||||
        if (!isset($_user)) {
 | 
			
		||||
            error_log("NewContent::postForUser no user given");
 | 
			
		||||
| 
						 | 
				
			
			@ -284,13 +281,15 @@ class NewContent implements \Federator\Api\APIInterface
 | 
			
		|||
                if ($actor !== '') {
 | 
			
		||||
                    $followerUsername = basename((string) (parse_url($actor, PHP_URL_PATH) ?? ''));
 | 
			
		||||
                    $followerDomain = parse_url($actor, PHP_URL_HOST);
 | 
			
		||||
                    $newIdUrl = \Federator\DIO\Followers::generateNewFollowId($dbh, $host);
 | 
			
		||||
                    $newActivity->setID($newIdUrl);
 | 
			
		||||
                    if (is_string($followerDomain)) {
 | 
			
		||||
                        $followerId = "{$followerUsername}@{$followerDomain}";
 | 
			
		||||
                        $success = \Federator\DIO\Followers::addFollow($dbh, $followerId, $user->id, $followerDomain);
 | 
			
		||||
                        // $success = \Federator\DIO\Followers::sendFollowRequest($dbh, $connector, $cache, $user->id, $followerId, $followerDomain);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if ($success === false) {
 | 
			
		||||
                    error_log("NewContent::postForUser: Failed to add follower for user $user->id");
 | 
			
		||||
                    error_log("NewContent::postForUser Failed to add follower for user $user->id");
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -318,12 +317,19 @@ class NewContent implements \Federator\Api\APIInterface
 | 
			
		|||
                                    $followerDomain = parse_url($actor, PHP_URL_HOST);
 | 
			
		||||
                                    if (is_string($followerDomain)) {
 | 
			
		||||
                                        $followerId = "{$followerUsername}@{$followerDomain}";
 | 
			
		||||
                                        $success = \Federator\DIO\Followers::removeFollow($dbh, $followerId, $user->id);
 | 
			
		||||
                                        $removedId = \Federator\DIO\Followers::removeFollow($dbh, $followerId, $user->id);
 | 
			
		||||
                                        if ($removedId !== false) {
 | 
			
		||||
                                            $object->setID($removedId);
 | 
			
		||||
                                            $newActivity->setObject($object);
 | 
			
		||||
                                            $success = true;
 | 
			
		||||
                                        } else {
 | 
			
		||||
                                            error_log("NewContent::postForUser Failed to remove follow for user $user->id");
 | 
			
		||||
                                        }
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            if ($success === false) {
 | 
			
		||||
                                error_log("NewContent::postForUser: Failed to remove follower for user $user->id");
 | 
			
		||||
                                error_log("NewContent::postForUser Failed to remove follower for user $user->id");
 | 
			
		||||
                            }
 | 
			
		||||
                            break;
 | 
			
		||||
                        case 'Like':
 | 
			
		||||
| 
						 | 
				
			
			@ -334,7 +340,7 @@ class NewContent implements \Federator\Api\APIInterface
 | 
			
		|||
                                    // \Federator\DIO\Votes::removeVote($dbh, $user->id, $targetId);
 | 
			
		||||
                                    \Federator\DIO\Posts::deletePost($dbh, $targetId);
 | 
			
		||||
                                } else {
 | 
			
		||||
                                    error_log("NewContent::postForUser: Error in Undo Like/Dislike for user $user->id, targetId is not a string");
 | 
			
		||||
                                    error_log("NewContent::postForUser Error in Undo Like/Dislike for user $user->id, targetId is not a string");
 | 
			
		||||
                                }
 | 
			
		||||
                            }
 | 
			
		||||
                            break;
 | 
			
		||||
| 
						 | 
				
			
			@ -358,7 +364,7 @@ class NewContent implements \Federator\Api\APIInterface
 | 
			
		|||
                } else if (is_string($object)) {
 | 
			
		||||
                    \Federator\DIO\Posts::deletePost($dbh, $object);
 | 
			
		||||
                } else {
 | 
			
		||||
                    error_log("NewContent::postForUser: Error in Undo for user $user->id, object is not a string or object");
 | 
			
		||||
                    error_log("NewContent::postForUser Error in Undo for user $user->id, object is not a string or object");
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -370,7 +376,7 @@ class NewContent implements \Federator\Api\APIInterface
 | 
			
		|||
                    // \Federator\DIO\Votes::addVote($dbh, $user->id, $targetId, 'like');
 | 
			
		||||
                    \Federator\DIO\Posts::savePost($dbh, $user->id, $newActivity);
 | 
			
		||||
                } else {
 | 
			
		||||
                    error_log("NewContent::postForUser: Error in Add Like/Dislike for user $user->id, targetId is not a string");
 | 
			
		||||
                    error_log("NewContent::postForUser Error in Add Like/Dislike for user $user->id, targetId is not a string");
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
| 
						 | 
				
			
			@ -391,13 +397,118 @@ class NewContent implements \Federator\Api\APIInterface
 | 
			
		|||
                break;
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                error_log("NewContent::postForUser: Unhandled activity type $type for user $user->id");
 | 
			
		||||
                error_log("NewContent::postForUser Unhandled activity type $type for user $user->id");
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            $response = self::sendActivity($dbh, $host, $user, $recipient, $newActivity);
 | 
			
		||||
        } catch (\Exception $e) {
 | 
			
		||||
            error_log("NewContent::postForUser Failed to send activity: " . $e->getMessage());
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        if (empty($response)) {
 | 
			
		||||
            error_log("NewContent::postForUser Sent activity to $recipient->id");
 | 
			
		||||
        } else {
 | 
			
		||||
            error_log("NewContent::postForUser Sent activity to $recipient->id with response: " . json_encode($response, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * send activity to federated server
 | 
			
		||||
     *
 | 
			
		||||
     * @param \mysqli $dbh database handle
 | 
			
		||||
     * @param string $host host url of our server (e.g. federator)
 | 
			
		||||
     * @param \Federator\Data\User $sender source user
 | 
			
		||||
     * @param \Federator\Data\FedUser $target federated target user
 | 
			
		||||
     * @param \Federator\Data\ActivityPub\Common\Activity $activity activity to send
 | 
			
		||||
     * @return string|true the generated follow ID on success, false on failure
 | 
			
		||||
     */
 | 
			
		||||
    public static function sendActivity($dbh, $host, $sender, $target, $activity)
 | 
			
		||||
    {
 | 
			
		||||
        if ($dbh === false) {
 | 
			
		||||
            throw new \Federator\Exceptions\ServerError("NewContent::sendActivity Failed to get database handle");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $inboxUrl = $target->inboxURL;
 | 
			
		||||
 | 
			
		||||
        $json = json_encode($activity, JSON_UNESCAPED_SLASHES);
 | 
			
		||||
 | 
			
		||||
        if ($json === false) {
 | 
			
		||||
            throw new \Exception('Failed to encode JSON: ' . json_last_error_msg());
 | 
			
		||||
        }
 | 
			
		||||
        $digest = 'SHA-256=' . base64_encode(hash('sha256', $json, true));
 | 
			
		||||
        $date = gmdate('D, d M Y H:i:s') . ' GMT';
 | 
			
		||||
        $parsed = parse_url($inboxUrl);
 | 
			
		||||
        if ($parsed === false) {
 | 
			
		||||
            throw new \Exception('Failed to parse URL: ' . $inboxUrl);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!isset($parsed['host']) || !isset($parsed['path'])) {
 | 
			
		||||
            throw new \Exception('Invalid inbox URL: missing host or path');
 | 
			
		||||
        }
 | 
			
		||||
        $extHost = $parsed['host'];
 | 
			
		||||
        $path = $parsed['path'];
 | 
			
		||||
 | 
			
		||||
        // Build the signature string
 | 
			
		||||
        $signatureString = "(request-target): post {$path}\n" .
 | 
			
		||||
            "host: {$extHost}\n" .
 | 
			
		||||
            "date: {$date}\n" .
 | 
			
		||||
            "digest: {$digest}";
 | 
			
		||||
 | 
			
		||||
        // Get rsa private key
 | 
			
		||||
        $privateKey = \Federator\DIO\User::getrsaprivate($dbh, $sender->id); // OR from DB
 | 
			
		||||
        if ($privateKey === false) {
 | 
			
		||||
            throw new \Exception('Failed to get private key');
 | 
			
		||||
        }
 | 
			
		||||
        $pkeyId = openssl_pkey_get_private($privateKey);
 | 
			
		||||
 | 
			
		||||
        if ($pkeyId === false) {
 | 
			
		||||
            throw new \Exception('Invalid private key');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        openssl_sign($signatureString, $signature, $pkeyId, OPENSSL_ALGO_SHA256);
 | 
			
		||||
        $signature_b64 = base64_encode($signature);
 | 
			
		||||
 | 
			
		||||
        // Build keyId (public key ID from your actor object)
 | 
			
		||||
        $keyId = $host . '/' . $sender->id . '#main-key';
 | 
			
		||||
 | 
			
		||||
        $signatureHeader = 'keyId="' . $keyId . '",algorithm="rsa-sha256",headers="(request-target) host date digest",signature="' . $signature_b64 . '"';
 | 
			
		||||
 | 
			
		||||
        $ch = curl_init($inboxUrl);
 | 
			
		||||
        if ($ch === false) {
 | 
			
		||||
            throw new \Exception('Failed to initialize cURL');
 | 
			
		||||
        }
 | 
			
		||||
        $headers = [
 | 
			
		||||
            'Host: ' . $extHost,
 | 
			
		||||
            'Date: ' . $date,
 | 
			
		||||
            'Digest: ' . $digest,
 | 
			
		||||
            'Content-Type: application/activity+json',
 | 
			
		||||
            'Signature: ' . $signatureHeader,
 | 
			
		||||
            'Accept: application/activity+json',
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
 | 
			
		||||
        curl_setopt($ch, CURLOPT_POST, true);
 | 
			
		||||
        curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
 | 
			
		||||
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
 | 
			
		||||
        $response = curl_exec($ch);
 | 
			
		||||
        curl_close($ch);
 | 
			
		||||
 | 
			
		||||
        // Log the response for debugging if needed
 | 
			
		||||
        if ($response === false) {
 | 
			
		||||
            throw new \Exception("Failed to send activity: " . curl_error($ch));
 | 
			
		||||
        } else {
 | 
			
		||||
            $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
 | 
			
		||||
            if ($httpcode != 200 && $httpcode != 202) {
 | 
			
		||||
                throw new \Exception("Unexpected HTTP code $httpcode: $response");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return $response;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * get internal represenation as json string
 | 
			
		||||
     * @return string json string or html
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -388,25 +388,97 @@ class Followers
 | 
			
		|||
        return $idurl; // Return the generated follow ID
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * generate new follow id
 | 
			
		||||
     *
 | 
			
		||||
     * @param \mysqli $dbh database handle
 | 
			
		||||
     * @param string $hostUrl the host URL (e.g. federator URL)
 | 
			
		||||
     * @return string the new follow id
 | 
			
		||||
     */
 | 
			
		||||
    public static function generateNewFollowId($dbh, $hostUrl)
 | 
			
		||||
    {
 | 
			
		||||
        // Generate a new unique follow ID
 | 
			
		||||
        do {
 | 
			
		||||
            $newId = bin2hex(openssl_random_pseudo_bytes(16));
 | 
			
		||||
            $newIdUrl = $hostUrl . '/' . $newId;
 | 
			
		||||
 | 
			
		||||
            // Check if the generated ID is unique
 | 
			
		||||
            $sql = 'select id from follows where id = ?';
 | 
			
		||||
            $stmt = $dbh->prepare($sql);
 | 
			
		||||
            if ($stmt === false) {
 | 
			
		||||
                throw new \Federator\Exceptions\ServerError("Followers::generateNewFollowId Failed to prepare id-check statement");
 | 
			
		||||
            }
 | 
			
		||||
            $stmt->bind_param("s", $newIdUrl);
 | 
			
		||||
            $foundId = 0;
 | 
			
		||||
            $ret = $stmt->bind_result($foundId);
 | 
			
		||||
            $stmt->execute();
 | 
			
		||||
            if ($ret) {
 | 
			
		||||
                $stmt->fetch();
 | 
			
		||||
            }
 | 
			
		||||
            $stmt->close();
 | 
			
		||||
        } while ($foundId > 0);
 | 
			
		||||
 | 
			
		||||
        return $newIdUrl;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * remove follow
 | 
			
		||||
     *
 | 
			
		||||
     * @param \mysqli $dbh database handle
 | 
			
		||||
     * @param string $sourceUser source user id
 | 
			
		||||
     * @param string $targetUserId target user id
 | 
			
		||||
     * @return bool true on success
 | 
			
		||||
     * @return string|false removed followId on success, false on failure
 | 
			
		||||
     */
 | 
			
		||||
    public static function removeFollow($dbh, $sourceUser, $targetUserId)
 | 
			
		||||
    {
 | 
			
		||||
        $sql = 'delete from follows where source_user = ? and target_user = ?';
 | 
			
		||||
        // Combine retrieval and removal in one query using MySQL's RETURNING (if supported)
 | 
			
		||||
        $sql = 'delete from follows where source_user = ? and target_user = ? RETURNING id';
 | 
			
		||||
        $stmt = $dbh->prepare($sql);
 | 
			
		||||
        if ($stmt === false) {
 | 
			
		||||
            throw new \Federator\Exceptions\ServerError("Followers::removeFollow Failed to prepare statement");
 | 
			
		||||
        if ($stmt !== false) {
 | 
			
		||||
            $stmt->bind_param("ss", $sourceUser, $targetUserId);
 | 
			
		||||
            if ($stmt->execute()) {
 | 
			
		||||
                $stmt->bind_result($followId);
 | 
			
		||||
                if ($stmt->fetch() === true) {
 | 
			
		||||
                    $stmt->close();
 | 
			
		||||
                    if (!empty($followId)) {
 | 
			
		||||
                        return $followId;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            $stmt->close();
 | 
			
		||||
        } else {
 | 
			
		||||
            // Fallback for MySQL versions that do not support RETURNING
 | 
			
		||||
            // First, fetch the id of the follow to be removed
 | 
			
		||||
            $sql = 'select id from follows where source_user = ? and target_user = ?';
 | 
			
		||||
            $stmt = $dbh->prepare($sql);
 | 
			
		||||
            if ($stmt === false) {
 | 
			
		||||
                throw new \Federator\Exceptions\ServerError("Followers::removeFollow Failed to prepare select statement");
 | 
			
		||||
            }
 | 
			
		||||
            $stmt->bind_param("ss", $sourceUser, $targetUserId);
 | 
			
		||||
            $stmt->execute();
 | 
			
		||||
            $stmt->bind_result($followId);
 | 
			
		||||
            $found = $stmt->fetch();
 | 
			
		||||
            $stmt->close();
 | 
			
		||||
 | 
			
		||||
            if ($found === false || empty($followId)) {
 | 
			
		||||
                return false; // No such follow found
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Now, delete the row
 | 
			
		||||
            $sql = 'delete from follows where source_user = ? and target_user = ?';
 | 
			
		||||
            $stmt = $dbh->prepare($sql);
 | 
			
		||||
            if ($stmt === false) {
 | 
			
		||||
                throw new \Federator\Exceptions\ServerError("Followers::removeFollow Failed to prepare delete statement");
 | 
			
		||||
            }
 | 
			
		||||
            $stmt->bind_param("ss", $sourceUser, $targetUserId);
 | 
			
		||||
            $stmt->execute();
 | 
			
		||||
            $affectedRows = $stmt->affected_rows;
 | 
			
		||||
            $stmt->close();
 | 
			
		||||
 | 
			
		||||
            return $affectedRows > 0 ? $followId : false;
 | 
			
		||||
        }
 | 
			
		||||
        $stmt->bind_param("ss", $sourceUser, $targetUserId);
 | 
			
		||||
        $stmt->execute();
 | 
			
		||||
        $affectedRows = $stmt->affected_rows;
 | 
			
		||||
        $stmt->close();
 | 
			
		||||
        return $affectedRows > 0;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,7 +52,7 @@ class InboxJob extends \Federator\Api
 | 
			
		|||
     */
 | 
			
		||||
    public function perform(): bool
 | 
			
		||||
    {
 | 
			
		||||
        error_log("InboxJob: Starting inbox job");
 | 
			
		||||
        error_log("InboxJob: Starting job");
 | 
			
		||||
        $user = $this->args['user'];
 | 
			
		||||
        $recipientId = $this->args['recipientId'];
 | 
			
		||||
        $activity = $this->args['activity'];
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,7 +52,7 @@ class NewContentJob extends \Federator\Api
 | 
			
		|||
     */
 | 
			
		||||
    public function perform(): bool
 | 
			
		||||
    {
 | 
			
		||||
        error_log("NewContentJob: Starting inbox job");
 | 
			
		||||
        error_log("NewContentJob: Starting job");
 | 
			
		||||
        $user = $this->args['user'];
 | 
			
		||||
        $recipientId = $this->args['recipientId'];
 | 
			
		||||
        $activity = $this->args['activity'];
 | 
			
		||||
| 
						 | 
				
			
			@ -63,8 +63,11 @@ class NewContentJob extends \Federator\Api
 | 
			
		|||
            error_log("NewContentJob: Failed to create activity from JSON");
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        $domain = $this->config['generic']['externaldomain'];
 | 
			
		||||
 | 
			
		||||
        \Federator\Api\V1\NewContent::postForUser($this->dbh, $this->connector, $this->cache, $user, $recipientId, $activity);
 | 
			
		||||
        $ourUrl = 'https://' . $domain;
 | 
			
		||||
 | 
			
		||||
        \Federator\Api\V1\NewContent::postForUser($this->dbh, $this->connector, $this->cache, $ourUrl, $user, $recipientId, $activity);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -139,23 +139,25 @@ class ContentNation implements Connector
 | 
			
		|||
            $activities = $r['activities'];
 | 
			
		||||
            $config = $this->main->getConfig();
 | 
			
		||||
            $domain = $config['generic']['externaldomain'];
 | 
			
		||||
            $ourUrl = 'https://' . $domain;
 | 
			
		||||
 | 
			
		||||
            $imgpath = $this->config['userdata']['path'];
 | 
			
		||||
            $userdata = $this->config['userdata']['url'];
 | 
			
		||||
            foreach ($activities as $activity) {
 | 
			
		||||
                switch ($activity['type']) {
 | 
			
		||||
                    case 'Article':
 | 
			
		||||
                        $create = new \Federator\Data\ActivityPub\Common\Create();
 | 
			
		||||
                        $create->setAActor('https://' . $domain . '/' . $userId);
 | 
			
		||||
                        $create->setAActor($ourUrl . '/' . $userId);
 | 
			
		||||
                        $create->setID($activity['id'])
 | 
			
		||||
                            ->setPublished($activity['published'] ?? $activity['timestamp'])
 | 
			
		||||
                            ->addTo('https://' . $domain . '/' . $userId . '/followers')
 | 
			
		||||
                            ->addTo($ourUrl . '/' . $userId . '/followers')
 | 
			
		||||
                            ->addCC("https://www.w3.org/ns/activitystreams#Public");
 | 
			
		||||
                        $create->setURL('https://' . $domain . '/' . $activity['profilename'] . '/' . $activity['name']);
 | 
			
		||||
                        $create->setID('https://' . $domain . '/' . $activity['profilename'] . '/' . $activity['id']);
 | 
			
		||||
                        $create->setURL($ourUrl . '/' . $activity['profilename'] . '/' . $activity['name']);
 | 
			
		||||
                        $create->setID($ourUrl . '/' . $activity['profilename'] . '/' . $activity['id']);
 | 
			
		||||
                        $apArticle = new \Federator\Data\ActivityPub\Common\Article();
 | 
			
		||||
                        if (array_key_exists('tags', $activity)) {
 | 
			
		||||
                            foreach ($activity['tags'] as $tag) {
 | 
			
		||||
                                $href = 'https://' . $domain . '/search.htm?tagsearch=' . urlencode($tag);
 | 
			
		||||
                                $href = $ourUrl . '/search.htm?tagsearch=' . urlencode($tag);
 | 
			
		||||
                                $tagObj = new \Federator\Data\ActivityPub\Common\Tag();
 | 
			
		||||
                                $tagObj->setHref($href)
 | 
			
		||||
                                    ->setName('#' . urlencode(str_replace(' ', '', $tag)))
 | 
			
		||||
| 
						 | 
				
			
			@ -165,7 +167,7 @@ class ContentNation implements Connector
 | 
			
		|||
                        }
 | 
			
		||||
                        $apArticle->setPublished($activity['published'])
 | 
			
		||||
                            ->setName($activity['title'])
 | 
			
		||||
                            ->setAttributedTo('https://' . $domain . '/' . $activity['profilename'])
 | 
			
		||||
                            ->setAttributedTo($ourUrl . '/' . $activity['profilename'])
 | 
			
		||||
                            ->setContent(
 | 
			
		||||
                                $activity['teaser'] ??
 | 
			
		||||
                                $this->main->translate(
 | 
			
		||||
| 
						 | 
				
			
			@ -175,10 +177,10 @@ class ContentNation implements Connector
 | 
			
		|||
                                )
 | 
			
		||||
                            )
 | 
			
		||||
                            ->addTo("https://www.w3.org/ns/activitystreams#Public")
 | 
			
		||||
                            ->addCC('https://' . $domain . '/' . $userId . '/followers.json');
 | 
			
		||||
                            ->addCC($ourUrl . '/' . $userId . '/followers.json');
 | 
			
		||||
                        $articleimage = $activity['imagealt'] ??
 | 
			
		||||
                            $this->main->translate($activity['language'], 'article', 'image');
 | 
			
		||||
                        $idurl = 'https://' . $domain . '/' . $userId . '/' . $activity['name'];
 | 
			
		||||
                        $idurl = $ourUrl . '/' . $userId . '/' . $activity['name'];
 | 
			
		||||
                        $apArticle->setID($idurl)
 | 
			
		||||
                            ->setURL($idurl);
 | 
			
		||||
                        $image = $activity['image'] ?? $activity['profileimg'];
 | 
			
		||||
| 
						 | 
				
			
			@ -200,15 +202,15 @@ class ContentNation implements Connector
 | 
			
		|||
 | 
			
		||||
                    case 'Comment':
 | 
			
		||||
                        $create = new \Federator\Data\ActivityPub\Common\Create();
 | 
			
		||||
                        $create->setAActor('https://' . $domain . '/' . $userId);
 | 
			
		||||
                        $create->setAActor($ourUrl . '/' . $userId);
 | 
			
		||||
                        $create->setID($activity['id'])
 | 
			
		||||
                            ->setPublished($activity['published'] ?? $activity['timestamp'])
 | 
			
		||||
                            ->addTo('https://' . $domain . '/' . $userId . '/followers')
 | 
			
		||||
                            ->addTo($ourUrl . '/' . $userId . '/followers')
 | 
			
		||||
                            ->addCC("https://www.w3.org/ns/activitystreams#Public");
 | 
			
		||||
                        $commentJson = $activity;
 | 
			
		||||
                        $commentJson['type'] = 'Note';
 | 
			
		||||
                        $commentJson['summary'] = $activity['subject'];
 | 
			
		||||
                        $commentJson['id'] = 'https://' . $domain . '/' . $activity['articleOwnerName'] . '/' . $activity['articleName'] . '#' . $activity['id'];
 | 
			
		||||
                        $commentJson['id'] = $ourUrl . '/' . $activity['articleOwnerName'] . '/' . $activity['articleName'] . '#' . $activity['id'];
 | 
			
		||||
                        $note = \Federator\Data\ActivityPub\Factory::newFromJson($commentJson, "");
 | 
			
		||||
                        if ($note === null) {
 | 
			
		||||
                            error_log("ContentNation::getRemotePostsByUser couldn't create comment");
 | 
			
		||||
| 
						 | 
				
			
			@ -218,11 +220,11 @@ class ContentNation implements Connector
 | 
			
		|||
                        }
 | 
			
		||||
                        $note->setID($commentJson['id']);
 | 
			
		||||
                        if (!isset($commentJson['parent']) || $commentJson['parent'] === null) {
 | 
			
		||||
                            $note->setInReplyTo('https://' . $domain . '/' . $activity['articleOwnerName'] . '/' . $activity['articleName']);
 | 
			
		||||
                            $note->setInReplyTo($ourUrl . '/' . $activity['articleOwnerName'] . '/' . $activity['articleName']);
 | 
			
		||||
                        } else {
 | 
			
		||||
                            $note->setInReplyTo('https://' . $domain . '/' . $activity['articleOwnerName'] . '/' . $activity['articleName'] . "#" . $commentJson['parent']);
 | 
			
		||||
                            $note->setInReplyTo($ourUrl . '/' . $activity['articleOwnerName'] . '/' . $activity['articleName'] . "#" . $commentJson['parent']);
 | 
			
		||||
                        }
 | 
			
		||||
                        $url = 'https://' . $domain . '/' . $activity['articleOwnerName'] . '/' . $activity['articleName'] . '#' . $activity['id'];
 | 
			
		||||
                        $url = $ourUrl . '/' . $activity['articleOwnerName'] . '/' . $activity['articleName'] . '#' . $activity['id'];
 | 
			
		||||
                        $create->setURL($url);
 | 
			
		||||
                        $create->setID($url);
 | 
			
		||||
                        $create->setObject($note);
 | 
			
		||||
| 
						 | 
				
			
			@ -233,7 +235,7 @@ class ContentNation implements Connector
 | 
			
		|||
                        // Build Like/Dislike as top-level activity
 | 
			
		||||
                        $likeType = $activity['upvote'] === true ? 'Like' : 'Dislike';
 | 
			
		||||
                        $like = new \Federator\Data\ActivityPub\Common\Activity($likeType);
 | 
			
		||||
                        $like->setAActor('https://' . $domain . '/' . $userId);
 | 
			
		||||
                        $like->setAActor($ourUrl . '/' . $userId);
 | 
			
		||||
                        $like->setID($activity['id'])
 | 
			
		||||
                            ->setPublished($activity['published'] ?? $activity['timestamp']);
 | 
			
		||||
                        // $like->addTo("https://www.w3.org/ns/activitystreams#Public")
 | 
			
		||||
| 
						 | 
				
			
			@ -246,7 +248,7 @@ class ContentNation implements Connector
 | 
			
		|||
                                [$activity['username']]
 | 
			
		||||
                            )
 | 
			
		||||
                        );
 | 
			
		||||
                        $objectUrl = 'https://' . $domain . '/' . $userId . '/' . $activity['articlename'];
 | 
			
		||||
                        $objectUrl = $ourUrl . '/' . $userId . '/' . $activity['articlename'];
 | 
			
		||||
                        $like->setURL($objectUrl . '#' . $activity['id']);
 | 
			
		||||
                        $like->setID($objectUrl . '#' . $activity['id']);
 | 
			
		||||
                        $like->setObject($objectUrl);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue