From d180a987149df30dacceb85b07d0bfbf9adc265e Mon Sep 17 00:00:00 2001 From: Christoph Wurst Date: Mon, 16 Sep 2019 18:09:43 +0200 Subject: [PATCH] Make it possible to broadcast events to (web) clients Signed-off-by: Christoph Wurst --- core/Command/Broadcast/Test.php | 100 ++++++++++++++++++ core/register_command.php | 2 + lib/composer/composer/autoload_classmap.php | 4 + lib/composer/composer/autoload_static.php | 4 + .../Broadcast/Events/BroadcastEvent.php | 59 +++++++++++ .../EventDispatcher/EventDispatcher.php | 11 ++ .../Broadcast/Events/IBroadcastEvent.php | 57 ++++++++++ .../EventDispatcher/ABroadcastedEvent.php | 71 +++++++++++++ 8 files changed, 308 insertions(+) create mode 100644 core/Command/Broadcast/Test.php create mode 100644 lib/private/Broadcast/Events/BroadcastEvent.php create mode 100644 lib/public/Broadcast/Events/IBroadcastEvent.php create mode 100644 lib/public/EventDispatcher/ABroadcastedEvent.php diff --git a/core/Command/Broadcast/Test.php b/core/Command/Broadcast/Test.php new file mode 100644 index 00000000000..acbccf8c618 --- /dev/null +++ b/core/Command/Broadcast/Test.php @@ -0,0 +1,100 @@ + + * + * @author 2019 Christoph Wurst + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OC\Core\Command\Broadcast; + +use OCP\EventDispatcher\ABroadcastedEvent; +use OCP\EventDispatcher\IEventDispatcher; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class Test extends Command { + + /** @var IEventDispatcher */ + private $eventDispatcher; + + public function __construct(IEventDispatcher $eventDispatcher) { + parent::__construct(); + $this->eventDispatcher = $eventDispatcher; + } + + protected function configure(): void { + $this + ->setName('broadcast:test') + ->setDescription('test the SSE broadcaster') + ->addArgument( + 'uid', + InputArgument::REQUIRED, + 'the UID of the users to receive the event' + ) + ->addArgument( + 'name', + InputArgument::OPTIONAL, + 'the event name', + 'test' + ); + } + + protected function execute(InputInterface $input, OutputInterface $output) { + $name = $input->getArgument('name'); + $uid = $input->getArgument('uid'); + + $event = new class($name, $uid) extends ABroadcastedEvent { + /** @var string */ + private $name; + /** @var string */ + private $uid; + + public function __construct(string $name, + string $uid) { + parent::__construct(); + $this->name = $name; + $this->uid = $uid; + } + + public function broadcastAs(): string { + return $this->name; + } + + public function getUids(): array { + return [ + $this->uid, + ]; + } + + public function jsonSerialize() { + return [ + 'description' => 'this is a test event', + ]; + } + }; + + $this->eventDispatcher->dispatch('broadcasttest', $event); + + return 0; + } + +} diff --git a/core/register_command.php b/core/register_command.php index 90d001d6965..3b23b61039b 100644 --- a/core/register_command.php +++ b/core/register_command.php @@ -78,6 +78,8 @@ if (\OC::$server->getConfig()->getSystemValue('installed', false)) { $application->add(new OC\Core\Command\Background\WebCron(\OC::$server->getConfig())); $application->add(new OC\Core\Command\Background\Ajax(\OC::$server->getConfig())); + $application->add(\OC::$server->query(\OC\Core\Command\Broadcast\Test::class)); + $application->add(new OC\Core\Command\Config\App\DeleteConfig(\OC::$server->getConfig())); $application->add(new OC\Core\Command\Config\App\GetConfig(\OC::$server->getConfig())); $application->add(new OC\Core\Command\Config\App\SetConfig(\OC::$server->getConfig())); diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 203b6cfecda..7d8946e87c2 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -99,6 +99,7 @@ return array( 'OCP\\BackgroundJob\\Job' => $baseDir . '/lib/public/BackgroundJob/Job.php', 'OCP\\BackgroundJob\\QueuedJob' => $baseDir . '/lib/public/BackgroundJob/QueuedJob.php', 'OCP\\BackgroundJob\\TimedJob' => $baseDir . '/lib/public/BackgroundJob/TimedJob.php', + 'OCP\\Broadcast\\Events\\IBroadcastEvent' => $baseDir . '/lib/public/Broadcast/Events/IBroadcastEvent.php', 'OCP\\Calendar\\BackendTemporarilyUnavailableException' => $baseDir . '/lib/public/Calendar/BackendTemporarilyUnavailableException.php', 'OCP\\Calendar\\ICalendar' => $baseDir . '/lib/public/Calendar/ICalendar.php', 'OCP\\Calendar\\IManager' => $baseDir . '/lib/public/Calendar/IManager.php', @@ -174,6 +175,7 @@ return array( 'OCP\\Encryption\\IFile' => $baseDir . '/lib/public/Encryption/IFile.php', 'OCP\\Encryption\\IManager' => $baseDir . '/lib/public/Encryption/IManager.php', 'OCP\\Encryption\\Keys\\IStorage' => $baseDir . '/lib/public/Encryption/Keys/IStorage.php', + 'OCP\\EventDispatcher\\ABroadcastedEvent' => $baseDir . '/lib/public/EventDispatcher/ABroadcastedEvent.php', 'OCP\\EventDispatcher\\Event' => $baseDir . '/lib/public/EventDispatcher/Event.php', 'OCP\\EventDispatcher\\IEventDispatcher' => $baseDir . '/lib/public/EventDispatcher/IEventDispatcher.php', 'OCP\\EventDispatcher\\IEventListener' => $baseDir . '/lib/public/EventDispatcher/IEventListener.php', @@ -596,6 +598,7 @@ return array( 'OC\\BackgroundJob\\Legacy\\RegularJob' => $baseDir . '/lib/private/BackgroundJob/Legacy/RegularJob.php', 'OC\\BackgroundJob\\QueuedJob' => $baseDir . '/lib/private/BackgroundJob/QueuedJob.php', 'OC\\BackgroundJob\\TimedJob' => $baseDir . '/lib/private/BackgroundJob/TimedJob.php', + 'OC\\Broadcast\\Events\\BroadcastEvent' => $baseDir . '/lib/private/Broadcast/Events/BroadcastEvent.php', 'OC\\Cache\\CappedMemoryCache' => $baseDir . '/lib/private/Cache/CappedMemoryCache.php', 'OC\\Cache\\File' => $baseDir . '/lib/private/Cache/File.php', 'OC\\Calendar\\Manager' => $baseDir . '/lib/private/Calendar/Manager.php', @@ -653,6 +656,7 @@ return array( 'OC\\Core\\Command\\Background\\Cron' => $baseDir . '/core/Command/Background/Cron.php', 'OC\\Core\\Command\\Background\\WebCron' => $baseDir . '/core/Command/Background/WebCron.php', 'OC\\Core\\Command\\Base' => $baseDir . '/core/Command/Base.php', + 'OC\\Core\\Command\\Broadcast\\Test' => $baseDir . '/core/Command/Broadcast/Test.php', 'OC\\Core\\Command\\Check' => $baseDir . '/core/Command/Check.php', 'OC\\Core\\Command\\Config\\App\\Base' => $baseDir . '/core/Command/Config/App/Base.php', 'OC\\Core\\Command\\Config\\App\\DeleteConfig' => $baseDir . '/core/Command/Config/App/DeleteConfig.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 7641293473c..c372f9cde9f 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -128,6 +128,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OCP\\BackgroundJob\\Job' => __DIR__ . '/../../..' . '/lib/public/BackgroundJob/Job.php', 'OCP\\BackgroundJob\\QueuedJob' => __DIR__ . '/../../..' . '/lib/public/BackgroundJob/QueuedJob.php', 'OCP\\BackgroundJob\\TimedJob' => __DIR__ . '/../../..' . '/lib/public/BackgroundJob/TimedJob.php', + 'OCP\\Broadcast\\Events\\IBroadcastEvent' => __DIR__ . '/../../..' . '/lib/public/Broadcast/Events/IBroadcastEvent.php', 'OCP\\Calendar\\BackendTemporarilyUnavailableException' => __DIR__ . '/../../..' . '/lib/public/Calendar/BackendTemporarilyUnavailableException.php', 'OCP\\Calendar\\ICalendar' => __DIR__ . '/../../..' . '/lib/public/Calendar/ICalendar.php', 'OCP\\Calendar\\IManager' => __DIR__ . '/../../..' . '/lib/public/Calendar/IManager.php', @@ -203,6 +204,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OCP\\Encryption\\IFile' => __DIR__ . '/../../..' . '/lib/public/Encryption/IFile.php', 'OCP\\Encryption\\IManager' => __DIR__ . '/../../..' . '/lib/public/Encryption/IManager.php', 'OCP\\Encryption\\Keys\\IStorage' => __DIR__ . '/../../..' . '/lib/public/Encryption/Keys/IStorage.php', + 'OCP\\EventDispatcher\\ABroadcastedEvent' => __DIR__ . '/../../..' . '/lib/public/EventDispatcher/ABroadcastedEvent.php', 'OCP\\EventDispatcher\\Event' => __DIR__ . '/../../..' . '/lib/public/EventDispatcher/Event.php', 'OCP\\EventDispatcher\\IEventDispatcher' => __DIR__ . '/../../..' . '/lib/public/EventDispatcher/IEventDispatcher.php', 'OCP\\EventDispatcher\\IEventListener' => __DIR__ . '/../../..' . '/lib/public/EventDispatcher/IEventListener.php', @@ -625,6 +627,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\BackgroundJob\\Legacy\\RegularJob' => __DIR__ . '/../../..' . '/lib/private/BackgroundJob/Legacy/RegularJob.php', 'OC\\BackgroundJob\\QueuedJob' => __DIR__ . '/../../..' . '/lib/private/BackgroundJob/QueuedJob.php', 'OC\\BackgroundJob\\TimedJob' => __DIR__ . '/../../..' . '/lib/private/BackgroundJob/TimedJob.php', + 'OC\\Broadcast\\Events\\BroadcastEvent' => __DIR__ . '/../../..' . '/lib/private/Broadcast/Events/BroadcastEvent.php', 'OC\\Cache\\CappedMemoryCache' => __DIR__ . '/../../..' . '/lib/private/Cache/CappedMemoryCache.php', 'OC\\Cache\\File' => __DIR__ . '/../../..' . '/lib/private/Cache/File.php', 'OC\\Calendar\\Manager' => __DIR__ . '/../../..' . '/lib/private/Calendar/Manager.php', @@ -682,6 +685,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Core\\Command\\Background\\Cron' => __DIR__ . '/../../..' . '/core/Command/Background/Cron.php', 'OC\\Core\\Command\\Background\\WebCron' => __DIR__ . '/../../..' . '/core/Command/Background/WebCron.php', 'OC\\Core\\Command\\Base' => __DIR__ . '/../../..' . '/core/Command/Base.php', + 'OC\\Core\\Command\\Broadcast\\Test' => __DIR__ . '/../../..' . '/core/Command/Broadcast/Test.php', 'OC\\Core\\Command\\Check' => __DIR__ . '/../../..' . '/core/Command/Check.php', 'OC\\Core\\Command\\Config\\App\\Base' => __DIR__ . '/../../..' . '/core/Command/Config/App/Base.php', 'OC\\Core\\Command\\Config\\App\\DeleteConfig' => __DIR__ . '/../../..' . '/core/Command/Config/App/DeleteConfig.php', diff --git a/lib/private/Broadcast/Events/BroadcastEvent.php b/lib/private/Broadcast/Events/BroadcastEvent.php new file mode 100644 index 00000000000..f3282b5207c --- /dev/null +++ b/lib/private/Broadcast/Events/BroadcastEvent.php @@ -0,0 +1,59 @@ + + * + * @author 2019 Christoph Wurst + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OC\Broadcast\Events; + +use JsonSerializable; +use OCP\Broadcast\Events\IBroadcastEvent; +use OCP\EventDispatcher\ABroadcastedEvent; +use OCP\EventDispatcher\Event; + +class BroadcastEvent extends Event implements IBroadcastEvent { + + /** @var ABroadcastedEvent */ + private $event; + + public function __construct(ABroadcastedEvent $event) { + parent::__construct(); + + $this->event = $event; + } + + public function getName(): string { + return $this->event->broadcastAs(); + } + + public function getUids(): array { + return $this->event->getUids(); + } + + public function getPayload(): JsonSerializable { + return $this->event; + } + + public function setBroadcasted(): void { + $this->event->setBroadcasted(); + } + +} diff --git a/lib/private/EventDispatcher/EventDispatcher.php b/lib/private/EventDispatcher/EventDispatcher.php index 8830bae79d8..d9d7985f7cc 100644 --- a/lib/private/EventDispatcher/EventDispatcher.php +++ b/lib/private/EventDispatcher/EventDispatcher.php @@ -25,7 +25,10 @@ declare(strict_types=1); namespace OC\EventDispatcher; +use OC\Broadcast\Events\BroadcastEvent; +use OCP\Broadcast\Events\IBroadcastEvent; use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\ABroadcastedEvent; use OCP\EventDispatcher\IEventDispatcher; use OCP\IContainer; use OCP\ILogger; @@ -73,6 +76,14 @@ class EventDispatcher implements IEventDispatcher { public function dispatch(string $eventName, Event $event): void { $this->dispatcher->dispatch($event, $eventName); + + if ($event instanceof ABroadcastedEvent && !$event->isPropagationStopped()) { + // Propagate broadcast + $this->dispatch( + IBroadcastEvent::class, + new BroadcastEvent($event) + ); + } } public function dispatchTyped(Event $event): void { diff --git a/lib/public/Broadcast/Events/IBroadcastEvent.php b/lib/public/Broadcast/Events/IBroadcastEvent.php new file mode 100644 index 00000000000..0aa7ff45eec --- /dev/null +++ b/lib/public/Broadcast/Events/IBroadcastEvent.php @@ -0,0 +1,57 @@ + + * + * @author 2019 Christoph Wurst + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCP\Broadcast\Events; + +use JsonSerializable; + +/** + * @since 18.0.0 + */ +interface IBroadcastEvent { + + /** + * @return string the name of the event + * @since 18.0.0 + */ + public function getName(): string; + + /** + * @return string[] + * @since 18.0.0 + */ + public function getUids(): array; + + /** + * @return JsonSerializable the data to be sent to the client + * @since 18.0.0 + */ + public function getPayload(): JsonSerializable; + + /** + * @since 18.0.0 + */ + public function setBroadcasted(): void; + +} diff --git a/lib/public/EventDispatcher/ABroadcastedEvent.php b/lib/public/EventDispatcher/ABroadcastedEvent.php new file mode 100644 index 00000000000..1c240b4fb73 --- /dev/null +++ b/lib/public/EventDispatcher/ABroadcastedEvent.php @@ -0,0 +1,71 @@ + + * + * @author 2019 Christoph Wurst + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCP\EventDispatcher; + +use JsonSerializable; + +/** + * @since 18.0.0 + */ +abstract class ABroadcastedEvent extends Event implements JsonSerializable { + + /** + * @since 18.0.0 + */ + private $broadcasted = false; + + /** + * Get the name of the event, as received on the client-side + * + * Uses the fully qualified event class name by default + * + * @return string + * @since 18.0.0 + */ + public function broadcastAs(): string { + return get_class($this); + } + + /** + * @return string[] + * @since 18.0.0 + */ + abstract public function getUids(): array; + + /** + * @since 18.0.0 + */ + public function setBroadcasted(): void { + $this->broadcasted = true; + } + + /** + * @since 18.0.0 + */ + public function isBroadcasted(): bool { + return $this->broadcasted; + } + +} -- 2.39.5