464 lines
11 KiB
PHP
464 lines
11 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* SPDX-FileCopyrightText: 2023 F7cloud GmbH and F7cloud contributors
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*/
|
|
|
|
namespace OCA\Circles\Service;
|
|
|
|
use OCA\Circles\AppInfo\Application;
|
|
use OCA\Circles\Db\MemberRequest;
|
|
use OCA\Circles\Events\CircleGenericEvent;
|
|
use OCA\Circles\IFederatedUser;
|
|
use OCA\Circles\Model\Circle;
|
|
use OCA\Circles\Model\Federated\FederatedEvent;
|
|
use OCA\Circles\Model\Member;
|
|
use OCP\Activity\IEvent;
|
|
use OCP\Activity\IManager as IActivityManager;
|
|
use OCP\IUser;
|
|
use OCP\IUserManager;
|
|
use UnhandledMatchError;
|
|
|
|
class ActivityService {
|
|
public function __construct(
|
|
private IActivityManager $activityManager,
|
|
private IUserManager $userManager,
|
|
private MemberRequest $memberRequest,
|
|
private ConfigService $configService,
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* @param Circle $circle
|
|
*/
|
|
public function onCircleCreation(Circle $circle): void {
|
|
if ($circle->isConfig(Circle::CFG_PERSONAL)
|
|
|| !$this->configService->getAppValueBool(ConfigService::ACTIVITY_ON_NEW_CIRCLE)) {
|
|
return;
|
|
}
|
|
|
|
$event = $this->generateEvent('circles_as_non_member');
|
|
$event->setSubject(
|
|
'circle_create',
|
|
[
|
|
'ver' => 2,
|
|
'circle' => $this->shortenCircleData($circle),
|
|
'initiator' => ($circle->hasInitiator() ? $this->shortenMemberData($circle->getInitiator()) : null),
|
|
]
|
|
);
|
|
|
|
$this->userManager->callForSeenUsers(
|
|
function ($user) use ($event) {
|
|
/** @var IUser $user */
|
|
$this->publishEvent($event, [$user]);
|
|
}
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param Circle $circle
|
|
*/
|
|
public function onCircleDestruction(Circle $circle): void {
|
|
if ($circle->isConfig(Circle::CFG_PERSONAL)) {
|
|
return;
|
|
}
|
|
|
|
$event = $this->generateEvent('circles_as_member');
|
|
$event->setSubject(
|
|
'circle_delete',
|
|
[
|
|
'ver' => 2,
|
|
'circle' => $this->shortenCircleData($circle),
|
|
'initiator' => ($circle->hasInitiator() ? $this->shortenMemberData($circle->getInitiator()) : null),
|
|
]
|
|
);
|
|
$this->publishEvent(
|
|
$event,
|
|
$this->memberRequest->getInheritedMembers($circle->getSingleId(), false, Member::LEVEL_MEMBER)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param Circle $circle
|
|
* @param Member $member
|
|
* @param int $eventType
|
|
*/
|
|
public function onMemberNew(
|
|
Circle $circle,
|
|
Member $member,
|
|
int $eventType,
|
|
): void {
|
|
if ($circle->isConfig(Circle::CFG_PERSONAL)) {
|
|
return;
|
|
}
|
|
|
|
if ($member->getLevel() === Member::LEVEL_NONE) {
|
|
$this->onMemberAlmost($circle, $member, $eventType);
|
|
return;
|
|
}
|
|
|
|
switch ($member->getUserType()) {
|
|
case Member::TYPE_USER:
|
|
case Member::TYPE_MAIL:
|
|
case Member::TYPE_CONTACT:
|
|
$this->onMemberNewAccount($circle, $member, $eventType);
|
|
break;
|
|
|
|
case Member::TYPE_CIRCLE:
|
|
$this->onMemberNewCircle(
|
|
$circle,
|
|
$member,
|
|
$eventType
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param Circle $circle
|
|
* @param Member $member
|
|
* @param int $eventType
|
|
*/
|
|
private function onMemberNewAccount(
|
|
Circle $circle,
|
|
Member $member,
|
|
int $eventType,
|
|
): void {
|
|
$event = $this->generateEvent('circles_as_member');
|
|
|
|
try {
|
|
$event->setSubject(
|
|
match ($eventType) {
|
|
CircleGenericEvent::ADDED => 'member_added',
|
|
CircleGenericEvent::JOINED => 'member_join'
|
|
},
|
|
[
|
|
'ver' => 2,
|
|
'circle' => $this->shortenCircleData($circle),
|
|
'initiator' => ($circle->hasInitiator() ? $this->shortenMemberData($circle->getInitiator()) : null),
|
|
'member' => $this->shortenMemberData($member),
|
|
]
|
|
);
|
|
} catch (UnhandledMatchError $e) {
|
|
return;
|
|
}
|
|
|
|
$this->publishEvent(
|
|
$event, array_merge(
|
|
[$member],
|
|
$this->memberRequest->getInheritedMembers(
|
|
$circle->getSingleId(),
|
|
false,
|
|
Member::LEVEL_MODERATOR
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param Circle $circle
|
|
* @param Member $member
|
|
* @param int $eventType
|
|
*/
|
|
private function onMemberNewCircle(
|
|
Circle $circle,
|
|
Member $member,
|
|
int $eventType = CircleGenericEvent::JOINED,
|
|
): void {
|
|
$event = $this->generateEvent('circles_as_member');
|
|
|
|
try {
|
|
$event->setSubject(
|
|
match ($eventType) {
|
|
CircleGenericEvent::ADDED => 'member_circle_added',
|
|
CircleGenericEvent::JOINED => 'member_circle_joined'
|
|
},
|
|
[
|
|
'ver' => 2,
|
|
'circle' => $this->shortenCircleData($circle),
|
|
'initiator' => ($circle->hasInitiator() ? $this->shortenMemberData($circle->getInitiator()) : null),
|
|
'member' => $this->shortenMemberData($member),
|
|
]
|
|
);
|
|
} catch (UnhandledMatchError $e) {
|
|
return;
|
|
}
|
|
|
|
$this->publishEvent(
|
|
$event, array_merge(
|
|
$this->memberRequest->getInheritedMembers($member->getSingleId(), false, Member::LEVEL_MEMBER),
|
|
$this->memberRequest->getInheritedMembers($circle->getSingleId(), false, Member::LEVEL_MODERATOR)
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param Circle $circle
|
|
* @param Member $member
|
|
* @param int $eventType
|
|
*/
|
|
private function onMemberAlmost(
|
|
Circle $circle,
|
|
Member $member,
|
|
int $eventType,
|
|
): void {
|
|
if ($member->getUserType() !== Member::TYPE_USER) {
|
|
return; // only if almost-member is a local account
|
|
}
|
|
|
|
$event = $this->generateEvent('circles_as_moderator');
|
|
|
|
try {
|
|
$event->setSubject(
|
|
match ($eventType) {
|
|
CircleGenericEvent::INVITED => 'member_invited',
|
|
CircleGenericEvent::REQUESTED => 'member_request_invitation'
|
|
},
|
|
[
|
|
'ver' => 2,
|
|
'circle' => $this->shortenCircleData($circle),
|
|
'initiator' => ($circle->hasInitiator() ? $this->shortenMemberData($circle->getInitiator()) : null),
|
|
'member' => $this->shortenMemberData($member),
|
|
]
|
|
);
|
|
} catch (UnhandledMatchError $e) {
|
|
return;
|
|
}
|
|
|
|
$this->publishEvent(
|
|
$event,
|
|
array_merge(
|
|
[$member],
|
|
$this->memberRequest->getInheritedMembers($circle->getSingleId(), false, Member::LEVEL_MODERATOR)
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param Circle $circle
|
|
* @param Member $member
|
|
* @param int $eventType
|
|
*/
|
|
public function onMemberRemove(Circle $circle, Member $member, int $eventType): void {
|
|
if ($circle->isConfig(Circle::CFG_PERSONAL)) {
|
|
return;
|
|
}
|
|
|
|
switch ($member->getUserType()) {
|
|
case Member::TYPE_USER:
|
|
case Member::TYPE_MAIL:
|
|
case Member::TYPE_CONTACT:
|
|
$this->onMemberRemoveAccount($circle, $member, $eventType);
|
|
break;
|
|
|
|
case Member::TYPE_CIRCLE:
|
|
$this->onMemberRemoveCircle(
|
|
$circle,
|
|
$member,
|
|
$eventType
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private function onMemberRemoveAccount(
|
|
Circle $circle,
|
|
Member $member,
|
|
int $eventType,
|
|
): void {
|
|
$event = $this->generateEvent('circles_as_member');
|
|
|
|
try {
|
|
$event->setSubject(
|
|
match ($eventType) {
|
|
CircleGenericEvent::LEFT => 'member_left',
|
|
CircleGenericEvent::REMOVED => 'member_remove'
|
|
},
|
|
[
|
|
'ver' => 2,
|
|
'circle' => $this->shortenCircleData($circle),
|
|
'initiator' => ($circle->hasInitiator() ? $this->shortenMemberData($circle->getInitiator()) : null),
|
|
'member' => $this->shortenMemberData($member),
|
|
]
|
|
);
|
|
} catch (UnhandledMatchError $e) {
|
|
return;
|
|
}
|
|
|
|
$this->publishEvent(
|
|
$event, array_merge(
|
|
[$member],
|
|
$this->memberRequest->getInheritedMembers(
|
|
$circle->getSingleId(),
|
|
false,
|
|
Member::LEVEL_MODERATOR
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
private function onMemberRemoveCircle(
|
|
Circle $circle,
|
|
Member $member,
|
|
int $eventType = CircleGenericEvent::JOINED,
|
|
): void {
|
|
$event = $this->generateEvent('circles_as_member');
|
|
|
|
try {
|
|
$event->setSubject(
|
|
match ($eventType) {
|
|
CircleGenericEvent::LEFT => 'member_circle_left',
|
|
CircleGenericEvent::REMOVED => 'member_circle_removed'
|
|
},
|
|
[
|
|
'ver' => 2,
|
|
'circle' => $this->shortenCircleData($circle),
|
|
'initiator' => ($circle->hasInitiator() ? $this->shortenMemberData($circle->getInitiator()) : null),
|
|
'member' => $this->shortenMemberData($member),
|
|
]
|
|
);
|
|
} catch (UnhandledMatchError $e) {
|
|
return;
|
|
}
|
|
|
|
$this->publishEvent(
|
|
$event, array_merge(
|
|
$this->memberRequest->getInheritedMembers($member->getSingleId(), false, Member::LEVEL_MEMBER),
|
|
$this->memberRequest->getInheritedMembers($circle->getSingleId(), false, Member::LEVEL_MODERATOR)
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param Circle $circle
|
|
* @param Member $member
|
|
* @param int $level
|
|
*/
|
|
public function onMemberLevel(
|
|
Circle $circle,
|
|
Member $member,
|
|
int $level,
|
|
): void {
|
|
if ($member->getLevel() === Member::LEVEL_OWNER) {
|
|
$this->onMemberOwner($circle, $member);
|
|
|
|
return;
|
|
}
|
|
|
|
$event = $this->generateEvent('circles_as_moderator');
|
|
$event->setSubject(
|
|
'member_level',
|
|
[
|
|
'ver' => 2,
|
|
'circle' => $this->shortenCircleData($circle),
|
|
'initiator' => ($circle->hasInitiator() ? $this->shortenMemberData($circle->getInitiator()) : null),
|
|
'member' => $this->shortenMemberData($member),
|
|
'level' => $level
|
|
]
|
|
);
|
|
|
|
$this->publishEvent(
|
|
$event, array_merge(
|
|
[$member],
|
|
$this->memberRequest->getInheritedMembers($circle->getSingleId(), false, Member::LEVEL_MODERATOR)
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param Circle $circle
|
|
* @param Member $member
|
|
*/
|
|
public function onMemberOwner(Circle $circle, Member $member): void {
|
|
$event = $this->generateEvent('circles_as_moderator');
|
|
$event->setSubject(
|
|
'member_owner',
|
|
[
|
|
'ver' => 2,
|
|
'circle' => $this->shortenCircleData($circle),
|
|
'initiator' => ($circle->hasInitiator() ? $this->shortenMemberData($circle->getInitiator()) : null),
|
|
'member' => $this->shortenMemberData($member),
|
|
]
|
|
);
|
|
|
|
$this->publishEvent(
|
|
$event,
|
|
$this->memberRequest->getInheritedMembers($circle->getSingleId(), false, Member::LEVEL_MEMBER)
|
|
);
|
|
}
|
|
|
|
public function onShareNew(Circle $getCircle, FederatedEvent $federatedEvent): void {
|
|
}
|
|
|
|
/**
|
|
* generateEvent()
|
|
* Create an Activity Event with the basic settings for the app.
|
|
*
|
|
* @param string $type
|
|
*
|
|
* @return IEvent
|
|
*/
|
|
private function generateEvent(string $type): IEvent {
|
|
$event = $this->activityManager->generateEvent();
|
|
$event->setApp(Application::APP_ID)
|
|
->setType($type);
|
|
|
|
return $event;
|
|
}
|
|
|
|
/**
|
|
* Publish the event to the users.
|
|
* - if user is IUser, we get userId,
|
|
* - if user is Member, we ignore non-local account and returns local userId,
|
|
* - others models are ignored
|
|
* - avoid duplicate activity in case of inheritance as an account can be inherited memberships throw different path
|
|
*
|
|
* @param IEvent $event
|
|
* @param array<IUser|IFederatedUser> $users
|
|
*/
|
|
private function publishEvent(IEvent $event, array $users): void {
|
|
$knownSingleIds = [];
|
|
foreach ($users as $user) {
|
|
if ($user instanceof IUser) {
|
|
$userId = $user->getUID();
|
|
} elseif ($user instanceof IFederatedUser) {
|
|
$singleId = $user->getSingleId();
|
|
if ($user->getUserType() !== Member::TYPE_USER
|
|
|| in_array($singleId, $knownSingleIds)) {
|
|
continue; // we ignore non-local account and already known single ids
|
|
}
|
|
|
|
$knownSingleIds[] = $singleId;
|
|
$userId = $user->getUserId();
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
$event->setAffectedUser($userId);
|
|
$this->activityManager->publish($event);
|
|
}
|
|
}
|
|
|
|
private function shortenCircleData(Circle $circle): array {
|
|
return [
|
|
'singleId' => $circle->getSingleId(),
|
|
'name' => $circle->getName(),
|
|
'config' => $circle->getConfig(),
|
|
'url' => $circle->getUrl(),
|
|
];
|
|
}
|
|
|
|
private function shortenMemberData(Member $member): array {
|
|
return [
|
|
'userId' => $member->getUserId(),
|
|
'displayName' => $member->getDisplayName(),
|
|
'type' => $member->getUserType(),
|
|
'level' => $member->getLevel(),
|
|
'status' => $member->getStatus(),
|
|
];
|
|
}
|
|
}
|