sidekick-1.0.x-dev/sidekick.module

sidekick.module
<?php

/**
 * @file
 * Hooks implementation for the Sidekick module.
 */

use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\Html;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\HtmlCommand;
use Drupal\Core\Ajax\InvokeCommand;
use Drupal\Core\Ajax\MessageCommand;
use Drupal\Core\Ajax\RemoveCommand;
use Drupal\Core\Ajax\SettingsCommand;
use Drupal\Core\Entity\ContentEntityFormInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\WidgetInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\file\Entity\File;
use Drupal\Core\Url;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\RequestOptions;
use Drupal\sidekick\ImageWidget;

/**
 * Implements hook_form_node_form_alter().
 */
function sidekick_form_node_form_alter(
  array &$form,
  FormStateInterface $form_state,
  $form_id,
): void {
  $config = Drupal::config('sidekick.settings');
  $display_type = $config->get('display_type');
  if (Drupal::currentUser()->hasPermission('sidekick content generation')
    && $display_type != 'no_display') {
    $language_code = $form_state->get('langcode');
    $sidekick_service = Drupal::service('sidekick.service');
    $sidekickKeyStatus = $sidekick_service->checkKeyStatus(
      $language_code,
    );

    if (!empty($sidekickKeyStatus['status']) && $sidekickKeyStatus['status'] == 200) {
      $weight = -11;
      $form['sidekick'] = [
        '#type' => 'details',
        '#title' => t('Sidekick'),
        '#group' => 'advanced',
        '#open' => TRUE,
        '#weight' => $weight,
        '#attributes' => ['class' => ['sidekick-frame']],
      ];
      $form['sidekick_message'] = [
        '#type' => 'html_tag',
        '#tag' => 'div',
        '#attributes' => [
          'class' => ['sidekick-message'],
        ],
        '#weight' => -100,
      ];
      // --Whole node content.
      $node = $form_state->getFormObject()->getEntity();
      if ($node->isNew()) {
        $form['#attached']['drupalSettings']['sidekick']['nodeDetails'] = [
          'version' => '1.0',
          'eventName' => 'page-changed',
          'data' => [
            'url' => '',
            'title' => '',
            'content' => '',
            'structuredContent' => [],
            'targetAudience' => $config->get('target_audience'),
            'pageBriefing' => $config->get('page_briefing'),
          ],
        ];
      }
      else {
        $build = Drupal::service('entity_type.manager')
          ->getViewBuilder($node->getEntityTypeId())
          ->view($node, 'default');
        $output = Drupal::service('renderer')->render($build);
        $content = Html::decodeEntities(strip_tags((string) $output));
        $node_url = Drupal::urlGenerator()->generateFromRoute(
          'entity.node.canonical',
          ['node' => $node->id(), ['absolute' => TRUE]]
        );
        $form['#attached']['drupalSettings']['sidekick']['nodeDetails'] = [
          'sidekickData' => [
            'version' => '1.0',
            'eventName' => 'page-changed',
            'data' => [
              'url' => $node_url,
              'title' => '',
              'content' => $content,
              'structuredContent' => [],
              'targetAudience' => $config->get('target_audience'),
              'pageBriefing' => $config->get('page_briefing'),
            ],
          ],
        ];
      }

      $api_url = Url::fromUri(
        'https://assistant.ai-sidekick.app/chat',
        [
          'query' => $sidekick_service->getQueryParameter(
            $language_code
          ),
        ]
      )->toString();

      if ('inline' == $display_type
        || 'inline_closed' == $display_type) {
        $form['sidekick']['iframe'] = [
          '#type' => 'html_tag',
          '#tag' => 'iframe',
          '#attributes' => [
            'src' => $api_url,
            'title' => t('Sidekick'),
            'referrerpolicy' => 'origin',
            'allow' => 'clipboard-write',
            'class' => ['sidekick-iframe'],
          ],
        ];
        if ('inline_closed' == $display_type) {
          $form['sidekick']['#open'] = FALSE;
        }
      }
      else {
        $form['iframe'] = [
          '#type' => 'html_tag',
          '#tag' => 'iframe',
          '#attributes' => [
            'src' => $api_url,
            'title' => t('Sidekick'),
            'referrerpolicy' => 'origin',
            'allow' => 'clipboard-write',
            'class' => ['sidekick-iframe-modal hidden'],
          ],
        ];
        $form['sidekick']['popup'] = [
          '#type' => 'html_tag',
          '#tag' => 'a',
          '#value' => t('Open Sidekick'),
          '#attributes' => [
            'class' => ['btn btn-primary button open-sidekick-modal'],
          ],
        ];
      }

      // This for silently send a ajax to get new content render for api.
      /* ---- TODO ----
       *  An AJAX Request to Send the content after each change
       */
      $form['sidekick']['sidekick_messaging_button'] = [
        '#type' => 'button',
        '#name' => 'sidekick-messaging-button',
        '#id' => 'sidekick-messaging-button',
        '#value' => t('Update messaging api data'),
        '#attributes' => [
          'class' => [
            'sidekick-messaging-button',
            'visually-hidden',
          ],
        ],
        '#ajax' => [
          'callback' => 'sidekick_messaging',
          'disable-refocus' => TRUE,
          'progress' => FALSE,
        ],
      ];
      $form['#attributes']['class'][] = 'sidekick-messaging-form';
      $form['#attached']['library'][] = 'sidekick/sidekick_js';
      $form['#attached']['library'][] = 'sidekick/sidekick_css';
    }
  }
}

