ILIAS  trunk Revision v11.0_alpha-1838-g59fc79e306b
All Data Structures Namespaces Files Functions Variables Enumerations Enumerator Modules Pages
class.ilChatroomViewGUI.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
26 
34 {
35  public function joinWithCustomName(): void
36  {
37  $this->redirectIfNoPermission('read');
38 
39  $this->gui->switchToVisibleMode();
40  $this->setupTemplate();
41  $room = ilChatroom::byObjectId($this->gui->getObject()->getId());
42  $chat_user = new ilChatroomUser($this->ilUser, $room);
43  $failure = true;
44  $username = '';
45  $custom_username = false;
46 
47  if ($this->hasRequestValue('custom_username_radio')) {
48  if (
49  $this->hasRequestValue('custom_username_text') &&
50  $this->getRequestValue('custom_username_radio', $this->refinery->kindlyTo()->string()) === 'custom_username'
51  ) {
52  $custom_username = true;
53  $username = $this->getRequestValue('custom_username_text', $this->refinery->kindlyTo()->string());
54  $failure = false;
55  } elseif (
56  method_exists(
57  $chat_user,
58  'build' . $this->getRequestValue('custom_username_radio', $this->refinery->kindlyTo()->string())
59  )
60  ) {
61  $username = $chat_user->{
62  'build' . $this->getRequestValue('custom_username_radio', $this->refinery->kindlyTo()->string())
63  }();
64  $failure = false;
65  }
66  }
67 
68  if (!$failure && trim($username) !== '') {
69  if (!$room->isSubscribed($chat_user->getUserId())) {
70  $chat_user->setUsername($chat_user->buildUniqueUsername($username));
71  $chat_user->setProfilePictureVisible(!$custom_username);
72  }
73 
74  $this->showRoom($room, $chat_user);
75  } else {
76  $this->mainTpl->setOnScreenMessage('failure', $this->ilLng->txt('no_username_given'));
77  $this->showNameSelection($chat_user);
78  }
79  }
80 
84  private function setupTemplate(): void
85  {
86  ilLinkifyUtil::initLinkify($this->mainTpl);
87  $this->mainTpl->addJavaScript('assets/js/socket.io.min.js');
88  $this->mainTpl->addJavaScript('assets/js/Chatroom.min.js');
89  $this->mainTpl->addJavaScript('assets/js/AdvancedSelectionList.js');
90 
91  $this->mainTpl->setPermanentLink($this->gui->getObject()->getType(), $this->gui->getObject()->getRefId());
92  }
93 
97  private function showRoom(ilChatroom $room, ilChatroomUser $chat_user): void
98  {
99  $this->redirectIfNoPermission('read');
100 
101  $user_id = $chat_user->getUserId();
102 
103  $ref_id = $this->getRequestValue('ref_id', $this->refinery->kindlyTo()->int());
104  $this->navigationHistory->addItem(
105  $ref_id,
106  $this->ilCtrl->getLinkTargetByClass(ilRepositoryGUI::class, 'view'),
107  'chtr'
108  );
109 
110  if ($room->isUserBanned($user_id)) {
111  $this->cancelJoin($this->ilLng->txt('banned'));
112  return;
113  }
114 
115  $scope = $room->getRoomId();
116  $connector = $this->gui->getConnector();
117  $response = $connector->connect($scope, $user_id);
118 
119  if (!$response) {
120  $this->mainTpl->setOnScreenMessage('failure', $this->ilLng->txt('unable_to_connect'), true);
121  $this->ilCtrl->redirectByClass(ilInfoScreenGUI::class, 'showSummary');
122  }
123 
124  if (!$room->isSubscribed($chat_user->getUserId())) {
125  $room->connectUser($chat_user);
126  }
127 
128  $response = $connector->sendEnterPrivateRoom($scope, $user_id);
129  if (!$response) {
130  $this->mainTpl->setOnScreenMessage('failure', $this->ilLng->txt('unable_to_connect'), true);
131  $this->ilCtrl->redirectByClass(ilInfoScreenGUI::class, 'showSummary');
132  }
133 
134  $messages = $room->getSetting('display_past_msgs') ? array_reverse(array_filter(
135  $room->getLastMessages($room->getSetting('display_past_msgs'), $chat_user),
136  fn($entry) => $entry->type !== 'notice'
137  )) : [];
138 
139  $is_moderator = ilChatroom::checkUserPermissions('moderate', $ref_id, false);
140  $show_auto_messages = !$this->ilUser->getPref('chat_hide_automsg_' . $room->getRoomId());
141 
142  $build = $this->buildChat($room, $connector->getSettings());
143 
144  $room_tpl = $build->template(false, $build->initialData(
145  $room->getConnectedUsers(),
146  $show_auto_messages,
147  $this->ilCtrl->getLinkTarget($this->gui, 'view-lostConnection', '', false),
148  [
149  'moderator' => $is_moderator,
150  'id' => $chat_user->getUserId(),
151  'login' => $chat_user->getUsername(),
152  'broadcast_typing' => $chat_user->enabledBroadcastTyping(),
153  'profile_picture_visible' => $chat_user->isProfilePictureVisible(),
154  ],
155  $messages
156  ), $this->panel($this->ilLng->txt('write_message'), $this->sendMessageForm()), $this->panel($this->ilLng->txt('messages'), $this->legacy('<div id="chat_messages"></div>')));
157 
158  $this->mainTpl->setContent($room_tpl->get());
159  $this->mainTpl->setRightContent($this->userList() . $this->chatFunctions($show_auto_messages, $is_moderator));
160  }
161 
162  public function readOnlyChatWindow(ilChatroom $room, array $messages): ilTemplate
163  {
164  $build = $this->buildChat($room, $this->gui->getConnector()->getSettings());
165 
166  return $build->template(true, $build->initialData([], true, null, [
167  'moderator' => false,
168  'id' => -1,
169  'login' => null,
170  'broadcast_typing' => false,
171  ], $messages), $this->panel($this->ilLng->txt('messages'), $this->legacy('<div id="chat_messages"></div>')), '');
172  }
173 
174  private function sendMessageForm(): Component
175  {
176  $template = new ilTemplate('tpl.chatroom_send_message_form.html', true, true, 'components/ILIAS/Chatroom');
177  $this->renderSendMessageBox($template);
178 
179  return $this->legacy($template->get());
180  }
181 
182  private function userList(): string
183  {
184  $roomRightTpl = new ilTemplate('tpl.chatroom_right.html', true, true, 'components/ILIAS/Chatroom');
185  $this->renderRightUsersBlock($roomRightTpl);
186 
187  return $this->panel($this->ilLng->txt('users'), $this->legacy($roomRightTpl->get()));
188  }
189 
190  private function chatFunctions(bool $show_auto_messages, bool $is_moderator): string
191  {
192  $txt = $this->ilLng->txt(...);
193  $js_escape = json_encode(...);
194  $format = fn($format, ...$args) => sprintf($format, ...array_map($js_escape, $args));
195  $register = fn($name, $c) => $c->withOnLoadCode(fn($id) => $format(
196  'il.Chatroom.bus.send(%s, document.getElementById(%s));',
197  $name,
198  $id
199  ));
200 
201  $b = $this->uiFactory->button();
202  $toggle = fn($label, $enabled) => $b->toggle($label, '#', '#', $enabled)->withAriaLabel($label);
203 
204  $bind = fn($key, $m) => $m->withAdditionalOnLoadCode(fn(string $id) => $format(
205  '$(() => il.Chatroom.bus.send(%s, {
206  node: document.getElementById(%s),
207  showModal: () => $(document).trigger(%s, {}),
208  closeModal: () => $(document).trigger(%s, {})
209  }));',
210  $key,
211  $id,
212  $m->getShowSignal()->getId(),
213  $m->getCloseSignal()->getId()
214  ));
215 
216  $interrupt = fn($key, $label, $text, $button = null) => $bind($key, $this->uiFactory->modal()->interruptive(
217  $label,
218  $text,
219  ''
220  ))->withActionButtonLabel($button ?? $label);
221 
222  $auto_scroll = $register('auto-scroll-toggle', $toggle($txt('auto_scroll'), true));
223  $messages = $register('system-messages-toggle', $toggle($txt('chat_show_auto_messages'), $show_auto_messages));
224 
225  $invite = $bind('invite-modal', $this->uiFactory->modal()->roundtrip($txt('chat_invite'), $this->legacy($txt('invite_to_private_room')), [
226  $this->uiFactory->input()->field()->text($txt('chat_invite')),
227  ])->withSubmitLabel($txt('chat_invite')));
228 
229  $buttons = [];
230  $buttons[] = $register('invite-button', $b->shy($txt('invite_to_private_room'), ''));
231  if ($is_moderator) {
232  $buttons[] = $register('clear-history-button', $b->shy($txt('clear_room_history'), ''));
233  }
234 
235  return $this->panel($txt('chat_functions'), [
236  $this->legacy('<div id="chat_function_list">'),
237  ...$buttons,
238  $invite,
239  $interrupt('kick-modal', $txt('chat_kick'), $txt('kick_question')),
240  $interrupt('ban-modal', $txt('chat_ban'), $txt('ban_question')),
241  $interrupt('clear-history-modal', $txt('clear_room_history'), $txt('clear_room_history_question')),
242  $this->legacy('</div>'),
243  $this->legacy(sprintf('<div>%s%s</div>', $this->checkbox($auto_scroll), $this->checkbox($messages))),
244  ]);
245  }
246 
247  private function checkbox(Component $component): string
248  {
249  return sprintf('<div class="chatroom-centered-checkboxes">%s</div>', $this->uiRenderer->render($component));
250  }
251 
252  private function legacy(string $html): Component
253  {
254  return $this->uifactory->legacy()->content($html);
255  }
256 
260  private function panel(string $title, $body): string
261  {
262  if (is_array($body)) {
263  $body = $this->uiFactory->legacy()->content(join('', array_map($this->uiRenderer->render(...), $body)));
264  }
265  $panel = $this->uiFactory->panel()->secondary()->legacy($title, $body);
266 
267  return $this->uiRenderer->render($panel);
268  }
269 
270  public function toggleAutoMessageDisplayState(): void
271  {
272  $this->redirectIfNoPermission('read');
273 
274  $room = ilChatroom::byObjectId($this->gui->getObject()->getId());
275 
276  $state = 0;
277  if ($this->http->wrapper()->post()->has('state')) {
278  $state = $this->http->wrapper()->post()->retrieve('state', $this->refinery->kindlyTo()->int());
279  }
280 
282  $this->ilUser->getId(),
283  'chat_hide_automsg_' . $room->getRoomId(),
284  (string) ((int) (!(bool) $state))
285  );
286 
287  $this->http->saveResponse(
288  $this->http->response()
289  ->withHeader(ResponseHeader::CONTENT_TYPE, 'application/json')
290  ->withBody(Streams::ofString(json_encode(['success' => true], JSON_THROW_ON_ERROR)))
291  );
292  $this->http->sendResponse();
293  $this->http->close();
294  }
295 
299  private function cancelJoin(string $message): void
300  {
301  $this->mainTpl->setOnScreenMessage('failure', $message);
302  }
303 
304  protected function renderSendMessageBox(ilTemplate $roomTpl): void
305  {
306  $roomTpl->setVariable('PLACEHOLDER', $this->ilLng->txt('chat_osc_write_a_msg'));
307  $roomTpl->setVariable('LBL_SEND', $this->ilLng->txt('send'));
308  }
309 
310  protected function renderRightUsersBlock(ilTemplate $roomTpl): void
311  {
312  $roomTpl->setVariable('LBL_NO_FURTHER_USERS', $this->ilLng->txt('no_further_users'));
313  }
314 
315  private function showNameSelection(ilChatroomUser $chat_user): void
316  {
317  $name_options = $chat_user->getChatNameSuggestions();
318  $formFactory = new ilChatroomFormFactory();
319  $selectionForm = $formFactory->getUserChatNameSelectionForm($name_options);
320 
321  $this->ilCtrl->saveParameter($this->gui, 'sub');
322 
323  $selectionForm->addCommandButton('view-joinWithCustomName', $this->ilLng->txt('enter'));
324  $selectionForm->setFormAction(
325  $this->ilCtrl->getFormAction($this->gui, 'view-joinWithCustomName')
326  );
327 
328  $this->mainTpl->setVariable('ADM_CONTENT', $selectionForm->getHTML());
329  }
330 
337  public function executeDefault(string $requestedMethod): void
338  {
339  $this->redirectIfNoPermission('read');
340 
341  $this->gui->switchToVisibleMode();
342  $this->setupTemplate();
343 
344  $chatSettings = new ilSetting('chatroom');
345  if (!$chatSettings->get('chat_enabled', '0')) {
346  $this->ilCtrl->redirect($this->gui, 'settings-general');
347  }
348 
349  $room = ilChatroom::byObjectId($this->gui->getObject()->getId());
350 
351  if (!$room->getSetting('allow_anonymous') && $this->ilUser->isAnonymous()) {
352  $this->cancelJoin($this->ilLng->txt('chat_anonymous_not_allowed'));
353  return;
354  }
355 
356  $chat_user = new ilChatroomUser($this->ilUser, $room);
357 
358  if ($room->getSetting('allow_custom_usernames')) {
359  if ($room->isSubscribed($chat_user->getUserId())) {
360  $chat_user->setUsername($chat_user->getUsername());
361  $this->showRoom($room, $chat_user);
362  } else {
363  $this->showNameSelection($chat_user);
364  }
365  } else {
366  $chat_user->setUsername($this->ilUser->getPublicName());
367  $chat_user->setProfilePictureVisible(true);
368  $this->showRoom($room, $chat_user);
369  }
370  }
371 
372  public function logout(): void
373  {
374  $pid = $this->tree->getParentId($this->gui->getRefId());
375  $this->ilCtrl->setParameterByClass(ilRepositoryGUI::class, 'ref_id', $pid);
376  $this->ilCtrl->redirectByClass(ilRepositoryGUI::class);
377  }
378 
379  public function lostConnection(): void
380  {
381  if ($this->http->wrapper()->query()->has('msg')) {
382  match ($this->http->wrapper()->query()->retrieve('msg', $this->refinery->kindlyTo()->string())) {
383  'kicked' => $this->mainTpl->setOnScreenMessage('failure', $this->ilLng->txt('kicked'), true),
384  'banned' => $this->mainTpl->setOnScreenMessage('failure', $this->ilLng->txt('banned'), true),
385  default => $this->mainTpl->setOnScreenMessage('failure', $this->ilLng->txt('lost_connection'), true),
386  };
387  } else {
388  $this->mainTpl->setOnScreenMessage('failure', $this->ilLng->txt('lost_connection'), true);
389  }
390 
391  $this->ilCtrl->redirectByClass(ilInfoScreenGUI::class, 'showSummary');
392  }
393 
394  public function getUserProfileImages(): void
395  {
396  global $DIC;
397 
398  $response = [];
399 
400  $request = json_decode($this->http->request()->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR);
401 
403 
404  $users = $this->refinery->kindlyTo()->listOf($this->refinery->byTrying([
405  $this->refinery->kindlyTo()->recordOf([
406  'id' => $this->refinery->kindlyTo()->int(),
407  'username' => $this->refinery->kindlyTo()->string(),
408  'profile_picture_visible' => $this->refinery->kindlyTo()->bool(),
409  ]),
410  $this->refinery->kindlyTo()->recordOf([
411  'id' => $this->refinery->kindlyTo()->int(),
412  'username' => $this->refinery->kindlyTo()->string(),
413  ]),
414  ]))->transform($request['profiles'] ?? []);
415 
416  $user_ids = array_column($users, 'id');
417 
418  $public_data = ilUserUtil::getNamePresentation($user_ids, true, false, '', false, true, false, true);
419 
420  foreach ($users as $user) {
421  if ($user['profile_picture_visible'] ?? false) {
422  $public_image = $public_data[$user['id']]['img'] ?? '';
423  } else {
425  $avatar = $DIC["user.avatar.factory"]->avatar('xsmall');
426  $avatar->setUsrId(ANONYMOUS_USER_ID);
427  $avatar->setName(ilStr::subStr($user['username'], 0, 2));
428  $public_image = $avatar->getUrl();
429  }
430 
431  $response[json_encode($user, JSON_THROW_ON_ERROR)] = $public_image;
432  }
433 
434  $this->sendJSONResponse($response);
435  }
436 
437  public function userEntry(): void
438  {
439  global $DIC;
440 
441  $kindly = $this->refinery->kindlyTo();
442  $s = $kindly->string();
443  $int = $kindly->int();
444  $get = $this->http->wrapper()->query()->retrieve(...);
445  $get_or = fn($k, $t, $d = null) => $get($k, $this->refinery->byTrying([$t, $this->refinery->always($d)]));
446 
447  $ref_id = $get('ref_id', $int);
448  $user_id = $get('user_id', $int);
449  $username = $get('username', $s);
450  $actions = $get_or('actions', $kindly->dictOf($s), []);
451 
452  $avatar = $DIC["user.avatar.factory"]->avatar('xsmall');
453  $avatar->setUsrId(ANONYMOUS_USER_ID);
454  $avatar->setName(ilStr::subStr($username, 0, 2));
455  $public_image = $avatar->getUrl();
456  $item = $this->uiFactory->item()->standard($username)->withLeadImage($this->uiFactory->image()->standard(
457  $public_image,
458  'Profile image of ' . $username
459  ));
460 
462  $item = $item->withProperties([
463  $this->ilLng->txt('role') => $this->ilLng->txt('il_chat_moderator'),
464  ]);
465  }
466  $item = $item->withActions($this->uiFactory->dropdown()->standard($this->buildUserActions($user_id, $actions)));
467 
468 
469  $this->sendResponse($this->uiRenderer->renderAsync($item), 'text/html');
470  }
471 
476  private function buildUserActions(int $user_id, array $actions): array
477  {
478  $chat_settings = new ilSetting('chatroom');
479  $osc_enabled = $chat_settings->get('chat_enabled') && $chat_settings->get('enable_osc');
480  $translations = [
481  'kick' => $this->ilLng->txt('chat_kick'),
482  'ban' => $this->ilLng->txt('chat_ban'),
483  ];
484 
485  if ($osc_enabled && ilObjUser::_lookupPref($user_id, 'chat_osc_accept_msg') === 'y') {
486  $translations['chat'] = $this->ilLng->txt('start_private_chat');
487  }
488 
489  $buttons = [];
490  foreach ($actions as $key => $bus_id) {
491  $label = $translations[$key] ?? false;
492  if ($label) {
493  $buttons[] = $this->uiFactory->button()->shy($label, '')->withAdditionalOnLoadCode(fn(string $id): string => (
494  'il.Chatroom.bus.send(' . json_encode(
495  $bus_id,
496  JSON_THROW_ON_ERROR
497  ) . ', document.getElementById(' . json_encode($id, JSON_THROW_ON_ERROR) . '));'
498  ));
499  }
500  }
501 
502  return $buttons;
503  }
504 
505  private function buildChat(ilChatroom $room, ilChatroomServerSettings $settings): BuildChat
506  {
507  return new BuildChat($this->ilCtrl, $this->ilLng, $this->gui, $room, $settings, $this->ilUser, $this->uiFactory, $this->uiRenderer);
508  }
509 }
static checkUserPermissions($permissions, int $ref_id, bool $send_info=true)
Checks user permissions by given array and ref_id.
getSetting(string $name)
getUserId()
Returns Ilias User ID.
$scope
Definition: ltiregstart.php:47
const ANONYMOUS_USER_ID
Definition: constants.php:27
panel(string $title, $body)
redirectIfNoPermission($permission)
Checks for requested permissions and redirects if the permission check failed.
static initLinkify(?ilGlobalTemplateInterface $a_tpl=null)
Class ilChatroomViewGUI.
getRequestValue(string $key, Transformation $trafo, $default=null)
getLastMessages(int $number, ilChatroomUser $chatuser)
$response
Definition: xapitoken.php:93
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
renderSendMessageBox(ilTemplate $roomTpl)
static _lookupPref(int $a_usr_id, string $a_keyword)
static subStr(string $a_str, int $a_start, ?int $a_length=null)
Definition: class.ilStr.php:24
getConnectedUsers(bool $only_data=true)
$c
Definition: deliver.php:25
static setTokenMaxLifetimeInSeconds(int $token_max_lifetime_in_seconds)
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
sendResponse(string $content, string $type)
Sends a response and exits the php process.
$messages
Definition: xapiexit.php:21
cancelJoin(string $message)
Calls ilUtil::sendFailure method using given $message as parameter.
static http()
Fetches the global http state from ILIAS.
$ref_id
Definition: ltiauth.php:65
setVariable($variable, $value='')
Sets a variable value.
Definition: IT.php:544
static getNamePresentation( $a_user_id, bool $a_user_image=false, bool $a_profile_link=false, string $a_profile_back_link='', bool $a_force_first_lastname=false, bool $a_omit_login=false, bool $a_sortable=true, bool $a_return_data_array=false, $a_ctrl_path='ilpublicuserprofilegui')
Default behaviour is:
Class ilChatroomGUIHandler.
buildChat(ilChatroom $room, ilChatroomServerSettings $settings)
static checkPermissionsOfUser(int $usr_id, $permissions, int $ref_id)
Checks user permissions in question for a given user id in relation to a given ref_id.
setupTemplate()
Adds CSS and JavaScript files that should be included in the header.
isSubscribed(int $chat_userid)
global $DIC
Definition: shib_login.php:22
isUserBanned(int $user_id)
showRoom(ilChatroom $room, ilChatroomUser $chat_user)
Prepares and displays chatroom and connects user to it.
connectUser(ilChatroomUser $user)
$txt
Definition: error.php:31
sendJSONResponse($response)
Sends a json encoded response and exits the php process.
getChatNameSuggestions()
Returns an array of chat-name suggestions.
executeDefault(string $requestedMethod)
Chatroom and Chatuser get prepared before $this->showRoom method is called.
chatFunctions(bool $show_auto_messages, bool $is_moderator)
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
Class ilChatroomUser.
static byObjectId(int $object_id)
showNameSelection(ilChatroomUser $chat_user)
static _writePref(int $a_usr_id, string $a_keyword, string $a_value)
$message
Definition: xapiexit.php:31
getUsername()
Returns username from Object or SESSION.
buildUserActions(int $user_id, array $actions)
readOnlyChatWindow(ilChatroom $room, array $messages)
renderRightUsersBlock(ilTemplate $roomTpl)
checkbox(Component $component)