From 1cd9b61a98ffbb263f7fb3be7d3843d95915cf84 Mon Sep 17 00:00:00 2001 From: Marvin Beym Date: Wed, 21 Jan 2026 12:46:50 +0100 Subject: [PATCH] Show confirmation modal on contact unlink --- .../classes/class.ilBuddySystemGUI.php | 40 ++++++ .../Contact/classes/class.ilContactGUI.php | 31 +++++ .../ILIAS/Contact/resources/buddy_system.js | 116 ++++++++++++++---- lang/ilias_de.lang | 1 + lang/ilias_en.lang | 1 + 5 files changed, 162 insertions(+), 27 deletions(-) diff --git a/components/ILIAS/Contact/BuddySystem/classes/class.ilBuddySystemGUI.php b/components/ILIAS/Contact/BuddySystem/classes/class.ilBuddySystemGUI.php index 3c1f5c19c550..45567b38bd61 100755 --- a/components/ILIAS/Contact/BuddySystem/classes/class.ilBuddySystemGUI.php +++ b/components/ILIAS/Contact/BuddySystem/classes/class.ilBuddySystemGUI.php @@ -18,7 +18,11 @@ declare(strict_types=1); +use ILIAS\Filesystem\Stream\Streams; +use ILIAS\HTTP\Response\ResponseHeader; use ILIAS\HTTP\Services; +use ILIAS\UI\Factory as UIFactory; +use ILIAS\UI\Renderer as UIRenderer; /** * Class ilBuddySystemGUI @@ -39,6 +43,8 @@ class ilBuddySystemGUI protected ilLanguage $lng; protected Services $http; private readonly ilGlobalTemplateInterface $main_tpl; + private UIFactory $ui_factory; + private UIRenderer $ui_renderer; public function __construct() { @@ -49,6 +55,8 @@ public function __construct() $this->ctrl = $DIC['ilCtrl']; $this->user = $DIC['ilUser']; $this->lng = $DIC['lng']; + $this->ui_factory = $DIC->ui()->factory(); + $this->ui_renderer = $DIC->ui()->renderer(); $this->buddyList = ilBuddyList::getInstanceByGlobalUser(); $this->stateFactory = ilBuddySystemRelationStateFactory::getInstance(); @@ -68,8 +76,14 @@ public static function initializeFrontend(ilGlobalTemplateInterface $page): void $DIC->language()->loadLanguageModule('buddysystem'); $page->addJavaScript('./assets/js/buddy_system.js'); + $page->addJavaScript('./assets/js/modal.min.js'); $config = new stdClass(); + $config->async_get_unlink_modal_confirmation_html = $DIC->ctrl()->getLinkTargetByClass([ + ilUIPluginRouterGUI::class, + self::class + ], 'asyncGetUnlinkModalConfirmationHtml', '', true, false); + $config->http_post_url = $DIC->ctrl()->getFormActionByClass([ ilUIPluginRouterGUI::class, self::class @@ -87,6 +101,32 @@ public static function initializeFrontend(ilGlobalTemplateInterface $page): void } } + public function asyncGetUnlinkModalConfirmationHtmlCommand(): never + { + $confirmation_modal = $this->ui_factory->modal()->interruptive( + $this->lng->txt('confirmation'), + $this->lng->txt('buddy_confirm_unlink'), + '' + ) + ->withActionButtonLabel($this->lng->txt('confirm')); + + $this->http->saveResponse( + $this->http->response()->withBody( + Streams::ofString( + json_encode([ + "html" => $this->ui_renderer->renderAsync($confirmation_modal), + "signals" => [ + "show" => $confirmation_modal->getShowSignal()->getId(), + "close" => $confirmation_modal->getCloseSignal()->getId() + ] + ], JSON_THROW_ON_ERROR) + ) + )->withHeader(ResponseHeader::CONTENT_TYPE, 'application/json') + ); + $this->http->sendResponse(); + $this->http->close(); + } + /** * @throws RuntimeException */ diff --git a/components/ILIAS/Contact/classes/class.ilContactGUI.php b/components/ILIAS/Contact/classes/class.ilContactGUI.php index ac7d882f92d0..e5ef9d95f4c3 100755 --- a/components/ILIAS/Contact/classes/class.ilContactGUI.php +++ b/components/ILIAS/Contact/classes/class.ilContactGUI.php @@ -375,9 +375,40 @@ private function updateState(): void return; } + if ($action === 'unlink') { + $this->ctrl->setParameterByClass(self::class, 'user_id', current($user_ids)); + $this->ctrl->redirectByClass(self::class, 'confirmUnlinkContact'); + } + $this->updateRelationState(current($user_ids), $action); } + private function confirmUnlinkContact(): void + { + $this->activateTab('my_contacts'); + $this->tabs_gui->activateSubTab('buddy_view_table'); + + $user_id = $this->http->wrapper()->query()->retrieve('user_id', $this->refinery->kindlyTo()->int()); + + $confirmation_gui = new ilConfirmationGUI(); + $confirmation_gui->setHeaderText($this->lng->txt('buddy_confirm_unlink')); + $confirmation_gui->addItem('user_id', (string) $user_id, ilObjUser::_lookupLogin($user_id)); + $confirmation_gui->setConfirm($this->lng->txt('confirm'), 'unlinkContact'); + $confirmation_gui->setCancel($this->lng->txt('cancel'), 'showContacts'); + $confirmation_gui->setFormAction($this->ctrl->getFormActionByClass(self::class, 'showContacts')); + + $this->tpl->setContent($confirmation_gui->getHTML()); + $this->tpl->printToStdout(); + } + + private function unlinkContact(): void + { + $user_id = $this->http->wrapper()->post()->retrieve('user_id', $this->refinery->kindlyTo()->int()); + + $this->updateRelationState($user_id, 'unlink'); + $this->ctrl->redirectByClass(self::class, 'showContacts'); + } + private function updateRelationState(int $user, string $action): void { $login = ilObjUser::_lookupLogin($user); diff --git a/components/ILIAS/Contact/resources/buddy_system.js b/components/ILIAS/Contact/resources/buddy_system.js index 9e2e02bc0ed2..8c58c2047bb4 100755 --- a/components/ILIAS/Contact/resources/buddy_system.js +++ b/components/ILIAS/Contact/resources/buddy_system.js @@ -25,6 +25,14 @@ const BuddySystemButton = { config: {}, + unlinkConfirmationModal: { + signals: { + show: '', + close: '', + }, + }, + modal: null, + setupConfirmationModal: false, setConfig(config) { this.config = config; @@ -40,32 +48,86 @@ const triggerButton = e.target.closest(triggerSelector); const container = triggerButton.closest(`.${this.config.bnt_class}`); - if (triggerButton.dataset.submitted === 'true') return Promise.resolve(); - - const values = new FormData(); - values.append('usr_id', container.dataset.buddyId); - values.append('action', triggerButton.dataset.action); - values.append(`cmd[${BuddySystem.config.transition_state_cmd}]`, 1); - - return disableButtons(container) - .then(() => fetch(BuddySystem.config.http_post_url, { - method: 'POST', - headers: { Accept: 'application/json' }, - body: values, - })) - .then((response) => { - if (!response.ok) throw new Error('Request failed'); - return response.json(); - }) - .then((data) => processResponse(container, data)) - .then(() => { - container.querySelector(toggleSelector)?.focus(); - }) - .catch((error) => { - console.error(error); - enableButtons(container); - container.querySelector(toggleSelector)?.focus(); - }); + + const widgetClickAction = () => { + if (triggerButton.dataset.submitted === 'true') return Promise.resolve(); + + const values = new FormData(); + values.append('usr_id', container.dataset.buddyId); + values.append('action', triggerButton.dataset.action); + values.append(`cmd[${BuddySystem.config.transition_state_cmd}]`, 1); + + return disableButtons(container) + .then(() => fetch(BuddySystem.config.http_post_url, { + method: 'POST', + headers: { Accept: 'application/json' }, + body: values, + })) + .then((response) => { + if (!response.ok) throw new Error('Request failed'); + return response.json(); + }) + .then((data) => processResponse(container, data)) + .then(() => { + container.querySelector(toggleSelector)?.focus(); + }) + .catch((error) => { + console.error(error); + enableButtons(container); + container.querySelector(toggleSelector)?.focus(); + }); + }; + + if (triggerButton.dataset.action === 'unlink') { + return showUnlinkConfirmationModal().then(() => widgetClickAction()); + } + + return widgetClickAction(); + }; + + const showUnlinkConfirmationModal = () => new Promise((resolve) => { + if (!this.setupConfirmationModal) { + fetch(BuddySystem.config.async_get_unlink_modal_confirmation_html) + .then((response) => { + if (!response.ok) throw new Error('Request failed'); + return response.json(); + }) + .then((data) => { + const wrapper = document.createElement('div'); + const modalFragment = document.createRange().createContextualFragment(data.html); + + wrapper.appendChild(modalFragment); + document.body.append(wrapper); + this.modal = wrapper.querySelector('dialog'); + + this.unlinkConfirmationModal.signals.show = data.signals.show; + this.unlinkConfirmationModal.signals.close = data.signals.close; + + this.modal.querySelector('input[type="submit"]').addEventListener( + 'click', + (event) => onModalSubmitClicked(event, resolve), + { once: true }, + ); + }).then(() => { + this.setupConfirmationModal = true; + global.jQuery(document).trigger(this.unlinkConfirmationModal.signals.show, {}); + }); + return; + } + + const submitButton = this.modal.querySelector('input[type="submit"]'); + submitButton.addEventListener( + 'click', + (event) => onModalSubmitClicked(event, resolve), + { once: true }, + ); + global.jQuery(document).trigger(this.unlinkConfirmationModal.signals.show, {}); + }); + + const onModalSubmitClicked = (event, resolve) => { + event.preventDefault(); + global.jQuery(document).trigger(this.unlinkConfirmationModal.signals.close, {}); + resolve(); }; const disableButtons = (container) => new Promise((resolve) => { @@ -298,7 +360,7 @@ const shouldReloadAwarenessTool = ( ['ilBuddySystemLinkedRelationState', 'ilBuddySystemRequestedRelationState'].includes(oldState) - && newState !== oldState + && newState !== oldState ); if (shouldReloadAwarenessTool) { if (typeof global.il.Awareness !== 'undefined') { diff --git a/lang/ilias_de.lang b/lang/ilias_de.lang index 568e69d0dfcd..260b13ff5ac1 100644 --- a/lang/ilias_de.lang +++ b/lang/ilias_de.lang @@ -2727,6 +2727,7 @@ buddysystem#:#buddy_bs_state_requested_p#:#Angefragt buddysystem#:#buddy_bs_state_unlinked#:#Nicht vernetzt buddysystem#:#buddy_bs_state_unlinked_a#:#Nicht vernetzt buddysystem#:#buddy_bs_state_unlinked_p#:#Nicht vernetzt +buddysystem#:#buddy_confirm_unlink#:#Möchten Sie die Verbindung zu diesem Kontakt wirklich aufheben? buddysystem#:#buddy_enable#:#Aktiviere „Benutzerkontakte“ buddysystem#:#buddy_enable_info#:#Falls aktiviert, können Benutzer im System über Kontaktanfragen miteinander in Verbindung treten. Eine zusätzliche persönliche Einstellung ermöglicht jedem Benutzer, Kontaktaufnahmen zuzulassen oder zu verhindern. buddysystem#:#buddy_handle_contact_request#:#Kontaktanfrage diff --git a/lang/ilias_en.lang b/lang/ilias_en.lang index 6b32452404fb..167a5f61f7ca 100644 --- a/lang/ilias_en.lang +++ b/lang/ilias_en.lang @@ -2728,6 +2728,7 @@ buddysystem#:#buddy_bs_state_requested_p#:#Requested buddysystem#:#buddy_bs_state_unlinked#:#Unlinked buddysystem#:#buddy_bs_state_unlinked_a#:#Unlinked buddysystem#:#buddy_bs_state_unlinked_p#:#Unlinked +buddysystem#:#buddy_confirm_unlink#:#Do you really want to disconnect from this contact? buddysystem#:#buddy_enable#:#Activate ‘Contacts’ buddysystem#:#buddy_enable_info#:#If enabled, users are allowed to contact each other by initiating contact requests. Additionally, there is a personal user setting to allow or prevent being contacted. buddysystem#:#buddy_handle_contact_request#:#Contact Request