/**
 * Ajax callback for sidekick messaging.
 */
function sidekick_messaging($form, FormStateInterface $form_state) {
  $form_object = $form_state->getFormObject();
  if ($form_object instanceof ContentEntityFormInterface) {
    /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */
    $entity_form_display = $form_state->get('form_display');
    $entity = $form_object->getEntity();
    $entity->in_preview = TRUE;
    $entity_form_display->extractFormValues($entity, $form, $form_state);
    $build = Drupal::service('entity_type.manager')
      ->getViewBuilder($entity->getEntityTypeId())
      ->view($entity, 'default');
    $output = Drupal::service('renderer')->render($build);
    $content = Html::decodeEntities(strip_tags((string) $output));

    $config = Drupal::config('sidekick.settings');
    $sideKickMessagingApiData = [
      'sidekickData' => [
        'version' => '1.0',
        'eventName' => 'page-changed',
        'data' => [
          'url' => '',
          'title' => $entity->label() ?? '',
          'content' => $content,
          'structuredContent' => [],
          'targetAudience' => $config->get('target_audience'),
          'pageBriefing' => $config->get('page_briefing'),
        ],
      ],
    ];
    $response = new AjaxResponse();
    $response->addCommand(
      new SettingsCommand($sideKickMessagingApiData, TRUE),
      TRUE
    );

    return $response;
  }

  return [];
}

/**
 * Implements hook_field_widget_third_party_settings_form().
 */
