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/
|
@ -19,8 +19,8 @@
|
|||||||
*
|
*
|
||||||
* @category Plugin
|
* @category Plugin
|
||||||
* @package GNUsocial
|
* @package GNUsocial
|
||||||
* @author Daniel Supernault <danielsupernault@gmail.com>
|
|
||||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||||
|
* @author Daniel Supernault <danielsupernault@gmail.com>
|
||||||
* @copyright 2018 Free Software Foundation http://fsf.org
|
* @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
|
* @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/
|
* @link https://www.gnu.org/software/social/
|
||||||
@ -37,23 +37,58 @@ require_once __DIR__ . DIRECTORY_SEPARATOR . "utils" . DIRECTORY_SEPARATOR . "po
|
|||||||
/**
|
/**
|
||||||
* @category Plugin
|
* @category Plugin
|
||||||
* @package GNUsocial
|
* @package GNUsocial
|
||||||
* @author Daniel Supernault <danielsupernault@gmail.com>
|
|
||||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
* @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
|
* @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/
|
* @link http://www.gnu.org/software/social/
|
||||||
*/
|
*/
|
||||||
class ActivityPubPlugin extends Plugin
|
class ActivityPubPlugin extends Plugin
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Returns a Actor's URI from its local $profile
|
||||||
|
* Works both for local and remote users.
|
||||||
|
*
|
||||||
|
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||||
|
* @param Profile $profile Actor's local profile
|
||||||
|
* @return string Actor's URI
|
||||||
|
*/
|
||||||
|
public static function actor_uri($profile)
|
||||||
|
{
|
||||||
|
if ($profile->isLocal()) {
|
||||||
|
return common_root_url()."index.php/user/".$profile->getID();
|
||||||
|
} else {
|
||||||
|
return $profile->getUri();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a Actor's URL from its local $profile
|
||||||
|
* Works both for local and remote users.
|
||||||
|
*
|
||||||
|
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||||
|
* @param Profile $profile Actor's local profile
|
||||||
|
* @return string Actor's URL
|
||||||
|
*/
|
||||||
|
public static function actor_url($profile)
|
||||||
|
{
|
||||||
|
return actor_uri($profile)."/";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function stripUrlPath($url)
|
||||||
|
{
|
||||||
|
$urlParts = parse_url($url);
|
||||||
|
$newUrl = $urlParts['scheme'] . "://" . $urlParts['host'] . "/";
|
||||||
|
return $newUrl;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get remote user's ActivityPub_profile via a identifier
|
* Get remote user's ActivityPub_profile via a identifier
|
||||||
* (https://www.w3.org/TR/activitypub/#obj-id)
|
|
||||||
*
|
*
|
||||||
* @author GNU Social
|
* @author GNU Social
|
||||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||||
* @param string $arg A remote user identifier
|
* @param string $arg A remote user identifier
|
||||||
* @return Activitypub_profile|null Valid profile in success | null otherwise
|
* @return Activitypub_profile|null Valid profile in success | null otherwise
|
||||||
*/
|
*/
|
||||||
protected function pull_remote_profile ($arg)
|
public static function pull_remote_profile($arg)
|
||||||
{
|
{
|
||||||
if (preg_match('!^((?:\w+\.)*\w+@(?:\w+\.)*\w+(?:\w+\-\w+)*\.\w+)$!', $arg)) {
|
if (preg_match('!^((?:\w+\.)*\w+@(?:\w+\.)*\w+(?:\w+\-\w+)*\.\w+)$!', $arg)) {
|
||||||
// webfinger lookup
|
// webfinger lookup
|
||||||
@ -96,29 +131,62 @@ class ActivityPubPlugin extends Plugin
|
|||||||
*/
|
*/
|
||||||
public function onRouterInitialized(URLMapper $m)
|
public function onRouterInitialized(URLMapper $m)
|
||||||
{
|
{
|
||||||
ActivityPubURLMapperOverwrite::overwrite_variable ($m, ':nickname',
|
ActivityPubURLMapperOverwrite::overwrite_variable(
|
||||||
|
$m,
|
||||||
|
'user/:id',
|
||||||
|
['action' => 'showstream'],
|
||||||
|
['id' => '[0-9]+'],
|
||||||
|
'apActorProfile'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Special route for webfinger purposes
|
||||||
|
ActivityPubURLMapperOverwrite::overwrite_variable(
|
||||||
|
$m,
|
||||||
|
':nickname',
|
||||||
['action' => 'showstream'],
|
['action' => 'showstream'],
|
||||||
['nickname' => Nickname::DISPLAY_FMT],
|
['nickname' => Nickname::DISPLAY_FMT],
|
||||||
'apActorProfile');
|
'apActorProfile'
|
||||||
|
);
|
||||||
|
|
||||||
$m->connect (':nickname/liked.json',
|
$m->connect(
|
||||||
['action' => 'apActorLikedCollection'],
|
':nickname/remote_follow',
|
||||||
['nickname' => Nickname::DISPLAY_FMT]);
|
['action' => 'apRemoteFollow'],
|
||||||
|
['nickname' => '[A-Za-z0-9_-]+']
|
||||||
|
);
|
||||||
|
|
||||||
$m->connect (':nickname/followers.json',
|
$m->connect(
|
||||||
|
'activitypub/authorize_follow',
|
||||||
|
['action' => 'apAuthorizeRemoteFollow']
|
||||||
|
);
|
||||||
|
|
||||||
|
$m->connect(
|
||||||
|
'user/:id/liked.json',
|
||||||
|
['action' => 'apActorLiked'],
|
||||||
|
['id' => '[0-9]+']
|
||||||
|
);
|
||||||
|
|
||||||
|
$m->connect(
|
||||||
|
'user/:id/followers.json',
|
||||||
['action' => 'apActorFollowers'],
|
['action' => 'apActorFollowers'],
|
||||||
['nickname' => Nickname::DISPLAY_FMT]);
|
['id' => '[0-9]+']
|
||||||
|
);
|
||||||
|
|
||||||
$m->connect (':nickname/following.json',
|
$m->connect(
|
||||||
|
'user/:id/following.json',
|
||||||
['action' => 'apActorFollowing'],
|
['action' => 'apActorFollowing'],
|
||||||
['nickname' => Nickname::DISPLAY_FMT]);
|
['id' => '[0-9]+']
|
||||||
|
);
|
||||||
|
|
||||||
$m->connect (':nickname/inbox.json',
|
$m->connect(
|
||||||
|
'user/:id/inbox.json',
|
||||||
['action' => 'apActorInbox'],
|
['action' => 'apActorInbox'],
|
||||||
['nickname' => Nickname::DISPLAY_FMT]);
|
['id' => '[0-9]+']
|
||||||
|
);
|
||||||
|
|
||||||
$m->connect ('inbox.json',
|
$m->connect(
|
||||||
array('action' => 'apSharedInbox'));
|
'inbox.json',
|
||||||
|
['action' => 'apSharedInbox']
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -131,7 +199,7 @@ class ActivityPubPlugin extends Plugin
|
|||||||
{
|
{
|
||||||
$versions[] = [ 'name' => 'ActivityPub',
|
$versions[] = [ 'name' => 'ActivityPub',
|
||||||
'version' => GNUSOCIAL_VERSION,
|
'version' => GNUSOCIAL_VERSION,
|
||||||
'author' => 'Daniel Supernault, Diogo Cordeiro',
|
'author' => 'Diogo Cordeiro, Daniel Supernault',
|
||||||
'homepage' => 'https://www.gnu.org/software/social/',
|
'homepage' => 'https://www.gnu.org/software/social/',
|
||||||
'rawdescription' =>
|
'rawdescription' =>
|
||||||
// Todo: Translation
|
// Todo: Translation
|
||||||
@ -145,13 +213,135 @@ class ActivityPubPlugin extends Plugin
|
|||||||
*
|
*
|
||||||
* @return boolean hook true
|
* @return boolean hook true
|
||||||
*/
|
*/
|
||||||
function onCheckSchema ()
|
public function onCheckSchema()
|
||||||
{
|
{
|
||||||
$schema = Schema::get();
|
$schema = Schema::get();
|
||||||
$schema->ensureTable('Activitypub_profile', Activitypub_profile::schemaDef());
|
$schema->ensureTable('Activitypub_profile', Activitypub_profile::schemaDef());
|
||||||
|
$schema->ensureTable('Activitypub_pending_follow_requests', Activitypub_pending_follow_requests::schemaDef());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/********************************************************
|
||||||
|
* Remote Subscription Events *
|
||||||
|
********************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add in an ActivityPub subscribe button
|
||||||
|
*
|
||||||
|
* @author GNU Social
|
||||||
|
* @param type $output
|
||||||
|
* @param type $profile
|
||||||
|
* @return boolean hook false
|
||||||
|
*/
|
||||||
|
public function onStartProfileRemoteSubscribe($output, $profile)
|
||||||
|
{
|
||||||
|
$this->onStartProfileListItemActionElements($output, $profile);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add in an ActivityPub subscribe button
|
||||||
|
*
|
||||||
|
* @author GNU Social
|
||||||
|
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||||
|
* @param Action|Widget $item
|
||||||
|
* @return boolean hook return value
|
||||||
|
* @throws ServerException
|
||||||
|
*/
|
||||||
|
public function onStartProfileListItemActionElements($item)
|
||||||
|
{ // FIXME: This one can accept both an Action and a Widget. Confusing! Refactor to (HTMLOutputter $out, Profile $target)!
|
||||||
|
if (common_logged_in()) {
|
||||||
|
// only non-logged in users get to see the "remote subscribe" form
|
||||||
|
return true;
|
||||||
|
} elseif (!$item->getTarget()->isLocal()) {
|
||||||
|
// we can (for now) only provide remote subscribe forms for local users
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($item instanceof ProfileAction) {
|
||||||
|
$output = $item;
|
||||||
|
} elseif ($item instanceof Widget) {
|
||||||
|
$output = $item->out;
|
||||||
|
} else {
|
||||||
|
// Bad $item class, don't know how to use this for outputting!
|
||||||
|
throw new ServerException('Bad item type for onStartProfileListItemActionElements');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add an ActivityPub subscribe
|
||||||
|
$output->elementStart('li', 'entity_subscribe');
|
||||||
|
$url = common_local_url(
|
||||||
|
'apRemoteFollow',
|
||||||
|
array('nickname' => $item->getTarget()->getNickname())
|
||||||
|
);
|
||||||
|
$output->element(
|
||||||
|
'a',
|
||||||
|
array('href' => $url,
|
||||||
|
'class' => 'entity_remote_subscribe'),
|
||||||
|
// TRANS: Link text for a user to subscribe to an OStatus user.
|
||||||
|
_m('Subscribe')
|
||||||
|
);
|
||||||
|
$output->elementEnd('li');
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add in an ActivityPub subscribe button
|
||||||
|
*
|
||||||
|
* @author GNU Social
|
||||||
|
* @param type $action
|
||||||
|
* @param string $target
|
||||||
|
* @return boolean hook return value
|
||||||
|
*/
|
||||||
|
public function showEntityRemoteSubscribe($action, $target='apRemoteFollow')
|
||||||
|
{
|
||||||
|
if (!$action->getScoped() instanceof Profile) {
|
||||||
|
// early return if we're not logged in
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($action->getScoped()->sameAs($action->getTarget())) {
|
||||||
|
$action->elementStart('div', 'entity_actions');
|
||||||
|
$action->elementStart('p', array('id' => 'entity_remote_subscribe',
|
||||||
|
'class' => 'entity_subscribe'));
|
||||||
|
$action->element(
|
||||||
|
'a',
|
||||||
|
array('href' => common_local_url($target),
|
||||||
|
'class' => 'entity_remote_subscribe'),
|
||||||
|
// TRANS: Link text for link to remote subscribe.
|
||||||
|
_m('Remote')
|
||||||
|
);
|
||||||
|
$action->elementEnd('p');
|
||||||
|
$action->elementEnd('div');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dummy string on AccountProfileBlock stating that ActivityPub is active
|
||||||
|
* this is more of a placeholder for eventual useful stuff ._.
|
||||||
|
*
|
||||||
|
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||||
|
* @return boolean hook return value
|
||||||
|
*/
|
||||||
|
public function onEndShowAccountProfileBlock(HTMLOutputter $out, Profile $profile)
|
||||||
|
{
|
||||||
|
if ($profile->isLocal()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$aprofile = Activitypub_profile::getKV('profile_id', $profile->id);
|
||||||
|
} catch (NoResultException $e) {
|
||||||
|
// Not a remote ActivityPub_profile! Maybe some other network
|
||||||
|
// that has imported a non-local user (e.g.: OStatus)?
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$out->elementStart('dl', 'entity_tags activitypub_profile');
|
||||||
|
$out->element('dt', null, _m('ActivityPub'));
|
||||||
|
$out->element('dd', null, _m('Active'));
|
||||||
|
$out->elementEnd('dl');
|
||||||
|
}
|
||||||
|
|
||||||
/********************************************************
|
/********************************************************
|
||||||
* Discovery Events *
|
* Discovery Events *
|
||||||
********************************************************/
|
********************************************************/
|
||||||
@ -164,13 +354,15 @@ class ActivityPubPlugin extends Plugin
|
|||||||
* @param string $preMention Character(s) that signals a mention ('@', '!'...)
|
* @param string $preMention Character(s) that signals a mention ('@', '!'...)
|
||||||
* @return array The matching IDs (without $preMention) and each respective position in the given string.
|
* @return array The matching IDs (without $preMention) and each respective position in the given string.
|
||||||
*/
|
*/
|
||||||
static function extractWebfingerIds ($text, $preMention='@')
|
public static function extractWebfingerIds($text, $preMention='@')
|
||||||
{
|
{
|
||||||
$wmatches = array();
|
$wmatches = array();
|
||||||
$result = preg_match_all ('/(?<!\S)'.preg_quote ($preMention, '/').'('.Nickname::WEBFINGER_FMT.')/',
|
$result = preg_match_all(
|
||||||
|
'/(?<!\S)'.preg_quote($preMention, '/').'('.Nickname::WEBFINGER_FMT.')/',
|
||||||
$text,
|
$text,
|
||||||
$wmatches,
|
$wmatches,
|
||||||
PREG_OFFSET_CAPTURE);
|
PREG_OFFSET_CAPTURE
|
||||||
|
);
|
||||||
if ($result === false) {
|
if ($result === false) {
|
||||||
common_log(LOG_ERR, __METHOD__ . ': Error parsing webfinger IDs from text (preg_last_error=='.preg_last_error().').');
|
common_log(LOG_ERR, __METHOD__ . ': Error parsing webfinger IDs from text (preg_last_error=='.preg_last_error().').');
|
||||||
} elseif (count($wmatches)) {
|
} elseif (count($wmatches)) {
|
||||||
@ -187,15 +379,17 @@ class ActivityPubPlugin extends Plugin
|
|||||||
* @param string $preMention Character(s) that signals a mention ('@', '!'...)
|
* @param string $preMention Character(s) that signals a mention ('@', '!'...)
|
||||||
* @return array The matching URLs (without @ or acct:) and each respective position in the given string.
|
* @return array The matching URLs (without @ or acct:) and each respective position in the given string.
|
||||||
*/
|
*/
|
||||||
static function extractUrlMentions ($text, $preMention='@')
|
public static function extractUrlMentions($text, $preMention='@')
|
||||||
{
|
{
|
||||||
$wmatches = array();
|
$wmatches = array();
|
||||||
// In the regexp below we need to match / _before_ URL_REGEX_VALID_PATH_CHARS because it otherwise gets merged
|
// In the regexp below we need to match / _before_ URL_REGEX_VALID_PATH_CHARS because it otherwise gets merged
|
||||||
// with the TLD before (but / is in URL_REGEX_VALID_PATH_CHARS anyway, it's just its positioning that is important)
|
// with the TLD before (but / is in URL_REGEX_VALID_PATH_CHARS anyway, it's just its positioning that is important)
|
||||||
$result = preg_match_all ('/(?:^|\s+)'.preg_quote ($preMention, '/').'('.URL_REGEX_DOMAIN_NAME.'(?:\/['.URL_REGEX_VALID_PATH_CHARS.']*)*)/',
|
$result = preg_match_all(
|
||||||
|
'/(?:^|\s+)'.preg_quote($preMention, '/').'('.URL_REGEX_DOMAIN_NAME.'(?:\/['.URL_REGEX_VALID_PATH_CHARS.']*)*)/',
|
||||||
$text,
|
$text,
|
||||||
$wmatches,
|
$wmatches,
|
||||||
PREG_OFFSET_CAPTURE);
|
PREG_OFFSET_CAPTURE
|
||||||
|
);
|
||||||
if ($result === false) {
|
if ($result === false) {
|
||||||
common_log(LOG_ERR, __METHOD__ . ': Error parsing profile URL mentions from text (preg_last_error=='.preg_last_error().').');
|
common_log(LOG_ERR, __METHOD__ . ': Error parsing profile URL mentions from text (preg_last_error=='.preg_last_error().').');
|
||||||
} elseif (count($wmatches)) {
|
} elseif (count($wmatches)) {
|
||||||
@ -216,7 +410,7 @@ class ActivityPubPlugin extends Plugin
|
|||||||
* @param array &$mention in/out param: set of found mentions
|
* @param array &$mention in/out param: set of found mentions
|
||||||
* @return boolean hook return value
|
* @return boolean hook return value
|
||||||
*/
|
*/
|
||||||
function onEndFindMentions(Profile $sender, $text, &$mentions)
|
public function onEndFindMentions(Profile $sender, $text, &$mentions)
|
||||||
{
|
{
|
||||||
$matches = array();
|
$matches = array();
|
||||||
|
|
||||||
@ -300,7 +494,7 @@ class ActivityPubPlugin extends Plugin
|
|||||||
* @param Profile &$profile
|
* @param Profile &$profile
|
||||||
* @return hook return code
|
* @return hook return code
|
||||||
*/
|
*/
|
||||||
function onStartCommandGetProfile ($command, $arg, &$profile)
|
public function onStartCommandGetProfile($command, $arg, &$profile)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$aprofile = $this->pull_remote_profile($arg);
|
$aprofile = $this->pull_remote_profile($arg);
|
||||||
@ -322,7 +516,7 @@ class ActivityPubPlugin extends Plugin
|
|||||||
* @param string $uri in/out
|
* @param string $uri in/out
|
||||||
* @return mixed hook return code
|
* @return mixed hook return code
|
||||||
*/
|
*/
|
||||||
function onStartGetProfileUri ($profile, &$uri)
|
public function onStartGetProfileUri($profile, &$uri)
|
||||||
{
|
{
|
||||||
$aprofile = Activitypub_profile::getKV('profile_id', $profile->id);
|
$aprofile = Activitypub_profile::getKV('profile_id', $profile->id);
|
||||||
if ($aprofile instanceof Activitypub_profile) {
|
if ($aprofile instanceof Activitypub_profile) {
|
||||||
@ -332,33 +526,6 @@ class ActivityPubPlugin extends Plugin
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dummy string on AccountProfileBlock stating that ActivityPub is active
|
|
||||||
* this is more of a placeholder for eventual useful stuff ._.
|
|
||||||
*
|
|
||||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
function onEndShowAccountProfileBlock (HTMLOutputter $out, Profile $profile)
|
|
||||||
{
|
|
||||||
if ($profile->isLocal()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
$aprofile = Activitypub_profile::getKV ('profile_id', $profile->id);
|
|
||||||
} catch (NoResultException $e) {
|
|
||||||
// Not a remote ActivityPub_profile! Maybe some other network
|
|
||||||
// that has imported a non-local user (e.g.: OStatus)?
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$out->elementStart('dl', 'entity_tags activitypub_profile');
|
|
||||||
$out->element('dt', null, _m('ActivityPub'));
|
|
||||||
$out->element('dd', null, _m('Active'));
|
|
||||||
$out->elementEnd('dl');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Profile from URI.
|
* Profile from URI.
|
||||||
*
|
*
|
||||||
@ -368,7 +535,7 @@ class ActivityPubPlugin extends Plugin
|
|||||||
* @param Profile &$profile in/out param: Profile got from URI
|
* @param Profile &$profile in/out param: Profile got from URI
|
||||||
* @return mixed hook return code
|
* @return mixed hook return code
|
||||||
*/
|
*/
|
||||||
function onStartGetProfileFromURI ($uri, &$profile)
|
public function onStartGetProfileFromURI($uri, &$profile)
|
||||||
{
|
{
|
||||||
// Don't want to do Web-based discovery on our own server,
|
// Don't want to do Web-based discovery on our own server,
|
||||||
// so we check locally first. This duplicates the functionality
|
// so we check locally first. This duplicates the functionality
|
||||||
@ -411,7 +578,7 @@ class ActivityPubPlugin extends Plugin
|
|||||||
* @return hook return value
|
* @return hook return value
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
function onEndSubscribe (Profile $profile, Profile $other)
|
public function onStartSubscribe(Profile $profile, Profile $other)
|
||||||
{
|
{
|
||||||
if (!$profile->isLocal() || $other->isLocal()) {
|
if (!$profile->isLocal() || $other->isLocal()) {
|
||||||
return true;
|
return true;
|
||||||
@ -438,7 +605,7 @@ class ActivityPubPlugin extends Plugin
|
|||||||
* @param Profile $other
|
* @param Profile $other
|
||||||
* @return hook return value
|
* @return hook return value
|
||||||
*/
|
*/
|
||||||
function onEndUnsubscribe (Profile $profile, Profile $other)
|
public function onStartUnsubscribe(Profile $profile, Profile $other)
|
||||||
{
|
{
|
||||||
if (!$profile->isLocal() || $other->isLocal()) {
|
if (!$profile->isLocal() || $other->isLocal()) {
|
||||||
return true;
|
return true;
|
||||||
@ -465,7 +632,7 @@ class ActivityPubPlugin extends Plugin
|
|||||||
* @param Notice $notice Notice being favored
|
* @param Notice $notice Notice being favored
|
||||||
* @return hook return value
|
* @return hook return value
|
||||||
*/
|
*/
|
||||||
function onEndFavorNotice (Profile $profile, Notice $notice)
|
public function onEndFavorNotice(Profile $profile, Notice $notice)
|
||||||
{
|
{
|
||||||
// Only distribute local users' favor actions, remote users
|
// Only distribute local users' favor actions, remote users
|
||||||
// will have already distributed theirs.
|
// will have already distributed theirs.
|
||||||
@ -524,7 +691,7 @@ class ActivityPubPlugin extends Plugin
|
|||||||
* @param Notice $notice Notice being favored
|
* @param Notice $notice Notice being favored
|
||||||
* @return hook return value
|
* @return hook return value
|
||||||
*/
|
*/
|
||||||
function onEndDisfavorNotice (Profile $profile, Notice $notice)
|
public function onEndDisfavorNotice(Profile $profile, Notice $notice)
|
||||||
{
|
{
|
||||||
// Only distribute local users' favor actions, remote users
|
// Only distribute local users' favor actions, remote users
|
||||||
// will have already distributed theirs.
|
// will have already distributed theirs.
|
||||||
@ -581,7 +748,7 @@ class ActivityPubPlugin extends Plugin
|
|||||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||||
* @return boolean hook flag
|
* @return boolean hook flag
|
||||||
*/
|
*/
|
||||||
public function onEndDeleteOwnNotice ($user, $notice)
|
public function onStartDeleteOwnNotice($user, $notice)
|
||||||
{
|
{
|
||||||
$profile = $user->getProfile();
|
$profile = $user->getProfile();
|
||||||
|
|
||||||
@ -634,7 +801,7 @@ class ActivityPubPlugin extends Plugin
|
|||||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||||
* @return boolean hook flag
|
* @return boolean hook flag
|
||||||
*/
|
*/
|
||||||
function onStartNoticeDistribute ($notice)
|
public function onStartNoticeDistribute($notice)
|
||||||
{
|
{
|
||||||
assert($notice->id > 0); // Ignore if not a valid notice
|
assert($notice->id > 0); // Ignore if not a valid notice
|
||||||
|
|
||||||
@ -674,8 +841,10 @@ class ActivityPubPlugin extends Plugin
|
|||||||
|
|
||||||
// Ignore for activity/non-post-verb notices
|
// Ignore for activity/non-post-verb notices
|
||||||
if (method_exists('ActivityUtils', 'compareVerbs')) {
|
if (method_exists('ActivityUtils', 'compareVerbs')) {
|
||||||
$is_post_verb = ActivityUtils::compareVerbs ($notice->verb,
|
$is_post_verb = ActivityUtils::compareVerbs(
|
||||||
array (ActivityVerb::POST));
|
$notice->verb,
|
||||||
|
array(ActivityVerb::POST)
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
$is_post_verb = ($notice->verb == ActivityVerb::POST ? true : false);
|
$is_post_verb = ($notice->verb == ActivityVerb::POST ? true : false);
|
||||||
}
|
}
|
||||||
@ -724,7 +893,7 @@ class ActivityPubPlugin extends Plugin
|
|||||||
* @param string out &$title
|
* @param string out &$title
|
||||||
* @return mixed hook return code
|
* @return mixed hook return code
|
||||||
*/
|
*/
|
||||||
function onStartNoticeSourceLink ($notice, &$name, &$url, &$title)
|
public function onStartNoticeSourceLink($notice, &$name, &$url, &$title)
|
||||||
{
|
{
|
||||||
// If we don't handle this, keep the event handler going
|
// If we don't handle this, keep the event handler going
|
||||||
if (!in_array($notice->source, array('ActivityPub', 'share'))) {
|
if (!in_array($notice->source, array('ActivityPub', 'share'))) {
|
||||||
@ -755,12 +924,50 @@ class ActivityPubPlugin extends Plugin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin return handler
|
||||||
|
*/
|
||||||
|
class ActivityPubReturn
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Return a valid answer
|
||||||
|
*
|
||||||
|
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||||
|
* @param array $res
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function answer($res)
|
||||||
|
{
|
||||||
|
header('Content-Type: application/activity+json');
|
||||||
|
echo json_encode($res, JSON_UNESCAPED_SLASHES | (isset($_GET["pretty"]) ? JSON_PRETTY_PRINT : null));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an error
|
||||||
|
*
|
||||||
|
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||||
|
* @param string $m
|
||||||
|
* @param int32 $code
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function error($m, $code = 500)
|
||||||
|
{
|
||||||
|
http_response_code($code);
|
||||||
|
header('Content-Type: application/activity+json');
|
||||||
|
$res[] = Activitypub_error::error_message_to_array($m);
|
||||||
|
echo json_encode($res, JSON_UNESCAPED_SLASHES);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overwrites variables in URL-mapping
|
* Overwrites variables in URL-mapping
|
||||||
*/
|
*/
|
||||||
class ActivityPubURLMapperOverwrite extends URLMapper
|
class ActivityPubURLMapperOverwrite extends URLMapper
|
||||||
{
|
{
|
||||||
static function overwrite_variable ($m, $path, $args, $paramPatterns, $newaction) {
|
public static function overwrite_variable($m, $path, $args, $paramPatterns, $newaction)
|
||||||
|
{
|
||||||
$mimes = [
|
$mimes = [
|
||||||
'application/activity+json',
|
'application/activity+json',
|
||||||
'application/ld+json',
|
'application/ld+json',
|
||||||
@ -780,40 +987,3 @@ class ActivityPubURLMapperOverwrite extends URLMapper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Plugin return handler
|
|
||||||
*/
|
|
||||||
class ActivityPubReturn
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Return a valid answer
|
|
||||||
*
|
|
||||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
|
||||||
* @param array $res
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
static function answer ($res)
|
|
||||||
{
|
|
||||||
header ('Content-Type: application/activity+json');
|
|
||||||
echo json_encode ($res, JSON_UNESCAPED_SLASHES | (isset ($_GET["pretty"]) ? JSON_PRETTY_PRINT : null));
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return an error
|
|
||||||
*
|
|
||||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
|
||||||
* @param string $m
|
|
||||||
* @param int32 $code
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
static function error ($m, $code = 500)
|
|
||||||
{
|
|
||||||
http_response_code ($code);
|
|
||||||
header ('Content-Type: application/activity+json');
|
|
||||||
$res[] = Activitypub_error::error_message_to_array ($m);
|
|
||||||
echo json_encode ($res, JSON_UNESCAPED_SLASHES);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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.
|
Please note we have a code of conduct, please follow it in all your interactions with the project.
|
||||||
|
|
||||||
# Coding Style
|
# Coding Style
|
||||||
- We are using [K&R Variant: Linux kernel](https://en.wikipedia.org/wiki/Indentation_style#Variant:_Linux_kernel) with spaces before every `(`.
|
- We follow every [PSR-2](https://www.php-fig.org/psr/psr-2/) ...
|
||||||
- Every function has a docblock explaining what it does and stating the author, parameters, types, return and exceptions
|
- ... except camelCase, that's too bad, we use snake_case
|
||||||
- We use snake_case
|
|
||||||
|
|
||||||
## Pull Request Process
|
## Merge Request Process
|
||||||
|
|
||||||
1. Ensure you strip any trailing spaces off
|
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
|
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
|
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
|
of using the software, and if you do not wish to share your
|
||||||
modifications, *YOU MAY NOT USE THIS PLUGIN*.
|
modifications, *YOU MAY NOT USE THIS PLUGIN*.
|
||||||
|
|
||||||
|
@ -51,13 +51,11 @@ class apActorFollowersAction extends ManagedAction
|
|||||||
*/
|
*/
|
||||||
protected function handle()
|
protected function handle()
|
||||||
{
|
{
|
||||||
$nickname = $this->trimmed ('nickname');
|
|
||||||
try {
|
try {
|
||||||
$user = User::getByNickname ($nickname);
|
$profile = Profile::getByID($this->trimmed('id'));
|
||||||
$profile = $user->getProfile ();
|
$url = ActivityPubPlugin::actor_url($profile);
|
||||||
$url = $profile->profileurl;
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
ActivityPubReturn::error ('Invalid username.');
|
ActivityPubReturn::error('Invalid Actor URI.', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($_GET["page"])) {
|
if (!isset($_GET["page"])) {
|
||||||
|
@ -51,13 +51,11 @@ class apActorFollowingAction extends ManagedAction
|
|||||||
*/
|
*/
|
||||||
protected function handle()
|
protected function handle()
|
||||||
{
|
{
|
||||||
$nickname = $this->trimmed ('nickname');
|
|
||||||
try {
|
try {
|
||||||
$user = User::getByNickname ($nickname);
|
$profile = Profile::getByID($this->trimmed('id'));
|
||||||
$profile = $user->getProfile ();
|
$url = ActivityPubPlugin::actor_url($profile);
|
||||||
$url = $profile->profileurl;
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
ActivityPubReturn::error ('Invalid username.');
|
ActivityPubReturn::error('Invalid Actor URI.', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($_GET["page"])) {
|
if (!isset($_GET["page"])) {
|
||||||
|
@ -51,13 +51,10 @@ class apActorInboxAction extends ManagedAction
|
|||||||
*/
|
*/
|
||||||
protected function handle()
|
protected function handle()
|
||||||
{
|
{
|
||||||
$nickname = $this->trimmed ('nickname');
|
|
||||||
try {
|
try {
|
||||||
$user = User::getByNickname ($nickname);
|
$profile = Profile::getByID($this->trimmed('id'));
|
||||||
$profile = $user->getProfile ();
|
|
||||||
$url = $profile->profileurl;
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
ActivityPubReturn::error ("Invalid username.");
|
ActivityPubReturn::error('Invalid Actor URI.', 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
@ -87,7 +84,12 @@ class apActorInboxAction extends ManagedAction
|
|||||||
ActivityPubReturn::error("Invalid Actor.", 404);
|
ActivityPubReturn::error("Invalid Actor.", 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
$to_profiles = array ($user);
|
// Public To:
|
||||||
|
$public_to = array("https://www.w3.org/ns/activitystreams#Public",
|
||||||
|
"Public",
|
||||||
|
"as:Public");
|
||||||
|
|
||||||
|
$to_profiles = array($profile);
|
||||||
|
|
||||||
// Process request
|
// Process request
|
||||||
switch ($data->type) {
|
switch ($data->type) {
|
||||||
@ -109,6 +111,12 @@ class apActorInboxAction extends ManagedAction
|
|||||||
case "Announce":
|
case "Announce":
|
||||||
require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Announce.php";
|
require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Announce.php";
|
||||||
break;
|
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:
|
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,8 +19,8 @@
|
|||||||
*
|
*
|
||||||
* @category Plugin
|
* @category Plugin
|
||||||
* @package GNUsocial
|
* @package GNUsocial
|
||||||
* @author Daniel Supernault <danielsupernault@gmail.com>
|
|
||||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||||
|
* @author Daniel Supernault <danielsupernault@gmail.com>
|
||||||
* @copyright 2018 Free Software Foundation http://fsf.org
|
* @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
|
* @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/
|
* @link https://www.gnu.org/software/social/
|
||||||
@ -34,7 +34,6 @@ if (!defined ('GNUSOCIAL')) {
|
|||||||
*
|
*
|
||||||
* @category Plugin
|
* @category Plugin
|
||||||
* @package GNUsocial
|
* @package GNUsocial
|
||||||
* @author Daniel Supernault <danielsupernault@gmail.com>
|
|
||||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
* @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
|
* @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/
|
* @link http://www.gnu.org/software/social/
|
||||||
@ -47,19 +46,29 @@ class apActorProfileAction extends ManagedAction
|
|||||||
/**
|
/**
|
||||||
* Handle the Actor Profile request
|
* Handle the Actor Profile request
|
||||||
*
|
*
|
||||||
* @author Daniel Supernault <danielsupernault@gmail.com>
|
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
protected function handle()
|
protected function handle()
|
||||||
{
|
{
|
||||||
$nickname = $this->trimmed ('nickname');
|
if (!empty($id = $this->trimmed('id'))) {
|
||||||
try {
|
try {
|
||||||
$user = User::getByNickname ($nickname);
|
$profile = Profile::getByID($id);
|
||||||
$profile = $user->getProfile ();
|
} catch (Exception $e) {
|
||||||
|
ActivityPubReturn::error('Invalid Actor URI.', 404);
|
||||||
}
|
}
|
||||||
catch (Exception $e) {
|
unset($id);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
$profile = User::getByNickname($this->trimmed('nickname'))->getProfile();
|
||||||
|
} catch (Exception $e) {
|
||||||
ActivityPubReturn::error('Invalid username.', 404);
|
ActivityPubReturn::error('Invalid username.', 404);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$profile->isLocal()) {
|
||||||
|
ActivityPubReturn::error("This is not a local user.");
|
||||||
|
}
|
||||||
|
|
||||||
$res = Activitypub_profile::profile_to_array($profile);
|
$res = Activitypub_profile::profile_to_array($profile);
|
||||||
|
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
@ -69,7 +69,6 @@ class apSharedInboxAction extends ManagedAction
|
|||||||
}
|
}
|
||||||
|
|
||||||
$discovery = new Activitypub_explorer;
|
$discovery = new Activitypub_explorer;
|
||||||
|
|
||||||
// Get valid Actor object
|
// Get valid Actor object
|
||||||
try {
|
try {
|
||||||
$actor_profile = $discovery->lookup($data->actor);
|
$actor_profile = $discovery->lookup($data->actor);
|
||||||
@ -77,43 +76,19 @@ class apSharedInboxAction extends ManagedAction
|
|||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
ActivityPubReturn::error("Invalid Actor.", 404);
|
ActivityPubReturn::error("Invalid Actor.", 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
unset($discovery);
|
unset($discovery);
|
||||||
|
|
||||||
// Public To:
|
// Public To:
|
||||||
$public_to = array ("https://www.w3.org/ns/activitystreams#Public",
|
$public_to = ["https://www.w3.org/ns/activitystreams#Public",
|
||||||
"Public",
|
"Public",
|
||||||
"as:Public");
|
"as:Public"
|
||||||
|
];
|
||||||
|
|
||||||
|
$to_profiles = "https://www.w3.org/ns/activitystreams#Public";
|
||||||
|
|
||||||
// Process request
|
// Process request
|
||||||
switch ($data->type) {
|
switch ($data->type) {
|
||||||
case "Create":
|
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";
|
require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Create.php";
|
||||||
break;
|
break;
|
||||||
case "Follow":
|
case "Follow":
|
||||||
@ -131,6 +106,12 @@ class apSharedInboxAction extends ManagedAction
|
|||||||
case "Delete":
|
case "Delete":
|
||||||
require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Delete.php";
|
require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Delete.php";
|
||||||
break;
|
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:
|
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;
|
||||||
|
}
|
@ -30,7 +30,7 @@ if (!defined ('GNUSOCIAL')) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Notice::getByUri ($data->object)->repeat ($actor_profile, "ActivityPub");
|
Notice::getByUri($data->object->id)->repeat($actor_profile, "ActivityPub");
|
||||||
ActivityPubReturn::answer("Notice repeated successfully.");
|
ActivityPubReturn::answer("Notice repeated successfully.");
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
ActivityPubReturn::error($e->getMessage(), 403);
|
ActivityPubReturn::error($e->getMessage(), 403);
|
||||||
|
@ -32,6 +32,9 @@ if (!defined ('GNUSOCIAL')) {
|
|||||||
$valid_object_types = array("Note");
|
$valid_object_types = array("Note");
|
||||||
|
|
||||||
// Validate data
|
// Validate data
|
||||||
|
if (!isset($data->id)) {
|
||||||
|
ActivityPubReturn::error("Id not specified.");
|
||||||
|
}
|
||||||
if (!(isset($data->object->type) && in_array($data->object->type, $valid_object_types))) {
|
if (!(isset($data->object->type) && in_array($data->object->type, $valid_object_types))) {
|
||||||
ActivityPubReturn::error("Invalid Object type.");
|
ActivityPubReturn::error("Invalid Object type.");
|
||||||
}
|
}
|
||||||
@ -43,6 +46,9 @@ if (!isset ($data->object->url)) {
|
|||||||
} elseif (!filter_var($data->object->url, FILTER_VALIDATE_URL)) {
|
} elseif (!filter_var($data->object->url, FILTER_VALIDATE_URL)) {
|
||||||
ActivityPubReturn::error("Invalid Object Url.");
|
ActivityPubReturn::error("Invalid Object Url.");
|
||||||
}
|
}
|
||||||
|
if (!isset($data->object->to)) {
|
||||||
|
ActivityPubReturn::error("Object To was not specified.");
|
||||||
|
}
|
||||||
|
|
||||||
$content = $data->object->content;
|
$content = $data->object->content;
|
||||||
|
|
||||||
@ -68,9 +74,34 @@ if (isset ($data->object->reply_to)) {
|
|||||||
|
|
||||||
$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)
|
$discovery = new Activitypub_explorer;
|
||||||
{
|
if ($to_profiles == "https://www.w3.org/ns/activitystreams#Public") {
|
||||||
$act->context->attention[$to->getUri ()] = "http://activitystrea.ms/schema/1.0/person";
|
$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)
|
// Reject notice if it is too long (without the HTML)
|
||||||
@ -79,7 +110,7 @@ if (Notice::contentTooLong ($content)) {
|
|||||||
ActivityPubReturn::error("That's too long. Maximum notice size is %d character.");
|
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
|
// $options gets filled with possible scoping settings
|
||||||
ToSelector::fillActivity($this, $act, $options);
|
ToSelector::fillActivity($this, $act, $options);
|
||||||
|
|
||||||
@ -91,12 +122,11 @@ $actobj->content = common_render_content ($content, $actor_profile, $reply_to);
|
|||||||
$act->objects[] = $actobj;
|
$act->objects[] = $actobj;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$res = array ("@context" => "https://www.w3.org/ns/activitystreams",
|
$res = Activitypub_create::create_to_array(
|
||||||
"id" => $data->id,
|
$data->id,
|
||||||
"url" => $data->object->url,
|
$data->actor,
|
||||||
"type" => "Create",
|
Activitypub_notice::notice_to_array(Notice::saveActivity($act, $actor_profile, $options))
|
||||||
"actor" => $data->actor,
|
);
|
||||||
"object" => Activitypub_notice::notice_to_array (Notice::saveActivity ($act, $actor_profile, $options)));
|
|
||||||
ActivityPubReturn::answer($res);
|
ActivityPubReturn::answer($res);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
ActivityPubReturn::error($e->getMessage());
|
ActivityPubReturn::error($e->getMessage());
|
||||||
|
@ -30,12 +30,10 @@ if (!defined ('GNUSOCIAL')) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Notice::getByUri ($data->object)->deleteAs ($actor_profile);
|
$notice = Notice::getByUri($data->object->id);
|
||||||
$res = array ("@context" => "https://www.w3.org/ns/activitystreams",
|
$notice_to_array = Activitypub_notice::notice_to_array($notice);
|
||||||
"type" => "Delete",
|
$notice->deleteAs($actor_profile);
|
||||||
"actor" => $data->actor,
|
ActivityPubReturn::answer(Activitypub_delete::delete_to_array($notice_to_array));
|
||||||
"object" => $data->object);
|
|
||||||
ActivityPubReturn::answer ($res);
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
ActivityPubReturn::error($e->getMessage(), 403);
|
ActivityPubReturn::error($e->getMessage(), 403);
|
||||||
}
|
}
|
||||||
|
@ -45,11 +45,7 @@ try {
|
|||||||
try {
|
try {
|
||||||
if (!Subscription::exists($actor_profile, $object_profile)) {
|
if (!Subscription::exists($actor_profile, $object_profile)) {
|
||||||
Subscription::start($actor_profile, $object_profile);
|
Subscription::start($actor_profile, $object_profile);
|
||||||
$res = array ("@context" => "https://www.w3.org/ns/activitystreams",
|
ActivityPubReturn::answer(Activitypub_accept::accept_to_array(Activitypub_follow::follow_to_array($data->actor, $data->object)));
|
||||||
"type" => "Follow",
|
|
||||||
"actor" => $data->actor,
|
|
||||||
"object" => $data->object);
|
|
||||||
ActivityPubReturn::answer ($res);
|
|
||||||
} else {
|
} else {
|
||||||
ActivityPubReturn::error("Already following.", 409);
|
ActivityPubReturn::error("Already following.", 409);
|
||||||
}
|
}
|
||||||
|
@ -29,13 +29,13 @@ if (!defined ('GNUSOCIAL')) {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isset($data->object->id)) {
|
||||||
|
ActivityPubReturn::error("Id not specified.");
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Fave::addNew ($actor_profile, Notice::getByUri ($data->object));
|
Fave::addNew($actor_profile, Notice::getByUri($data->object->id));
|
||||||
$res = array ("@context" => "https://www.w3.org/ns/activitystreams",
|
ActivityPubReturn::answer(Activitypub_like::like_to_array(Activitypub_notice::notice_to_array($data->actor, json_decode($data->object))));
|
||||||
"type" => "Like",
|
|
||||||
"actor" => $data->actor,
|
|
||||||
"object" => $data->object);
|
|
||||||
ActivityPubReturn::answer ($res);
|
|
||||||
} catch (Exception $e) {
|
} 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
|
@ -38,11 +38,21 @@ switch ($data->object->type) {
|
|||||||
case "Like":
|
case "Like":
|
||||||
try {
|
try {
|
||||||
// Validate data
|
// Validate data
|
||||||
if (!isset ($data->object->object)) {
|
if (!isset($data->object->object->id)) {
|
||||||
ActivityPubReturn::error ("Object Notice URL was not specified.");
|
ActivityPubReturn::error("Notice ID was not specified.");
|
||||||
}
|
}
|
||||||
Fave::removeEntry ($actor_profile, Notice::getByUri ($data->object->object));
|
Fave::removeEntry($actor_profile, Notice::getByUri($data->object->object->id));
|
||||||
ActivityPubReturn::answer ("Notice disfavorited successfully.");
|
// 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) {
|
} catch (Exception $e) {
|
||||||
ActivityPubReturn::error($e->getMessage(), 403);
|
ActivityPubReturn::error($e->getMessage(), 403);
|
||||||
}
|
}
|
||||||
@ -60,16 +70,22 @@ case "Follow":
|
|||||||
ActivityPubReturn::error("Invalid Object Actor URL.", 404);
|
ActivityPubReturn::error("Invalid Object Actor URL.", 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
if (Subscription::exists($actor_profile, $object_profile)) {
|
if (Subscription::exists($actor_profile, $object_profile)) {
|
||||||
Subscription::cancel($actor_profile, $object_profile);
|
Subscription::cancel($actor_profile, $object_profile);
|
||||||
ActivityPubReturn::answer ("You are no longer following this person.");
|
// 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 {
|
} else {
|
||||||
ActivityPubReturn::error("You are not following this person already.", 409);
|
ActivityPubReturn::error("You are not following this person already.", 409);
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
|
||||||
ActivityPubReturn::error ("Invalid Object Actor URL.", 404);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ActivityPubReturn::error("Invalid object type.");
|
ActivityPubReturn::error("Invalid object type.");
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -65,8 +65,7 @@ class Activitypub_attachment extends Managed_DataObject
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Image
|
// Image
|
||||||
if (substr ($res["mimetype"], 0, 5) == "image")
|
if (substr($res["mimetype"], 0, 5) == "image") {
|
||||||
{
|
|
||||||
$res["meta"]= [
|
$res["meta"]= [
|
||||||
'width' => $attachment->width,
|
'width' => $attachment->width,
|
||||||
'height' => $attachment->height
|
'height' => $attachment->height
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -64,22 +64,22 @@ class Activitypub_notice extends Managed_DataObject
|
|||||||
}
|
}
|
||||||
|
|
||||||
$to = array();
|
$to = array();
|
||||||
foreach ($notice->getAttentionProfileIDs () as $to_id) {
|
foreach ($notice->getAttentionProfiles() as $to_profile) {
|
||||||
$to[] = Profile::getById ($to_id)->getUri ();
|
$to[] = $to_profile->getUri();
|
||||||
}
|
}
|
||||||
if (!is_null($to)) {
|
if (empty($to)) {
|
||||||
$to = array("https://www.w3.org/ns/activitystreams#Public");
|
$to = array("https://www.w3.org/ns/activitystreams#Public");
|
||||||
}
|
}
|
||||||
|
|
||||||
$item = [
|
$item = [
|
||||||
'id' => $notice->getUrl (),
|
'id' => $notice->getUri(),
|
||||||
'type' => 'Notice',
|
'type' => 'Note',
|
||||||
'actor' => $notice->getProfile()->getUrl(),
|
'actor' => $notice->getProfile()->getUrl(),
|
||||||
'published' => $notice->getCreated(),
|
'published' => $notice->getCreated(),
|
||||||
'to' => $to,
|
'to' => $to,
|
||||||
'content' => $notice->getContent(),
|
'content' => $notice->getContent(),
|
||||||
'url' => $notice->getUrl(),
|
'url' => $notice->getUrl(),
|
||||||
'reply_to' => empty($notice->reply_to) ? null : Notice::getById($notice->reply_to)->getUrl (),
|
'reply_to' => empty($notice->reply_to) ? null : Notice::getById($notice->reply_to)->getUri(),
|
||||||
'is_local' => $notice->isLocal(),
|
'is_local' => $notice->isLocal(),
|
||||||
'conversation' => intval($notice->conversation),
|
'conversation' => intval($notice->conversation),
|
||||||
'attachment' => $attachments,
|
'attachment' => $attachments,
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
@ -35,7 +35,6 @@ if (!defined ('GNUSOCIAL')) {
|
|||||||
* @category Plugin
|
* @category Plugin
|
||||||
* @package GNUsocial
|
* @package GNUsocial
|
||||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
* @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
|
* @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/
|
* @link http://www.gnu.org/software/social/
|
||||||
*/
|
*/
|
||||||
@ -51,7 +50,7 @@ class Activitypub_profile extends Profile
|
|||||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||||
* @return array array of column definitions
|
* @return array array of column definitions
|
||||||
*/
|
*/
|
||||||
static function schemaDef ()
|
public static function schemaDef()
|
||||||
{
|
{
|
||||||
return array(
|
return array(
|
||||||
'fields' => array(
|
'fields' => array(
|
||||||
@ -82,7 +81,8 @@ class Activitypub_profile extends Profile
|
|||||||
*/
|
*/
|
||||||
public static function profile_to_array($profile)
|
public static function profile_to_array($profile)
|
||||||
{
|
{
|
||||||
$url = $profile->getURL ();
|
$uri = ActivityPubPlugin::actor_uri($profile);
|
||||||
|
$id = $profile->getID();
|
||||||
$res = [
|
$res = [
|
||||||
'@context' => [
|
'@context' => [
|
||||||
"https://www.w3.org/ns/activitystreams",
|
"https://www.w3.org/ns/activitystreams",
|
||||||
@ -90,29 +90,35 @@ class Activitypub_profile extends Profile
|
|||||||
"@language" => "en"
|
"@language" => "en"
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'id' => $profile->getID (),
|
'id' => $uri,
|
||||||
'type' => 'Person',
|
'type' => 'Person',
|
||||||
'nickname' => $profile->getNickname (),
|
'preferredUsername' => $profile->getNickname(),
|
||||||
'is_local' => $profile->isLocal(),
|
'is_local' => $profile->isLocal(),
|
||||||
'inbox' => "{$url}/inbox.json",
|
'inbox' => common_local_url("apActorInbox", array("id" => $id)),
|
||||||
'sharedInbox' => common_root_url ()."inbox.json",
|
'name' => $profile->getFullname(),
|
||||||
'outbox' => "{$url}/outbox.json",
|
'followers' => common_local_url("apActorFollowers", array("id" => $id)),
|
||||||
'display_name' => $profile->getFullname (),
|
|
||||||
'followers' => "{$url}/followers.json",
|
|
||||||
'followers_count' => $profile->subscriberCount(),
|
'followers_count' => $profile->subscriberCount(),
|
||||||
'following' => "{$url}/following.json",
|
'following' => common_local_url("apActorFollowing", array("id" => $id)),
|
||||||
'following_count' => $profile->subscriptionCount(),
|
'following_count' => $profile->subscriptionCount(),
|
||||||
'liked' => "{$url}/liked.json",
|
'liked' => common_local_url("apActorLiked", array("id" => $id)),
|
||||||
'liked_count' => Fave::countByProfile($profile),
|
'liked_count' => Fave::countByProfile($profile),
|
||||||
'summary' => ($desc = $profile->getDescription()) == null ? "" : $desc,
|
'summary' => ($desc = $profile->getDescription()) == null ? "" : $desc,
|
||||||
'url' => $profile->getURL (),
|
'icon' => [
|
||||||
'avatar' => [
|
|
||||||
'type' => 'Image',
|
'type' => 'Image',
|
||||||
'width' => 96,
|
'width' => AVATAR_PROFILE_SIZE,
|
||||||
'height' => 96,
|
'height' => AVATAR_PROFILE_SIZE,
|
||||||
'url' => $profile->avatarUrl(AVATAR_PROFILE_SIZE)
|
'url' => $profile->avatarUrl(AVATAR_PROFILE_SIZE)
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
return $res;
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,12 +135,12 @@ class Activitypub_profile extends Profile
|
|||||||
|
|
||||||
$profile->created = $this->created = $this->modified = common_sql_now();
|
$profile->created = $this->created = $this->modified = common_sql_now();
|
||||||
|
|
||||||
$fields = array (
|
$fields = [
|
||||||
'uri' => 'profileurl',
|
'uri' => 'profileurl',
|
||||||
'nickname' => 'nickname',
|
'nickname' => 'nickname',
|
||||||
'fullname' => 'fullname',
|
'fullname' => 'fullname',
|
||||||
'bio' => 'bio'
|
'bio' => 'bio'
|
||||||
);
|
];
|
||||||
|
|
||||||
foreach ($fields as $af => $pf) {
|
foreach ($fields as $af => $pf) {
|
||||||
$profile->$pf = $this->$af;
|
$profile->$pf = $this->$af;
|
||||||
@ -178,7 +184,7 @@ class Activitypub_profile extends Profile
|
|||||||
* @return Activitypub_profile
|
* @return Activitypub_profile
|
||||||
* @throws Exception if no Activitypub_profile exists for given Profile
|
* @throws Exception if no Activitypub_profile exists for given Profile
|
||||||
*/
|
*/
|
||||||
static function from_profile (Profile $profile)
|
public static function from_profile(Profile $profile)
|
||||||
{
|
{
|
||||||
$profile_id = $profile->getID();
|
$profile_id = $profile->getID();
|
||||||
|
|
||||||
@ -271,10 +277,8 @@ class Activitypub_profile extends Profile
|
|||||||
if (!empty($profiles_found)) {
|
if (!empty($profiles_found)) {
|
||||||
return self::from_profile($profiles_found[0]);
|
return self::from_profile($profiles_found[0]);
|
||||||
} else {
|
} else {
|
||||||
throw new Exception ('No valid ActivityPub profile found for given URI');
|
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.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -325,8 +329,10 @@ class Activitypub_profile extends Profile
|
|||||||
throw new Exception(_m('Not a valid webfinger address.'));
|
throw new Exception(_m('Not a valid webfinger address.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$hints = array_merge (array ('webfinger' => $addr),
|
$hints = array_merge(
|
||||||
DiscoveryHints::fromXRD ($xrd));
|
array('webfinger' => $addr),
|
||||||
|
DiscoveryHints::fromXRD($xrd)
|
||||||
|
);
|
||||||
|
|
||||||
// If there's an Hcard, let's grab its info
|
// If there's an Hcard, let's grab its info
|
||||||
if (array_key_exists('hcard', $hints)) {
|
if (array_key_exists('hcard', $hints)) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
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']));
|
||||||
|
}
|
||||||
|
}
|
@ -28,8 +28,9 @@ if (!defined ('GNUSOCIAL')) {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
class DiscoveryHints {
|
class DiscoveryHints
|
||||||
static function fromXRD(XML_XRD $xrd)
|
{
|
||||||
|
public static function fromXRD(XML_XRD $xrd)
|
||||||
{
|
{
|
||||||
$hints = array();
|
$hints = array();
|
||||||
|
|
||||||
@ -60,7 +61,7 @@ class DiscoveryHints {
|
|||||||
return $hints;
|
return $hints;
|
||||||
}
|
}
|
||||||
|
|
||||||
static function fromHcardUrl($url)
|
public static function fromHcardUrl($url)
|
||||||
{
|
{
|
||||||
$client = new HTTPClient();
|
$client = new HTTPClient();
|
||||||
$client->setHeader('Accept', 'text/html,application/xhtml+xml');
|
$client->setHeader('Accept', 'text/html,application/xhtml+xml');
|
||||||
@ -76,11 +77,13 @@ class DiscoveryHints {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self::hcardHints($response->getBody(),
|
return self::hcardHints(
|
||||||
$response->getEffectiveUrl());
|
$response->getBody(),
|
||||||
|
$response->getEffectiveUrl()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static function hcardHints($body, $url)
|
public static function hcardHints($body, $url)
|
||||||
{
|
{
|
||||||
$hcard = self::_hcard($body, $url);
|
$hcard = self::_hcard($body, $url);
|
||||||
|
|
||||||
@ -119,7 +122,7 @@ class DiscoveryHints {
|
|||||||
return $hints;
|
return $hints;
|
||||||
}
|
}
|
||||||
|
|
||||||
static function _hcard($body, $url)
|
public static function _hcard($body, $url)
|
||||||
{
|
{
|
||||||
$mf2 = new Mf2\Parser($body, $url);
|
$mf2 = new Mf2\Parser($body, $url);
|
||||||
$mf2 = $mf2->parse();
|
$mf2 = $mf2->parse();
|
||||||
|
@ -74,44 +74,75 @@ class Activitypub_explorer
|
|||||||
// First check if we already have it locally and, if so, return it
|
// 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 the local fetch fails: grab it remotely, store locally and return
|
||||||
if (! ($this->grab_local_user($url) || $this->grab_remote_user($url))) {
|
if (! ($this->grab_local_user($url) || $this->grab_remote_user($url))) {
|
||||||
throw new Exception ("User not found");
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a local user profiles from its URL and joins it on
|
* Get a local user profiles from its URL and joins it on
|
||||||
* $this->discovered_actor_profiles
|
* $this->discovered_actor_profiles
|
||||||
*
|
*
|
||||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||||
* @param string $url User's url
|
* @param string $uri Actor's uri
|
||||||
* @return boolean success state
|
* @return boolean success state
|
||||||
*/
|
*/
|
||||||
private function grab_local_user ($url)
|
private function grab_local_user($uri)
|
||||||
{
|
{
|
||||||
if (($actor_profile = self::get_profile_by_url ($url)) != false) {
|
// Ensure proper remote URI
|
||||||
$this->discovered_actor_profiles[]= $actor_profile;
|
// If an exceptiong ocurrs here it's better to just leave everything
|
||||||
return true;
|
// 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 {
|
} else {
|
||||||
/******************************** XXX: ********************************
|
// This potential local user is not a remote user.
|
||||||
* Sometimes it is not true that the user is not locally available, *
|
// Let's check for pure blood!
|
||||||
* mostly when it is a local user and URLs slightly changed *
|
$profile = User::getByNickname($this->temp_res["preferredUsername"])->getProfile();
|
||||||
* e.g.: GS instance owner changed from standard urls to pretty urls *
|
}
|
||||||
* (not sure if this is necessary, but anyway) *
|
|
||||||
**********************************************************************/
|
|
||||||
|
|
||||||
// Iff we really are in the same instance
|
// We found something!
|
||||||
$root_url_len = strlen (common_root_url ());
|
$this->discovered_actor_profiles[]= $profile;
|
||||||
if (substr ($url, 0, $root_url_len) == common_root_url ()) {
|
unset($this->temp_res); // IMPORTANT to avoid _dangerous_ noise in the Explorer system
|
||||||
// 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 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
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,6 +156,7 @@ class Activitypub_explorer
|
|||||||
*/
|
*/
|
||||||
private function grab_remote_user($url)
|
private function grab_remote_user($url)
|
||||||
{
|
{
|
||||||
|
if (!isset($this->temp_res)) {
|
||||||
$client = new HTTPClient();
|
$client = new HTTPClient();
|
||||||
$headers = array();
|
$headers = array();
|
||||||
$headers[] = 'Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams"';
|
$headers[] = 'Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams"';
|
||||||
@ -134,6 +166,10 @@ class Activitypub_explorer
|
|||||||
throw new Exception("Invalid Actor URL.");
|
throw new Exception("Invalid Actor URL.");
|
||||||
}
|
}
|
||||||
$res = json_decode($response->getBody(), JSON_UNESCAPED_SLASHES);
|
$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!!!
|
if (isset($res["orderedItems"])) { // It's a potential collection of actors!!!
|
||||||
foreach ($res["orderedItems"] as $profile) {
|
foreach ($res["orderedItems"] as $profile) {
|
||||||
if ($this->_lookup($profile) == false) {
|
if ($this->_lookup($profile) == false) {
|
||||||
@ -163,9 +199,9 @@ class Activitypub_explorer
|
|||||||
private function store_profile($res)
|
private function store_profile($res)
|
||||||
{
|
{
|
||||||
$aprofile = new Activitypub_profile;
|
$aprofile = new Activitypub_profile;
|
||||||
$aprofile->uri = $res["url"];
|
$aprofile->uri = $res["id"];
|
||||||
$aprofile->nickname = $res["nickname"];
|
$aprofile->nickname = $res["preferredUsername"];
|
||||||
$aprofile->fullname = $res["display_name"];
|
$aprofile->fullname = $res["name"];
|
||||||
$aprofile->bio = substr($res["summary"], 0, 1000);
|
$aprofile->bio = substr($res["summary"], 0, 1000);
|
||||||
$aprofile->inboxuri = $res["inbox"];
|
$aprofile->inboxuri = $res["inbox"];
|
||||||
$aprofile->sharedInboxuri = isset($res["sharedInbox"]) ? $res["sharedInbox"] : $res["inbox"];
|
$aprofile->sharedInboxuri = isset($res["sharedInbox"]) ? $res["sharedInbox"] : $res["inbox"];
|
||||||
@ -185,39 +221,13 @@ class Activitypub_explorer
|
|||||||
*/
|
*/
|
||||||
private static function validate_remote_response($res)
|
private static function validate_remote_response($res)
|
||||||
{
|
{
|
||||||
if (!isset ($res["url"], $res["nickname"], $res["display_name"], $res["summary"], $res["inbox"])) {
|
if (!isset($res["id"], $res["preferredUsername"], $res["name"], $res["summary"], $res["inbox"])) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
* Given a valid actor profile url returns its inboxes
|
||||||
*
|
*
|
||||||
|
@ -44,7 +44,7 @@ if (!defined ('GNUSOCIAL')) {
|
|||||||
class Activitypub_postman
|
class Activitypub_postman
|
||||||
{
|
{
|
||||||
private $actor;
|
private $actor;
|
||||||
private $to = array ();
|
private $to = [];
|
||||||
private $client;
|
private $client;
|
||||||
private $headers;
|
private $headers;
|
||||||
|
|
||||||
@ -55,12 +55,12 @@ class Activitypub_postman
|
|||||||
* @param Profile of sender
|
* @param Profile of sender
|
||||||
* @param Activitypub_profile $to array of destinataries
|
* @param Activitypub_profile $to array of destinataries
|
||||||
*/
|
*/
|
||||||
public function __construct ($from, $to = array ())
|
public function __construct($from, $to = [])
|
||||||
{
|
{
|
||||||
$this->client = new HTTPClient();
|
$this->client = new HTTPClient();
|
||||||
$this->actor = $from;
|
$this->actor = $from;
|
||||||
$this->to = $to;
|
$this->to = $to;
|
||||||
$this->headers = array();
|
$this->headers = [];
|
||||||
$this->headers[] = 'Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams"';
|
$this->headers[] = 'Accept: application/ld+json; profile="https://www.w3.org/ns/activitystreams"';
|
||||||
$this->headers[] = 'User-Agent: GNUSocialBot v0.1 - https://gnu.io/social';
|
$this->headers[] = 'User-Agent: GNUSocialBot v0.1 - https://gnu.io/social';
|
||||||
}
|
}
|
||||||
@ -69,15 +69,28 @@ class Activitypub_postman
|
|||||||
* Send a follow notification to remote instance
|
* Send a follow notification to remote instance
|
||||||
*
|
*
|
||||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||||
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function follow()
|
public function follow()
|
||||||
{
|
{
|
||||||
$data = array ("@context" => "https://www.w3.org/ns/activitystreams",
|
$data = Activitypub_follow::follow_to_array($this->actor->getUrl(), $this->to[0]->getUrl());
|
||||||
"type" => "Follow",
|
|
||||||
"actor" => $this->actor->getUrl (),
|
|
||||||
"object" => $this->to[0]->getUrl ());
|
|
||||||
$this->client->setBody(json_encode($data));
|
$this->client->setBody(json_encode($data));
|
||||||
$this->client->post ($this->to[0]->get_inbox (), $this->headers);
|
$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);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception("An unknown error occurred.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -87,16 +100,25 @@ class Activitypub_postman
|
|||||||
*/
|
*/
|
||||||
public function undo_follow()
|
public function undo_follow()
|
||||||
{
|
{
|
||||||
$data = array ("@context" => "https://www.w3.org/ns/activitystreams",
|
$data = Activitypub_undo::undo_to_array(
|
||||||
"type" => "Undo",
|
Activitypub_follow::follow_to_array(
|
||||||
"actor" => $this->actor->getUrl (),
|
$this->actor->getUrl(),
|
||||||
"object" => array (
|
$this->to[0]->getUrl()
|
||||||
"type" => "Follow",
|
|
||||||
"object" => $this->to[0]->getUrl ()
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
$this->client->setBody(json_encode($data));
|
$this->client->setBody(json_encode($data));
|
||||||
$this->client->post ($this->to[0]->get_inbox (), $this->headers);
|
$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());
|
||||||
|
$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.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -107,10 +129,10 @@ class Activitypub_postman
|
|||||||
*/
|
*/
|
||||||
public function like($notice)
|
public function like($notice)
|
||||||
{
|
{
|
||||||
$data = array ("@context" => "https://www.w3.org/ns/activitystreams",
|
$data = Activitypub_like::like_to_array(
|
||||||
"type" => "Like",
|
$this->actor->getUrl(),
|
||||||
"actor" => $this->actor->getUrl (),
|
Activitypub_notice::notice_to_array($notice)
|
||||||
"object" => $notice->getUri ());
|
);
|
||||||
$this->client->setBody(json_encode($data));
|
$this->client->setBody(json_encode($data));
|
||||||
foreach ($this->to_inbox() as $inbox) {
|
foreach ($this->to_inbox() as $inbox) {
|
||||||
$this->client->post($inbox, $this->headers);
|
$this->client->post($inbox, $this->headers);
|
||||||
@ -125,12 +147,10 @@ class Activitypub_postman
|
|||||||
*/
|
*/
|
||||||
public function undo_like($notice)
|
public function undo_like($notice)
|
||||||
{
|
{
|
||||||
$data = array ("@context" => "https://www.w3.org/ns/activitystreams",
|
$data = Activitypub_undo::undo_to_array(
|
||||||
"type" => "Undo",
|
Activitypub_like::like_to_array(
|
||||||
"actor" => $this->actor->getUrl (),
|
$this->actor->getUrl(),
|
||||||
"object" => array (
|
Activitypub_notice::notice_to_array($notice)
|
||||||
"type" => "Like",
|
|
||||||
"object" => $notice->getUri ()
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
$this->client->setBody(json_encode($data));
|
$this->client->setBody(json_encode($data));
|
||||||
@ -139,28 +159,6 @@ class Activitypub_postman
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
* Send a Create notification to remote instances
|
||||||
*
|
*
|
||||||
@ -169,16 +167,10 @@ class Activitypub_postman
|
|||||||
*/
|
*/
|
||||||
public function create($notice)
|
public function create($notice)
|
||||||
{
|
{
|
||||||
$data = array ("@context" => "https://www.w3.org/ns/activitystreams",
|
$data = Activitypub_create::create_to_array(
|
||||||
"id" => $notice->getUri (),
|
$notice->getUri(),
|
||||||
"type" => "Create",
|
$this->actor->getUrl(),
|
||||||
"actor" => $this->actor->getUrl (),
|
Activitypub_notice::notice_to_array($notice)
|
||||||
"to" => "https://www.w3.org/ns/activitystreams#Public",
|
|
||||||
"object" => array (
|
|
||||||
"type" => "Note",
|
|
||||||
"url" => $notice->getUrl (),
|
|
||||||
"content" => $notice->getContent ()
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
if (isset($notice->reply_to)) {
|
if (isset($notice->reply_to)) {
|
||||||
$data["object"]["reply_to"] = $notice->getParent()->getUri();
|
$data["object"]["reply_to"] = $notice->getParent()->getUri();
|
||||||
@ -189,6 +181,24 @@ class Activitypub_postman
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* Send a Delete notification to remote instances holding the notice
|
||||||
*
|
*
|
||||||
@ -197,14 +207,22 @@ class Activitypub_postman
|
|||||||
*/
|
*/
|
||||||
public function delete($notice)
|
public function delete($notice)
|
||||||
{
|
{
|
||||||
$data = array ("@context" => "https://www.w3.org/ns/activitystreams",
|
$data = Activitypub_delete::delete_to_array(Activitypub_notice::notice_to_array($notice));
|
||||||
"type" => "Delete",
|
|
||||||
"actor" => $this->actor->getUrl (),
|
|
||||||
"object" => $notice->getUri ()
|
|
||||||
);
|
|
||||||
$this->client->setBody(json_encode($data));
|
$this->client->setBody(json_encode($data));
|
||||||
|
$errors = array();
|
||||||
foreach ($this->to_inbox() as $inbox) {
|
foreach ($this->to_inbox() as $inbox) {
|
||||||
$this->client->post ($inbox, $this->headers);
|
$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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user