-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
[RW-1134] Show chat popup to anonymous users
- Loading branch information
Showing
12 changed files
with
321 additions
and
91 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
_core: | ||
default_config_hash: fU8Wp0OCsudl5x0QSV_mUWWt2KbQuzhVwEB-o9A8peg | ||
ocha_ai_chat: | ||
allow_for_anonymous: false | ||
instructions_replace: false | ||
login_instructions: | | ||
<p>The use of <strong>Ask ReliefWeb</strong> requires an account on ReliefWeb.</p> | ||
<p>Please <a href="/user/login/hid?destination=@destination" target="_parent">login</a> or <a href="https://auth.humanitarian.id/register" target="_parent">create a new account</a>.</p> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
ReliefWeb AI | ||
============ | ||
|
||
AI usage for ReliefWeb. | ||
|
||
## AI chat | ||
|
||
Currently this module simply alters the chat form to display the chat popup to anonymous users but asking them to log in or register an account to use the chat. This behavior can be controller via the configuration of this module. |
7 changes: 7 additions & 0 deletions
7
html/modules/custom/reliefweb_ai/config/install/reliefweb_ai.settings.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
ocha_ai_chat: | ||
allow_for_anonymous: false | ||
instructions_replace: false | ||
login_instructions: | | ||
<p>The use of <strong>Ask ReliefWeb</strong> requires an account on ReliefWeb.</p> | ||
<p>Please <a href="/user/login/hid?destination=@destination" target="_parent">login</a> or <a href="https://auth.humanitarian.id/register" target="_parent">create a new account</a>.</p> | ||
18 changes: 18 additions & 0 deletions
18
html/modules/custom/reliefweb_ai/config/schema/reliefweb_ai.schema.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
reliefweb_ai.settings: | ||
type: config_object | ||
label: 'ReliefWeb AI settings.' | ||
mapping: | ||
ocha_ai_chat: | ||
type: mapping | ||
label: 'Settings for the OCHA AI chat.' | ||
mapping: | ||
allow_for_anonymous: | ||
type: boolean | ||
label: 'Allow anonymous user to access the chat.' | ||
instructions_replace: | ||
type: boolean | ||
label: 'If TRUE, replace the chat instructions when the chat is disabled (error, anonymous access etc.), otherwise append the extra instructions.' | ||
login_instructions: | ||
type: text | ||
label: 'Login or register instructions for anonymous users.' | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
type: module | ||
name: ReliefWeb AI | ||
description: 'AI usage for ReliefWeb' | ||
package: reliefweb | ||
core_version_requirement: ^10 | ||
dependencies: | ||
- drupal:ocha_ai |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
<?php | ||
|
||
/** | ||
* @file | ||
* ReliefWeb AI module file. | ||
*/ | ||
|
||
use Drupal\Component\Utility\UrlHelper; | ||
use Drupal\Core\Block\BlockPluginInterface; | ||
use Drupal\Core\Form\FormStateInterface; | ||
use Drupal\Core\Render\Element; | ||
use Drupal\reliefweb_ai\OchaAiChatPopupBlockHandler; | ||
|
||
/** | ||
* Implements hook_form_FORM_ID_alter() for `ocha_ai_chat_chat_form`. | ||
*/ | ||
function reliefweb_ai_form_ocha_ai_chat_chat_form_alter(array &$form, FormStateInterface $form_state, string $form_id) { | ||
$config = \Drupal::config('reliefweb_ai.settings'); | ||
$current_user = \Drupal::currentUser(); | ||
$url = \Drupal::request()?->query?->get('url'); | ||
|
||
// Add some caching context and tags. | ||
$form['#cache']['contexts'] = array_merge($form['#cache']['contexts'] ?? [], [ | ||
'user.roles', 'url.query_args', | ||
]); | ||
$form['#cache']['tags'] = array_merge($form['#cache']['tags'] ?? [], [ | ||
'config:reliefweb_ai.settings', | ||
]); | ||
|
||
// Add a more unique class to the chat submit button. | ||
if (isset($form['actions']['submit'])) { | ||
$form['actions']['submit']['#attributes']['class'][] = 'ocha-ai-chat-ask'; | ||
} | ||
|
||
// Message to display if the form is disabled for any reason. | ||
$disabled = ''; | ||
|
||
// Check if the user is anonymous and not allowed to access the chat. | ||
if ($current_user->isAnonymous() && !$config->get('ocha_ai_chat.allow_for_anonymous')) { | ||
$disabled = $config->get('ocha_ai_chat.login_instructions') ?? ''; | ||
|
||
// Redirect to the current page if possible. | ||
if (!empty($url)) { | ||
$disabled = strtr($disabled, [ | ||
'@destination' => UrlHelper::encodePath(parse_url($url, \PHP_URL_PATH)), | ||
]); | ||
} | ||
} | ||
|
||
if (empty($disabled)) { | ||
// Check if we have a URL to allow the chat. | ||
if (empty($url)) { | ||
$instructions = t('<p>Something went wrong.</p>'); | ||
} | ||
// Otherwise check if the language or type of the document. | ||
else { | ||
$router = \Drupal::service('router.no_access_checks'); | ||
$parameters = $router->match($url); | ||
$node = $parameters['node'] ?? NULL; | ||
|
||
// Disable the form if it's not a report. | ||
if (!isset($node) || $node->bundle() !== 'report') { | ||
$disabled = t('<p>Something went wrong.</p>'); | ||
} | ||
else { | ||
// No need to show the source when chatting with a single report. | ||
if (isset($form['source'])) { | ||
$form['source']['#access'] = FALSE; | ||
} | ||
|
||
// Only English documents are supported due to LLM limitations. | ||
$is_english_report = FALSE; | ||
foreach ($node->field_language as $item) { | ||
if ($item->target_id == 267) { | ||
$is_english_report = TRUE; | ||
break; | ||
} | ||
} | ||
if (!$is_english_report) { | ||
$disabled = t('<p>Sorry, only <strong>English</strong> reports are supported.</p>'); | ||
} | ||
|
||
// Non supported content formats. | ||
foreach ($node->field_content_format as $item) { | ||
if ($item->target_id == 12) { | ||
$disabled = t('<p>Sorry, <strong>maps</strong> are not supported.</p>'); | ||
break; | ||
} | ||
elseif ($item->target_id == 12570) { | ||
$disabled = t('<p>Sorry, <strong>infographics</strong> are not supported.</p>'); | ||
break; | ||
} | ||
elseif ($item->target_id == 38974) { | ||
$disabled = t('<p>Sorry, <strong>interactive reports</strong> are not supported.</p>'); | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
if (!empty($disabled)) { | ||
// Check whether we are requested to replace the instructions or append the | ||
// disabled instructions. | ||
$replace = $config->get('ocha_ai_chat.instructions_replace') === TRUE || !isset($form['chat']['content']); | ||
|
||
if (!$replace) { | ||
// We cannot just append the instructions to the current ones because of | ||
// the text format may include sanitation that removes the target | ||
// attributes. We indeed need to preserve those attributes in the login | ||
// instructions so the login and register links open in parent window | ||
// and not in the chat iframe. | ||
// So first we format the current instructions and then append the extra | ||
// instructions. | ||
$text = $form['chat']['content']['#text'] ?? ''; | ||
$format = $form['chat']['content']['#format'] ?? 'markdown_editor'; | ||
$instructions = (string) check_markup($text, $format); | ||
$disabled = $instructions . $disabled; | ||
} | ||
|
||
// Replace the instructions with a simple markup render element. | ||
$form['chat']['content'] = [ | ||
'#type' => 'markup', | ||
'#markup' => $disabled, | ||
'#prefix' => '<div id="ocha-ai-chat-instructions" class="ocha-ai-chat-chat-form__instructions">', | ||
'#suffix' => '</div>', | ||
]; | ||
|
||
// Disable or hide the reset of the form. | ||
foreach (Element::children($form['chat']) as $key) { | ||
if ($key !== 'content') { | ||
$form['chat'][$key]['#access'] = FALSE; | ||
} | ||
} | ||
foreach (Element::children($form) as $key) { | ||
if ($key !== 'chat') { | ||
$form[$key]['#disabled'] = TRUE; | ||
} | ||
} | ||
|
||
$form['#cache']['max-age'] = 3600; | ||
} | ||
} | ||
|
||
/** | ||
* Implements hook_block_view_alter(). | ||
*/ | ||
function reliefweb_ai_block_view_alter(array &$build, BlockPluginInterface $block): void { | ||
// Alter the chat popup block, notably to adjust the caching. | ||
if ($block->getPluginId() === 'ocha_ai_chat_chat_popup') { | ||
$build['#pre_render'][] = [OchaAiChatPopupBlockHandler::class, 'alterBuild']; | ||
return; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
services: | ||
reliefweb_ai.ocha_ai_chat_cache_subscriber: | ||
class: Drupal\reliefweb_ai\EventSubscriber\OchaAiChatCacheSubscriber | ||
arguments: ['@config.factory', '@current_user'] | ||
tags: | ||
- { name: event_subscriber } |
73 changes: 73 additions & 0 deletions
73
html/modules/custom/reliefweb_ai/src/EventSubscriber/OchaAiChatCacheSubscriber.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
<?php | ||
|
||
namespace Drupal\reliefweb_ai\EventSubscriber; | ||
|
||
use Drupal\Core\Cache\CacheableResponseInterface; | ||
use Drupal\Core\Config\ConfigFactoryInterface; | ||
use Drupal\Core\Session\AccountProxyInterface; | ||
use Symfony\Component\EventDispatcher\EventSubscriberInterface; | ||
use Symfony\Component\HttpKernel\Event\ResponseEvent; | ||
use Symfony\Component\HttpKernel\KernelEvents; | ||
|
||
/** | ||
* Modify caching of the OCHA AI chat form. | ||
*/ | ||
class OchaAiChatCacheSubscriber implements EventSubscriberInterface { | ||
|
||
/** | ||
* Constructor. | ||
* | ||
* @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory | ||
* The config factory. | ||
* @param \Drupal\Core\Session\AccountProxyInterface $currentUser | ||
* The current user. | ||
*/ | ||
public function __construct( | ||
protected ConfigFactoryInterface $configFactory, | ||
protected AccountProxyInterface $currentUser, | ||
) {} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public static function getSubscribedEvents(): array { | ||
return [ | ||
// We need to run last to ensure the cache is not overridden. | ||
KernelEvents::RESPONSE => ['onResponse', -1000], | ||
]; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function onResponse(ResponseEvent $event): void { | ||
$request = $event->getRequest(); | ||
$route = $request->attributes->get('_route'); | ||
|
||
if ($route === 'ocha_ai_chat.chat_form' || $route === 'ocha_ai_chat.chat_form.popup') { | ||
$response = $event->getResponse(); | ||
|
||
// Ajax response are not cacheable so only handle normal form response. | ||
if ($response instanceof CacheableResponseInterface) { | ||
$config = $this->configFactory->get('reliefweb_ai.settings'); | ||
|
||
$cache_metadata = $response->getCacheableMetadata(); | ||
|
||
// Vary the cache by role, url parameters and config since they control | ||
// what is displayed to the user. | ||
$cache_metadata->addCacheContexts(['user.roles', 'url.query_args']); | ||
$cache_metadata->addCacheTags(['config:reliefweb_ai.settings']); | ||
|
||
// Cache the response for 1 hour for anonymous user when we just show | ||
// a disabled form asking to log in or register. | ||
if ($this->currentUser->isAnonymous() && !$config->get('ocha_ai_chat.allow_for_anonymous')) { | ||
// Cache for 1 hour. | ||
$cache_metadata->setCacheMaxAge(3600); | ||
// Ensure varnish for example can cache the page. | ||
$response->headers->set('Cache-Control', 'public, max-age=3600'); | ||
} | ||
} | ||
} | ||
} | ||
|
||
} |
37 changes: 37 additions & 0 deletions
37
html/modules/custom/reliefweb_ai/src/OchaAiChatPopupBlockHandler.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Drupal\reliefweb_ai; | ||
|
||
use Drupal\Core\Security\TrustedCallbackInterface; | ||
|
||
/** | ||
* Handle modification to the OCHA AI chat popup block. | ||
*/ | ||
class OchaAiChatPopupBlockHandler implements TrustedCallbackInterface { | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public static function trustedCallbacks(): array { | ||
return ['alterBuild']; | ||
} | ||
|
||
/** | ||
* Alter the block build. | ||
* | ||
* @param array $build | ||
* Block build. | ||
* | ||
* @return array | ||
* Modified block build. | ||
*/ | ||
public static function alterBuild(array $build): array { | ||
unset($build['content']['#cache']['max-age']); | ||
$build['content']['#cache']['contexts'][] = 'user.roles'; | ||
$build['content']['#cache']['contexts'][] = 'url.path'; | ||
return $build; | ||
} | ||
|
||
} |
Oops, something went wrong.