function sidekick_field_widget_third_party_settings_form(
  WidgetInterface $plugin,
  FieldDefinitionInterface $field_definition,
  $form_mode,
  $form,
  FormStateInterface $form_state,
) {
  $element = [];
  if (in_array($plugin->getPluginId(), [
    'string_textfield',
    'text_textfield',
    'string_textarea',
  ])) {
    $sidekick_module_options = $user_input = [];
    $language_code = $form_state->get('langcode');
    $sidekick_service = Drupal::service('sidekick.service');

    // Module.
    $modules = $sidekick_service->getModules(
      $language_code
    );
    if (!empty($modules['status']) && $modules['status'] == 401) {
      return $element;
    }

    if ($modules) {
      foreach ($modules['message']->data as $module) {
        $sidekick_module_options[$module->identifier] = ucfirst(
          str_replace('_', ' ', $module->identifier)
        );
        $user_input[$module->identifier] = $module->user_input;
      }
    }
    // Language.
    $languages = $sidekick_service->getLanguages(
      $language_code
    );
    $sidekick_language_options = [
      'auto' => t(
        'Auto (Select node language)'
      ),
    ];
    if ($languages) {
      foreach ($languages['message']->data as $language) {
        $sidekick_language_options[$language->identifier] = $language->name;
      }
    }
    // Writing style.
    $writing_styles = $sidekick_service->getWritingStyles(
      $language_code
    );
    $writing_style_options = [];
    if ($writing_styles) {
      foreach ($writing_styles['message']->data as $writing_style) {
        $writing_style_options[$writing_style->identifier] = $writing_style->label;
      }
    }
    // salutation.
    $salutation_options = [
      'formal' => t('Formal'),
      'informal' => t('Informal'),
      'avoid' => t('Avoid'),
    ];

    $element['sidekick_enabled'] = [
      '#type' => 'checkbox',
      '#title' => t('Enable Sidekick'),
      '#default_value' => $plugin->getThirdPartySetting(
        'sidekick',
        'sidekick_enabled'
      ),
      '#description' => t('If checked, Enable Sidekick API.'),
    ];
    $element['sidekick_settings'] = [
      '#type' => 'details',
      '#group' => 'advanced',
      '#open' => TRUE,
      '#title' => t('Sidekick API'),
      '#states' => [
        'visible' => [
          ':input[name="fields['
          . $field_definition->getName()
          . '][settings_edit_form][third_party_settings][sidekick][sidekick_enabled]"]'
          => ['checked' => TRUE],
        ],
      ],
    ];
    $element['sidekick_settings']['salutation'] = [
      '#type' => 'select',
      '#title' => t('Salutation'),
      '#options' => $salutation_options,
      '#default_value' => $plugin->getThirdPartySetting(
        'sidekick',
        'sidekick_settings'
      )['salutation'] ?? 'avoid',
      '#description' => t('Sidekick API Salutation to use.'),
    ];
    $element['sidekick_settings']['language'] = [
      '#type' => 'select',
      '#title' => t('Language'),
      '#options' => $sidekick_language_options,
      '#default_value' => $plugin->getThirdPartySetting(
        'sidekick',
        'sidekick_settings'
      )['language'] ?? 'auto',
      '#description' => t('Sidekick API Language to use.'),
    ];
    $element['sidekick_settings']['writing_style'] = [
      '#type' => 'select',
      '#title' => t('Writing Style'),
      '#options' => $writing_style_options,
      '#default_value' => $plugin->getThirdPartySetting(
        'sidekick',
        'sidekick_settings'
      )['writing_style'] ?? 'default',
      '#description' => t('Sidekick API Writing Style to use.'),
    ];
    $module = $plugin->getThirdPartySetting(
      'sidekick',
      'sidekick_settings'
    )['module'] ?? 'meta_description';
    $element['sidekick_settings']['module'] = [
      '#type' => 'select',
      '#title' => t('AI Automation'),
      '#api_user_input' => json_encode($user_input),
      '#field_in_action' => $field_definition->getName(),
      '#options' => $sidekick_module_options,
      '#default_value' => $module,
      '#description' => t('Sidekick API Module to use.'),
      '#ajax' => [
        'callback' => 'sidekick_create_module_user_input',
        'disable-refocus' => FALSE,
        'event' => 'change',
        'wrapper' => 'user-input-container',
      ],
    ];
    $element['sidekick_settings']['container'] = [
      '#type' => 'container',
      '#attributes' => ['id' => 'user-input-container'],
    ];
    $element['sidekick_settings']['tokens'] = [
      '#theme' => 'token_tree_link',
      '#token_types' => [
        'node',
      ],
    ];
    $element['sidekick_settings']['auto'] = [
      '#type' => 'checkbox',
      '#title' => t('Only run if empty and entity gets saved'),
      '#default_value' => $plugin->getThirdPartySetting(
        'sidekick',
        'sidekick_settings'
      )['auto'] ?? 0,
      '#description' => t(
        'If checked, auto run if empty and entity gets saved.'
      ),
    ];
    $element['sidekick_settings']['manual'] = [
      '#type' => 'checkbox',
      '#title' => t('Show manual trigger button'),
      '#default_value' => $plugin->getThirdPartySetting(
        'sidekick',
        'sidekick_settings'
      )['manual'] ?? 0,
      '#description' => t('If checked, show manual trigger button.'),
    ];

    if ($module) {
      foreach ($user_input[$module] as $inputfield) {
        if ($module == 'meta_description' && $inputfield->identifier == 'prefer') {
          $element['sidekick_settings']['container'][$module][$inputfield->identifier] = [
            '#type' => 'select',
            '#title' => ucfirst($inputfield->identifier),
            '#default_value' => $plugin->getThirdPartySetting(
              'sidekick',
              'sidekick_settings'
            )['container'][$module][$inputfield->identifier] ?? '',
            '#options' => [
              'quality' => t('quality'),
              'speed' => t('speed'),
            ],
            '#description' => $inputfield->description,
          ];
        }
        else {
          $element['sidekick_settings']['container'][$module][$inputfield->identifier] = [
            '#type' => 'textfield',
            '#placeholder' => $inputfield->identifier,
            '#title' => ucfirst($inputfield->identifier),
            '#default_value' => $plugin->getThirdPartySetting(
              'sidekick',
              'sidekick_settings'
            )['container'][$module][$inputfield->identifier] ?? '',
            '#description' => t('Token are supported'),
          ];
        }
      }
    }
    else {
      // ---- TODO ----
      echo 'Initial load';
    }
  }

  return $element;
}

