neu mit benachrichtigungslink
This commit is contained in:
parent
d0b1141648
commit
67a735654a
|
|
@ -12,6 +12,10 @@ use Flarum\User\User;
|
||||||
|
|
||||||
class NotificationPayloadFactory
|
class NotificationPayloadFactory
|
||||||
{
|
{
|
||||||
|
private const DEFAULT_TEAMS_APP_ID = 'd92b1be6-ea92-46b1-bcb0-c08ffdd1a61a';
|
||||||
|
private const TEAMS_ENTITY_ID = '8ffe419f-ca44-4627-a4b2-f223da2d07aa';
|
||||||
|
private const LANDING_PAGE_PATH = '/static/flarum-notify-v2.html';
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
protected SettingsRepositoryInterface $settings
|
protected SettingsRepositoryInterface $settings
|
||||||
) {}
|
) {}
|
||||||
|
|
@ -19,53 +23,123 @@ class NotificationPayloadFactory
|
||||||
/**
|
/**
|
||||||
* Build the payload for Microsoft Teams Activity Feed.
|
* Build the payload for Microsoft Teams Activity Feed.
|
||||||
*
|
*
|
||||||
* Informational notification that opens the Teams app side pane
|
* The activity opens the personal Teams tab. The tab loads the static
|
||||||
* and marks the activity item as read.
|
* landing page, which receives the exact Flarum discussion/post URL as a
|
||||||
|
* validated query parameter and presents it as an external link.
|
||||||
*
|
*
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
public function make(User $recipient, BlueprintInterface $blueprint): array
|
public function make(User $recipient, BlueprintInterface $blueprint): array
|
||||||
{
|
{
|
||||||
// Activity Feed type (informational, but clickable via /l/entity)
|
|
||||||
$activityType = 'systemDefault';
|
|
||||||
|
|
||||||
$forumName = trim((string) $this->settings->get('forum_title', 'sbp forum')) ?: 'sbp forum';
|
$forumName = trim((string) $this->settings->get('forum_title', 'sbp forum')) ?: 'sbp forum';
|
||||||
|
|
||||||
// Resolve dynamic data
|
|
||||||
$discussionTitle = $this->resolveDiscussionTitle($blueprint) ?? $forumName;
|
$discussionTitle = $this->resolveDiscussionTitle($blueprint) ?? $forumName;
|
||||||
$previewSnippet = $this->buildPreviewText($blueprint);
|
$previewSnippet = $this->buildPreviewText($blueprint);
|
||||||
$reasonText = $this->buildReasonTextEn($blueprint);
|
$reasonText = $this->buildReasonTextEn($blueprint);
|
||||||
|
|
||||||
// Teams App + Personal Tab (THIS is what makes it clickable & read)
|
$landingPageUrl = $this->buildLandingPageUrl($blueprint);
|
||||||
$teamsAppId = 'd92b1be6-ea92-46b1-bcb0-c08ffdd1a61a';
|
$teamsTabLink = $this->buildTeamsTabLink($landingPageUrl);
|
||||||
$teamsEntityId = '8ffe419f-ca44-4627-a4b2-f223da2d07aa';
|
|
||||||
|
|
||||||
// Opens the app side pane and marks the activity as read
|
|
||||||
$teamsTabLink = sprintf(
|
|
||||||
'https://teams.microsoft.com/l/entity/%s/%s?webUrl=%s',
|
|
||||||
$teamsAppId,
|
|
||||||
$teamsEntityId,
|
|
||||||
rawurlencode('https://forum.sbp.de/static/flarum-notify.html?v=2')
|
|
||||||
);
|
|
||||||
return [
|
return [
|
||||||
'topic' => [
|
'topic' => [
|
||||||
'source' => 'text',
|
'source' => 'text',
|
||||||
'value' => $discussionTitle,
|
'value' => $discussionTitle,
|
||||||
'webUrl' => $teamsTabLink,
|
'webUrl' => $teamsTabLink,
|
||||||
],
|
],
|
||||||
'activityType' => $activityType,
|
'activityType' => 'systemDefault',
|
||||||
'previewText' => [
|
'previewText' => [
|
||||||
'content' => mb_substr($previewSnippet, 0, 150),
|
'content' => mb_substr($previewSnippet, 0, 150),
|
||||||
],
|
],
|
||||||
'templateParameters' => [
|
'templateParameters' => [
|
||||||
[
|
[
|
||||||
'name' => 'systemDefaultText',
|
'name' => 'systemDefaultText',
|
||||||
'value' => $reasonText,
|
'value' => $reasonText,
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the URL of the static Teams landing page.
|
||||||
|
*
|
||||||
|
* The exact Flarum target is passed as an absolute URL. The landing page
|
||||||
|
* validates that it belongs to the same origin before exposing the link.
|
||||||
|
*/
|
||||||
|
protected function buildLandingPageUrl(BlueprintInterface $blueprint): string
|
||||||
|
{
|
||||||
|
$baseUrl = $this->resolveForumBaseUrl();
|
||||||
|
|
||||||
|
if ($baseUrl === null) {
|
||||||
|
return 'https://forum.sbp.de' . self::LANDING_PAGE_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
$landingPageUrl = $baseUrl . self::LANDING_PAGE_PATH;
|
||||||
|
$subjectUrl = $this->resolveSubjectUrl($blueprint);
|
||||||
|
|
||||||
|
if ($subjectUrl === null) {
|
||||||
|
return $landingPageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $landingPageUrl . '?' . http_build_query(
|
||||||
|
['target' => $subjectUrl],
|
||||||
|
'',
|
||||||
|
'&',
|
||||||
|
PHP_QUERY_RFC3986
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a Teams deep link to the personal app tab.
|
||||||
|
*/
|
||||||
|
protected function buildTeamsTabLink(string $webUrl): string
|
||||||
|
{
|
||||||
|
$teamsAppId = trim((string) $this->settings->get(
|
||||||
|
'sbp-jm-msteams-webhook.teams_app_id',
|
||||||
|
self::DEFAULT_TEAMS_APP_ID
|
||||||
|
));
|
||||||
|
|
||||||
|
if ($teamsAppId === '') {
|
||||||
|
$teamsAppId = self::DEFAULT_TEAMS_APP_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sprintf(
|
||||||
|
'https://teams.microsoft.com/l/entity/%s/%s?webUrl=%s',
|
||||||
|
rawurlencode($teamsAppId),
|
||||||
|
rawurlencode(self::TEAMS_ENTITY_ID),
|
||||||
|
rawurlencode($webUrl)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve and validate the configured public forum base URL.
|
||||||
|
*/
|
||||||
|
protected function resolveForumBaseUrl(): ?string
|
||||||
|
{
|
||||||
|
$baseUrl = rtrim(
|
||||||
|
trim((string) $this->settings->get(
|
||||||
|
'sbp-jm-msteams-webhook.forum_base_url',
|
||||||
|
''
|
||||||
|
)),
|
||||||
|
'/'
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($baseUrl === '') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parts = parse_url($baseUrl);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!is_array($parts)
|
||||||
|
|| ($parts['scheme'] ?? null) !== 'https'
|
||||||
|
|| empty($parts['host'])
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve the discussion title from the notification subject.
|
* Resolve the discussion title from the notification subject.
|
||||||
*/
|
*/
|
||||||
|
|
@ -85,32 +159,49 @@ class NotificationPayloadFactory
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve the URL to the discussion or specific post.
|
* Resolve the URL to the discussion or exact post.
|
||||||
* (Not used for navigation in Teams, but kept for completeness.)
|
|
||||||
*/
|
*/
|
||||||
protected function resolveSubjectUrl(BlueprintInterface $blueprint): ?string
|
protected function resolveSubjectUrl(BlueprintInterface $blueprint): ?string
|
||||||
{
|
{
|
||||||
$baseUrl = rtrim(
|
$baseUrl = $this->resolveForumBaseUrl();
|
||||||
(string) $this->settings->get('sbp-jm-msteams-webhook.forum_base_url', ''),
|
|
||||||
'/'
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($baseUrl === '') {
|
if ($baseUrl === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$subject = $blueprint->getSubject();
|
$subject = $blueprint->getSubject();
|
||||||
|
|
||||||
if ($subject instanceof Discussion) {
|
if ($subject instanceof Discussion) {
|
||||||
return sprintf('%s/d/%s', $baseUrl, $subject->id);
|
if ($subject->id === null) {
|
||||||
|
return $baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sprintf(
|
||||||
|
'%s/d/%s',
|
||||||
|
$baseUrl,
|
||||||
|
rawurlencode((string) $subject->id)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($subject instanceof Post) {
|
if ($subject instanceof Post) {
|
||||||
$discussionId = $subject->discussion_id;
|
$discussionId = $subject->discussion_id;
|
||||||
$postNumber = $subject->number;
|
$postNumber = $subject->number;
|
||||||
|
|
||||||
if ($discussionId !== null && $postNumber !== null) {
|
if ($discussionId !== null && $postNumber !== null) {
|
||||||
return sprintf('%s/d/%s/%s', $baseUrl, $discussionId, $postNumber);
|
return sprintf(
|
||||||
|
'%s/d/%s/%s',
|
||||||
|
$baseUrl,
|
||||||
|
rawurlencode((string) $discussionId),
|
||||||
|
rawurlencode((string) $postNumber)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($discussionId !== null) {
|
||||||
|
return sprintf(
|
||||||
|
'%s/d/%s',
|
||||||
|
$baseUrl,
|
||||||
|
rawurlencode((string) $discussionId)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,12 +214,12 @@ class NotificationPayloadFactory
|
||||||
protected function buildReasonTextEn(BlueprintInterface $blueprint): string
|
protected function buildReasonTextEn(BlueprintInterface $blueprint): string
|
||||||
{
|
{
|
||||||
return match ($blueprint::getType()) {
|
return match ($blueprint::getType()) {
|
||||||
'newPost' => 'There was a new reply in a discussion you are following',
|
'newPost' => 'There was a new reply in a discussion you are following',
|
||||||
'postMentioned' => 'You were mentioned in a post',
|
'postMentioned' => 'You were mentioned in a post',
|
||||||
'userMentioned' => 'You were mentioned',
|
'userMentioned' => 'You were mentioned',
|
||||||
'postLiked' => 'Your post was liked',
|
'postLiked' => 'Your post was liked',
|
||||||
'discussionRenamed' => 'A discussion was renamed',
|
'discussionRenamed' => 'A discussion was renamed',
|
||||||
default => 'New forum activity',
|
default => 'New forum activity',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -146,7 +237,7 @@ class NotificationPayloadFactory
|
||||||
? (string) ($actor->display_name ?? $actor->username ?? 'Someone')
|
? (string) ($actor->display_name ?? $actor->username ?? 'Someone')
|
||||||
: 'Someone';
|
: 'Someone';
|
||||||
|
|
||||||
$type = $blueprint::getType();
|
$type = $blueprint::getType();
|
||||||
$subject = $blueprint->getSubject();
|
$subject = $blueprint->getSubject();
|
||||||
|
|
||||||
$subjectTitle = null;
|
$subjectTitle = null;
|
||||||
|
|
@ -157,12 +248,12 @@ class NotificationPayloadFactory
|
||||||
}
|
}
|
||||||
|
|
||||||
$text = match ($type) {
|
$text = match ($type) {
|
||||||
'newPost' => sprintf('%s replied to a discussion', $actorName),
|
'newPost' => sprintf('%s replied to a discussion', $actorName),
|
||||||
'postMentioned' => sprintf('%s mentioned you in a post', $actorName),
|
'postMentioned' => sprintf('%s mentioned you in a post', $actorName),
|
||||||
'userMentioned' => sprintf('%s mentioned you', $actorName),
|
'userMentioned' => sprintf('%s mentioned you', $actorName),
|
||||||
'postLiked' => sprintf('%s liked your post', $actorName),
|
'postLiked' => sprintf('%s liked your post', $actorName),
|
||||||
'discussionRenamed' => sprintf('%s renamed a discussion', $actorName),
|
'discussionRenamed' => sprintf('%s renamed a discussion', $actorName),
|
||||||
default => sprintf('%s: new forum activity', $actorName),
|
default => sprintf('%s: new forum activity', $actorName),
|
||||||
};
|
};
|
||||||
|
|
||||||
if ($subjectTitle) {
|
if ($subjectTitle) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue