diff --git a/README.md b/README.md index 05a9380..f58c716 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,9 @@ # flarum-msteams-webhook -Diese Version enthält ein **sehr rudimentäres Admin-UI** für Flarum 1.x. +Diese Version enthält: -## Enthalten - -- PHP-Backend für das Spiegeln von Web/Alert-Benachrichtigungen nach Microsoft Teams -- rudimentäre Settings im Flarum-Adminbereich -- JavaScript-Build-Setup für das Admin-Frontend +- rudimentäres Admin-UI für Flarum 1.x +- Test-Command `php flarum teams:test-user ` ## Build des Admin-Frontends diff --git a/composer.json b/composer.json index ce63ac6..4c0ae13 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ }, "autoload": { "psr-4": { - "SbpJm\\FlarumMSTeamsWebhook\\": "src/" + "SbpJm\FlarumMSTeamsWebhook\": "src/" } }, "extra": { @@ -38,4 +38,4 @@ }, "minimum-stability": "dev", "prefer-stable": true -} \ No newline at end of file +} diff --git a/extend.php b/extend.php index f2afe63..1ee5d76 100644 --- a/extend.php +++ b/extend.php @@ -3,6 +3,7 @@ declare(strict_types=1); use Flarum\Extend; +use SbpJm\FlarumMSTeamsWebhook\Console\TestTeamsUserCommand; use SbpJm\FlarumMSTeamsWebhook\Listener\MirrorAlertsToTeams; return [ @@ -11,6 +12,9 @@ return [ (new Extend\Frontend('admin')) ->js(__DIR__ . '/js/dist/admin.js'), + (new Extend\Console()) + ->command(TestTeamsUserCommand::class), + (new Extend\Notification()) ->beforeSending(MirrorAlertsToTeams::class), ]; diff --git a/js/src/admin/index.js b/js/src/admin/index.js index c2c14bb..754c3a0 100644 --- a/js/src/admin/index.js +++ b/js/src/admin/index.js @@ -1,4 +1,6 @@ -app.initializers.add('sbp-jm-msteams-webhook', function (app) { +import app from 'flarum/admin/app'; + +app.initializers.add('sbp-jm-msteams-webhook', function () { app.extensionData .for('sbp-jm-msteams-webhook') .registerSetting( @@ -53,7 +55,7 @@ app.initializers.add('sbp-jm-msteams-webhook', function (app) { help: 'Wie der Zielbenutzer in Microsoft Graph aufgelöst wird.', type: 'select', options: { - email: 'E-Mail des Flarum-Benutzers', + email: 'E-Mail des Flarum-Benutzers / UPN wenn identisch', upn: 'UPN aus User-Preference (Fallback auf E-Mail)', preference: 'Freier Preference-Key am Benutzer', }, @@ -83,7 +85,7 @@ app.initializers.add('sbp-jm-msteams-webhook', function (app) { { setting: 'sbp-jm-msteams-webhook.activity_type', label: 'Teams Activity Type', - help: 'Für die erste Version am besten systemDefault verwenden.', + help: 'Für den Schnelltest am besten systemDefault verwenden.', type: 'text', placeholder: 'systemDefault', }, diff --git a/src/Console/TestTeamsUserCommand.php b/src/Console/TestTeamsUserCommand.php new file mode 100644 index 0000000..cb8c3fb --- /dev/null +++ b/src/Console/TestTeamsUserCommand.php @@ -0,0 +1,75 @@ +setName('teams:test-user') + ->setDescription('Sends a test Microsoft Teams activity feed notification to a Flarum user.') + ->addArgument('flarumUserId', InputArgument::REQUIRED, 'The numeric Flarum user ID') + ->addOption('target', null, InputOption::VALUE_OPTIONAL, 'Override the resolved Microsoft Graph target (UPN or Entra user ID)') + ->addOption('show-target', null, InputOption::VALUE_NONE, 'Only show the resolved Microsoft Graph target, do not send anything'); + } + + protected function fire(): int + { + $userId = (int) $this->input->getArgument('flarumUserId'); + $user = User::query()->find($userId); + + if (!$user instanceof User) { + $this->error('Flarum user not found: ' . $userId); + + return 1; + } + + $resolvedTarget = $this->targetResolver->resolve($user); + $overrideTarget = $this->input->getOption('target'); + $target = is_string($overrideTarget) && trim($overrideTarget) !== '' ? trim($overrideTarget) : $resolvedTarget; + + $this->info('Flarum user: #' . $user->id . ' (' . ($user->username ?? $user->display_name ?? 'unknown') . ')'); + $this->info('Email: ' . ($user->email ?? '')); + $this->info('Resolved Graph target: ' . ($target ?? '[none]')); + + if ($this->input->getOption('show-target')) { + return $target ? 0 : 1; + } + + if ($target === null) { + $this->error('No Microsoft Graph target could be resolved. Check user_lookup_strategy and related settings.'); + + return 1; + } + + try { + $blueprint = new TestActivityBlueprint($user); + $this->notifier->notifyToTarget($user, $blueprint, $target); + $this->info('Test notification sent successfully.'); + + return 0; + } catch (\Throwable $e) { + $this->error('Sending failed: ' . $e->getMessage()); + + return 1; + } + } +} diff --git a/src/Notification/TestActivityBlueprint.php b/src/Notification/TestActivityBlueprint.php new file mode 100644 index 0000000..7df77a8 --- /dev/null +++ b/src/Notification/TestActivityBlueprint.php @@ -0,0 +1,42 @@ +recipient; + } + + public function getData(): ?array + { + return [ + 'test' => true, + ]; + } + + public static function getType(): string + { + return 'teamsTest'; + } + + public static function getSubjectModel(): ?string + { + return null; + } +} diff --git a/src/Service/TeamsActivityNotifier.php b/src/Service/TeamsActivityNotifier.php index 8d75502..bf53dd2 100644 --- a/src/Service/TeamsActivityNotifier.php +++ b/src/Service/TeamsActivityNotifier.php @@ -31,6 +31,11 @@ class TeamsActivityNotifier return; } + $this->notifyToTarget($user, $blueprint, $target); + } + + public function notifyToTarget(User $user, BlueprintInterface $blueprint, string $target): void + { $payload = $this->payloadFactory->make($user, $blueprint); $token = $this->tokenProvider->getAccessToken(); diff --git a/src/Support/NotificationPayloadFactory.php b/src/Support/NotificationPayloadFactory.php index 7d79e58..3612219 100644 --- a/src/Support/NotificationPayloadFactory.php +++ b/src/Support/NotificationPayloadFactory.php @@ -63,6 +63,10 @@ class NotificationPayloadFactory protected function buildPreviewText(BlueprintInterface $blueprint): string { + if ($blueprint::getType() === 'teamsTest') { + return 'Flarum ➜ Teams Test erfolgreich angestoßen'; + } + $actor = $blueprint->getFromUser(); $actorName = $actor instanceof User ? (string) ($actor->display_name ?? $actor->username ?? 'Jemand') : 'Jemand'; $type = $blueprint::getType();