/**
 * Create the user input for Call as per the Selection of Module.
 *
 * @param array $form
 *   The form fields for this plugin.
 * @param \Drupal\Core\Form\FormStateInterface $form_state
 *   The submitted form state.
 *
 * @return Drupal\Core\Ajax\AjaxResponse
 *   Returns AJAX response
 */
function sidekick_create_module_user_input(array $form, FormStateInterface $form_state) {
  $api_user_input = json_decode(
    $form_state->getTriggeringElement()['#api_user_input']
  );
  $fieldInAction = $form_state->getTriggeringElement()['#field_in_action'];
  $module = $form_state->getTriggeringElement()['#value'];
  $previousSelectedModule = $form['fields'][$fieldInAction]['plugin']['settings_edit_form']['third_party_settings']['sidekick']['sidekick_enabled']['#groups']['advanced'][0]['container'];

  $element = [];
  $setValue = '';
  foreach ($api_user_input->$module as $inputfield) {
    if ($module == 'meta_description' && $inputfield->identifier == 'prefer') {
      $element['sidekick_settings']['container'][$module][$inputfield->identifier] = [
        '#type' => 'select',
        '#name' => 'fields[' . $fieldInAction . '][settings_edit_form][third_party_settings][sidekick][sidekick_settings][container][' . $module . '][' . $inputfield->identifier . ']',
        '#title' => ucfirst($inputfield->identifier),
        '#options' => [
          'quality' => ucfirst('quality'),
          'speed' => ucfirst('speed'),
        ],
        '#description' => $inputfield->description,
      ];
    }
    else {
      if (isset($previousSelectedModule[$module])) {
        $setValue = $previousSelectedModule[$module][$inputfield->identifier]['#value'];
      }
      $element['sidekick_settings']['container'][$module][$inputfield->identifier] = [
        '#type' => 'textfield',
        '#name' => 'fields[' . $fieldInAction . '][settings_edit_form][third_party_settings][sidekick][sidekick_settings][container][' . $module . '][' . $inputfield->identifier . ']',
        '#title' => ucfirst($inputfield->identifier),
        '#placeholder' => $inputfield->identifier,
        '#value' => $setValue,
        '#description' => t('Token are supported'),
      ];
      /* ---- TODO ----
      if(in_array('required', $inputfield->validation)){
      $element['sidekick_settings']['container'][$module][
      $inputfield->identifier]['#required'] = true;
      }*/
    }
  }

  $response = new AjaxResponse();
  $response->addCommand(new HtmlCommand('#user-input-container', $element));

  return $response;
}

