activitypub-1.0.x-dev/src/Services/ActivityPubUtility.php
src/Services/ActivityPubUtility.php
<?php
namespace Drupal\activitypub\Services;
use ActivityPhp\Server;
use Drupal\activitypub\Entity\ActivityPubActivityInterface;
use Drupal\activitypub\Entity\ActivityPubActorInterface;
use Drupal\activitypub\Event\VisibilityOptionsEvent;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\File\FileUrlGeneratorInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\file\Entity\File;
use Drupal\media\MediaInterface;
use Drupal\user\UserInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
class ActivityPubUtility implements ActivityPubUtilityInterface {
Use StringTranslationTrait;
/**
* The image style storage.
*
* @var \Drupal\image\ImageStyleStorageInterface
*/
protected $imageStyleStorage;
/**
* The actor storage.
*
* @var \Drupal\activitypub\Entity\Storage\ActivityPubActorStorageInterface
*/
protected $actorStorage;
/**
* The activitypub storage.
*
* @var \Drupal\activitypub\Entity\Storage\ActivityPubActivityStorageInterface
*/
protected $activityStorage;
/**
* The request stack
*
* @var \Symfony\Component\HttpFoundation\RequestStack $requestStack
*/
protected $requestStack;
/**
* The config factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* The file url generator.
*
* @var \Drupal\Core\File\FileUrlGeneratorInterface
*/
protected $fileUrlGenerator;
/**
* The event dispatcher.
*
* @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* The ActivityPub signature service.
*
* @var \Drupal\activitypub\Services\ActivityPubSignatureInterface
*/
protected $activityPubSignature;
/**
* ActivityPubUtility constructor
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\activitypub\Services\ActivityPubSignatureInterface $activitypub_signature
* The ActivityPub Signature service.
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
* The request stack.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory.
* @param \Drupal\Core\File\FileUrlGeneratorInterface $file_url_generator
* The file url generator.
* @param \Symfony\Contracts\EventDispatcher\EventDispatcherInterface $event_dispatcher
* The event dispatcher service.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, ActivityPubSignatureInterface $activitypub_signature, RequestStack $request_stack, ConfigFactoryInterface $config_factory, FileUrlGeneratorInterface $file_url_generator, EventDispatcherInterface $event_dispatcher) {
$this->imageStyleStorage = $entity_type_manager->getStorage('image_style');
$this->actorStorage = $entity_type_manager->getStorage('activitypub_actor');
$this->activityStorage = $entity_type_manager->getStorage('activitypub_activity');
$this->activityPubSignature = $activitypub_signature;
$this->requestStack = $request_stack;
$this->configFactory = $config_factory;
$this->fileUrlGenerator = $file_url_generator;
$this->eventDispatcher = $event_dispatcher;
}
/**
* {@inheritdoc}
*/
public function getActivityPubName(ActivityPubActorInterface $actor) {
return $actor->getName() . '@' . $this->requestStack->getCurrentRequest()->getHost();
}
/**
* {@inheritdoc}
*/
public function getActivityPubID(ActivityPubActorInterface $actor) {
return $this->requestStack->getCurrentRequest()->getSchemeAndHttpHost() . $this->requestStack->getCurrentRequest()->getBasePath() . '/user/' . $actor->getOwnerId() . '/activitypub/' . $actor->getName();
}
/**
* {@inheritdoc}
*/
public function getActivityPubUserImage(UserInterface $user, string $type = 'avatar') {
$image_url = NULL;
$config = $this->configFactory->get('activitypub.settings');
$image_user_field = $config->get($type . '_user_field');
/** @var \Drupal\file\FileInterface $image */
if ($image = $this->getUserImage($user, $image_user_field)) {
/** @var \Drupal\image\ImageStyleInterface $image_style */
$image_style = $this->imageStyleStorage->load($config->get($type . '_user_style'));
$image_url = $image_style->buildUrl($image->getFileUri());
}
elseif ($type == 'avatar') {
$avatar_path = $config->get('avatar_default_path');
if ($avatar_path == 'assets/avatar.png') {
$avatar_path = \Drupal::service('extension.list.module')->getPath('activitypub') . '/' . $avatar_path;
}
$image_url = $this->fileUrlGenerator->generateAbsoluteString($avatar_path);
}
return $image_url;
}
/**
* Get the file entity for a given user field which can be a file or media
* field.
*
* @param \Drupal\user\UserInterface $user
* The user.
* @param string $image_user_field
* The field name.
*
* @return \Drupal\file\Entity\File|null
*/
private function getUserImage(UserInterface $user, string $image_user_field): ?File {
$image = NULL;
if (!empty($image_user_field) &&
$user->hasField($image_user_field) &&
($image = $user->get($image_user_field)->entity)) {
if ($image instanceof MediaInterface) {
$fid = $image->getSource()->getSourceFieldValue($image);
$image = File::load($fid);
}
}
return $image;
}
/**
* {@inheritdoc}
*/
public function getServer(array $config = []) {
$config += [
'cache' => [
'ttl' => activitypub_cache_ttl(),
'stream' => activitypub_cache_path()
],
'logger' => [
'driver' => '\Psr\Log\NullLogger'
],
];
// Override for tests.
if ($test_prefix = drupal_valid_test_ua()) {
$config['cache'] = ['enabled' => FALSE];
$config['http']['agent'] = drupal_generate_test_ua($test_prefix);
}
$config['instance']['types'] = 'ignore';
return new Server($config);
}
/**
* {@inheritdoc}
*/
public function alterNodeInfo(array &$data) {
$data['protocols'] = ['activitypub'];
$data['usage']['users']['total'] = $this->actorStorage->getActorCount(['status' => 1]);
$data['usage']['localPosts'] = $this->activityStorage->getActivityCount(['collection' => ['outbox', 'liked'], 'status' => 1, 'visibility' => ActivityPubActivityInterface::VISIBILITY_PUBLIC]);
}
/**
* {@inheritdoc}
*/
public function onEntityDelete(EntityInterface $entity) {
if ($entity instanceof ContentEntityInterface && !($entity instanceof ActivityPubActivityInterface)) {
if ($entity instanceof UserInterface) {
$actors = $this->actorStorage->loadByProperties(['uid' => $entity->id()]);
$this->actorStorage->delete($actors);
}
elseif ($entity instanceof ActivityPubActorInterface) {
$this->activityPubSignature->deleteKeys($entity->getName());
$activities = $this->activityStorage->loadByProperties(['uid' => $entity->getOwnerId()]);
$this->activityStorage->delete($activities);
}
elseif (method_exists($entity, 'getOwnerId')) {
// Find a single activity for this entity.
$conditions = [
'uid' => $entity->getOwnerId() ,
'entity_id' => $entity->id(),
'entity_type_id' => $entity->getEntityTypeId(),
'collection' => ActivityPubActivityInterface::OUTBOX
];
$activities = $this->activityStorage->loadByProperties($conditions);
if (count($activities) == 1) {
/** @var \Drupal\activitypub\Entity\ActivityPubActivityInterface $activity */
$activity = array_shift($activities);
$activity->onEntityDelete($entity);
$activity->delete();
}
}
}
}
/**
* {@inheritdoc}
*/
public function getVisibilityOptions() {
$options = [
ActivityPubActivityInterface::VISIBILITY_PUBLIC => $this->t('Public'),
ActivityPubActivityInterface::VISIBILITY_FOLLOWERS => $this->t('Followers'),
ActivityPubActivityInterface::VISIBILITY_UNLISTED => $this->t('Unlisted'),
ActivityPubActivityInterface::VISIBILITY_PRIVATE => $this->t('Private'),
];
$event = new VisibilityOptionsEvent($options);
$this->eventDispatcher->dispatch($event, VisibilityOptionsEvent::ADD_OPTIONS);
return $event->getVisibilityOptions();
}
/**
* {@inheritdoc}
*/
public function getOutboxIgnoreTypes() {
return ['Follow', 'Undo', 'Delete'];
}
/**
* {@inheritdoc}
*/
public function getTimelineTypes() {
return ['Create', 'Like', 'Announce'];
}
/**
* {@inheritdoc}
*/
public function getNotificationTypes() {
return ['Like', 'Announce', 'Follow', 'Create'];
}
}
