Profile update and some more is now added

This commit is contained in:
Michael 2018-09-30 20:26:30 +00:00
parent 761bdafa34
commit 5456ef0185
2 changed files with 167 additions and 46 deletions

View file

@ -44,27 +44,25 @@ use Friendica\Core\Config;
* To-do: * To-do:
* *
* Receiver: * Receiver:
* - Update Note * - Update (Image, Video, Article, Note)
* - Delete Note + - Event
* - Delete Person
* - Undo Announce * - Undo Announce
* - Reject Follow *
* - Undo Accept * Check what this is meant to do:
* - Undo Follow
* - Add * - Add
* - Create Image
* - Create Video
* - Event
* - Remove
* - Block * - Block
* - Flag * - Flag
* - Remove
* - Undo Block
* - Undo Accept (Problem: This could invert a contact accept or an event accept)
* *
* Transmitter: * Transmitter:
* - Delete (Application, Group, Organization, Person, Service)
* - Event
*
* Complicated:
* - Announce * - Announce
* - Undo Announce * - Undo Announce
* - Update Person
* - Reject Follow
* - Event
* *
* General: * General:
* - Attachments * - Attachments
@ -81,7 +79,9 @@ class ActivityPub
'diaspora' => 'https://diasporafoundation.org/ns/', 'diaspora' => 'https://diasporafoundation.org/ns/',
'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers', 'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers',
'sensitive' => 'as:sensitive', 'Hashtag' => 'as:Hashtag']]; 'sensitive' => 'as:sensitive', 'Hashtag' => 'as:Hashtag']];
const ACCOUNT_TYPES = ['Person', 'Organization', 'Service', 'Group', 'Application'];
const CONTENT_TYPES = ['Note', 'Article', 'Video', 'Image'];
const ACTIVITY_TYPES = ['Like', 'Dislike', 'Accept', 'Reject', 'TentativeAccept'];
/** /**
* @brief Checks if the web request is done for the AP protocol * @brief Checks if the web request is done for the AP protocol
* *
@ -243,7 +243,6 @@ class ActivityPub
*/ */
public static function profile($uid) public static function profile($uid)
{ {
$accounttype = ['Person', 'Organization', 'Service', 'Group', 'Application'];
$condition = ['uid' => $uid, 'blocked' => false, 'account_expired' => false, $condition = ['uid' => $uid, 'blocked' => false, 'account_expired' => false,
'account_removed' => false, 'verified' => true]; 'account_removed' => false, 'verified' => true];
$fields = ['guid', 'nickname', 'pubkey', 'account-type', 'page-flags']; $fields = ['guid', 'nickname', 'pubkey', 'account-type', 'page-flags'];
@ -267,7 +266,7 @@ class ActivityPub
$data = ['@context' => self::CONTEXT]; $data = ['@context' => self::CONTEXT];
$data['id'] = $contact['url']; $data['id'] = $contact['url'];
$data['diaspora:guid'] = $user['guid']; $data['diaspora:guid'] = $user['guid'];
$data['type'] = $accounttype[$user['account-type']]; $data['type'] = self::ACCOUNT_TYPES[$user['account-type']];
$data['following'] = System::baseUrl() . '/following/' . $user['nickname']; $data['following'] = System::baseUrl() . '/following/' . $user['nickname'];
$data['followers'] = System::baseUrl() . '/followers/' . $user['nickname']; $data['followers'] = System::baseUrl() . '/followers/' . $user['nickname'];
$data['inbox'] = System::baseUrl() . '/inbox/' . $user['nickname']; $data['inbox'] = System::baseUrl() . '/inbox/' . $user['nickname'];
@ -431,6 +430,29 @@ class ActivityPub
return $data; return $data;
} }
/**
* @brief Fetches a list of inboxes of followers of a given user
*
* @param integer $uid User ID
*
* @return array of follower inboxes
*/
private static function fetchTargetInboxesforUser($uid)
{
$inboxes = [];
$contacts = DBA::select('contact', ['notify', 'batch'], ['uid' => $uid,
'rel' => [Contact::FOLLOWER, Contact::FRIEND], 'network' => Protocol::ACTIVITYPUB,
'archive' => false, 'pending' => false]);
while ($contact = DBA::fetch($contacts)) {
$contact = defaults($contact, 'batch', $contact['notify']);
$inboxes[$contact] = $contact;
}
DBA::close($contacts);
return $inboxes;
}
/** /**
* @brief Fetches an array of inboxes for the given item and user * @brief Fetches an array of inboxes for the given item and user
* *
@ -461,14 +483,7 @@ class ActivityPub
foreach ($permissions[$element] as $receiver) { foreach ($permissions[$element] as $receiver) {
if ($receiver == $item_profile['followers']) { if ($receiver == $item_profile['followers']) {
$contacts = DBA::select('contact', ['notify', 'batch'], ['uid' => $uid, $inboxes = self::fetchTargetInboxesforUser($uid);
'rel' => [Contact::FOLLOWER, Contact::FRIEND], 'network' => Protocol::ACTIVITYPUB,
'archive' => false, 'pending' => false]);
while ($contact = DBA::fetch($contacts)) {
$contact = defaults($contact, 'batch', $contact['notify']);
$inboxes[$contact] = $contact;
}
DBA::close($contacts);
} else { } else {
$profile = APContact::getByURL($receiver); $profile = APContact::getByURL($receiver);
if (!empty($profile)) { if (!empty($profile)) {
@ -716,6 +731,36 @@ class ActivityPub
return $data; return $data;
} }
/**
* @brief Transmits a profile change to the followers
*
* @param integer $uid User ID
*/
public static function transmitProfileUpdate($uid)
{
$owner = User::getOwnerDataById($uid);
$profile = APContact::getByURL($owner['url']);
$data = ['@context' => 'https://www.w3.org/ns/activitystreams',
'id' => System::baseUrl() . '/activity/' . System::createGUID(),
'type' => 'Update',
'actor' => $owner['url'],
'object' => self::profile($uid),
'published' => DateTimeFormat::utcNow(DateTimeFormat::ATOM),
'to' => [$profile['followers']],
'cc' => []];
logger('Sending profile update to followers for user ' . $uid, LOGGER_DEBUG);
$signed = LDSignature::sign($data, $owner);
$inboxes = self::fetchTargetInboxesforUser($uid);
foreach ($inboxes as $inbox) {
logger('Deliver profile update for user ' . $uid . ' to ' . $inbox .' via ActivityPub', LOGGER_DEBUG);
HTTPSignature::transmit($signed, $inbox, $uid);
}
}
/** /**
* @brief Transmits a given activity to a target * @brief Transmits a given activity to a target
* *
@ -1088,12 +1133,19 @@ class ActivityPub
break; break;
case 'Update': case 'Update':
if (in_array($object_data['object_type'], ['Person', 'Organization', 'Service', 'Group', 'Application'])) { if (in_array($object_data['object_type'], self::CONTENT_TYPES)) {
/// @todo
} elseif (in_array($object_data['object_type'], self::ACCOUNT_TYPES)) {
self::updatePerson($object_data, $body); self::updatePerson($object_data, $body);
} }
break; break;
case 'Delete': case 'Delete':
if ($object_data['object_type'] == 'Tombstone') {
self::deleteItem($object_data, $body);
} elseif (in_array($object_data['object_type'], self::ACCOUNT_TYPES)) {
self::deletePerson($object_data, $body);
}
break; break;
case 'Follow': case 'Follow':
@ -1106,10 +1158,16 @@ class ActivityPub
} }
break; break;
case 'Reject':
if ($object_data['object_type'] == 'Follow') {
self::rejectFollowUser($object_data);
}
break;
case 'Undo': case 'Undo':
if ($object_data['object_type'] == 'Follow') { if ($object_data['object_type'] == 'Follow') {
self::undoFollowUser($object_data); self::undoFollowUser($object_data);
} elseif (in_array($object_data['object_type'], ['Like', 'Dislike', 'Accept', 'Reject', 'TentativeAccept'])) { } elseif (in_array($object_data['object_type'], self::ACTIVITY_TYPES)) {
self::undoActivity($object_data); self::undoActivity($object_data);
} }
break; break;
@ -1324,26 +1382,18 @@ class ActivityPub
return false; return false;
} }
switch ($data['type']) { if (in_array($data['type'], self::CONTENT_TYPES)) {
case 'Note':
case 'Article':
case 'Video':
return self::processObject($data); return self::processObject($data);
}
case 'Announce': if ($data['type'] == 'Announce') {
if (empty($data['object'])) { if (empty($data['object'])) {
return false; return false;
} }
return self::fetchObject($data['object']); return self::fetchObject($data['object']);
case 'Person':
case 'Tombstone':
break;
default:
logger('Unknown object type: ' . $data['type'], LOGGER_DEBUG);
break;
} }
logger('Unhandled object type: ' . $data['type'], LOGGER_DEBUG);
} }
/** /**
@ -1548,6 +1598,20 @@ class ActivityPub
self::postItem($activity, $item, $body); self::postItem($activity, $item, $body);
} }
/**
* @brief Delete items
*
* @param array $activity
* @param $body
*/
private static function deleteItem($activity)
{
$owner = Contact::getIdForURL($activity['owner']);
$object = JsonLD::fetchElement($activity, 'object', 'id');
logger('Deleting item ' . $object . ' from ' . $owner, LOGGER_DEBUG);
Item::delete(['uri' => $object, 'owner-id' => $owner]);
}
/** /**
* @brief * @brief
* *
@ -1710,6 +1774,32 @@ class ActivityPub
APContact::getByURL($activity['object']['id'], true); APContact::getByURL($activity['object']['id'], true);
} }
/**
* @brief Delete the given profile
*
* @param array $activity
*/
private static function deletePerson($activity)
{
if (empty($activity['object']['id']) || empty($activity['object']['actor'])) {
logger('Empty object id or actor.', LOGGER_DEBUG);
return;
}
if ($activity['object']['id'] != $activity['object']['actor']) {
logger('Object id does not match actor.', LOGGER_DEBUG);
return;
}
$contacts = DBA::select('contact', ['id'], ['nurl' => normalise_link($activity['object']['id'])]);
while ($contact = DBA::fetch($contacts)) {
Contact::remove($contact["id"]);
}
DBA::close($contacts);
logger('Deleted contact ' . $activity['object']['id'], LOGGER_DEBUG);
}
/** /**
* @brief Accept a follow request * @brief Accept a follow request
* *
@ -1743,6 +1833,35 @@ class ActivityPub
logger('Accept contact request from contact ' . $cid . ' for user ' . $uid, LOGGER_DEBUG); logger('Accept contact request from contact ' . $cid . ' for user ' . $uid, LOGGER_DEBUG);
} }
/**
* @brief Reject a follow request
*
* @param array $activity
*/
private static function rejectFollowUser($activity)
{
$actor = JsonLD::fetchElement($activity, 'object', 'actor');
$uid = User::getIdForURL($actor);
if (empty($uid)) {
return;
}
$owner = User::getOwnerDataById($uid);
$cid = Contact::getIdForURL($activity['owner'], $uid);
if (empty($cid)) {
logger('No contact found for ' . $activity['owner'], LOGGER_DEBUG);
return;
}
if (DBA::exists('contact', ['id' => $cid, 'rel' => Contact::SHARING, 'pending' => true])) {
Contact::remove($cid);
logger('Rejected contact request from contact ' . $cid . ' for user ' . $uid . ' - contact had been removed.', LOGGER_DEBUG);
} else {
logger('Rejected contact request from contact ' . $cid . ' for user ' . $uid . '.', LOGGER_DEBUG);
}
}
/** /**
* @brief Undo activity like "like" or "dislike" * @brief Undo activity like "like" or "dislike"
* *

View file

@ -1,12 +1,13 @@
<?php <?php
/** /**
* @file src/Worker/ProfileUpdate.php * @file src/Worker/ProfileUpdate.php
* @brief Send updated profile data to Diaspora * @brief Send updated profile data to Diaspora and ActivityPub
*/ */
namespace Friendica\Worker; namespace Friendica\Worker;
use Friendica\Protocol\Diaspora; use Friendica\Protocol\Diaspora;
use Friendica\Protocol\ActivityPub;
class ProfileUpdate { class ProfileUpdate {
public static function execute($uid = 0) { public static function execute($uid = 0) {
@ -14,6 +15,7 @@ class ProfileUpdate {
return; return;
} }
ActivityPub::transmitProfileUpdate($uid);
Diaspora::sendProfile($uid); Diaspora::sendProfile($uid);
} }
} }