/**
 * Implements hook_field_widget_settings_summary_alter().
 */
function sidekick_field_widget_settings_summary_alter(&$summary, $context) {
  if (in_array($context['widget']->getPluginId(), [
    'string_textfield',
    'text_textfield',
    'string_textarea',
  ])) {
    if ($context['widget']
      ->getThirdPartySetting('sidekick', 'sidekick_enabled')) {
      $summary[] = t('Sidekick enabled.');
      if (isset(
        $context['widget']->getThirdPartySetting(
          'sidekick', 'sidekick_settings'
        )['salutation']
      )) {
        $salutation = ucfirst(
          str_replace('_', ' ',
            $context['widget']->getThirdPartySetting(
              'sidekick', 'sidekick_settings'
            )['salutation']
          )
        );
        $summary[] = new TranslatableMarkup(
            'Salutation: :salutation',
            [':salutation' => $salutation]
        );
      }
      if (isset(
        $context['widget']->getThirdPartySetting(
          'sidekick',
          'sidekick_settings'
        )['module']
      )) {
        $module = ucfirst(
          str_replace('_', ' ',
            $context['widget']->getThirdPartySetting(
              'sidekick', 'sidekick_settings'
            )['module']
          )
        );
        $summary[] = new TranslatableMarkup(
          'Module: :salutation',
          [':salutation' => $module]
        );
      }
      if (isset(
        $context['widget']->getThirdPartySetting(
          'sidekick', 'sidekick_settings'
        )['language']
      )) {
        $language = ucfirst(
          str_replace('_', ' ',
            $context['widget']->getThirdPartySetting(
              'sidekick', 'sidekick_settings'
            )['language']
          )
        );
        $summary[] = new TranslatableMarkup(
          'Language: :language',
          [':language' => $language]
        );
      }
      if (isset(
        $context['widget']->getThirdPartySetting(
          'sidekick', 'sidekick_settings'
        )['writing_style']
      )) {
        $writing_style = ucfirst(
          str_replace('_', ' ',
            $context['widget']->getThirdPartySetting(
              'sidekick', 'sidekick_settings'
            )['writing_style']
          )
        );
        $summary[] = new TranslatableMarkup(
          'Writing style: :writing_style',
          [':writing_style' => $writing_style]
        );
      }
    }
  }
}

/**
 * Implements hook_field_widget_complete_form_alter().
 */
