diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 819cb4cc48bf..56a25854ab92 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -38,6 +38,7 @@ use OCA\Talk\Dashboard\TalkWidget; use OCA\Talk\Events\ChatEvent; use OCA\Talk\Events\RoomEvent; +use OCA\Talk\Deck\DeckPluginLoader; use OCA\Talk\Files\Listener as FilesListener; use OCA\Talk\Files\TemplateLoader as FilesTemplateLoader; use OCA\Talk\Flow\Operation; @@ -99,6 +100,7 @@ public function register(IRegistrationContext $context): void { $context->registerEventListener(BeforeTemplateRenderedEvent::class, PublicShareAuthTemplateLoader::class); $context->registerEventListener(\OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent::class, UnifiedSearchCSSLoader::class); $context->registerEventListener(UserChangedEvent::class, UserDisplayNameListener::class); + $context->registerEventListener(\OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent::class, DeckPluginLoader::class); $context->registerSearchProvider(ConversationSearch::class); $context->registerSearchProvider(CurrentMessageSearch::class); diff --git a/lib/Deck/DeckPluginLoader.php b/lib/Deck/DeckPluginLoader.php new file mode 100644 index 000000000000..7fd27226aeb8 --- /dev/null +++ b/lib/Deck/DeckPluginLoader.php @@ -0,0 +1,54 @@ + + * + * @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 OCA\Talk\Deck; + +use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\IRequest; +use OCP\Util; + +class DeckPluginLoader implements IEventListener { + /** @var IRequest */ + private $request; + + public function __construct(IRequest $request) { + $this->request = $request; + } + + public function handle(Event $event): void { + if (!($event instanceof BeforeTemplateRenderedEvent)) { + return; + } + + if (!$event->isLoggedIn()) { + return; + } + + if (strpos($this->request->getPathInfo(), '/apps/deck') === 0) { + Util::addScript('spreed', 'collections'); + Util::addScript('spreed', 'deck'); + } + } +} diff --git a/lib/Listener/BeforeTemplateRenderedListener.php b/lib/Listener/BeforeTemplateRenderedListener.php new file mode 100644 index 000000000000..c3aa084e23f3 --- /dev/null +++ b/lib/Listener/BeforeTemplateRenderedListener.php @@ -0,0 +1,55 @@ + + * + * @author Julius Härtl + * + * @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 . + * + */ + +declare(strict_types=1); + + +namespace OCA\Deck\Listeners; + +use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\IRequest; +use OCP\Util; + +class BeforeTemplateRenderedListener implements IEventListener { + private $request; + + public function __construct(IRequest $request) { + $this->request = $request; + } + + public function handle(Event $event): void { + if (!($event instanceof BeforeTemplateRenderedEvent)) { + return; + } + + if (!$event->isLoggedIn()) { + return; + } + + if (strpos($this->request->getPathInfo(), '/apps/deck') === 0) { + Util::addScript('deck', 'calendar'); + } + } +} diff --git a/src/collections.js b/src/collections.js index 8d6b9cd79750..27d79afd6c71 100644 --- a/src/collections.js +++ b/src/collections.js @@ -49,7 +49,7 @@ import RoomSelector from './views/RoomSelector' ComponentVM.$root.$on('close', () => { ComponentVM.$el.remove() ComponentVM.$destroy() - reject(new Error('User canceled resource selection')) + reject(new Error('User cancelled resource selection')) }) ComponentVM.$root.$on('select', (id) => { resolve(id) diff --git a/src/deck.js b/src/deck.js new file mode 100644 index 000000000000..582d71bf3a5e --- /dev/null +++ b/src/deck.js @@ -0,0 +1,78 @@ +/* + * @copyright Copyright (c) 2020 Vincent Petry + * + * @author Vincent Petry + * + * @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 . + * + */ + +import Vue from 'vue' +import { generateFilePath, generateUrl } from '@nextcloud/router' +import { getRequestToken } from '@nextcloud/auth' +import { translate, translatePlural } from '@nextcloud/l10n' +import { showSuccess, showError } from '@nextcloud/dialogs' +import { postRichObjectToConversation } from './services/messagesService' + +// CSP config for webpack dynamic chunk loading +// eslint-disable-next-line +__webpack_nonce__ = btoa(getRequestToken()) + +// Correct the root of the app for chunk loading +// OC.linkTo matches the apps folders +// OC.generateUrl ensure the index.php (or not) +// We do not want the index.php since we're loading files +// eslint-disable-next-line +__webpack_public_path__ = generateFilePath('spreed', '', 'js/') + +Vue.prototype.t = translate +Vue.prototype.n = translatePlural +Vue.prototype.OC = OC +Vue.prototype.OCA = OCA + +document.addEventListener('DOMContentLoaded', function() { + + if (!window.OCA.Deck) { + return + } + + window.OCA.Deck.registerCardAction({ + label: t('spreed', 'Post to a conversation'), + icon: 'icon-talk', + callback: (card) => { + OCP.Collaboration.trigger('room').then(async(token) => { + try { + const response = await postRichObjectToConversation(token, { + objectType: 'deck-card', + objectId: card.id, + metaData: JSON.stringify(card), + }) + const messageId = response.data.ocs.data.id + const targetUrl = generateUrl('/call/{token}#message_{messageId}', { token, messageId }) + showSuccess(t('spreed', 'Deck card has been posted to the selected conversation.', { + link: targetUrl, + }), { + isHTML: true, + }) + } catch (exception) { + console.error('Error posting deck card to conversation', exception, exception.response?.status) + showError(t('spreed', 'An error occurred while posting deck card to conversation.')) + } + }) + }, + }) + +}) diff --git a/src/services/messagesService.js b/src/services/messagesService.js index 02d4af8fcd0c..8a2b14951b8f 100644 --- a/src/services/messagesService.js +++ b/src/services/messagesService.js @@ -23,6 +23,8 @@ import axios from '@nextcloud/axios' import { generateOcsUrl } from '@nextcloud/router' import store from '../store/index' +import SHA1 from 'crypto-js/sha1' +import Hex from 'crypto-js/enc-hex' /** * Fetches messages that belong to a particular conversation @@ -114,9 +116,32 @@ const deleteMessage = async function({ token, id }) { return axios.delete(generateOcsUrl('apps/spreed/api/v1/chat', 2) + token + '/' + id) } +/** + * Post a rich object to a conversation + * + * @param {string} token conversation token + * @param {string} objectType object type + * @param {string} objectId object id + * @param {string} metaData JSON metadata of the rich object encoded as string + * @param {string} referenceId generated reference id, leave empty to generate it based on the other args + */ +const postRichObjectToConversation = async function(token, { objectType, objectId, metaData, referenceId }) { + if (!referenceId) { + const tempId = 'richobject-' + objectType + '-' + objectId + '-' + token + '-' + (new Date().getTime()) + referenceId = Hex.stringify(SHA1(tempId)) + } + return axios.post(generateOcsUrl('apps/spreed/api/v1', 2) + `chat/${token}/share`, { + objectType, + objectId, + metaData, + referenceId, + }) +} + export { fetchMessages, lookForNewMessages, postNewMessage, deleteMessage, + postRichObjectToConversation, } diff --git a/src/views/RoomSelector.vue b/src/views/RoomSelector.vue index 7b0842aa685a..dbfe28713207 100644 --- a/src/views/RoomSelector.vue +++ b/src/views/RoomSelector.vue @@ -24,9 +24,9 @@