Now the code is following most of PSR Various changes from various branches (some testing will be required) Fixed various issues
This commit is contained in:
parent
e377b87ff7
commit
eaad9423dd
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/vendor/
|
File diff suppressed because it is too large
Load Diff
@ -6,11 +6,10 @@ email, or any other method with the owners of this repository before making a ch
|
||||
Please note we have a code of conduct, please follow it in all your interactions with the project.
|
||||
|
||||
# Coding Style
|
||||
- We are using [K&R Variant: Linux kernel](https://en.wikipedia.org/wiki/Indentation_style#Variant:_Linux_kernel) with spaces before every `(`.
|
||||
- Every function has a docblock explaining what it does and stating the author, parameters, types, return and exceptions
|
||||
- We use snake_case
|
||||
- We follow every [PSR-2](https://www.php-fig.org/psr/psr-2/) ...
|
||||
- ... except camelCase, that's too bad, we use snake_case
|
||||
|
||||
## Pull Request Process
|
||||
## Merge Request Process
|
||||
|
||||
1. Ensure you strip any trailing spaces off
|
||||
2. Increase the version numbers in any examples files and the README.md to the new version that this
|
||||
|
@ -67,4 +67,3 @@ License along with this program, in the file "COPYING". If not, see
|
||||
to your users under the same license. This is a legal requirement
|
||||
of using the software, and if you do not wish to share your
|
||||
modifications, *YOU MAY NOT USE THIS PLUGIN*.
|
||||
|
||||
|
@ -25,8 +25,8 @@
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined ('GNUSOCIAL')) {
|
||||
exit (1);
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -40,64 +40,62 @@ if (!defined ('GNUSOCIAL')) {
|
||||
*/
|
||||
class apActorFollowersAction extends ManagedAction
|
||||
{
|
||||
protected $needLogin = false;
|
||||
protected $canPost = true;
|
||||
protected $needLogin = false;
|
||||
protected $canPost = true;
|
||||
|
||||
/**
|
||||
* Handle the Followers Collection request
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @return void
|
||||
*/
|
||||
protected function handle ()
|
||||
{
|
||||
$nickname = $this->trimmed ('nickname');
|
||||
try {
|
||||
$user = User::getByNickname ($nickname);
|
||||
$profile = $user->getProfile ();
|
||||
$url = $profile->profileurl;
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error ('Invalid username.');
|
||||
}
|
||||
/**
|
||||
* Handle the Followers Collection request
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @return void
|
||||
*/
|
||||
protected function handle()
|
||||
{
|
||||
try {
|
||||
$profile = Profile::getByID($this->trimmed('id'));
|
||||
$url = ActivityPubPlugin::actor_url($profile);
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error('Invalid Actor URI.', 404);
|
||||
}
|
||||
|
||||
if (!isset ($_GET["page"])) {
|
||||
$page = 1;
|
||||
} else {
|
||||
$page = intval ($this->trimmed ('page'));
|
||||
}
|
||||
if (!isset($_GET["page"])) {
|
||||
$page = 1;
|
||||
} else {
|
||||
$page = intval($this->trimmed('page'));
|
||||
}
|
||||
|
||||
if ($page <= 0) {
|
||||
ActivityPubReturn::error ('Invalid page number.');
|
||||
}
|
||||
if ($page <= 0) {
|
||||
ActivityPubReturn::error('Invalid page number.');
|
||||
}
|
||||
|
||||
/* Fetch Followers */
|
||||
try {
|
||||
$since = ($page - 1) * PROFILES_PER_MINILIST;
|
||||
$limit = (($page - 1) == 0 ? 1 : $page) * PROFILES_PER_MINILIST;
|
||||
$sub = $profile->getSubscribers ($since, $limit);
|
||||
} catch (NoResultException $e) {
|
||||
ActivityPubReturn::error ('This user has no followers.');
|
||||
}
|
||||
/* Fetch Followers */
|
||||
try {
|
||||
$since = ($page - 1) * PROFILES_PER_MINILIST;
|
||||
$limit = (($page - 1) == 0 ? 1 : $page) * PROFILES_PER_MINILIST;
|
||||
$sub = $profile->getSubscribers($since, $limit);
|
||||
} catch (NoResultException $e) {
|
||||
ActivityPubReturn::error('This user has no followers.');
|
||||
}
|
||||
|
||||
/* Calculate total items */
|
||||
$total_subs = $profile->subscriberCount ();
|
||||
$total_pages = ceil ($total_subs / PROFILES_PER_MINILIST);
|
||||
/* Calculate total items */
|
||||
$total_subs = $profile->subscriberCount();
|
||||
$total_pages = ceil($total_subs / PROFILES_PER_MINILIST);
|
||||
|
||||
if ($total_pages == 0) {
|
||||
ActivityPubReturn::error ('This user has no followers.');
|
||||
}
|
||||
if ($total_pages == 0) {
|
||||
ActivityPubReturn::error('This user has no followers.');
|
||||
}
|
||||
|
||||
if ($page > $total_pages) {
|
||||
ActivityPubReturn::error ("There are only {$total_pages} pages.");
|
||||
}
|
||||
if ($page > $total_pages) {
|
||||
ActivityPubReturn::error("There are only {$total_pages} pages.");
|
||||
}
|
||||
|
||||
/* Get followers' URLs */
|
||||
$subs = array ();
|
||||
while ($sub->fetch ()) {
|
||||
$subs[] = $sub->profileurl;
|
||||
}
|
||||
/* Get followers' URLs */
|
||||
$subs = array();
|
||||
while ($sub->fetch()) {
|
||||
$subs[] = $sub->profileurl;
|
||||
}
|
||||
|
||||
$res = [
|
||||
$res = [
|
||||
'@context' => [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
@ -110,6 +108,6 @@ class apActorFollowersAction extends ManagedAction
|
||||
'orderedItems' => $subs
|
||||
];
|
||||
|
||||
ActivityPubReturn::answer ($res);
|
||||
}
|
||||
ActivityPubReturn::answer($res);
|
||||
}
|
||||
}
|
||||
|
@ -25,8 +25,8 @@
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined ('GNUSOCIAL')) {
|
||||
exit (1);
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -40,64 +40,62 @@ if (!defined ('GNUSOCIAL')) {
|
||||
*/
|
||||
class apActorFollowingAction extends ManagedAction
|
||||
{
|
||||
protected $needLogin = false;
|
||||
protected $canPost = true;
|
||||
protected $needLogin = false;
|
||||
protected $canPost = true;
|
||||
|
||||
/**
|
||||
* Handle the Following Collection request
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @return void
|
||||
*/
|
||||
protected function handle ()
|
||||
{
|
||||
$nickname = $this->trimmed ('nickname');
|
||||
try {
|
||||
$user = User::getByNickname ($nickname);
|
||||
$profile = $user->getProfile ();
|
||||
$url = $profile->profileurl;
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error ('Invalid username.');
|
||||
}
|
||||
/**
|
||||
* Handle the Following Collection request
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @return void
|
||||
*/
|
||||
protected function handle()
|
||||
{
|
||||
try {
|
||||
$profile = Profile::getByID($this->trimmed('id'));
|
||||
$url = ActivityPubPlugin::actor_url($profile);
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error('Invalid Actor URI.', 404);
|
||||
}
|
||||
|
||||
if (!isset ($_GET["page"])) {
|
||||
$page = 1;
|
||||
} else {
|
||||
$page = intval ($this->trimmed ('page'));
|
||||
}
|
||||
if (!isset($_GET["page"])) {
|
||||
$page = 1;
|
||||
} else {
|
||||
$page = intval($this->trimmed('page'));
|
||||
}
|
||||
|
||||
if ($page <= 0) {
|
||||
ActivityPubReturn::error ('Invalid page number.');
|
||||
}
|
||||
if ($page <= 0) {
|
||||
ActivityPubReturn::error('Invalid page number.');
|
||||
}
|
||||
|
||||
/* Fetch Following */
|
||||
try {
|
||||
$since = ($page - 1) * PROFILES_PER_MINILIST;
|
||||
$limit = (($page - 1) == 0 ? 1 : $page) * PROFILES_PER_MINILIST;
|
||||
$sub = $profile->getSubscribed ($since, $limit);
|
||||
} catch (NoResultException $e) {
|
||||
ActivityPubReturn::error ('This user is not following anyone.');
|
||||
}
|
||||
/* Fetch Following */
|
||||
try {
|
||||
$since = ($page - 1) * PROFILES_PER_MINILIST;
|
||||
$limit = (($page - 1) == 0 ? 1 : $page) * PROFILES_PER_MINILIST;
|
||||
$sub = $profile->getSubscribed($since, $limit);
|
||||
} catch (NoResultException $e) {
|
||||
ActivityPubReturn::error('This user is not following anyone.');
|
||||
}
|
||||
|
||||
/* Calculate total items */
|
||||
$total_subs = $profile->subscriptionCount();
|
||||
$total_pages = ceil ($total_subs / PROFILES_PER_MINILIST);
|
||||
/* Calculate total items */
|
||||
$total_subs = $profile->subscriptionCount();
|
||||
$total_pages = ceil($total_subs / PROFILES_PER_MINILIST);
|
||||
|
||||
if ($total_pages == 0) {
|
||||
ActivityPubReturn::error ('This user is not following anyone.');
|
||||
}
|
||||
if ($total_pages == 0) {
|
||||
ActivityPubReturn::error('This user is not following anyone.');
|
||||
}
|
||||
|
||||
if ($page > $total_pages) {
|
||||
ActivityPubReturn::error ("There are only {$total_pages} pages.");
|
||||
}
|
||||
if ($page > $total_pages) {
|
||||
ActivityPubReturn::error("There are only {$total_pages} pages.");
|
||||
}
|
||||
|
||||
/* Get followed' URLs */
|
||||
$subs = array ();
|
||||
while ($sub->fetch ()) {
|
||||
$subs[] = $sub->profileurl;
|
||||
}
|
||||
/* Get followed' URLs */
|
||||
$subs = array();
|
||||
while ($sub->fetch()) {
|
||||
$subs[] = $sub->profileurl;
|
||||
}
|
||||
|
||||
$res = [
|
||||
$res = [
|
||||
'@context' => [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
@ -110,6 +108,6 @@ class apActorFollowingAction extends ManagedAction
|
||||
'orderedItems' => $subs
|
||||
];
|
||||
|
||||
ActivityPubReturn::answer ($res);
|
||||
}
|
||||
ActivityPubReturn::answer($res);
|
||||
}
|
||||
}
|
||||
|
@ -25,8 +25,8 @@
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined ('GNUSOCIAL')) {
|
||||
exit (1);
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -40,57 +40,59 @@ if (!defined ('GNUSOCIAL')) {
|
||||
*/
|
||||
class apActorInboxAction extends ManagedAction
|
||||
{
|
||||
protected $needLogin = false;
|
||||
protected $canPost = true;
|
||||
protected $needLogin = false;
|
||||
protected $canPost = true;
|
||||
|
||||
/**
|
||||
* Handle the Actor Inbox request
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @return void
|
||||
*/
|
||||
protected function handle ()
|
||||
{
|
||||
$nickname = $this->trimmed ('nickname');
|
||||
try {
|
||||
$user = User::getByNickname ($nickname);
|
||||
$profile = $user->getProfile ();
|
||||
$url = $profile->profileurl;
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error ("Invalid username.");
|
||||
}
|
||||
/**
|
||||
* Handle the Actor Inbox request
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @return void
|
||||
*/
|
||||
protected function handle()
|
||||
{
|
||||
try {
|
||||
$profile = Profile::getByID($this->trimmed('id'));
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error('Invalid Actor URI.', 404);
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
ActivityPubReturn::error ("C2S not implemented just yet.");
|
||||
}
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
ActivityPubReturn::error("C2S not implemented just yet.");
|
||||
}
|
||||
|
||||
$data = json_decode (file_get_contents ('php://input'));
|
||||
$data = json_decode(file_get_contents('php://input'));
|
||||
|
||||
// Validate data
|
||||
if (!(isset ($data->type))) {
|
||||
ActivityPubReturn::error ("Type was not specified.");
|
||||
}
|
||||
if (!isset ($data->actor)) {
|
||||
ActivityPubReturn::error ("Actor was not specified.");
|
||||
}
|
||||
if (!isset ($data->object)) {
|
||||
ActivityPubReturn::error ("Object was not specified.");
|
||||
}
|
||||
// Validate data
|
||||
if (!(isset($data->type))) {
|
||||
ActivityPubReturn::error("Type was not specified.");
|
||||
}
|
||||
if (!isset($data->actor)) {
|
||||
ActivityPubReturn::error("Actor was not specified.");
|
||||
}
|
||||
if (!isset($data->object)) {
|
||||
ActivityPubReturn::error("Object was not specified.");
|
||||
}
|
||||
|
||||
// Get valid Actor object
|
||||
try {
|
||||
require_once dirname (__DIR__) . DIRECTORY_SEPARATOR . "utils" . DIRECTORY_SEPARATOR . "explorer.php";
|
||||
$actor_profile = new Activitypub_explorer;
|
||||
$actor_profile = $actor_profile->lookup ($data->actor);
|
||||
$actor_profile = $actor_profile[0];
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error ("Invalid Actor.", 404);
|
||||
}
|
||||
// Get valid Actor object
|
||||
try {
|
||||
require_once dirname(__DIR__) . DIRECTORY_SEPARATOR . "utils" . DIRECTORY_SEPARATOR . "explorer.php";
|
||||
$actor_profile = new Activitypub_explorer;
|
||||
$actor_profile = $actor_profile->lookup($data->actor);
|
||||
$actor_profile = $actor_profile[0];
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error("Invalid Actor.", 404);
|
||||
}
|
||||
|
||||
$to_profiles = array ($user);
|
||||
// Public To:
|
||||
$public_to = array("https://www.w3.org/ns/activitystreams#Public",
|
||||
"Public",
|
||||
"as:Public");
|
||||
|
||||
// Process request
|
||||
switch ($data->type) {
|
||||
$to_profiles = array($profile);
|
||||
|
||||
// Process request
|
||||
switch ($data->type) {
|
||||
case "Create":
|
||||
require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Create.php";
|
||||
break;
|
||||
@ -109,8 +111,14 @@ class apActorInboxAction extends ManagedAction
|
||||
case "Announce":
|
||||
require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Announce.php";
|
||||
break;
|
||||
case "Accept":
|
||||
require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Accept.php";
|
||||
break;
|
||||
case "Reject":
|
||||
require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Reject.php";
|
||||
break;
|
||||
default:
|
||||
ActivityPubReturn::error ("Invalid type value.");
|
||||
ActivityPubReturn::error("Invalid type value.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
152
actions/apactorliked.php
Normal file
152
actions/apactorliked.php
Normal file
@ -0,0 +1,152 @@
|
||||
<?php
|
||||
/**
|
||||
* GNU social - a federating social network
|
||||
*
|
||||
* ActivityPubPlugin implementation for GNU Social
|
||||
*
|
||||
* LICENCE: This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @author Daniel Supernault <danielsupernault@gmail.com>
|
||||
* @copyright 2018 Free Software Foundation http://fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actor's Liked Collection
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*/
|
||||
class apActorLikedAction extends ManagedAction
|
||||
{
|
||||
protected $needLogin = false;
|
||||
protected $canPost = true;
|
||||
|
||||
/**
|
||||
* Handle the Liked Collection request
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @return void
|
||||
*/
|
||||
protected function handle()
|
||||
{
|
||||
$nickname = $this->trimmed('nickname');
|
||||
try {
|
||||
$user = User::getByNickname($nickname);
|
||||
$profile = $user->getProfile();
|
||||
$url = $profile->profileurl;
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error('Invalid username.');
|
||||
}
|
||||
|
||||
$limit = intval($this->trimmed('limit'));
|
||||
$since_id = intval($this->trimmed('since_id'));
|
||||
$max_id = intval($this->trimmed('max_id'));
|
||||
|
||||
$limit = empty($limit) ? 40 : $limit; // Default is 40
|
||||
$since_id = empty($since_id) ? null : $since_id;
|
||||
$max_id = empty($max_id) ? null : $max_id;
|
||||
|
||||
// Max is 80
|
||||
if ($limit > 80) {
|
||||
$limit = 80;
|
||||
}
|
||||
|
||||
$fave = $this->fetch_faves($user->getID(), $limit, $since_id, $max_id);
|
||||
|
||||
$faves = array();
|
||||
while ($fave->fetch()) {
|
||||
$faves[] = $this->pretty_fave(clone ($fave));
|
||||
}
|
||||
|
||||
$res = [
|
||||
'@context' => [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
[
|
||||
"@language" => "en"
|
||||
]
|
||||
],
|
||||
'id' => "{$url}/liked.json",
|
||||
'type' => 'OrderedCollection',
|
||||
'totalItems' => Fave::countByProfile($profile),
|
||||
'orderedItems' => $faves
|
||||
];
|
||||
|
||||
ActivityPubReturn::answer($res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a fave object and turns it in a pretty array to be used
|
||||
* as a plugin answer
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param Fave $fave_object
|
||||
* @return array pretty array representating a Fave
|
||||
*/
|
||||
protected function pretty_fave($fave_object)
|
||||
{
|
||||
$res = array("uri" => $fave_object->uri,
|
||||
"created" => $fave_object->created,
|
||||
"object" => Activitypub_notice::notice_to_array(Notice::getByID($fave_object->notice_id)));
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch faves
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param int32 $user_id
|
||||
* @param int32 $limit
|
||||
* @param int32 $since_id
|
||||
* @param int32 $max_id
|
||||
* @return Fave fetchable fave collection
|
||||
*/
|
||||
private static function fetch_faves(
|
||||
$user_id,
|
||||
$limit = 40,
|
||||
$since_id = null,
|
||||
$max_id = null
|
||||
) {
|
||||
$fav = new Fave();
|
||||
|
||||
$fav->user_id = $user_id;
|
||||
|
||||
$fav->orderBy('modified DESC');
|
||||
|
||||
if ($since_id != null) {
|
||||
$fav->whereAdd("notice_id > {$since_id}");
|
||||
}
|
||||
|
||||
if ($max_id != null) {
|
||||
$fav->whereAdd("notice_id < {$max_id}");
|
||||
}
|
||||
|
||||
$fav->limit($limit);
|
||||
|
||||
$fav->find();
|
||||
|
||||
return $fav;
|
||||
}
|
||||
}
|
@ -1,149 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* GNU social - a federating social network
|
||||
*
|
||||
* ActivityPubPlugin implementation for GNU Social
|
||||
*
|
||||
* LICENCE: This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @author Daniel Supernault <danielsupernault@gmail.com>
|
||||
* @copyright 2018 Free Software Foundation http://fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined ('GNUSOCIAL')) {
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actor's Liked Collection
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*/
|
||||
class apActorLikedCollectionAction extends ManagedAction
|
||||
{
|
||||
protected $needLogin = false;
|
||||
protected $canPost = true;
|
||||
|
||||
/**
|
||||
* Handle the Liked Collection request
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @return void
|
||||
*/
|
||||
protected function handle ()
|
||||
{
|
||||
$nickname = $this->trimmed ('nickname');
|
||||
try {
|
||||
$user = User::getByNickname ($nickname);
|
||||
$profile = $user->getProfile ();
|
||||
$url = $profile->profileurl;
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error ('Invalid username.');
|
||||
}
|
||||
|
||||
$limit = intval ($this->trimmed ('limit'));
|
||||
$since_id = intval ($this->trimmed ('since_id'));
|
||||
$max_id = intval ($this->trimmed ('max_id'));
|
||||
|
||||
$limit = empty ($limit) ? 40 : $limit; // Default is 40
|
||||
$since_id = empty ($since_id) ? null : $since_id;
|
||||
$max_id = empty ($max_id) ? null : $max_id;
|
||||
|
||||
// Max is 80
|
||||
if ($limit > 80) {
|
||||
$limit = 80;
|
||||
}
|
||||
|
||||
$fave = $this->fetch_faves ($user->getID(), $limit, $since_id, $max_id);
|
||||
|
||||
$faves = array ();
|
||||
while ($fave->fetch ()) {
|
||||
$faves[] = $this->pretty_fave (clone ($fave));
|
||||
}
|
||||
|
||||
$res = [
|
||||
'@context' => [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
[
|
||||
"@language" => "en"
|
||||
]
|
||||
],
|
||||
'id' => "{$url}/liked.json",
|
||||
'type' => 'OrderedCollection',
|
||||
'totalItems' => Fave::countByProfile ($profile),
|
||||
'orderedItems' => $faves
|
||||
];
|
||||
|
||||
ActivityPubReturn::answer ($res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a fave object and turns it in a pretty array to be used
|
||||
* as a plugin answer
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param Fave $fave_object
|
||||
* @return array pretty array representating a Fave
|
||||
*/
|
||||
protected function pretty_fave ($fave_object)
|
||||
{
|
||||
$res = array("uri" => $fave_object->uri,
|
||||
"created" => $fave_object->created,
|
||||
"object" => Activitypub_notice::notice_to_array (Notice::getByID ($fave_object->notice_id)));
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch faves
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param int32 $user_id
|
||||
* @param int32 $limit
|
||||
* @param int32 $since_id
|
||||
* @param int32 $max_id
|
||||
* @return Fave fetchable fave collection
|
||||
*/
|
||||
private static function fetch_faves ($user_id, $limit = 40, $since_id = null,
|
||||
$max_id = null)
|
||||
{
|
||||
$fav = new Fave ();
|
||||
|
||||
$fav->user_id = $user_id;
|
||||
|
||||
$fav->orderBy ('modified DESC');
|
||||
|
||||
if ($since_id != null) {
|
||||
$fav->whereAdd ("notice_id > {$since_id}");
|
||||
}
|
||||
|
||||
if ($max_id != null) {
|
||||
$fav->whereAdd ("notice_id < {$max_id}");
|
||||
}
|
||||
|
||||
$fav->limit ($limit);
|
||||
|
||||
$fav->find ();
|
||||
|
||||
return $fav;
|
||||
}
|
||||
}
|
@ -19,14 +19,14 @@
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Daniel Supernault <danielsupernault@gmail.com>
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @author Daniel Supernault <danielsupernault@gmail.com>
|
||||
* @copyright 2018 Free Software Foundation http://fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined ('GNUSOCIAL')) {
|
||||
exit (1);
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -34,35 +34,44 @@ if (!defined ('GNUSOCIAL')) {
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Daniel Supernault <danielsupernault@gmail.com>
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*/
|
||||
class apActorProfileAction extends ManagedAction
|
||||
{
|
||||
protected $needLogin = false;
|
||||
protected $canPost = true;
|
||||
protected $needLogin = false;
|
||||
protected $canPost = true;
|
||||
|
||||
/**
|
||||
* Handle the Actor Profile request
|
||||
*
|
||||
* @author Daniel Supernault <danielsupernault@gmail.com>
|
||||
* @return void
|
||||
*/
|
||||
protected function handle()
|
||||
{
|
||||
$nickname = $this->trimmed ('nickname');
|
||||
try {
|
||||
$user = User::getByNickname ($nickname);
|
||||
$profile = $user->getProfile ();
|
||||
}
|
||||
catch (Exception $e) {
|
||||
ActivityPubReturn::error ('Invalid username.', 404);
|
||||
}
|
||||
|
||||
$res = Activitypub_profile::profile_to_array ($profile);
|
||||
|
||||
ActivityPubReturn::answer ($res);
|
||||
/**
|
||||
* Handle the Actor Profile request
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @return void
|
||||
*/
|
||||
protected function handle()
|
||||
{
|
||||
if (!empty($id = $this->trimmed('id'))) {
|
||||
try {
|
||||
$profile = Profile::getByID($id);
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error('Invalid Actor URI.', 404);
|
||||
}
|
||||
unset($id);
|
||||
} else {
|
||||
try {
|
||||
$profile = User::getByNickname($this->trimmed('nickname'))->getProfile();
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error('Invalid username.', 404);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$profile->isLocal()) {
|
||||
ActivityPubReturn::error("This is not a local user.");
|
||||
}
|
||||
|
||||
$res = Activitypub_profile::profile_to_array($profile);
|
||||
|
||||
ActivityPubReturn::answer($res);
|
||||
}
|
||||
}
|
||||
|
92
actions/apauthorizeremotefollow.php
Normal file
92
actions/apauthorizeremotefollow.php
Normal file
@ -0,0 +1,92 @@
|
||||
<?php
|
||||
/**
|
||||
* GNU social - a federating social network
|
||||
*
|
||||
* ActivityPubPlugin implementation for GNU Social
|
||||
*
|
||||
* LICENCE: This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @author Daniel Supernault <danielsupernault@gmail.com>
|
||||
* @copyright 2018 Free Software Foundation http://fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Authorize Remote Follow
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*/
|
||||
class apAuthorizeRemoteFollowAction extends Action
|
||||
{
|
||||
/**
|
||||
* Prepare to handle the Authorize Remote Follow request.
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param array $args
|
||||
* @return boolean
|
||||
*/
|
||||
protected function prepare(array $args=array())
|
||||
{
|
||||
parent::prepare($args);
|
||||
|
||||
if (!common_logged_in()) {
|
||||
// XXX: selfURL() didn't work. :<
|
||||
common_set_returnto($_SERVER['REQUEST_URI']);
|
||||
if (Event::handle('RedirectToLogin', array($this, null))) {
|
||||
common_redirect(common_local_url('login'), 303);
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
if (!isset($_GET["acct"])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the Authorize Remote Follow Request.
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
protected function handle()
|
||||
{
|
||||
$other = Activitypub_profile::get_from_uri($_GET["acct"]);
|
||||
$actor_profile = common_current_user()->getProfile();
|
||||
$object_profile = $other->local_profile();
|
||||
if (!Subscription::exists($actor_profile, $object_profile)) {
|
||||
Subscription::start($actor_profile, $object_profile);
|
||||
}
|
||||
try {
|
||||
$postman = new Activitypub_postman($actor_profile, [$other]);
|
||||
$postman->follow();
|
||||
} catch (Exception $e) {
|
||||
// Meh, let the exception go on its merry way, it shouldn't be all
|
||||
// that important really.
|
||||
}
|
||||
common_redirect(common_local_url('userbyid', array('id' => $other->profile_id)), 303);
|
||||
}
|
||||
}
|
239
actions/apremotefollow.php
Normal file
239
actions/apremotefollow.php
Normal file
@ -0,0 +1,239 @@
|
||||
<?php
|
||||
/**
|
||||
* GNU social - a federating social network
|
||||
*
|
||||
* ActivityPubPlugin implementation for GNU Social
|
||||
*
|
||||
* LICENCE: This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @author Daniel Supernault <danielsupernault@gmail.com>
|
||||
* @copyright 2018 Free Software Foundation http://fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remote Follow
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*/
|
||||
class apRemoteFollowAction extends Action
|
||||
{
|
||||
public $nickname;
|
||||
public $local_profile;
|
||||
public $remote_identifier;
|
||||
public $err;
|
||||
|
||||
/**
|
||||
* Prepare to handle the Remote Follow request.
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param array $args
|
||||
* @return boolean
|
||||
*/
|
||||
protected function prepare(array $args=array())
|
||||
{
|
||||
parent::prepare($args);
|
||||
|
||||
if (common_logged_in()) {
|
||||
// TRANS: Client error.
|
||||
$this->clientError(_m('You can use the local subscription!'));
|
||||
}
|
||||
|
||||
// Local user the remote wants to subscribe to
|
||||
$this->nickname = $this->trimmed('nickname');
|
||||
$this->local_profile = User::getByNickname($this->nickname)->getProfile();
|
||||
|
||||
// Webfinger or profile URL of the remote user
|
||||
$this->remote_identifier = $this->trimmed('remote_identifier');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the Remote Follow Request.
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
protected function handle()
|
||||
{
|
||||
parent::handle();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
/* Use a session token for CSRF protection. */
|
||||
$token = $this->trimmed('token');
|
||||
if (!$token || $token != common_session_token()) {
|
||||
// TRANS: Client error displayed when the session token does not match or is not given.
|
||||
$this->showForm(_m('There was a problem with your session token. '.
|
||||
'Try again, please.'));
|
||||
return;
|
||||
}
|
||||
$this->activitypub_connect();
|
||||
} else {
|
||||
$this->showForm();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form.
|
||||
*
|
||||
* @author GNU Social
|
||||
* @param string|null $err
|
||||
*/
|
||||
public function showForm($err = null)
|
||||
{
|
||||
$this->err = $err;
|
||||
if ($this->boolean('ajax')) {
|
||||
$this->startHTML('text/xml;charset=utf-8');
|
||||
$this->elementStart('head');
|
||||
// TRANS: Form title.
|
||||
$this->element('title', null, _m('TITLE', 'Subscribe to user'));
|
||||
$this->elementEnd('head');
|
||||
$this->elementStart('body');
|
||||
$this->showContent();
|
||||
$this->elementEnd('body');
|
||||
$this->endHTML();
|
||||
} else {
|
||||
$this->showPage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Page content.
|
||||
*
|
||||
* @author GNU Social
|
||||
*/
|
||||
public function showContent()
|
||||
{
|
||||
// TRANS: Form legend. %s is a nickname.
|
||||
$header = sprintf(_m('Subscribe to %s'), $this->nickname);
|
||||
// TRANS: Button text to subscribe to a profile.
|
||||
$submit = _m('BUTTON', 'Subscribe');
|
||||
$this->elementStart(
|
||||
'form',
|
||||
['id' => 'form_activitypub_connect',
|
||||
'method' => 'post',
|
||||
'class' => 'form_settings',
|
||||
'action' => common_local_url(
|
||||
'apRemoteFollow',
|
||||
['nickname' => $this->nickname]
|
||||
)
|
||||
]
|
||||
);
|
||||
$this->elementStart('fieldset');
|
||||
$this->element('legend', null, $header);
|
||||
$this->hidden('token', common_session_token());
|
||||
|
||||
$this->elementStart('ul', 'form_data');
|
||||
$this->elementStart('li', array('id' => 'activitypub_nickname'));
|
||||
|
||||
// TRANS: Field label.
|
||||
$this->input(
|
||||
'nickname',
|
||||
_m('User nickname'),
|
||||
$this->nickname,
|
||||
// TRANS: Field title.
|
||||
_m('Nickname of the user you want to follow.')
|
||||
);
|
||||
|
||||
$this->elementEnd('li');
|
||||
$this->elementStart('li', array('id' => 'activitypub_profile'));
|
||||
// TRANS: Field label.
|
||||
$this->input(
|
||||
'remote_identifier',
|
||||
_m('Profile Account'),
|
||||
$this->remote_identifier,
|
||||
// TRANS: Tooltip for field label "Profile Account".
|
||||
_m('Your account ID (e.g. user@example.net).')
|
||||
);
|
||||
$this->elementEnd('li');
|
||||
$this->elementEnd('ul');
|
||||
$this->submit('submit', $submit);
|
||||
$this->elementEnd('fieldset');
|
||||
$this->elementEnd('form');
|
||||
}
|
||||
|
||||
/**
|
||||
* Start connecting the two instances (will be finished with the authorization)
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @return void
|
||||
*/
|
||||
public function activitypub_connect()
|
||||
{
|
||||
$remote_profile = null;
|
||||
try { // Try with ActivityPub system
|
||||
$remote_profile = Activitypub_profile::get_from_uri($this->remote_identifier);
|
||||
} catch (Exception $e) { // Fallback to compatibility WebFinger system
|
||||
$validate = new Validate();
|
||||
$opts = array('allowed_schemes' => array('http', 'https', 'acct'));
|
||||
if ($validate->uri($this->remote_identifier, $opts)) {
|
||||
$bits = parse_url($this->remote_identifier);
|
||||
if ($bits['scheme'] == 'acct') {
|
||||
$remote_profile = $this->connect_webfinger($bits['path']);
|
||||
}
|
||||
} elseif (strpos($this->remote_identifier, '@') !== false) {
|
||||
$remote_profile = $this->connect_webfinger($this->remote_identifier);
|
||||
}
|
||||
}
|
||||
if (!empty($remote_profile)) {
|
||||
$url = ActivityPubPlugin::stripUrlPath($remote_profile->get_uri())."activitypub/authorize_follow?acct=".$this->local_profile->getUri();
|
||||
common_log(LOG_INFO, "Sending remote subscriber $this->remote_identifier to $url");
|
||||
common_redirect($url, 303);
|
||||
return;
|
||||
}
|
||||
|
||||
// TRANS: Client error.
|
||||
$this->clientError(_m('Must provide a remote profile.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is used by activitypub_connect () and
|
||||
* is a step of the process
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param type $acct
|
||||
* @return Profile Profile resulting of WebFinger connection
|
||||
*/
|
||||
private function connect_webfinger($acct)
|
||||
{
|
||||
$link = ActivityPubPlugin::pull_remote_profile($acct);
|
||||
if (!is_null($link)) {
|
||||
return $link;
|
||||
}
|
||||
// TRANS: Client error.
|
||||
$this->clientError(_m('Could not confirm remote profile address.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Page title
|
||||
*
|
||||
* @return string Page title
|
||||
*/
|
||||
public function title()
|
||||
{
|
||||
// TRANS: Page title.
|
||||
return _m('ActivityPub Connect');
|
||||
}
|
||||
}
|
@ -25,8 +25,8 @@
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined ('GNUSOCIAL')) {
|
||||
exit (1);
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -40,80 +40,55 @@ if (!defined ('GNUSOCIAL')) {
|
||||
*/
|
||||
class apSharedInboxAction extends ManagedAction
|
||||
{
|
||||
protected $needLogin = false;
|
||||
protected $canPost = true;
|
||||
protected $needLogin = false;
|
||||
protected $canPost = true;
|
||||
|
||||
/**
|
||||
* Handle the Shared Inbox request
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @return void
|
||||
*/
|
||||
protected function handle ()
|
||||
{
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
ActivityPubReturn::error ("Only POST requests allowed.");
|
||||
}
|
||||
/**
|
||||
* Handle the Shared Inbox request
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @return void
|
||||
*/
|
||||
protected function handle()
|
||||
{
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
ActivityPubReturn::error("Only POST requests allowed.");
|
||||
}
|
||||
|
||||
$data = json_decode (file_get_contents ('php://input'));
|
||||
$data = json_decode(file_get_contents('php://input'));
|
||||
|
||||
// Validate data
|
||||
if (!isset ($data->type)) {
|
||||
ActivityPubReturn::error ("Type was not specified.");
|
||||
}
|
||||
if (!isset ($data->actor)) {
|
||||
ActivityPubReturn::error ("Actor was not specified.");
|
||||
}
|
||||
if (!isset ($data->object)) {
|
||||
ActivityPubReturn::error ("Object was not specified.");
|
||||
}
|
||||
// Validate data
|
||||
if (!isset($data->type)) {
|
||||
ActivityPubReturn::error("Type was not specified.");
|
||||
}
|
||||
if (!isset($data->actor)) {
|
||||
ActivityPubReturn::error("Actor was not specified.");
|
||||
}
|
||||
if (!isset($data->object)) {
|
||||
ActivityPubReturn::error("Object was not specified.");
|
||||
}
|
||||
|
||||
$discovery = new Activitypub_explorer;
|
||||
$discovery = new Activitypub_explorer;
|
||||
// Get valid Actor object
|
||||
try {
|
||||
$actor_profile = $discovery->lookup($data->actor);
|
||||
$actor_profile = $actor_profile[0];
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error("Invalid Actor.", 404);
|
||||
}
|
||||
unset($discovery);
|
||||
|
||||
// Get valid Actor object
|
||||
try {
|
||||
$actor_profile = $discovery->lookup ($data->actor);
|
||||
$actor_profile = $actor_profile[0];
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error ("Invalid Actor.", 404);
|
||||
}
|
||||
// Public To:
|
||||
$public_to = ["https://www.w3.org/ns/activitystreams#Public",
|
||||
"Public",
|
||||
"as:Public"
|
||||
];
|
||||
|
||||
unset ($discovery);
|
||||
$to_profiles = "https://www.w3.org/ns/activitystreams#Public";
|
||||
|
||||
// Public To:
|
||||
$public_to = array ("https://www.w3.org/ns/activitystreams#Public",
|
||||
"Public",
|
||||
"as:Public");
|
||||
|
||||
// Process request
|
||||
switch ($data->type) {
|
||||
// Process request
|
||||
switch ($data->type) {
|
||||
case "Create":
|
||||
if (!isset($data->to)) {
|
||||
ActivityPubReturn::error ("To was not specified.");
|
||||
}
|
||||
$discovery = new Activitypub_explorer;
|
||||
$to_profiles = array ();
|
||||
// Generate To objects
|
||||
if (is_array ($data->to)) {
|
||||
// Remove duplicates from To actors set
|
||||
array_unique ($data->to);
|
||||
foreach ($data->to as $to_url) {
|
||||
try {
|
||||
$to_profiles = array_merge ($to_profiles, $discovery->lookup ($to_url));
|
||||
} catch (Exception $e) {
|
||||
// XXX: Invalid actor found, not sure how we handle those
|
||||
}
|
||||
}
|
||||
} else if (empty ($data->to) || in_array ($data->to, $public_to)) {
|
||||
// No need to do anything else at this point, let's just break out the if
|
||||
} else {
|
||||
try {
|
||||
$to_profiles[]= $discovery->lookup ($data->to);
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error ("Invalid Actor.", 404);
|
||||
}
|
||||
}
|
||||
unset ($discovery);
|
||||
require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Create.php";
|
||||
break;
|
||||
case "Follow":
|
||||
@ -131,8 +106,14 @@ class apSharedInboxAction extends ManagedAction
|
||||
case "Delete":
|
||||
require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Delete.php";
|
||||
break;
|
||||
case "Accept":
|
||||
require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Accept.php";
|
||||
break;
|
||||
case "Reject":
|
||||
require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Reject.php";
|
||||
break;
|
||||
default:
|
||||
ActivityPubReturn::error ("Invalid type value.");
|
||||
ActivityPubReturn::error("Invalid type value.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
58
actions/inbox/Accept.php
Normal file
58
actions/inbox/Accept.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/**
|
||||
* GNU social - a federating social network
|
||||
*
|
||||
* ActivityPubPlugin implementation for GNU Social
|
||||
*
|
||||
* LICENCE: This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @author Daniel Supernault <danielsupernault@gmail.com>
|
||||
* @copyright 2018 Free Software Foundation http://fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Validate data
|
||||
if (!isset($data->type)) {
|
||||
ActivityPubReturn::error("Type was not specified.");
|
||||
}
|
||||
|
||||
switch ($data->object->type) {
|
||||
case "Follow":
|
||||
// Validate data
|
||||
if (!isset($data->object->object)) {
|
||||
ActivityPubReturn::error("Object Actor URL was not specified.");
|
||||
}
|
||||
// Get valid Object profile
|
||||
try {
|
||||
$object_profile = new Activitypub_explorer;
|
||||
$object_profile = $object_profile->lookup($data->object->object)[0];
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error("Invalid Object Actor URL.", 404);
|
||||
}
|
||||
|
||||
$pending_list = new Activitypub_pending_follow_requests($actor_profile->getID(), $object_profile->getID());
|
||||
$pending_list->remove();
|
||||
ActivityPubReturn::answer($data); // You are now being followed by this person.
|
||||
break;
|
||||
default:
|
||||
ActivityPubReturn::error("Invalid object type.");
|
||||
break;
|
||||
}
|
@ -25,13 +25,13 @@
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined ('GNUSOCIAL')) {
|
||||
exit (1);
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
Notice::getByUri ($data->object)->repeat ($actor_profile, "ActivityPub");
|
||||
ActivityPubReturn::answer ("Notice repeated successfully.");
|
||||
Notice::getByUri($data->object->id)->repeat($actor_profile, "ActivityPub");
|
||||
ActivityPubReturn::answer("Notice repeated successfully.");
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error ($e->getMessage (), 403);
|
||||
ActivityPubReturn::error($e->getMessage(), 403);
|
||||
}
|
||||
|
@ -25,79 +25,109 @@
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined ('GNUSOCIAL')) {
|
||||
exit (1);
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$valid_object_types = array ("Note");
|
||||
$valid_object_types = array("Note");
|
||||
|
||||
// Validate data
|
||||
if (!(isset ($data->object->type) && in_array ($data->object->type, $valid_object_types))) {
|
||||
ActivityPubReturn::error ("Invalid Object type.");
|
||||
if (!isset($data->id)) {
|
||||
ActivityPubReturn::error("Id not specified.");
|
||||
}
|
||||
if (!isset ($data->object->content)) {
|
||||
ActivityPubReturn::error ("Object content was not specified.");
|
||||
if (!(isset($data->object->type) && in_array($data->object->type, $valid_object_types))) {
|
||||
ActivityPubReturn::error("Invalid Object type.");
|
||||
}
|
||||
if (!isset ($data->object->url)) {
|
||||
ActivityPubReturn::error ("Object url was not specified.");
|
||||
} else if (!filter_var ($data->object->url, FILTER_VALIDATE_URL)) {
|
||||
ActivityPubReturn::error ("Invalid Object Url.");
|
||||
if (!isset($data->object->content)) {
|
||||
ActivityPubReturn::error("Object content was not specified.");
|
||||
}
|
||||
if (!isset($data->object->url)) {
|
||||
ActivityPubReturn::error("Object url was not specified.");
|
||||
} elseif (!filter_var($data->object->url, FILTER_VALIDATE_URL)) {
|
||||
ActivityPubReturn::error("Invalid Object Url.");
|
||||
}
|
||||
if (!isset($data->object->to)) {
|
||||
ActivityPubReturn::error("Object To was not specified.");
|
||||
}
|
||||
|
||||
$content = $data->object->content;
|
||||
|
||||
$act = new Activity ();
|
||||
$act = new Activity();
|
||||
$act->verb = ActivityVerb::POST;
|
||||
$act->time = time ();
|
||||
$act->actor = $actor_profile->asActivityObject ();
|
||||
$act->time = time();
|
||||
$act->actor = $actor_profile->asActivityObject();
|
||||
|
||||
$act->context = new ActivityContext ();
|
||||
$act->context = new ActivityContext();
|
||||
|
||||
// Is this a reply?
|
||||
if (isset ($data->object->reply_to)) {
|
||||
try {
|
||||
$reply_to = Notice::getByUri ($data->object->reply_to);
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error ("Invalid Object reply_to value.");
|
||||
}
|
||||
$act->context->replyToID = $reply_to->getUri ();
|
||||
$act->context->replyToUrl = $reply_to->getUrl ();
|
||||
if (isset($data->object->reply_to)) {
|
||||
try {
|
||||
$reply_to = Notice::getByUri($data->object->reply_to);
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error("Invalid Object reply_to value.");
|
||||
}
|
||||
$act->context->replyToID = $reply_to->getUri();
|
||||
$act->context->replyToUrl = $reply_to->getUrl();
|
||||
} else {
|
||||
$reply_to = null;
|
||||
$reply_to = null;
|
||||
}
|
||||
|
||||
$act->context->attention = common_get_attentions ($content, $actor_profile, $reply_to);
|
||||
$act->context->attention = common_get_attentions($content, $actor_profile, $reply_to);
|
||||
|
||||
foreach ($to_profiles as $to)
|
||||
{
|
||||
$act->context->attention[$to->getUri ()] = "http://activitystrea.ms/schema/1.0/person";
|
||||
$discovery = new Activitypub_explorer;
|
||||
if ($to_profiles == "https://www.w3.org/ns/activitystreams#Public") {
|
||||
$to_profiles = array();
|
||||
}
|
||||
// Generate To objects
|
||||
if (is_array($data->object->to)) {
|
||||
// Remove duplicates from To actors set
|
||||
array_unique($data->object->to);
|
||||
foreach ($data->object->to as $to_url) {
|
||||
try {
|
||||
$to_profiles = array_merge($to_profiles, $discovery->lookup($to_url));
|
||||
} catch (Exception $e) {
|
||||
// XXX: Invalid actor found, not sure how we handle those
|
||||
}
|
||||
}
|
||||
} elseif (empty($data->object->to) || in_array($data->object->to, $public_to)) {
|
||||
// No need to do anything else at this point, let's just break out the if
|
||||
} else {
|
||||
try {
|
||||
$to_profiles[]= $discovery->lookup($data->object->to);
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error("Invalid Actor.", 404);
|
||||
}
|
||||
}
|
||||
unset($discovery);
|
||||
|
||||
foreach ($to_profiles as $to) {
|
||||
$act->context->attention[ActivityPubPlugin::actor_uri($to)] = "http://activitystrea.ms/schema/1.0/person";
|
||||
}
|
||||
|
||||
// Reject notice if it is too long (without the HTML)
|
||||
// This is done after MediaFile::fromUpload etc. just to act the same as the ApiStatusesUpdateAction
|
||||
if (Notice::contentTooLong ($content)) {
|
||||
ActivityPubReturn::error ("That's too long. Maximum notice size is %d character.");
|
||||
if (Notice::contentTooLong($content)) {
|
||||
ActivityPubReturn::error("That's too long. Maximum notice size is %d character.");
|
||||
}
|
||||
|
||||
$options = array ('source' => 'ActivityPub', 'uri' => $data->id, 'url' => $data->object->url);
|
||||
$options = array('source' => 'ActivityPub', 'uri' => isset($data->id) ? $data->id : $data->object->url, 'url' => $data->object->url);
|
||||
// $options gets filled with possible scoping settings
|
||||
ToSelector::fillActivity ($this, $act, $options);
|
||||
ToSelector::fillActivity($this, $act, $options);
|
||||
|
||||
$actobj = new ActivityObject ();
|
||||
$actobj = new ActivityObject();
|
||||
$actobj->type = ActivityObject::NOTE;
|
||||
$actobj->content = common_render_content ($content, $actor_profile, $reply_to);
|
||||
$actobj->content = common_render_content($content, $actor_profile, $reply_to);
|
||||
|
||||
// Finally add the activity object to our activity
|
||||
$act->objects[] = $actobj;
|
||||
|
||||
try {
|
||||
$res = array ("@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"id" => $data->id,
|
||||
"url" => $data->object->url,
|
||||
"type" => "Create",
|
||||
"actor" => $data->actor,
|
||||
"object" => Activitypub_notice::notice_to_array (Notice::saveActivity ($act, $actor_profile, $options)));
|
||||
ActivityPubReturn::answer ($res);
|
||||
$res = Activitypub_create::create_to_array(
|
||||
$data->id,
|
||||
$data->actor,
|
||||
Activitypub_notice::notice_to_array(Notice::saveActivity($act, $actor_profile, $options))
|
||||
);
|
||||
ActivityPubReturn::answer($res);
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error ($e->getMessage ());
|
||||
ActivityPubReturn::error($e->getMessage());
|
||||
}
|
||||
|
@ -25,17 +25,15 @@
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined ('GNUSOCIAL')) {
|
||||
exit (1);
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
Notice::getByUri ($data->object)->deleteAs ($actor_profile);
|
||||
$res = array ("@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Delete",
|
||||
"actor" => $data->actor,
|
||||
"object" => $data->object);
|
||||
ActivityPubReturn::answer ($res);
|
||||
$notice = Notice::getByUri($data->object->id);
|
||||
$notice_to_array = Activitypub_notice::notice_to_array($notice);
|
||||
$notice->deleteAs($actor_profile);
|
||||
ActivityPubReturn::answer(Activitypub_delete::delete_to_array($notice_to_array));
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error ($e->getMessage (), 403);
|
||||
ActivityPubReturn::error($e->getMessage(), 403);
|
||||
}
|
||||
|
@ -25,34 +25,30 @@
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined ('GNUSOCIAL')) {
|
||||
exit (1);
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Validate Object
|
||||
if (!is_string ($data->object)) {
|
||||
ActivityPubReturn::error ("Invalid Object object, URL expected.");
|
||||
if (!is_string($data->object)) {
|
||||
ActivityPubReturn::error("Invalid Object object, URL expected.");
|
||||
}
|
||||
|
||||
// Get valid Object profile
|
||||
try {
|
||||
$object_profile = new Activitypub_explorer;
|
||||
$object_profile = $object_profile->lookup ($data->object)[0];
|
||||
} catch(Exception $e) {
|
||||
ActivityPubReturn::error ("Invalid Object Actor URL.", 404);
|
||||
$object_profile = new Activitypub_explorer;
|
||||
$object_profile = $object_profile->lookup($data->object)[0];
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error("Invalid Object Actor URL.", 404);
|
||||
}
|
||||
|
||||
try {
|
||||
if (!Subscription::exists ($actor_profile, $object_profile)) {
|
||||
Subscription::start ($actor_profile, $object_profile);
|
||||
$res = array ("@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Follow",
|
||||
"actor" => $data->actor,
|
||||
"object" => $data->object);
|
||||
ActivityPubReturn::answer ($res);
|
||||
} else {
|
||||
ActivityPubReturn::error ("Already following.", 409);
|
||||
}
|
||||
if (!Subscription::exists($actor_profile, $object_profile)) {
|
||||
Subscription::start($actor_profile, $object_profile);
|
||||
ActivityPubReturn::answer(Activitypub_accept::accept_to_array(Activitypub_follow::follow_to_array($data->actor, $data->object)));
|
||||
} else {
|
||||
ActivityPubReturn::error("Already following.", 409);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error ("Invalid Object Actor URL.", 404);
|
||||
ActivityPubReturn::error("Invalid Object Actor URL.", 404);
|
||||
}
|
||||
|
@ -25,17 +25,17 @@
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined ('GNUSOCIAL')) {
|
||||
exit (1);
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!isset($data->object->id)) {
|
||||
ActivityPubReturn::error("Id not specified.");
|
||||
}
|
||||
|
||||
try {
|
||||
Fave::addNew ($actor_profile, Notice::getByUri ($data->object));
|
||||
$res = array ("@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Like",
|
||||
"actor" => $data->actor,
|
||||
"object" => $data->object);
|
||||
ActivityPubReturn::answer ($res);
|
||||
Fave::addNew($actor_profile, Notice::getByUri($data->object->id));
|
||||
ActivityPubReturn::answer(Activitypub_like::like_to_array(Activitypub_notice::notice_to_array($data->actor, json_decode($data->object))));
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error ($e->getMessage (), 403);
|
||||
ActivityPubReturn::error($e->getMessage(), 403);
|
||||
}
|
||||
|
32
actions/inbox/Reject.php
Normal file
32
actions/inbox/Reject.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/**
|
||||
* GNU social - a federating social network
|
||||
*
|
||||
* ActivityPubPlugin implementation for GNU Social
|
||||
*
|
||||
* LICENCE: This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @author Daniel Supernault <danielsupernault@gmail.com>
|
||||
* @copyright 2018 Free Software Foundation http://fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// This is a dummy file as there is nothing to do if we fall in this case
|
@ -25,53 +25,69 @@
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined ('GNUSOCIAL')) {
|
||||
exit (1);
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Validate data
|
||||
if (!isset ($data->type)) {
|
||||
ActivityPubReturn::error ("Type was not specified.");
|
||||
if (!isset($data->type)) {
|
||||
ActivityPubReturn::error("Type was not specified.");
|
||||
}
|
||||
|
||||
switch ($data->object->type) {
|
||||
case "Like":
|
||||
try {
|
||||
// Validate data
|
||||
if (!isset ($data->object->object)) {
|
||||
ActivityPubReturn::error ("Object Notice URL was not specified.");
|
||||
}
|
||||
Fave::removeEntry ($actor_profile, Notice::getByUri ($data->object->object));
|
||||
ActivityPubReturn::answer ("Notice disfavorited successfully.");
|
||||
// Validate data
|
||||
if (!isset($data->object->object->id)) {
|
||||
ActivityPubReturn::error("Notice ID was not specified.");
|
||||
}
|
||||
Fave::removeEntry($actor_profile, Notice::getByUri($data->object->object->id));
|
||||
// Notice disfavorited successfully.
|
||||
ActivityPubReturn::answer(
|
||||
Activitypub_undo::undo_to_array(
|
||||
Activitypub_like::like_to_array(
|
||||
Activitypub_notice::notice_to_array(
|
||||
$actor_profile->getUrl(),
|
||||
$data->object->object
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error ($e->getMessage (), 403);
|
||||
ActivityPubReturn::error($e->getMessage(), 403);
|
||||
}
|
||||
break;
|
||||
case "Follow":
|
||||
// Validate data
|
||||
if (!isset ($data->object->object)) {
|
||||
ActivityPubReturn::error ("Object Actor URL was not specified.");
|
||||
if (!isset($data->object->object)) {
|
||||
ActivityPubReturn::error("Object Actor URL was not specified.");
|
||||
}
|
||||
// Get valid Object profile
|
||||
try {
|
||||
$object_profile = new Activitypub_explorer;
|
||||
$object_profile = $object_profile->lookup ($data->object->object)[0];
|
||||
$object_profile = new Activitypub_explorer;
|
||||
$object_profile = $object_profile->lookup($data->object->object)[0];
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error ("Invalid Object Actor URL.", 404);
|
||||
ActivityPubReturn::error("Invalid Object Actor URL.", 404);
|
||||
}
|
||||
|
||||
try {
|
||||
if (Subscription::exists ($actor_profile, $object_profile)) {
|
||||
Subscription::cancel ($actor_profile, $object_profile);
|
||||
ActivityPubReturn::answer ("You are no longer following this person.");
|
||||
} else {
|
||||
ActivityPubReturn::error ("You are not following this person already.", 409);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
ActivityPubReturn::error ("Invalid Object Actor URL.", 404);
|
||||
if (Subscription::exists($actor_profile, $object_profile)) {
|
||||
Subscription::cancel($actor_profile, $object_profile);
|
||||
// You are no longer following this person.
|
||||
ActivityPubReturn::answer(
|
||||
Activitypub_undo::undo_to_array(
|
||||
Activitypub_accept::accept_to_array(
|
||||
Activitypub_follow::follow_to_array(
|
||||
$actor_profile->getUrl(),
|
||||
$object_profile->getUrl()
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
ActivityPubReturn::error("You are not following this person already.", 409);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ActivityPubReturn::error ("Invalid object type.");
|
||||
ActivityPubReturn::error("Invalid object type.");
|
||||
break;
|
||||
}
|
||||
|
58
classes/Activitypub_accept.php
Normal file
58
classes/Activitypub_accept.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/**
|
||||
* GNU social - a federating social network
|
||||
*
|
||||
* ActivityPubPlugin implementation for GNU Social
|
||||
*
|
||||
* LICENCE: This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @author Daniel Supernault <danielsupernault@gmail.com>
|
||||
* @copyright 2018 Free Software Foundation http://fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* ActivityPub error representation
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*/
|
||||
class Activitypub_accept extends Managed_DataObject
|
||||
{
|
||||
/**
|
||||
* Generates an ActivityPub representation of a Accept
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param array $object
|
||||
* @return pretty array to be used in a response
|
||||
*/
|
||||
public static function accept_to_array($object)
|
||||
{
|
||||
$res = array("@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Accept",
|
||||
"object" => $object
|
||||
);
|
||||
return $res;
|
||||
}
|
||||
}
|
59
classes/Activitypub_announce.php
Normal file
59
classes/Activitypub_announce.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* GNU social - a federating social network
|
||||
*
|
||||
* ActivityPubPlugin implementation for GNU Social
|
||||
*
|
||||
* LICENCE: This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @author Daniel Supernault <danielsupernault@gmail.com>
|
||||
* @copyright 2018 Free Software Foundation http://fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* ActivityPub error representation
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*/
|
||||
class Activitypub_announce extends Managed_DataObject
|
||||
{
|
||||
/**
|
||||
* Generates an ActivityPub representation of a Announce
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param array $object
|
||||
* @return pretty array to be used in a response
|
||||
*/
|
||||
public static function announce_to_array($actor, $object)
|
||||
{
|
||||
$res = array("@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Announce",
|
||||
"actor" => $actor,
|
||||
"object" => $object
|
||||
);
|
||||
return $res;
|
||||
}
|
||||
}
|
@ -25,8 +25,8 @@
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined ('GNUSOCIAL')) {
|
||||
exit (1);
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -40,39 +40,38 @@ if (!defined ('GNUSOCIAL')) {
|
||||
*/
|
||||
class Activitypub_attachment extends Managed_DataObject
|
||||
{
|
||||
/**
|
||||
* Generates a pretty array from an Attachment object
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param Attachment $attachment
|
||||
* @return pretty array to be used in a response
|
||||
*/
|
||||
public static function attachment_to_array ($attachment)
|
||||
{
|
||||
$res = [
|
||||
/**
|
||||
* Generates a pretty array from an Attachment object
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param Attachment $attachment
|
||||
* @return pretty array to be used in a response
|
||||
*/
|
||||
public static function attachment_to_array($attachment)
|
||||
{
|
||||
$res = [
|
||||
'@context' => [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
[
|
||||
"@language" => "en"
|
||||
]
|
||||
],
|
||||
'id' => $attachment->getID (),
|
||||
'id' => $attachment->getID(),
|
||||
'mimetype' => $attachment->mimetype,
|
||||
'url' => $attachment->getUrl (),
|
||||
'url' => $attachment->getUrl(),
|
||||
'size' => intval($attachment->size), // $attachment->getSize ()
|
||||
'title' => $attachment->getTitle (),
|
||||
'title' => $attachment->getTitle(),
|
||||
'meta' => null
|
||||
];
|
||||
|
||||
// Image
|
||||
if (substr ($res["mimetype"], 0, 5) == "image")
|
||||
{
|
||||
$res["meta"]= [
|
||||
// Image
|
||||
if (substr($res["mimetype"], 0, 5) == "image") {
|
||||
$res["meta"]= [
|
||||
'width' => $attachment->width,
|
||||
'height' => $attachment->height
|
||||
];
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
|
61
classes/Activitypub_create.php
Normal file
61
classes/Activitypub_create.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/**
|
||||
* GNU social - a federating social network
|
||||
*
|
||||
* ActivityPubPlugin implementation for GNU Social
|
||||
*
|
||||
* LICENCE: This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @author Daniel Supernault <danielsupernault@gmail.com>
|
||||
* @copyright 2018 Free Software Foundation http://fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* ActivityPub error representation
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*/
|
||||
class Activitypub_create extends Managed_DataObject
|
||||
{
|
||||
/**
|
||||
* Generates an ActivityPub representation of a Create
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param string $actor
|
||||
* @param array $object
|
||||
* @return pretty array to be used in a response
|
||||
*/
|
||||
public static function create_to_array($id, $actor, $object)
|
||||
{
|
||||
$res = array("@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"id" => $id,
|
||||
"type" => "Create",
|
||||
"actor" => $actor,
|
||||
"object" => $object
|
||||
);
|
||||
return $res;
|
||||
}
|
||||
}
|
59
classes/Activitypub_delete.php
Normal file
59
classes/Activitypub_delete.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* GNU social - a federating social network
|
||||
*
|
||||
* ActivityPubPlugin implementation for GNU Social
|
||||
*
|
||||
* LICENCE: This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @author Daniel Supernault <danielsupernault@gmail.com>
|
||||
* @copyright 2018 Free Software Foundation http://fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* ActivityPub error representation
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*/
|
||||
class Activitypub_delete extends Managed_DataObject
|
||||
{
|
||||
/**
|
||||
* Generates an ActivityPub representation of a Delete
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param array $object
|
||||
* @return pretty array to be used in a response
|
||||
*/
|
||||
public static function delete_to_array($object)
|
||||
{
|
||||
$res = array("@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Delete",
|
||||
"actor" => $object["actor"],
|
||||
"object" => $object
|
||||
);
|
||||
return $res;
|
||||
}
|
||||
}
|
@ -25,8 +25,8 @@
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined ('GNUSOCIAL')) {
|
||||
exit (1);
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -40,18 +40,18 @@ if (!defined ('GNUSOCIAL')) {
|
||||
*/
|
||||
class Activitypub_error extends Managed_DataObject
|
||||
{
|
||||
/**
|
||||
* Generates a pretty error from a string
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param string $m
|
||||
* @return pretty array to be used in a response
|
||||
*/
|
||||
public static function error_message_to_array ($m)
|
||||
{
|
||||
$res = [
|
||||
/**
|
||||
* Generates a pretty error from a string
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param string $m
|
||||
* @return pretty array to be used in a response
|
||||
*/
|
||||
public static function error_message_to_array($m)
|
||||
{
|
||||
$res = [
|
||||
'error'=> $m
|
||||
];
|
||||
return $res;
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
|
60
classes/Activitypub_follow.php
Normal file
60
classes/Activitypub_follow.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/**
|
||||
* GNU social - a federating social network
|
||||
*
|
||||
* ActivityPubPlugin implementation for GNU Social
|
||||
*
|
||||
* LICENCE: This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @author Daniel Supernault <danielsupernault@gmail.com>
|
||||
* @copyright 2018 Free Software Foundation http://fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* ActivityPub error representation
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*/
|
||||
class Activitypub_follow extends Managed_DataObject
|
||||
{
|
||||
/**
|
||||
* Generates an ActivityPub representation of a subscription
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param string $actor
|
||||
* @param string $object
|
||||
* @return pretty array to be used in a response
|
||||
*/
|
||||
public static function follow_to_array($actor, $object)
|
||||
{
|
||||
$res = array("@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Follow",
|
||||
"actor" => $actor,
|
||||
"object" => $object
|
||||
);
|
||||
return $res;
|
||||
}
|
||||
}
|
60
classes/Activitypub_like.php
Normal file
60
classes/Activitypub_like.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/**
|
||||
* GNU social - a federating social network
|
||||
*
|
||||
* ActivityPubPlugin implementation for GNU Social
|
||||
*
|
||||
* LICENCE: This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @author Daniel Supernault <danielsupernault@gmail.com>
|
||||
* @copyright 2018 Free Software Foundation http://fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* ActivityPub error representation
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*/
|
||||
class Activitypub_like extends Managed_DataObject
|
||||
{
|
||||
/**
|
||||
* Generates an ActivityPub representation of a Like
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param string $actor
|
||||
* @param array $object
|
||||
* @return pretty array to be used in a response
|
||||
*/
|
||||
public static function like_to_array($actor, $object)
|
||||
{
|
||||
$res = array("@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Like",
|
||||
"actor" => $actor,
|
||||
"object" => $object
|
||||
);
|
||||
return $res;
|
||||
}
|
||||
}
|
@ -25,8 +25,8 @@
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined ('GNUSOCIAL')) {
|
||||
exit (1);
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -41,51 +41,51 @@ if (!defined ('GNUSOCIAL')) {
|
||||
*/
|
||||
class Activitypub_notice extends Managed_DataObject
|
||||
{
|
||||
/**
|
||||
* Generates a pretty notice from a Notice object
|
||||
*
|
||||
* @author Daniel Supernault <danielsupernault@gmail.com>
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param Notice $notice
|
||||
* @return pretty array to be used in a response
|
||||
*/
|
||||
public static function notice_to_array ($notice)
|
||||
{
|
||||
$attachments = array ();
|
||||
foreach($notice->attachments () as $attachment) {
|
||||
$attachments[] = Activitypub_attachment::attachment_to_array ($attachment);
|
||||
}
|
||||
/**
|
||||
* Generates a pretty notice from a Notice object
|
||||
*
|
||||
* @author Daniel Supernault <danielsupernault@gmail.com>
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param Notice $notice
|
||||
* @return pretty array to be used in a response
|
||||
*/
|
||||
public static function notice_to_array($notice)
|
||||
{
|
||||
$attachments = array();
|
||||
foreach ($notice->attachments() as $attachment) {
|
||||
$attachments[] = Activitypub_attachment::attachment_to_array($attachment);
|
||||
}
|
||||
|
||||
$tags = array ();
|
||||
foreach($notice->getTags()as $tag) {
|
||||
if ($tag != "") { // Hacky workaround to avoid stupid outputs
|
||||
$tags[] = Activitypub_tag::tag_to_array ($tag);
|
||||
}
|
||||
}
|
||||
$tags = array();
|
||||
foreach ($notice->getTags() as $tag) {
|
||||
if ($tag != "") { // Hacky workaround to avoid stupid outputs
|
||||
$tags[] = Activitypub_tag::tag_to_array($tag);
|
||||
}
|
||||
}
|
||||
|
||||
$to = array ();
|
||||
foreach ($notice->getAttentionProfileIDs () as $to_id) {
|
||||
$to[] = Profile::getById ($to_id)->getUri ();
|
||||
}
|
||||
if (!is_null($to)) {
|
||||
$to = array ("https://www.w3.org/ns/activitystreams#Public");
|
||||
}
|
||||
$to = array();
|
||||
foreach ($notice->getAttentionProfiles() as $to_profile) {
|
||||
$to[] = $to_profile->getUri();
|
||||
}
|
||||
if (empty($to)) {
|
||||
$to = array("https://www.w3.org/ns/activitystreams#Public");
|
||||
}
|
||||
|
||||
$item = [
|
||||
'id' => $notice->getUrl (),
|
||||
'type' => 'Notice',
|
||||
'actor' => $notice->getProfile ()->getUrl (),
|
||||
'published' => $notice->getCreated (),
|
||||
$item = [
|
||||
'id' => $notice->getUri(),
|
||||
'type' => 'Note',
|
||||
'actor' => $notice->getProfile()->getUrl(),
|
||||
'published' => $notice->getCreated(),
|
||||
'to' => $to,
|
||||
'content' => $notice->getContent (),
|
||||
'url' => $notice->getUrl (),
|
||||
'reply_to' => empty($notice->reply_to) ? null : Notice::getById($notice->reply_to)->getUrl (),
|
||||
'is_local' => $notice->isLocal (),
|
||||
'conversation' => intval ($notice->conversation),
|
||||
'content' => $notice->getContent(),
|
||||
'url' => $notice->getUrl(),
|
||||
'reply_to' => empty($notice->reply_to) ? null : Notice::getById($notice->reply_to)->getUri(),
|
||||
'is_local' => $notice->isLocal(),
|
||||
'conversation' => intval($notice->conversation),
|
||||
'attachment' => $attachments,
|
||||
'tag' => $tags
|
||||
];
|
||||
|
||||
return $item;
|
||||
}
|
||||
return $item;
|
||||
}
|
||||
}
|
||||
|
105
classes/Activitypub_pending_follow_requests.php
Normal file
105
classes/Activitypub_pending_follow_requests.php
Normal file
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
/**
|
||||
* GNU social - a federating social network
|
||||
*
|
||||
* ActivityPubPlugin implementation for GNU Social
|
||||
*
|
||||
* LICENCE: This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @author Daniel Supernault <danielsupernault@gmail.com>
|
||||
* @copyright 2018 Free Software Foundation http://fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* ActivityPub's Pending follow requests
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*/
|
||||
class Activitypub_pending_follow_requests extends Managed_DataObject
|
||||
{
|
||||
public $__table = 'Activitypub_pending_follow_requests';
|
||||
public $local_profile_id;
|
||||
public $remote_profile_id;
|
||||
private $_reldb = null;
|
||||
|
||||
/**
|
||||
* Return table definition for Schema setup and DB_DataObject usage.
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @return array array of column definitions
|
||||
*/
|
||||
public static function schemaDef()
|
||||
{
|
||||
return array(
|
||||
'fields' => array(
|
||||
'local_profile_id' => array('type' => 'integer', 'not null' => true),
|
||||
'remote_profile_id' => array('type' => 'integer', 'not null' => true),
|
||||
'relation_id' => array('type' => 'serial', 'not null' => true),
|
||||
),
|
||||
'primary key' => array('relation_id'),
|
||||
'unique keys' => array(
|
||||
'Activitypub_pending_follow_requests_relation_id_key' => array('relation_id'),
|
||||
),
|
||||
'foreign keys' => array(
|
||||
'Activitypub_pending_follow_requests_local_profile_id_fkey' => array('profile', array('local_profile_id' => 'id')),
|
||||
'Activitypub_pending_follow_requests_remote_profile_id_fkey' => array('profile', array('remote_profile_id' => 'id')),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function __construct($actor, $remote_actor)
|
||||
{
|
||||
$this->local_profile_id = $actor;
|
||||
$this->remote_profile_id = $remote_actor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Follow request to table.
|
||||
*
|
||||
* @author Diogo Cordeiro
|
||||
* @param int32 $actor actor id
|
||||
* @param int32 $remote_actor remote actor id
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
return !$this->exists() && $this->insert();
|
||||
}
|
||||
|
||||
public function exists()
|
||||
{
|
||||
$this->_reldb = clone ($this);
|
||||
if ($this->_reldb->find() > 0) {
|
||||
$this->_reldb->fetch();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function remove()
|
||||
{
|
||||
return $this->exists() && $this->_reldb->delete();
|
||||
}
|
||||
}
|
@ -25,8 +25,8 @@
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined ('GNUSOCIAL')) {
|
||||
exit (1);
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -35,331 +35,337 @@ if (!defined ('GNUSOCIAL')) {
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @author Daniel Supernault <danielsupernault@gmail.com>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*/
|
||||
class Activitypub_profile extends Profile
|
||||
{
|
||||
public $__table = 'Activitypub_profile';
|
||||
public $__table = 'Activitypub_profile';
|
||||
|
||||
protected $_profile = null;
|
||||
protected $_profile = null;
|
||||
|
||||
/**
|
||||
* Return table definition for Schema setup and DB_DataObject usage.
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @return array array of column definitions
|
||||
*/
|
||||
static function schemaDef ()
|
||||
{
|
||||
return array (
|
||||
'fields' => array (
|
||||
'uri' => array ('type' => 'varchar', 'length' => 191, 'not null' => true),
|
||||
'profile_id' => array ('type' => 'integer'),
|
||||
'inboxuri' => array ('type' => 'varchar', 'length' => 191),
|
||||
'sharedInboxuri' => array ('type' => 'varchar', 'length' => 191),
|
||||
'created' => array ('type' => 'datetime', 'not null' => true),
|
||||
'modified' => array ('type' => 'datetime', 'not null' => true),
|
||||
/**
|
||||
* Return table definition for Schema setup and DB_DataObject usage.
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @return array array of column definitions
|
||||
*/
|
||||
public static function schemaDef()
|
||||
{
|
||||
return array(
|
||||
'fields' => array(
|
||||
'uri' => array('type' => 'varchar', 'length' => 191, 'not null' => true),
|
||||
'profile_id' => array('type' => 'integer'),
|
||||
'inboxuri' => array('type' => 'varchar', 'length' => 191),
|
||||
'sharedInboxuri' => array('type' => 'varchar', 'length' => 191),
|
||||
'created' => array('type' => 'datetime', 'not null' => true),
|
||||
'modified' => array('type' => 'datetime', 'not null' => true),
|
||||
),
|
||||
'primary key' => array ('uri'),
|
||||
'unique keys' => array (
|
||||
'Activitypub_profile_profile_id_key' => array ('profile_id'),
|
||||
'Activitypub_profile_inboxuri_key' => array ('inboxuri'),
|
||||
'primary key' => array('uri'),
|
||||
'unique keys' => array(
|
||||
'Activitypub_profile_profile_id_key' => array('profile_id'),
|
||||
'Activitypub_profile_inboxuri_key' => array('inboxuri'),
|
||||
),
|
||||
'foreign keys' => array (
|
||||
'Activitypub_profile_profile_id_fkey' => array ('profile', array ('profile_id' => 'id')),
|
||||
'foreign keys' => array(
|
||||
'Activitypub_profile_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a pretty profile from a Profile object
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param Profile $profile
|
||||
* @return pretty array to be used in a response
|
||||
*/
|
||||
public static function profile_to_array ($profile)
|
||||
{
|
||||
$url = $profile->getURL ();
|
||||
$res = [
|
||||
/**
|
||||
* Generates a pretty profile from a Profile object
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param Profile $profile
|
||||
* @return pretty array to be used in a response
|
||||
*/
|
||||
public static function profile_to_array($profile)
|
||||
{
|
||||
$uri = ActivityPubPlugin::actor_uri($profile);
|
||||
$id = $profile->getID();
|
||||
$res = [
|
||||
'@context' => [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
[
|
||||
"@language" => "en"
|
||||
]
|
||||
],
|
||||
'id' => $profile->getID (),
|
||||
'type' => 'Person',
|
||||
'nickname' => $profile->getNickname (),
|
||||
'is_local' => $profile->isLocal (),
|
||||
'inbox' => "{$url}/inbox.json",
|
||||
'sharedInbox' => common_root_url ()."inbox.json",
|
||||
'outbox' => "{$url}/outbox.json",
|
||||
'display_name' => $profile->getFullname (),
|
||||
'followers' => "{$url}/followers.json",
|
||||
'followers_count' => $profile->subscriberCount (),
|
||||
'following' => "{$url}/following.json",
|
||||
'following_count' => $profile->subscriptionCount (),
|
||||
'liked' => "{$url}/liked.json",
|
||||
'liked_count' => Fave::countByProfile ($profile),
|
||||
'summary' => ($desc = $profile->getDescription ()) == null ? "" : $desc,
|
||||
'url' => $profile->getURL (),
|
||||
'avatar' => [
|
||||
'id' => $uri,
|
||||
'type' => 'Person',
|
||||
'preferredUsername' => $profile->getNickname(),
|
||||
'is_local' => $profile->isLocal(),
|
||||
'inbox' => common_local_url("apActorInbox", array("id" => $id)),
|
||||
'name' => $profile->getFullname(),
|
||||
'followers' => common_local_url("apActorFollowers", array("id" => $id)),
|
||||
'followers_count' => $profile->subscriberCount(),
|
||||
'following' => common_local_url("apActorFollowing", array("id" => $id)),
|
||||
'following_count' => $profile->subscriptionCount(),
|
||||
'liked' => common_local_url("apActorLiked", array("id" => $id)),
|
||||
'liked_count' => Fave::countByProfile($profile),
|
||||
'summary' => ($desc = $profile->getDescription()) == null ? "" : $desc,
|
||||
'icon' => [
|
||||
'type' => 'Image',
|
||||
'width' => 96,
|
||||
'height' => 96,
|
||||
'url' => $profile->avatarUrl (AVATAR_PROFILE_SIZE)
|
||||
'width' => AVATAR_PROFILE_SIZE,
|
||||
'height' => AVATAR_PROFILE_SIZE,
|
||||
'url' => $profile->avatarUrl(AVATAR_PROFILE_SIZE)
|
||||
]
|
||||
];
|
||||
return $res;
|
||||
|
||||
if ($profile->isLocal()) {
|
||||
$res["sharedInbox"] = common_local_url("apSharedInbox", array("id" => $id));
|
||||
} else {
|
||||
$aprofile = new Activitypub_profile();
|
||||
$aprofile = $aprofile->from_profile($profile);
|
||||
$res["sharedInbox"] = $aprofile->sharedInboxuri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the current objects variables into the database
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @access public
|
||||
* @throws ServerException
|
||||
*/
|
||||
public function do_insert ()
|
||||
{
|
||||
$profile = new Profile ();
|
||||
return $res;
|
||||
}
|
||||
|
||||
$profile->created = $this->created = $this->modified = common_sql_now ();
|
||||
/**
|
||||
* Insert the current objects variables into the database
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @access public
|
||||
* @throws ServerException
|
||||
*/
|
||||
public function do_insert()
|
||||
{
|
||||
$profile = new Profile();
|
||||
|
||||
$fields = array (
|
||||
'uri' => 'profileurl',
|
||||
'nickname' => 'nickname',
|
||||
'fullname' => 'fullname',
|
||||
'bio' => 'bio'
|
||||
);
|
||||
$profile->created = $this->created = $this->modified = common_sql_now();
|
||||
|
||||
foreach ($fields as $af => $pf) {
|
||||
$profile->$pf = $this->$af;
|
||||
}
|
||||
$fields = [
|
||||
'uri' => 'profileurl',
|
||||
'nickname' => 'nickname',
|
||||
'fullname' => 'fullname',
|
||||
'bio' => 'bio'
|
||||
];
|
||||
|
||||
$this->profile_id = $profile->insert ();
|
||||
if ($this->profile_id === false) {
|
||||
$profile->query ('ROLLBACK');
|
||||
throw new ServerException ('Profile insertion failed.');
|
||||
}
|
||||
|
||||
$ok = $this->insert ();
|
||||
|
||||
if ($ok === false) {
|
||||
$profile->query ('ROLLBACK');
|
||||
throw new ServerException ('Cannot save ActivityPub profile.');
|
||||
}
|
||||
foreach ($fields as $af => $pf) {
|
||||
$profile->$pf = $this->$af;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the locally stored profile for this Activitypub_profile
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @return Profile
|
||||
* @throws NoProfileException if it was not found
|
||||
*/
|
||||
public function local_profile ()
|
||||
{
|
||||
$profile = Profile::getKV ('id', $this->profile_id);
|
||||
if (!$profile instanceof Profile) {
|
||||
throw new NoProfileException ($this->profile_id);
|
||||
}
|
||||
return $profile;
|
||||
$this->profile_id = $profile->insert();
|
||||
if ($this->profile_id === false) {
|
||||
$profile->query('ROLLBACK');
|
||||
throw new ServerException('Profile insertion failed.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an Activitypub_profile from a Profile
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param Profile $profile
|
||||
* @return Activitypub_profile
|
||||
* @throws Exception if no Activitypub_profile exists for given Profile
|
||||
*/
|
||||
static function from_profile (Profile $profile)
|
||||
{
|
||||
$profile_id = $profile->getID ();
|
||||
$ok = $this->insert();
|
||||
|
||||
$aprofile = self::getKV ('profile_id', $profile_id);
|
||||
if (!$aprofile instanceof Activitypub_profile) {
|
||||
// No Activitypub_profile for this profile_id,
|
||||
if (!$profile->isLocal ()) {
|
||||
// create one!
|
||||
$aprofile = self::create_from_local_profile ($profile);
|
||||
} else {
|
||||
throw new Exception ('No Activitypub_profile for Profile ID: '.$profile_id. ', this is a local user.');
|
||||
}
|
||||
}
|
||||
if ($ok === false) {
|
||||
$profile->query('ROLLBACK');
|
||||
throw new ServerException('Cannot save ActivityPub profile.');
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($profile as $key => $value) {
|
||||
$aprofile->$key = $value;
|
||||
}
|
||||
/**
|
||||
* Fetch the locally stored profile for this Activitypub_profile
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @return Profile
|
||||
* @throws NoProfileException if it was not found
|
||||
*/
|
||||
public function local_profile()
|
||||
{
|
||||
$profile = Profile::getKV('id', $this->profile_id);
|
||||
if (!$profile instanceof Profile) {
|
||||
throw new NoProfileException($this->profile_id);
|
||||
}
|
||||
return $profile;
|
||||
}
|
||||
|
||||
return $aprofile;
|
||||
/**
|
||||
* Generates an Activitypub_profile from a Profile
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param Profile $profile
|
||||
* @return Activitypub_profile
|
||||
* @throws Exception if no Activitypub_profile exists for given Profile
|
||||
*/
|
||||
public static function from_profile(Profile $profile)
|
||||
{
|
||||
$profile_id = $profile->getID();
|
||||
|
||||
$aprofile = self::getKV('profile_id', $profile_id);
|
||||
if (!$aprofile instanceof Activitypub_profile) {
|
||||
// No Activitypub_profile for this profile_id,
|
||||
if (!$profile->isLocal()) {
|
||||
// create one!
|
||||
$aprofile = self::create_from_local_profile($profile);
|
||||
} else {
|
||||
throw new Exception('No Activitypub_profile for Profile ID: '.$profile_id. ', this is a local user.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an existent local profile creates an ActivityPub profile.
|
||||
* One must be careful not to give a user profile to this function
|
||||
* as only remote users have ActivityPub_profiles on local instance
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param Profile $profile
|
||||
* @return Activitypub_profile
|
||||
*/
|
||||
private static function create_from_local_profile (Profile $profile)
|
||||
{
|
||||
$url = $profile->getURL ();
|
||||
$inboxes = Activitypub_explorer::get_actor_inboxes_uri ($url);
|
||||
|
||||
$aprofile->created = $aprofile->modified = common_sql_now ();
|
||||
|
||||
$aprofile = new Activitypub_profile;
|
||||
$aprofile->profile_id = $profile->getID ();
|
||||
$aprofile->uri = $url;
|
||||
$aprofile->nickname = $profile->getNickname ();
|
||||
$aprofile->fullname = $profile->getFullname ();
|
||||
$aprofile->bio = substr ($profile->getDescription (), 0, 1000);
|
||||
$aprofile->inboxuri = $inboxes["inbox"];
|
||||
$aprofile->sharedInboxuri = $inboxes["sharedInbox"];
|
||||
|
||||
$aprofile->insert ();
|
||||
|
||||
return $aprofile;
|
||||
foreach ($profile as $key => $value) {
|
||||
$aprofile->$key = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sharedInbox if possible, inbox otherwise
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @return string Inbox URL
|
||||
*/
|
||||
public function get_inbox ()
|
||||
{
|
||||
if (is_null ($this->sharedInboxuri)) {
|
||||
return $this->inboxuri;
|
||||
}
|
||||
return $aprofile;
|
||||
}
|
||||
|
||||
return $this->sharedInboxuri;
|
||||
/**
|
||||
* Given an existent local profile creates an ActivityPub profile.
|
||||
* One must be careful not to give a user profile to this function
|
||||
* as only remote users have ActivityPub_profiles on local instance
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param Profile $profile
|
||||
* @return Activitypub_profile
|
||||
*/
|
||||
private static function create_from_local_profile(Profile $profile)
|
||||
{
|
||||
$url = $profile->getURL();
|
||||
$inboxes = Activitypub_explorer::get_actor_inboxes_uri($url);
|
||||
|
||||
$aprofile->created = $aprofile->modified = common_sql_now();
|
||||
|
||||
$aprofile = new Activitypub_profile;
|
||||
$aprofile->profile_id = $profile->getID();
|
||||
$aprofile->uri = $url;
|
||||
$aprofile->nickname = $profile->getNickname();
|
||||
$aprofile->fullname = $profile->getFullname();
|
||||
$aprofile->bio = substr($profile->getDescription(), 0, 1000);
|
||||
$aprofile->inboxuri = $inboxes["inbox"];
|
||||
$aprofile->sharedInboxuri = $inboxes["sharedInbox"];
|
||||
|
||||
$aprofile->insert();
|
||||
|
||||
return $aprofile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sharedInbox if possible, inbox otherwise
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @return string Inbox URL
|
||||
*/
|
||||
public function get_inbox()
|
||||
{
|
||||
if (is_null($this->sharedInboxuri)) {
|
||||
return $this->inboxuri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for uri property
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @return string URI
|
||||
*/
|
||||
public function get_uri ()
|
||||
{
|
||||
return $this->uri;
|
||||
return $this->sharedInboxuri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for uri property
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @return string URI
|
||||
*/
|
||||
public function get_uri()
|
||||
{
|
||||
return $this->uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures a valid Activitypub_profile when provided with a valid URI.
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param string $url
|
||||
* @return Activitypub_profile
|
||||
* @throws Exception if it isn't possible to return an Activitypub_profile
|
||||
*/
|
||||
public static function get_from_uri($url)
|
||||
{
|
||||
$explorer = new Activitypub_explorer();
|
||||
$profiles_found = $explorer->lookup($url);
|
||||
if (!empty($profiles_found)) {
|
||||
return self::from_profile($profiles_found[0]);
|
||||
} else {
|
||||
throw new Exception('No valid ActivityPub profile found for given URI.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up, and if necessary create, an Activitypub_profile for the remote
|
||||
* entity with the given webfinger address.
|
||||
* This should never return null -- you will either get an object or
|
||||
* an exception will be thrown.
|
||||
*
|
||||
* @author GNU Social
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param string $addr webfinger address
|
||||
* @return Activitypub_profile
|
||||
* @throws Exception on error conditions
|
||||
*/
|
||||
public static function ensure_web_finger($addr)
|
||||
{
|
||||
// Normalize $addr, i.e. add 'acct:' if missing
|
||||
$addr = Discovery::normalize($addr);
|
||||
|
||||
// Try the cache
|
||||
$uri = self::cacheGet(sprintf('activitypub_profile:webfinger:%s', $addr));
|
||||
|
||||
if ($uri !== false) {
|
||||
if (is_null($uri)) {
|
||||
// Negative cache entry
|
||||
// TRANS: Exception.
|
||||
throw new Exception(_m('Not a valid webfinger address (via cache).'));
|
||||
}
|
||||
try {
|
||||
return self::get_from_uri($uri);
|
||||
} catch (Exception $e) {
|
||||
common_log(LOG_ERR, sprintf(__METHOD__ . ': Webfinger address cache inconsistent with database, did not find Activitypub_profile uri==%s', $uri));
|
||||
self::cacheSet(sprintf('activitypub_profile:webfinger:%s', $addr), false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures a valid Activitypub_profile when provided with a valid URI.
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param string $url
|
||||
* @return Activitypub_profile
|
||||
* @throws Exception if it isn't possible to return an Activitypub_profile
|
||||
*/
|
||||
public static function get_from_uri ($url)
|
||||
{
|
||||
$explorer = new Activitypub_explorer ();
|
||||
$profiles_found = $explorer->lookup ($url);
|
||||
if (!empty ($profiles_found)) {
|
||||
return self::from_profile ($profiles_found[0]);
|
||||
} else {
|
||||
throw new Exception ('No valid ActivityPub profile found for given URI');
|
||||
}
|
||||
// If it doesn't return a valid Activitypub_profile an exception will
|
||||
// have been thrown before getting to this point.
|
||||
// Now, try some discovery
|
||||
|
||||
$disco = new Discovery();
|
||||
|
||||
try {
|
||||
$xrd = $disco->lookup($addr);
|
||||
} catch (Exception $e) {
|
||||
// Save negative cache entry so we don't waste time looking it up again.
|
||||
// @todo FIXME: Distinguish temporary failures?
|
||||
self::cacheSet(sprintf('activitypub_profile:webfinger:%s', $addr), null);
|
||||
// TRANS: Exception.
|
||||
throw new Exception(_m('Not a valid webfinger address.'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up, and if necessary create, an Activitypub_profile for the remote
|
||||
* entity with the given webfinger address.
|
||||
* This should never return null -- you will either get an object or
|
||||
* an exception will be thrown.
|
||||
*
|
||||
* @author GNU Social
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param string $addr webfinger address
|
||||
* @return Activitypub_profile
|
||||
* @throws Exception on error conditions
|
||||
*/
|
||||
public static function ensure_web_finger ($addr)
|
||||
{
|
||||
// Normalize $addr, i.e. add 'acct:' if missing
|
||||
$addr = Discovery::normalize ($addr);
|
||||
$hints = array_merge(
|
||||
array('webfinger' => $addr),
|
||||
DiscoveryHints::fromXRD($xrd)
|
||||
);
|
||||
|
||||
// Try the cache
|
||||
$uri = self::cacheGet (sprintf ('activitypub_profile:webfinger:%s', $addr));
|
||||
|
||||
if ($uri !== false) {
|
||||
if (is_null ($uri)) {
|
||||
// Negative cache entry
|
||||
// TRANS: Exception.
|
||||
throw new Exception (_m ('Not a valid webfinger address (via cache).'));
|
||||
}
|
||||
try {
|
||||
return self::get_from_uri ($uri);
|
||||
} catch (Exception $e) {
|
||||
common_log (LOG_ERR, sprintf (__METHOD__ . ': Webfinger address cache inconsistent with database, did not find Activitypub_profile uri==%s', $uri));
|
||||
self::cacheSet (sprintf ('activitypub_profile:webfinger:%s', $addr), false);
|
||||
}
|
||||
}
|
||||
|
||||
// Now, try some discovery
|
||||
|
||||
$disco = new Discovery ();
|
||||
|
||||
try {
|
||||
$xrd = $disco->lookup ($addr);
|
||||
} catch (Exception $e) {
|
||||
// Save negative cache entry so we don't waste time looking it up again.
|
||||
// @todo FIXME: Distinguish temporary failures?
|
||||
self::cacheSet (sprintf ('activitypub_profile:webfinger:%s', $addr), null);
|
||||
// TRANS: Exception.
|
||||
throw new Exception (_m ('Not a valid webfinger address.'));
|
||||
}
|
||||
|
||||
$hints = array_merge (array ('webfinger' => $addr),
|
||||
DiscoveryHints::fromXRD ($xrd));
|
||||
|
||||
// If there's an Hcard, let's grab its info
|
||||
if (array_key_exists ('hcard', $hints)) {
|
||||
if (!array_key_exists ('profileurl', $hints) ||
|
||||
// If there's an Hcard, let's grab its info
|
||||
if (array_key_exists('hcard', $hints)) {
|
||||
if (!array_key_exists('profileurl', $hints) ||
|
||||
$hints['hcard'] != $hints['profileurl']) {
|
||||
$hcardHints = DiscoveryHints::fromHcardUrl ($hints['hcard']);
|
||||
$hints = array_merge ($hcardHints, $hints);
|
||||
}
|
||||
}
|
||||
$hcardHints = DiscoveryHints::fromHcardUrl($hints['hcard']);
|
||||
$hints = array_merge($hcardHints, $hints);
|
||||
}
|
||||
}
|
||||
|
||||
// If we got a profile page, try that!
|
||||
$profileUrl = null;
|
||||
if (array_key_exists ('profileurl', $hints)) {
|
||||
$profileUrl = $hints['profileurl'];
|
||||
try {
|
||||
common_log (LOG_INFO, "Discovery on acct:$addr with profile URL $profileUrl");
|
||||
$aprofile = self::get_from_uri ($hints['profileurl']);
|
||||
self::cacheSet (sprintf ('activitypub_profile:webfinger:%s', $addr), $aprofile->get_uri ());
|
||||
return $aprofile;
|
||||
} catch (Exception $e) {
|
||||
common_log (LOG_WARNING, "Failed creating profile from profile URL '$profileUrl': " . $e->getMessage ());
|
||||
// keep looking
|
||||
// If we got a profile page, try that!
|
||||
$profileUrl = null;
|
||||
if (array_key_exists('profileurl', $hints)) {
|
||||
$profileUrl = $hints['profileurl'];
|
||||
try {
|
||||
common_log(LOG_INFO, "Discovery on acct:$addr with profile URL $profileUrl");
|
||||
$aprofile = self::get_from_uri($hints['profileurl']);
|
||||
self::cacheSet(sprintf('activitypub_profile:webfinger:%s', $addr), $aprofile->get_uri());
|
||||
return $aprofile;
|
||||
} catch (Exception $e) {
|
||||
common_log(LOG_WARNING, "Failed creating profile from profile URL '$profileUrl': " . $e->getMessage());
|
||||
// keep looking
|
||||
//
|
||||
// @todo FIXME: This means an error discovering from profile page
|
||||
// may give us a corrupt entry using the webfinger URI, which
|
||||
// will obscure the correct page-keyed profile later on.
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: try hcard
|
||||
// XXX: try FOAF
|
||||
|
||||
// TRANS: Exception. %s is a webfinger address.
|
||||
throw new Exception (sprintf (_m ('Could not find a valid profile for "%s".'), $addr));
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: try hcard
|
||||
// XXX: try FOAF
|
||||
|
||||
// TRANS: Exception. %s is a webfinger address.
|
||||
throw new Exception(sprintf(_m('Could not find a valid profile for "%s".'), $addr));
|
||||
}
|
||||
}
|
||||
|
58
classes/Activitypub_reject.php
Normal file
58
classes/Activitypub_reject.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/**
|
||||
* GNU social - a federating social network
|
||||
*
|
||||
* ActivityPubPlugin implementation for GNU Social
|
||||
*
|
||||
* LICENCE: This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @author Daniel Supernault <danielsupernault@gmail.com>
|
||||
* @copyright 2018 Free Software Foundation http://fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* ActivityPub error representation
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*/
|
||||
class Activitypub_reject extends Managed_DataObject
|
||||
{
|
||||
/**
|
||||
* Generates an ActivityPub representation of a Reject
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param array $object
|
||||
* @return pretty array to be used in a response
|
||||
*/
|
||||
public static function reject_to_array($object)
|
||||
{
|
||||
$res = array("@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Reject",
|
||||
"object" => $object
|
||||
);
|
||||
return $res;
|
||||
}
|
||||
}
|
@ -25,8 +25,8 @@
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined ('GNUSOCIAL')) {
|
||||
exit (1);
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -40,16 +40,16 @@ if (!defined ('GNUSOCIAL')) {
|
||||
*/
|
||||
class Activitypub_tag extends Managed_DataObject
|
||||
{
|
||||
/**
|
||||
* Generates a pretty tag from a Tag object
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param Tag $tag
|
||||
* @return pretty array to be used in a response
|
||||
*/
|
||||
public static function tag_to_array ($tag)
|
||||
{
|
||||
$res = [
|
||||
/**
|
||||
* Generates a pretty tag from a Tag object
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param Tag $tag
|
||||
* @return pretty array to be used in a response
|
||||
*/
|
||||
public static function tag_to_array($tag)
|
||||
{
|
||||
$res = [
|
||||
'@context' => [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
[
|
||||
@ -57,9 +57,9 @@ class Activitypub_tag extends Managed_DataObject
|
||||
]
|
||||
],
|
||||
'name' => $tag,
|
||||
'url' => common_local_url ('tag', array('tag' => $tag))
|
||||
'url' => common_local_url('tag', array('tag' => $tag))
|
||||
];
|
||||
|
||||
return $res;
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
}
|
||||
|
59
classes/Activitypub_undo.php
Normal file
59
classes/Activitypub_undo.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* GNU social - a federating social network
|
||||
*
|
||||
* ActivityPubPlugin implementation for GNU Social
|
||||
*
|
||||
* LICENCE: This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @author Daniel Supernault <danielsupernault@gmail.com>
|
||||
* @copyright 2018 Free Software Foundation http://fsf.org
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* ActivityPub error representation
|
||||
*
|
||||
* @category Plugin
|
||||
* @package GNUsocial
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link http://www.gnu.org/software/social/
|
||||
*/
|
||||
class Activitypub_undo extends Managed_DataObject
|
||||
{
|
||||
/**
|
||||
* Generates an ActivityPub representation of a Undo
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param array $object
|
||||
* @return pretty array to be used in a response
|
||||
*/
|
||||
public static function undo_to_array($object)
|
||||
{
|
||||
$res = array("@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Undo",
|
||||
"actor" => $object["actor"],
|
||||
"object" => $object
|
||||
);
|
||||
return $res;
|
||||
}
|
||||
}
|
25
composer.json
Normal file
25
composer.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "dansup/activity-pub",
|
||||
"description": "ActivityPub plugin for GNU/Social",
|
||||
"type": "gnusocial-plugin",
|
||||
"require": {},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.2"
|
||||
},
|
||||
"license": "AGPL",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"name": "Daniel Supernault",
|
||||
"email": "danielsupernault@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Diogo Cordeiro",
|
||||
"email": "diogo@fc.up.pt"
|
||||
}
|
||||
]
|
||||
}
|
1423
composer.lock
generated
Normal file
1423
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
25
phpunit.xml
Normal file
25
phpunit.xml
Normal file
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false">
|
||||
<testsuites>
|
||||
<testsuite name="Feature">
|
||||
<directory suffix="Test.php">./tests/Feature</directory>
|
||||
</testsuite>
|
||||
|
||||
<testsuite name="Unit">
|
||||
<directory suffix="Test.php">./tests/Unit</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">./app</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
28
tests/CreatesApplication.php
Normal file
28
tests/CreatesApplication.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Tests;
|
||||
|
||||
trait CreatesApplication
|
||||
{
|
||||
/**
|
||||
* Creates the application.
|
||||
*
|
||||
* @return todo
|
||||
*/
|
||||
public static function createApplication()
|
||||
{
|
||||
if (!defined('INSTALLDIR')) {
|
||||
define('INSTALLDIR', __DIR__ . '/../../../');
|
||||
}
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
define('GNUSOCIAL', true);
|
||||
}
|
||||
if (!defined('STATUSNET')) {
|
||||
define('STATUSNET', true); // compatibility
|
||||
}
|
||||
|
||||
require INSTALLDIR . '/lib/common.php';
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
15
tests/TestCase.php
Normal file
15
tests/TestCase.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase as BaseTestCase;
|
||||
|
||||
abstract class TestCase extends BaseTestCase
|
||||
{
|
||||
use CreatesApplication;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->createApplication();
|
||||
}
|
||||
}
|
18
tests/Unit/ExampleTest.php
Normal file
18
tests/Unit/ExampleTest.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use Tests\TestCase;
|
||||
|
||||
class ExampleTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* A basic test example.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testBasicTest()
|
||||
{
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
}
|
29
tests/Unit/ProfileObjectTest.php
Normal file
29
tests/Unit/ProfileObjectTest.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use Tests\TestCase;
|
||||
|
||||
class ProfileObjectTest extends TestCase
|
||||
{
|
||||
public function testLibraryInstalled()
|
||||
{
|
||||
$this->assertTrue(class_exists('\Activitypub_profile'));
|
||||
}
|
||||
|
||||
public function testProfileObject()
|
||||
{
|
||||
// Mimic proper ACCEPT header
|
||||
$_SERVER['HTTP_ACCEPT'] = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams';
|
||||
|
||||
// Fetch profile
|
||||
$user = \Profile::getKV('id', 1);
|
||||
// Fetch ActivityPub Actor Object representation
|
||||
$profile = \Activitypub_profile::profile_to_array($user);
|
||||
|
||||
$this->assertTrue(is_array($profile));
|
||||
|
||||
$this->assertTrue(isset($profile['inbox']));
|
||||
$this->assertTrue(isset($profile['outbox']));
|
||||
}
|
||||
}
|
@ -24,12 +24,13 @@
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined ('GNUSOCIAL')) {
|
||||
exit (1);
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
class DiscoveryHints {
|
||||
static function fromXRD(XML_XRD $xrd)
|
||||
class DiscoveryHints
|
||||
{
|
||||
public static function fromXRD(XML_XRD $xrd)
|
||||
{
|
||||
$hints = array();
|
||||
|
||||
@ -60,7 +61,7 @@ class DiscoveryHints {
|
||||
return $hints;
|
||||
}
|
||||
|
||||
static function fromHcardUrl($url)
|
||||
public static function fromHcardUrl($url)
|
||||
{
|
||||
$client = new HTTPClient();
|
||||
$client->setHeader('Accept', 'text/html,application/xhtml+xml');
|
||||
@ -76,11 +77,13 @@ class DiscoveryHints {
|
||||
return null;
|
||||
}
|
||||
|
||||
return self::hcardHints($response->getBody(),
|
||||
$response->getEffectiveUrl());
|
||||
return self::hcardHints(
|
||||
$response->getBody(),
|
||||
$response->getEffectiveUrl()
|
||||
);
|
||||
}
|
||||
|
||||
static function hcardHints($body, $url)
|
||||
public static function hcardHints($body, $url)
|
||||
{
|
||||
$hcard = self::_hcard($body, $url);
|
||||
|
||||
@ -119,7 +122,7 @@ class DiscoveryHints {
|
||||
return $hints;
|
||||
}
|
||||
|
||||
static function _hcard($body, $url)
|
||||
public static function _hcard($body, $url)
|
||||
{
|
||||
$mf2 = new Mf2\Parser($body, $url);
|
||||
$mf2 = $mf2->parse();
|
||||
|
@ -25,8 +25,8 @@
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined ('GNUSOCIAL')) {
|
||||
exit (1);
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -42,205 +42,215 @@ if (!defined ('GNUSOCIAL')) {
|
||||
*/
|
||||
class Activitypub_explorer
|
||||
{
|
||||
private $discovered_actor_profiles = array ();
|
||||
private $discovered_actor_profiles = array();
|
||||
|
||||
/**
|
||||
* Get every profile from the given URL
|
||||
* This function cleans the $this->discovered_actor_profiles array
|
||||
* so that there is no erroneous data
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param string $url User's url
|
||||
* @return array of Profile objects
|
||||
*/
|
||||
public function lookup ($url)
|
||||
{
|
||||
$this->discovered_actor_profiles = array ();
|
||||
/**
|
||||
* Get every profile from the given URL
|
||||
* This function cleans the $this->discovered_actor_profiles array
|
||||
* so that there is no erroneous data
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param string $url User's url
|
||||
* @return array of Profile objects
|
||||
*/
|
||||
public function lookup($url)
|
||||
{
|
||||
$this->discovered_actor_profiles = array();
|
||||
|
||||
return $this->_lookup ($url);
|
||||
return $this->_lookup($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get every profile from the given URL
|
||||
* This is a recursive function that will accumulate the results on
|
||||
* $discovered_actor_profiles array
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param string $url User's url
|
||||
* @return array of Profile objects
|
||||
*/
|
||||
private function _lookup($url)
|
||||
{
|
||||
// First check if we already have it locally and, if so, return it
|
||||
// If the local fetch fails: grab it remotely, store locally and return
|
||||
if (! ($this->grab_local_user($url) || $this->grab_remote_user($url))) {
|
||||
throw new Exception("User not found.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get every profile from the given URL
|
||||
* This is a recursive function that will accumulate the results on
|
||||
* $discovered_actor_profiles array
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param string $url User's url
|
||||
* @return array of Profile objects
|
||||
*/
|
||||
private function _lookup ($url)
|
||||
{
|
||||
// First check if we already have it locally and, if so, return it
|
||||
// If the local fetch fails: grab it remotely, store locally and return
|
||||
if (! ($this->grab_local_user ($url) || $this->grab_remote_user ($url))) {
|
||||
throw new Exception ("User not found");
|
||||
}
|
||||
return $this->discovered_actor_profiles;
|
||||
}
|
||||
|
||||
|
||||
return $this->discovered_actor_profiles;
|
||||
/**
|
||||
* This ensures that we are using a valid ActivityPub URI
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param string $url
|
||||
* @return boolean success state (related to the response)
|
||||
* @throws Exception (If the HTTP request fails)
|
||||
*/
|
||||
private function ensure_proper_remote_uri($url)
|
||||
{
|
||||
$client = new HTTPClient();
|
||||
$headers = array();
|
||||
$headers[] = 'Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams"';
|
||||
$headers[] = 'User-Agent: GNUSocialBot v0.1 - https://gnu.io/social';
|
||||
$response = $client->get($url, $headers);
|
||||
if (!$response->isOk()) {
|
||||
throw new Exception("Invalid Actor URL.");
|
||||
}
|
||||
$res = json_decode($response->getBody(), JSON_UNESCAPED_SLASHES);
|
||||
if (self::validate_remote_response($res)) {
|
||||
$this->temp_res = $res;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a local user profiles from its URL and joins it on
|
||||
* $this->discovered_actor_profiles
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param string $url User's url
|
||||
* @return boolean success state
|
||||
*/
|
||||
private function grab_local_user ($url)
|
||||
{
|
||||
if (($actor_profile = self::get_profile_by_url ($url)) != false) {
|
||||
$this->discovered_actor_profiles[]= $actor_profile;
|
||||
return true;
|
||||
} else {
|
||||
/******************************** XXX: ********************************
|
||||
* Sometimes it is not true that the user is not locally available, *
|
||||
* mostly when it is a local user and URLs slightly changed *
|
||||
* e.g.: GS instance owner changed from standard urls to pretty urls *
|
||||
* (not sure if this is necessary, but anyway) *
|
||||
**********************************************************************/
|
||||
return false;
|
||||
}
|
||||
|
||||
// Iff we really are in the same instance
|
||||
$root_url_len = strlen (common_root_url ());
|
||||
if (substr ($url, 0, $root_url_len) == common_root_url ()) {
|
||||
// Grab the nickname and try to get the user
|
||||
if (($actor_profile = Profile::getKV ("nickname", substr ($url, $root_url_len))) != false) {
|
||||
$this->discovered_actor_profiles[]= $actor_profile;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
/**
|
||||
* Get a local user profiles from its URL and joins it on
|
||||
* $this->discovered_actor_profiles
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param string $uri Actor's uri
|
||||
* @return boolean success state
|
||||
*/
|
||||
private function grab_local_user($uri)
|
||||
{
|
||||
// Ensure proper remote URI
|
||||
// If an exceptiong ocurrs here it's better to just leave everything
|
||||
// break than to continue processing
|
||||
if ($this->ensure_proper_remote_uri($uri)) {
|
||||
$uri = $this->temp_res["id"];
|
||||
}
|
||||
try {
|
||||
// Try standard ActivityPub route
|
||||
$aprofile = Activitypub_profile::getKV("uri", $uri);
|
||||
if ($aprofile instanceof Activitypub_profile) {
|
||||
$profile = $aprofile->local_profile();
|
||||
} else {
|
||||
// This potential local user is not a remote user.
|
||||
// Let's check for pure blood!
|
||||
$profile = User::getByNickname($this->temp_res["preferredUsername"])->getProfile();
|
||||
}
|
||||
|
||||
// We found something!
|
||||
$this->discovered_actor_profiles[]= $profile;
|
||||
unset($this->temp_res); // IMPORTANT to avoid _dangerous_ noise in the Explorer system
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
// We can safely ignore every exception here as we are return false
|
||||
// when it fails the lookup for existing local representation
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a remote user(s) profile(s) from its URL and joins it on
|
||||
* $this->discovered_actor_profiles
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param string $url User's url
|
||||
* @return boolean success state
|
||||
*/
|
||||
private function grab_remote_user ($url)
|
||||
{
|
||||
$client = new HTTPClient ();
|
||||
$headers = array();
|
||||
$headers[] = 'Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams"';
|
||||
$headers[] = 'User-Agent: GNUSocialBot v0.1 - https://gnu.io/social';
|
||||
$response = $client->get ($url, $headers);
|
||||
if (!$response->isOk ()) {
|
||||
throw new Exception ("Invalid Actor URL.");
|
||||
}
|
||||
$res = json_decode ($response->getBody (), JSON_UNESCAPED_SLASHES);
|
||||
if (isset ($res["orderedItems"])) { // It's a potential collection of actors!!!
|
||||
foreach ($res["orderedItems"] as $profile) {
|
||||
if ($this->_lookup ($profile) == false) {
|
||||
// XXX: Invalid actor found, not sure how we handle those
|
||||
}
|
||||
}
|
||||
// Go through entire collection
|
||||
if (!is_null ($res["next"])) {
|
||||
$this->_lookup ($res["next"]);
|
||||
}
|
||||
return true;
|
||||
} else if (self::validate_remote_response ($res)) {
|
||||
$this->discovered_actor_profiles[]= $this->store_profile ($res);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
/**
|
||||
* Get a remote user(s) profile(s) from its URL and joins it on
|
||||
* $this->discovered_actor_profiles
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param string $url User's url
|
||||
* @return boolean success state
|
||||
*/
|
||||
private function grab_remote_user($url)
|
||||
{
|
||||
if (!isset($this->temp_res)) {
|
||||
$client = new HTTPClient();
|
||||
$headers = array();
|
||||
$headers[] = 'Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams"';
|
||||
$headers[] = 'User-Agent: GNUSocialBot v0.1 - https://gnu.io/social';
|
||||
$response = $client->get($url, $headers);
|
||||
if (!$response->isOk()) {
|
||||
throw new Exception("Invalid Actor URL.");
|
||||
}
|
||||
$res = json_decode($response->getBody(), JSON_UNESCAPED_SLASHES);
|
||||
} else {
|
||||
$res = $this->temp_res;
|
||||
unset($this->temp_res);
|
||||
}
|
||||
if (isset($res["orderedItems"])) { // It's a potential collection of actors!!!
|
||||
foreach ($res["orderedItems"] as $profile) {
|
||||
if ($this->_lookup($profile) == false) {
|
||||
// XXX: Invalid actor found, not sure how we handle those
|
||||
}
|
||||
}
|
||||
// Go through entire collection
|
||||
if (!is_null($res["next"])) {
|
||||
$this->_lookup($res["next"]);
|
||||
}
|
||||
return true;
|
||||
} elseif (self::validate_remote_response($res)) {
|
||||
$this->discovered_actor_profiles[]= $this->store_profile($res);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save remote user profile in local instance
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param array $res remote response
|
||||
* @return Profile remote Profile object
|
||||
*/
|
||||
private function store_profile ($res)
|
||||
{
|
||||
$aprofile = new Activitypub_profile;
|
||||
$aprofile->uri = $res["url"];
|
||||
$aprofile->nickname = $res["nickname"];
|
||||
$aprofile->fullname = $res["display_name"];
|
||||
$aprofile->bio = substr ($res["summary"], 0, 1000);
|
||||
$aprofile->inboxuri = $res["inbox"];
|
||||
$aprofile->sharedInboxuri = isset ($res["sharedInbox"]) ? $res["sharedInbox"] : $res["inbox"];
|
||||
return false;
|
||||
}
|
||||
|
||||
$aprofile->do_insert ();
|
||||
/**
|
||||
* Save remote user profile in local instance
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param array $res remote response
|
||||
* @return Profile remote Profile object
|
||||
*/
|
||||
private function store_profile($res)
|
||||
{
|
||||
$aprofile = new Activitypub_profile;
|
||||
$aprofile->uri = $res["id"];
|
||||
$aprofile->nickname = $res["preferredUsername"];
|
||||
$aprofile->fullname = $res["name"];
|
||||
$aprofile->bio = substr($res["summary"], 0, 1000);
|
||||
$aprofile->inboxuri = $res["inbox"];
|
||||
$aprofile->sharedInboxuri = isset($res["sharedInbox"]) ? $res["sharedInbox"] : $res["inbox"];
|
||||
|
||||
return $aprofile->local_profile ();
|
||||
$aprofile->do_insert();
|
||||
|
||||
return $aprofile->local_profile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a remote response in order to determine whether this
|
||||
* response is a valid profile or not
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param array $res remote response
|
||||
* @return boolean success state
|
||||
*/
|
||||
private static function validate_remote_response($res)
|
||||
{
|
||||
if (!isset($res["id"], $res["preferredUsername"], $res["name"], $res["summary"], $res["inbox"])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a remote response in order to determine whether this
|
||||
* response is a valid profile or not
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param array $res remote response
|
||||
* @return boolean success state
|
||||
*/
|
||||
private static function validate_remote_response ($res)
|
||||
{
|
||||
if (!isset ($res["url"], $res["nickname"], $res["display_name"], $res["summary"], $res["inbox"])) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
/**
|
||||
* Given a valid actor profile url returns its inboxes
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param string $url of Actor profile
|
||||
* @return boolean|array false if fails | array with inbox and shared inbox if successful
|
||||
*/
|
||||
public static function get_actor_inboxes_uri($url)
|
||||
{
|
||||
$client = new HTTPClient();
|
||||
$headers = array();
|
||||
$headers[] = 'Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams"';
|
||||
$headers[] = 'User-Agent: GNUSocialBot v0.1 - https://gnu.io/social';
|
||||
$response = $client->get($url, $headers);
|
||||
if (!$response->isOk()) {
|
||||
throw new Exception("Invalid Actor URL.");
|
||||
}
|
||||
$res = json_decode($response->getBody(), JSON_UNESCAPED_SLASHES);
|
||||
if (self::validate_remote_response($res)) {
|
||||
return array("inbox" => $res["inbox"],
|
||||
"sharedInbox" => isset($res["sharedInbox"]) ? $res["sharedInbox"] : $res["inbox"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a profile from it's profileurl
|
||||
* Unfortunately GNU Social cache is not truly reliable when handling
|
||||
* potential ActivityPub remote profiles, as so it is important to use
|
||||
* this hacky workaround (at least for now)
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param string $v URL
|
||||
* @return boolean|Profile false if fails | Profile object if successful
|
||||
*/
|
||||
public static function get_profile_by_url ($v)
|
||||
{
|
||||
$i = Managed_DataObject::getcached("Profile", "profileurl", $v);
|
||||
if (empty ($i)) { // false = cache miss
|
||||
$i = new Profile;
|
||||
$result = $i->get ("profileurl", $v);
|
||||
if ($result) {
|
||||
// Hit!
|
||||
$i->encache();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return $i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a valid actor profile url returns its inboxes
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param string $url of Actor profile
|
||||
* @return boolean|array false if fails | array with inbox and shared inbox if successful
|
||||
*/
|
||||
public static function get_actor_inboxes_uri ($url)
|
||||
{
|
||||
$client = new HTTPClient ();
|
||||
$headers = array();
|
||||
$headers[] = 'Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams"';
|
||||
$headers[] = 'User-Agent: GNUSocialBot v0.1 - https://gnu.io/social';
|
||||
$response = $client->get ($url, $headers);
|
||||
if (!$response->isOk ()) {
|
||||
throw new Exception ("Invalid Actor URL.");
|
||||
}
|
||||
$res = json_decode ($response->getBody (), JSON_UNESCAPED_SLASHES);
|
||||
if (self::validate_remote_response ($res)) {
|
||||
return array ("inbox" => $res["inbox"],
|
||||
"sharedInbox" => isset ($res["sharedInbox"]) ? $res["sharedInbox"] : $res["inbox"]);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -25,8 +25,8 @@
|
||||
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||
* @link https://www.gnu.org/software/social/
|
||||
*/
|
||||
if (!defined ('GNUSOCIAL')) {
|
||||
exit (1);
|
||||
if (!defined('GNUSOCIAL')) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -43,184 +43,202 @@ if (!defined ('GNUSOCIAL')) {
|
||||
*/
|
||||
class Activitypub_postman
|
||||
{
|
||||
private $actor;
|
||||
private $to = array ();
|
||||
private $client;
|
||||
private $headers;
|
||||
private $actor;
|
||||
private $to = [];
|
||||
private $client;
|
||||
private $headers;
|
||||
|
||||
/**
|
||||
* Create a postman to deliver something to someone
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param Profile of sender
|
||||
* @param Activitypub_profile $to array of destinataries
|
||||
*/
|
||||
public function __construct ($from, $to = array ())
|
||||
{
|
||||
$this->client = new HTTPClient ();
|
||||
$this->actor = $from;
|
||||
$this->to = $to;
|
||||
$this->headers = array();
|
||||
$this->headers[] = 'Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams"';
|
||||
$this->headers[] = 'User-Agent: GNUSocialBot v0.1 - https://gnu.io/social';
|
||||
/**
|
||||
* Create a postman to deliver something to someone
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param Profile of sender
|
||||
* @param Activitypub_profile $to array of destinataries
|
||||
*/
|
||||
public function __construct($from, $to = [])
|
||||
{
|
||||
$this->client = new HTTPClient();
|
||||
$this->actor = $from;
|
||||
$this->to = $to;
|
||||
$this->headers = [];
|
||||
$this->headers[] = 'Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams"';
|
||||
$this->headers[] = 'User-Agent: GNUSocialBot v0.1 - https://gnu.io/social';
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a follow notification to remote instance
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @throws Exception
|
||||
*/
|
||||
public function follow()
|
||||
{
|
||||
$data = Activitypub_follow::follow_to_array($this->actor->getUrl(), $this->to[0]->getUrl());
|
||||
$this->client->setBody(json_encode($data));
|
||||
$res = $this->client->post($this->to[0]->get_inbox(), $this->headers);
|
||||
$res_body = json_decode($res->getBody());
|
||||
|
||||
if ($res->isOk() || $res->getStatus() == 409) {
|
||||
$pending_list = new Activitypub_pending_follow_requests($this->actor->getID(), $this->to[0]->getID());
|
||||
if (! ($res->getStatus() == 409 || $res_body->type == "Accept")) {
|
||||
$pending_list->add();
|
||||
throw new Exception("Your follow request is pending acceptation.");
|
||||
}
|
||||
$pending_list->remove();
|
||||
return true;
|
||||
} elseif (isset($res_body[0]->error)) {
|
||||
throw new Exception($res_body[0]->error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a follow notification to remote instance
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public function follow ()
|
||||
{
|
||||
$data = array ("@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Follow",
|
||||
"actor" => $this->actor->getUrl (),
|
||||
"object" => $this->to[0]->getUrl ());
|
||||
$this->client->setBody (json_encode ($data));
|
||||
$this->client->post ($this->to[0]->get_inbox (), $this->headers);
|
||||
}
|
||||
throw new Exception("An unknown error occurred.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a Undo Follow notification to remote instance
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public function undo_follow ()
|
||||
{
|
||||
$data = array ("@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Undo",
|
||||
"actor" => $this->actor->getUrl (),
|
||||
"object" => array (
|
||||
"type" => "Follow",
|
||||
"object" => $this->to[0]->getUrl ()
|
||||
)
|
||||
/**
|
||||
* Send a Undo Follow notification to remote instance
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
*/
|
||||
public function undo_follow()
|
||||
{
|
||||
$data = Activitypub_undo::undo_to_array(
|
||||
Activitypub_follow::follow_to_array(
|
||||
$this->actor->getUrl(),
|
||||
$this->to[0]->getUrl()
|
||||
)
|
||||
);
|
||||
$this->client->setBody (json_encode ($data));
|
||||
$this->client->post ($this->to[0]->get_inbox (), $this->headers);
|
||||
}
|
||||
$this->client->setBody(json_encode($data));
|
||||
$res = $this->client->post($this->to[0]->get_inbox(), $this->headers);
|
||||
$res_body = json_decode($res->getBody());
|
||||
|
||||
/**
|
||||
* Send a Like notification to remote instances holding the notice
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param Notice $notice
|
||||
*/
|
||||
public function like ($notice)
|
||||
{
|
||||
$data = array ("@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Like",
|
||||
"actor" => $this->actor->getUrl (),
|
||||
"object" => $notice->getUri ());
|
||||
$this->client->setBody (json_encode ($data));
|
||||
foreach ($this->to_inbox () as $inbox) {
|
||||
$this->client->post ($inbox, $this->headers);
|
||||
}
|
||||
if ($res->isOk() || $res->getStatus() == 409) {
|
||||
$pending_list = new Activitypub_pending_follow_requests($this->actor->getID(), $this->to[0]->getID());
|
||||
$pending_list->remove();
|
||||
return true;
|
||||
}
|
||||
if (isset($res_body[0]->error)) {
|
||||
throw new Exception($res_body[0]->error);
|
||||
}
|
||||
throw new Exception("An unknown error occurred.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a Undo Like notification to remote instances holding the notice
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param Notice $notice
|
||||
*/
|
||||
public function undo_like ($notice)
|
||||
{
|
||||
$data = array ("@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Undo",
|
||||
"actor" => $this->actor->getUrl (),
|
||||
"object" => array (
|
||||
"type" => "Like",
|
||||
"object" => $notice->getUri ()
|
||||
)
|
||||
/**
|
||||
* Send a Like notification to remote instances holding the notice
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param Notice $notice
|
||||
*/
|
||||
public function like($notice)
|
||||
{
|
||||
$data = Activitypub_like::like_to_array(
|
||||
$this->actor->getUrl(),
|
||||
Activitypub_notice::notice_to_array($notice)
|
||||
);
|
||||
$this->client->setBody (json_encode ($data));
|
||||
foreach ($this->to_inbox () as $inbox) {
|
||||
$this->client->post ($inbox, $this->headers);
|
||||
}
|
||||
$this->client->setBody(json_encode($data));
|
||||
foreach ($this->to_inbox() as $inbox) {
|
||||
$this->client->post($inbox, $this->headers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a Announce notification to remote instances
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param Notice $notice
|
||||
*/
|
||||
public function announce ($notice)
|
||||
{
|
||||
$data = array ("@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"id" => $notice->getUri (),
|
||||
"url" => $notice->getUrl (),
|
||||
"type" => "Announce",
|
||||
"actor" => $this->actor->getUrl (),
|
||||
"to" => "https://www.w3.org/ns/activitystreams#Public",
|
||||
"object" => $notice->getUri ()
|
||||
);
|
||||
$this->client->setBody (json_encode ($data));
|
||||
foreach ($this->to_inbox () as $inbox) {
|
||||
$this->client->post ($inbox, $this->headers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a Create notification to remote instances
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param Notice $notice
|
||||
*/
|
||||
public function create ($notice)
|
||||
{
|
||||
$data = array ("@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"id" => $notice->getUri (),
|
||||
"type" => "Create",
|
||||
"actor" => $this->actor->getUrl (),
|
||||
"to" => "https://www.w3.org/ns/activitystreams#Public",
|
||||
"object" => array (
|
||||
"type" => "Note",
|
||||
"url" => $notice->getUrl (),
|
||||
"content" => $notice->getContent ()
|
||||
)
|
||||
);
|
||||
if (isset ($notice->reply_to)) {
|
||||
$data["object"]["reply_to"] = $notice->getParent ()->getUri ();
|
||||
}
|
||||
$this->client->setBody (json_encode ($data));
|
||||
foreach ($this->to_inbox () as $inbox) {
|
||||
$this->client->post ($inbox, $this->headers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a Delete notification to remote instances holding the notice
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param Notice $notice
|
||||
*/
|
||||
public function delete ($notice)
|
||||
{
|
||||
$data = array ("@context" => "https://www.w3.org/ns/activitystreams",
|
||||
"type" => "Delete",
|
||||
"actor" => $this->actor->getUrl (),
|
||||
"object" => $notice->getUri ()
|
||||
/**
|
||||
* Send a Undo Like notification to remote instances holding the notice
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param Notice $notice
|
||||
*/
|
||||
public function undo_like($notice)
|
||||
{
|
||||
$data = Activitypub_undo::undo_to_array(
|
||||
Activitypub_like::like_to_array(
|
||||
$this->actor->getUrl(),
|
||||
Activitypub_notice::notice_to_array($notice)
|
||||
)
|
||||
);
|
||||
$this->client->setBody (json_encode ($data));
|
||||
foreach ($this->to_inbox () as $inbox) {
|
||||
$this->client->post ($inbox, $this->headers);
|
||||
$this->client->setBody(json_encode($data));
|
||||
foreach ($this->to_inbox() as $inbox) {
|
||||
$this->client->post($inbox, $this->headers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a Create notification to remote instances
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param Notice $notice
|
||||
*/
|
||||
public function create($notice)
|
||||
{
|
||||
$data = Activitypub_create::create_to_array(
|
||||
$notice->getUri(),
|
||||
$this->actor->getUrl(),
|
||||
Activitypub_notice::notice_to_array($notice)
|
||||
);
|
||||
if (isset($notice->reply_to)) {
|
||||
$data["object"]["reply_to"] = $notice->getParent()->getUri();
|
||||
}
|
||||
$this->client->setBody(json_encode($data));
|
||||
foreach ($this->to_inbox() as $inbox) {
|
||||
$this->client->post($inbox, $this->headers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a Announce notification to remote instances
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param Notice $notice
|
||||
*/
|
||||
public function announce($notice)
|
||||
{
|
||||
$data = Activitypub_announce::announce_to_array(
|
||||
$this->actor->getUrl(),
|
||||
Activitypub_notice::notice_to_array($notice)
|
||||
);
|
||||
$this->client->setBody(json_encode($data));
|
||||
foreach ($this->to_inbox() as $inbox) {
|
||||
$this->client->post($inbox, $this->headers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a Delete notification to remote instances holding the notice
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @param Notice $notice
|
||||
*/
|
||||
public function delete($notice)
|
||||
{
|
||||
$data = Activitypub_delete::delete_to_array(Activitypub_notice::notice_to_array($notice));
|
||||
$this->client->setBody(json_encode($data));
|
||||
$errors = array();
|
||||
foreach ($this->to_inbox() as $inbox) {
|
||||
$res = $this->client->post($inbox, $this->headers);
|
||||
if (!$res->isOk()) {
|
||||
$res_body = json_decode($res->getBody());
|
||||
if (isset($res_body[0]->error)) {
|
||||
$errors[] = ($res_body[0]->error);
|
||||
continue;
|
||||
}
|
||||
$errors[] = ("An unknown error occurred.");
|
||||
}
|
||||
}
|
||||
if (!empty($errors)) {
|
||||
throw new Exception(json_encode($errors));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean list of inboxes to deliver messages
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @return array To Inbox URLs
|
||||
*/
|
||||
private function to_inbox()
|
||||
{
|
||||
$to_inboxes = array();
|
||||
foreach ($this->to as $to_profile) {
|
||||
$to_inboxes[] = $to_profile->get_inbox();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean list of inboxes to deliver messages
|
||||
*
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @return array To Inbox URLs
|
||||
*/
|
||||
private function to_inbox ()
|
||||
{
|
||||
$to_inboxes = array ();
|
||||
foreach ($this->to as $to_profile) {
|
||||
$to_inboxes[] = $to_profile->get_inbox ();
|
||||
}
|
||||
|
||||
return array_unique ($to_inboxes);
|
||||
}
|
||||
return array_unique($to_inboxes);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user