function sidekick_field_widget_complete_form_alter(
  &$field_widget_complete_form,
  FormStateInterface $form_state,
  $context,
) {
  $language_code = $form_state->get('langcode');
  $sidekick_service = Drupal::service('sidekick.service');
  $sidekickKeyStatus = $sidekick_service->checkKeyStatus(
    $language_code,
  );
  if (!empty($sidekickKeyStatus['status']) && $sidekickKeyStatus['status'] == 200) {
    $widget = $context['widget'];
    if (in_array($widget->getPluginId(), [
      'string_textfield',
      'text_textfield',
      'string_textarea',
    ]) && $widget->getThirdPartySetting(
      'sidekick',
      'sidekick_enabled',
      FALSE
    )) {
      if ($widget->getThirdPartySetting(
        'sidekick',
        'sidekick_settings'
      )['manual']) {
        $field_widget_complete_form['#attributes']['class'] = array_merge(
          $field_widget_complete_form['#attributes']['class'], ['sidekick-field-wrapper']
        );
        $field_name = $field_widget_complete_form['widget']['#field_name'];
        $wrapper_id = 'edit-' . str_replace(
          '_',
          '-',
          strtolower($field_name)
        ) . '-0-value';
        $field_widget_complete_form[$field_name . '_sidekick'] = [
          '#type' => 'button',
          '#name' => $wrapper_id . '__sidekick',
          '#value' => '',
          '#executes_submit_callback' => FALSE,
          '#field_in_action' => $field_name,
          '#input_type' => $widget->getPluginId(),
          '#sidekick_module' => $widget->getThirdPartySetting(
            'sidekick',
            'sidekick_settings'
          )['module'] ?? 'meta_description',
          '#sidekick_language' => $widget->getThirdPartySetting(
            'sidekick',
            'sidekick_settings'
          )['language'] ?? 'auto',
          '#writing_style' => $widget->getThirdPartySetting(
            'sidekick',
            'sidekick_settings'
          )['writing_style'] ?? 'default',
          '#salutation' => $widget->getThirdPartySetting(
            'sidekick',
            'sidekick_settings'
          )['salutation'] ?? 'avoid',
          '#user_input' => $widget->getThirdPartySetting(
            'sidekick',
            'sidekick_settings'
          )['container'] ?? '',
          '#ajax' => [
            'callback' => 'sidekick_ajax_callback',
            'wrapper' => $wrapper_id,
          ],
        ];

        $field_widget_complete_form[$field_name . '_sidekick']['#attributes']['class'][] = 'sidekick-button-icon';
        $field_widget_complete_form['#attached']['library'][] = 'sidekick/sidekick_css';
        $field_widget_complete_form['#attached']['library'][] = 'sidekick/sidekick_js';
      }
    }
  }
}

/**
 * Ajax callback for the Sidekick button.
 */
function sidekick_ajax_callback(array $form, FormStateInterface $form_state) {
  $form_object = $form_state->getFormObject();
  if ($form_object instanceof ContentEntityFormInterface) {
    /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */
    $entity_form_display = $form_state->get('form_display');
    $entity = $form_object->getEntity();
    $entity->in_preview = TRUE;
    $entity_form_display->extractFormValues($entity, $form, $form_state);
    $button = $form_state->getTriggeringElement();
    $element = '#' . explode('__', $button['#name'])[0];
    $build = Drupal::service('entity_type.manager')
      ->getViewBuilder($entity->getEntityTypeId())
      ->view($entity, 'default');
    $output = Drupal::service('renderer')->render($build);
    // Get text content send to sidekick.
    $content = Html::decodeEntities(strip_tags((string) $output));
    $entity->in_preview = NULL;

    $userFormFieldValue = $form_state->getValue(
      $button['#field_in_action']
    )[0]['value'];

    $user_input = [];
    if ($button['#user_input']) {
      $language_code = $form_state->get('langcode');
      $sidekick_service = Drupal::service('sidekick.service');

      // Module.
      $modules = $sidekick_service->getModules(
        $language_code
      );

      $token_service = Drupal::token();
      if ($modules) {
        foreach ($modules['message']->data as $module) {
          if ($module->identifier == $button['#sidekick_module']) {
            foreach ($module->user_input as $key => $input) {
              $user_input[$key]['identifier'] = $input->identifier;
              $wholePageContent = $token_service->replace(
                $content,
                ['node' => $build['#node']],
                ['clear' => TRUE]
              );
              $processedValue = $token_service->replace(
                  $button['#user_input'][$button['#sidekick_module']][$input->identifier],
                  ['node' => $build['#node']],
                  ['clear' => TRUE]
                ) . ' ' . $userFormFieldValue . ' ' . $wholePageContent;
              if (in_array('array', $input->validation)) {
                $user_input[$key]['value'] = [$processedValue];
              }
              elseif ($input->identifier == 'prefer') {
                if (!empty($form['#entity_type']) && $form['#entity_type'] == 'node') {
                  $entity_type = 'node';
                  $bundle = $entity->getEntityTypeId();
                  $form_mode = 'default';

                  $form_display = Drupal::service('entity_display.repository')
                    ->getFormDisplay($entity_type, $bundle, $form_mode);
                  $specific_widget_type = $form_display->getComponent($button['#field_in_action']);
                }
                $user_input[$key]['value'] = $specific_widget_type["third_party_settings"]["sidekick"]["sidekick_settings"]["container"]["meta_description"]['prefer'];
              }
              else {
                $user_input[$key]['value'] = $processedValue;
              }
            }
          }
        }
      }
    }
    else {
      // To maintain old code working.
      $user_input = [
        [
          "identifier" => "content",
          "value" => $content,
        ],
        [
          "identifier" => "title",
          "value" => $entity->label() ?? '',
        ],
      ];
    }

    $language_code = $button['#sidekick_language'];
    if ($language_code == 'auto') {
      $language_code = Drupal::languageManager()->getCurrentLanguage(
        LanguageInterface::TYPE_CONTENT
      )->getId();
    }
    if (strlen($language_code) == 5) {
      // @todo Sidekick accepts only 2 digit language codes, is there a better way to extract this from Drupal?
      $language_code = explode('-', $language_code)[0];
    }

    $sidekick_service = Drupal::service('sidekick.service');

    // Chat call.
    $chatData = $sidekick_service->getChat(
      $button['#sidekick_module'],
      $language_code,
      $button['#writing_style'],
      $button['#salutation'],
      $user_input
    );

    $data_from_sidekick = $chatData['message']->data->message->message ?? $chatData['message'];

    $response = new AjaxResponse();
    if ($chatData['status'] == 200) {
      if (
        ((str_contains($data_from_sidekick, "1.") !== FALSE &&
            str_contains($data_from_sidekick, "2.")) ||
          str_contains($data_from_sidekick, "```\n")) &&
        (
          $button['#input_type'] == 'text_textfield' ||
          $button['#input_type'] == 'string_textfield'
        )
      ) {
        if (str_contains($data_from_sidekick, "```\n")) {
          $parts = explode("```\n", $data_from_sidekick);
        }
        else {
          $parts = explode("\n", $data_from_sidekick);
        }
        $optionString = '';
        foreach ($parts as $key => $option) {
          $option = preg_replace('/\d+\.\s/', '', $option);
          $option = str_replace('```\n', '', $option);
          $option = str_replace('"', '', $option);
          if ($key == 0) {
            $response->addCommand(
              new InvokeCommand($element, 'val', [$option])
            );
          }
          else {
            $optionString = $optionString . '<li class="ui-menu-item"><a class="ui-menu-item-wrapper">' . $option . '</a></li>';
          }
        }
        $suggestionsClass = 'sidekick-suggestions-for-' . str_replace('#', '', $element);
        $response->addCommand(
          new RemoveCommand($suggestionsClass)
        );
        $response->addCommand(
          new InvokeCommand($element, 'after', ['<div class="sidekick-suggestions ' . $suggestionsClass . '"></div>'])
        );
        $response->addCommand(
          new HtmlCommand('.' . $suggestionsClass, '<ul class="ui-menu ui-widget ui-widget-content ui-autocomplete ui-front">' . $optionString . '</ul>')
        );
        $response->addCommand(new InvokeCommand(NULL, 'initSuggestions', [
          $element,
          '.' . $suggestionsClass,
        ]));
      }
      else {
        $response->addCommand(
          new InvokeCommand($element, 'val', [$data_from_sidekick])
        );
      }

      $response->addCommand(
        new RemoveCommand('.sidekick-message .messages')
      );
    }
    else {
      $response->addCommand(
        new MessageCommand(
          $data_from_sidekick,
          '.sidekick-message',
          ['type' => 'error']
        )
      );
    }

    return $response;
  }

  return [];
}

/**
 * Ajax callback for the Sidekick button.
 */
function sidekick_image_ajax_callback(
  array $form,
  FormStateInterface $form_state,
) {
  $form_object = $form_state->getFormObject();
  if ($form_object instanceof ContentEntityFormInterface) {
    /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */
    $button = $form_state->getTriggeringElement();

    $element = '[id^=' . explode('__', $button['#name'])[0] . ']';

    $field_name = $button['#field_name'];
    $sidekick_module = $button['#sidekick_module'];
    $support_languages = [
      'de' => 'de',
      'en' => 'en_US',
      'en_GB' => 'en_GB',
    ];
    $language = $form_object->getEntity()->language()->getId();
    $language = $support_languages[$language] ?? 'en';
    $file = File::load($form_state->getValue($field_name)[0]['fids'][0]);
    $image_url = Drupal::service('file_url_generator')
      ->generateAbsoluteString($file->getFileUri());

    $sidekick_api_key = Drupal::service('config.factory')
      ->get('sidekick.settings')
      ->get('api_key');
    $client = Drupal::service('http_client');
    $api_url = 'https://assistant.ai-sidekick.app/api/v1/chat';
    $request_options = [];
    $request_options[RequestOptions::QUERY] = ['language' => $language];
    $request_options[RequestOptions::BODY] = Json::encode([
      'module' => $sidekick_module,
      'user_input' => [
        [
          "identifier" => "url",
          "value" => $image_url ?? '',
        ],
      ],
    ]);
    $request_options[RequestOptions::HEADERS]['Content-Type'] = 'application/json';
    $request_options[RequestOptions::HEADERS]['Accept'] = 'application/json';
    $request_options[RequestOptions::HEADERS]['Authorization'] = 'Bearer ' . $sidekick_api_key;

    try {
      $response = $client->request('POST', $api_url, $request_options);
      $data = Json::decode((string) $response->getBody());
      $data_from_sidekick = $data['data']['message']['message'] ?? '';
      $response = new AjaxResponse();
      $response->addCommand(
        new InvokeCommand($element, 'val', [$data_from_sidekick])
      );
      return $response;
    }
    catch (ClientException $e) {
      $response = new AjaxResponse();
      return $response->addCommand(
        new MessageCommand(
          json_decode($e->getResponse()
            ->getBody()
            ->getContents())->message,
          '.sidekick-message',
          ['type' => 'error']
        )
      );
    }
  }

  return [];
}

/**
 * Implements hook_field_widget_info_alter().
 */
function sidekick_field_widget_info_alter(array &$info) {
  // This make new widget replace default to alter process.
  $info['image_image']['class'] = ImageWidget::class;
}

/**
 * Implements hook_theme().
 */
function sidekick_theme($existing, $type, $theme, $path) {
  return [
    'image_widget__default' => [
      'base hook' => 'image_widget',
    ],
    'usage_bar' => [
      'variables' => [
        'sidekickAccountUsage' => NULL,
      ],
    ],
  ];
}

/**
 * Implements hook_theme_suggestions_alter().
 */
function sidekick_theme_suggestions_alter(
  array &$suggestions,
  array $variables,
  $hook,
) {
  // core/themes/claro/templates/content-edit/image-widget.html.twig.
  // grouping alt and title, this make new template to have button between.
  if ($hook == 'image_widget') {
    $suggestions[] = $hook . '__default';
  }
}

/**
 * Implements hook_help().
 */
function sidekick_help($route_name, RouteMatchInterface $route_match) {
  $settings_form_url = Url::fromRoute('sidekick.settings_form')->toString();

  switch ($route_name) {
    case 'help.page.sidekick':
      $output = '';

      $output .= '<p>' . new TranslatableMarkup(
        'To save the Sidekick API key, go to <a href=":settings_form_url ">Settings form</a>.',
        [':settings_form_url ' => $settings_form_url]
      ) . '</p>';

      $output .= '<p>' . new TranslatableMarkup(
        'To configure a field to display a Sidekick button next to the field, go to the <strong>Manage form display</strong> tab for the content type. For example: <em>Structure > Content Types > Article > Manage form display</em>. Click on gear icon and check the Enable Sidekick checkbox. Select dropdown for different options and click on Update.'
      ) . '</p>';

      return [
        '#type' => 'markup',
        '#markup' => $output,
        '#allowed_tags' => ['a', 'p', 'strong', 'em', 'br'],
      ];
  }
}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc