View source
<?php
declare (strict_types=1);
namespace Drupal\entity_share_client\Form;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Component\Render\MarkupInterface;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Link;
use Drupal\Core\Pager\PagerManagerInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url;
use Drupal\entity_share_client\ImportContext;
use Drupal\entity_share_client\Service\FormHelperInterface;
use Drupal\entity_share_client\Service\ImportServiceInterface;
use Drupal\entity_share_client\Service\RemoteManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
class PullForm extends FormBase {
protected $remoteWebsites;
protected $channelsInfos;
protected $fieldMappings;
protected $entityTypeManager;
protected $remoteManager;
protected $formHelper;
protected $query;
protected $languageManager;
protected $renderer;
protected $moduleHandler;
protected $importService;
protected $pagerManager;
public function __construct(EntityTypeManagerInterface $entity_type_manager, RemoteManagerInterface $remote_manager, FormHelperInterface $form_helper, RequestStack $request_stack, LanguageManagerInterface $language_manager, RendererInterface $renderer, ModuleHandlerInterface $module_handler, ImportServiceInterface $import_service, PagerManagerInterface $pager_manager) {
$this->entityTypeManager = $entity_type_manager;
$this->remoteWebsites = $entity_type_manager
->getStorage('remote')
->loadMultiple();
$this->remoteManager = $remote_manager;
$this->formHelper = $form_helper;
$this->query = $request_stack
->getCurrentRequest()->query;
$this->languageManager = $language_manager;
$this->renderer = $renderer;
$this->moduleHandler = $module_handler;
$this->importService = $import_service;
$this->pagerManager = $pager_manager;
}
public static function create(ContainerInterface $container) {
return new static($container
->get('entity_type.manager'), $container
->get('entity_share_client.remote_manager'), $container
->get('entity_share_client.form_helper'), $container
->get('request_stack'), $container
->get('language_manager'), $container
->get('renderer'), $container
->get('module_handler'), $container
->get('entity_share_client.import_service'), $container
->get('pager.manager'));
}
public function getFormId() {
return 'entity_share_client_pull_form';
}
public function buildForm(array $form, FormStateInterface $form_state) {
$select_element = $this
->buildSelectElement($form_state, 'import_config');
if ($select_element) {
$select_element['#title'] = $this
->t('Import configuration');
$form['import_config'] = $select_element;
}
else {
$url = Url::fromRoute('entity.import_config.collection');
if ($url
->renderAccess($url
->toRenderArray())) {
$this
->messenger()
->addError($this
->t('Please configure <a href=":url">Import configuration</a> before trying to import content.', [
':url' => $url
->toString(),
]));
}
else {
$this
->messenger()
->addError($this
->t('There are no "Import configuration" available. Please contact the website administrator.'));
}
}
$select_element = $this
->buildSelectElement($form_state, 'remote');
if ($select_element) {
$select_element['#title'] = $this
->t('Remote website');
$select_element['#ajax'] = [
'callback' => [
get_class($this),
'buildAjaxChannelSelect',
],
'effect' => 'fade',
'method' => 'replace',
'wrapper' => 'channel-wrapper',
];
$form['remote'] = $select_element;
}
$form['channel_wrapper'] = [
'#type' => 'container',
'#attributes' => [
'id' => 'channel-wrapper',
],
];
$this
->buildChannelSelect($form, $form_state);
return $form;
}
public function validateForm(array &$form, FormStateInterface $form_state) {
}
public function submitForm(array &$form, FormStateInterface $form_state) {
$additional_query_parameters = [];
$get_offset = $this->query
->get('offset');
if (is_numeric($get_offset)) {
$additional_query_parameters['offset'] = $get_offset;
}
$get_page = $this->query
->get('page');
if (is_numeric($get_page)) {
$additional_query_parameters['page'] = $get_page;
}
$get_sort = $this->query
->get('sort', '');
if (!empty($get_sort)) {
$additional_query_parameters['sort'] = $get_sort;
}
$get_order = $this->query
->get('order', '');
if (!empty($get_order)) {
$additional_query_parameters['order'] = $get_order;
}
$this
->setFormRedirect($form_state, $additional_query_parameters);
}
public function validateSelectedEntities(array &$form, FormStateInterface $form_state) {
$selected_entities = $form_state
->getValue('entities');
if (!is_null($selected_entities)) {
$selected_entities = array_filter($selected_entities);
if (empty($selected_entities)) {
$form_state
->setErrorByName('entities', $this
->t('You must select at least one entity.'));
}
}
}
public function synchronizeSelectedEntities(array &$form, FormStateInterface $form_state) {
$selected_entities = $form_state
->getValue('entities');
$selected_entities = array_filter($selected_entities);
$remote_id = $form_state
->getValue('remote');
$channel_id = $form_state
->getValue('channel');
$import_config_id = $form_state
->getValue('import_config');
$import_context = new ImportContext($remote_id, $channel_id, $import_config_id);
$this->importService
->importEntities($import_context, array_values($selected_entities));
}
public function importChannel(array &$form, FormStateInterface $form_state) {
$storage = $form_state
->getStorage();
if (isset($storage['remote_channel_count'])) {
$remote_id = $form_state
->getValue('remote');
$channel_id = $form_state
->getValue('channel');
$import_config_id = $form_state
->getValue('import_config');
$import_context = new ImportContext($remote_id, $channel_id, $import_config_id);
$import_context
->setRemoteChannelCount($storage['remote_channel_count']);
$this->importService
->importChannel($import_context);
}
}
protected function getSelectOptions(string $field) {
$options = [];
switch ($field) {
case 'remote':
foreach ($this->remoteWebsites as $id => $remote_website) {
$options[$id] = $remote_website
->label();
}
break;
case 'channel':
foreach ($this->channelsInfos as $channel_id => $channel_infos) {
$options[$channel_id] = $channel_infos['label'];
}
break;
case 'import_config':
$import_configs = $this->entityTypeManager
->getStorage('import_config')
->loadMultiple();
foreach ($import_configs as $import_config) {
$options[$import_config
->id()] = $import_config
->label();
}
break;
}
return $options;
}
protected function buildSelectElement(FormStateInterface $form_state, string $field) {
$options = $this
->getSelectOptions($field);
if (!$options) {
return [];
}
$disabled = FALSE;
$default_value = $this->query
->get($field);
if (count($options) == 1) {
$disabled = TRUE;
$default_value = key($options);
$form_state
->setValue($field, $default_value);
}
return [
'#type' => 'select',
'#options' => $options,
'#default_value' => $default_value,
'#empty_value' => '',
'#required' => TRUE,
'#disabled' => $disabled,
];
}
public static function buildAjaxChannelSelect(array $form, FormStateInterface $form_state) {
return $form['channel_wrapper'];
}
public static function buildAjaxEntitiesSelectTable(array $form, FormStateInterface $form_state) {
return $form['channel_wrapper']['entities_wrapper'];
}
protected function buildChannelSelect(array &$form, FormStateInterface $form_state) {
$selected_remote_id = $form_state
->getValue('remote', $this->query
->get('remote'));
if (empty($this->remoteWebsites[$selected_remote_id])) {
return;
}
$selected_remote = $this->remoteWebsites[$selected_remote_id];
$this->channelsInfos = $this->remoteManager
->getChannelsInfos($selected_remote);
$select_element = $this
->buildSelectElement($form_state, 'channel');
if ($select_element) {
$select_element['#title'] = $this
->t('Channel');
$select_element['#ajax'] = [
'callback' => [
get_class($this),
'buildAjaxEntitiesSelectTable',
],
'effect' => 'fade',
'method' => 'replace',
'wrapper' => 'entities-wrapper',
];
$form['channel_wrapper']['channel'] = $select_element;
}
$form['channel_wrapper']['entities_wrapper'] = [
'#type' => 'container',
'#attributes' => [
'id' => 'entities-wrapper',
],
];
$this
->buildEntitiesSelectTable($form, $form_state);
}
protected function buildEntitiesSelectTable(array &$form, FormStateInterface $form_state) {
$triggering_element = $form_state
->getTriggeringElement();
$selected_import_config = $form_state
->getValue('import_config', $this->query
->get('import_config'));
$selected_remote_id = $form_state
->getValue('remote', $this->query
->get('remote'));
$selected_channel = $form_state
->getValue('channel', $this->query
->get('channel'));
$offset = !is_array($triggering_element) ? $this->query
->get('offset', 0) : 0;
if (!is_array($triggering_element) && is_numeric($this->query
->get('page'))) {
$offset = $this->query
->get('page') * 50;
}
if (empty($this->remoteWebsites[$selected_remote_id]) || empty($this->channelsInfos[$selected_channel])) {
return;
}
$channel_entity_type = $this->channelsInfos[$selected_channel]['channel_entity_type'];
$channel_bundle = $this->channelsInfos[$selected_channel]['channel_bundle'];
$selected_remote = $this->remoteWebsites[$selected_remote_id];
$this->fieldMappings = $this->remoteManager
->getfieldMappings($selected_remote);
$entity_storage = $this->entityTypeManager
->getStorage($channel_entity_type);
$entity_keys = $entity_storage
->getEntityType()
->getKeys();
$parsed_url = UrlHelper::parse($this->channelsInfos[$selected_channel]['url']);
$parsed_url['query']['page']['offset'] = $offset;
$searched_text = $form_state
->getValue('search', '');
if (empty($searched_text)) {
$get_searched_text = $this->query
->get('search', '');
if (!is_array($triggering_element) && !empty($get_searched_text)) {
$searched_text = $get_searched_text;
}
}
if (!empty($searched_text)) {
$search_filter_and_group = [
'channel_searched_text_group' => [
'group' => [
'conjunction' => 'OR',
],
],
];
foreach ($this->channelsInfos[$selected_channel]['search_configuration'] as $search_key => $search_info) {
$search_filter_and_group['search_filter_' . $search_key] = [
'condition' => [
'path' => $search_info['path'],
'operator' => 'CONTAINS',
'value' => $searched_text,
'memberOf' => 'channel_searched_text_group',
],
];
}
$parsed_url['query']['filter'] = isset($parsed_url['query']['filter']) ? array_merge_recursive($parsed_url['query']['filter'], $search_filter_and_group) : $search_filter_and_group;
}
$sort_field = $this->query
->get('order', '');
$sort_direction = $this->query
->get('sort', '');
$sort_context = [
'name' => $sort_field,
'sort' => $sort_direction,
'query' => [
'remote' => $selected_remote_id,
'channel' => $selected_channel,
'import_config' => $selected_import_config,
'search' => $searched_text,
],
];
if (!empty($sort_field) && !empty($sort_direction) && isset($this->fieldMappings[$channel_entity_type][$channel_bundle][$sort_field])) {
$parsed_url['query']['sort'] = [
$sort_field => [
'path' => $this->fieldMappings[$channel_entity_type][$channel_bundle][$sort_field],
'direction' => strtoupper($sort_direction),
],
];
}
$query = UrlHelper::buildQuery($parsed_url['query']);
$prepared_url = $parsed_url['path'] . '?' . $query;
$response = $this->remoteManager
->jsonApiRequest($selected_remote, 'GET', $prepared_url);
$json = Json::decode((string) $response
->getBody());
$form['channel_wrapper']['entities_wrapper']['search'] = [
'#type' => 'textfield',
'#title' => $this
->t('Search'),
'#default_value' => $searched_text,
'#weight' => -20,
'#ajax' => [
'callback' => [
get_class($this),
'buildAjaxEntitiesSelectTable',
],
'disable-refocus' => TRUE,
'effect' => 'fade',
'keypress' => TRUE,
'method' => 'replace',
'wrapper' => 'entities-wrapper',
],
];
if (isset($this->channelsInfos[$selected_channel]['search_configuration']) && !empty($this->channelsInfos[$selected_channel]['search_configuration'])) {
$search_list = [
'#theme' => 'item_list',
'#items' => [],
];
foreach ($this->channelsInfos[$selected_channel]['search_configuration'] as $search_info) {
$search_list['#items'][] = $search_info['label'];
}
$search_list = $this->renderer
->render($search_list);
$search_description = $this
->t('The search (CONTAINS operator) will occur on the following fields:') . $search_list;
}
else {
$search_description = $this
->t('There is no field on the server site to search on this channel.');
}
$form['channel_wrapper']['entities_wrapper']['search']['#description'] = $search_description;
if (isset($json['meta']['count'])) {
$this->pagerManager
->createPager($json['meta']['count'], 50);
$form['channel_wrapper']['entities_wrapper']['pager'] = [
'#type' => 'pager',
'#route_name' => 'entity_share_client.admin_content_pull_form',
'#parameters' => [
'remote' => $selected_remote_id,
'channel' => $selected_channel,
'import_config' => $selected_import_config,
'search' => $searched_text,
'order' => $sort_field,
'sort' => $sort_direction,
],
'#attached' => [
'library' => [
'entity_share_client/full-pager',
],
],
];
}
else {
$storage = $form_state
->getStorage();
$storage['links'] = $json['links'];
$form_state
->setStorage($storage);
$form['channel_wrapper']['entities_wrapper']['pager'] = [
'#type' => 'actions',
'#weight' => -10,
];
$pager_links = $this
->getBasicPagerLinks();
foreach ($pager_links as $key => $label) {
if (isset($json['links'][$key]['href'])) {
$form['channel_wrapper']['entities_wrapper']['pager'][$key] = [
'#type' => 'submit',
'#value' => $label,
'#submit' => [
'::goToPage',
],
];
}
}
}
if (!empty($sort_field) || !empty($sort_direction)) {
$form['channel_wrapper']['entities_wrapper']['reset_sort'] = [
'#type' => 'actions',
'#weight' => -15,
'reset_sort' => [
'#type' => 'submit',
'#value' => $this
->t('Reset sort'),
'#submit' => [
'::resetSort',
],
],
];
}
$label_header_machine_name = isset($entity_keys['label']) ? $entity_keys['label'] : 'label';
$header = [
'label' => $this
->getHeader($this
->t('Label'), $label_header_machine_name, $sort_context),
'type' => $this
->t('Type'),
'bundle' => $this
->t('Bundle'),
'language' => $this
->t('Language'),
'changed' => $this
->getHeader($this
->t('Remote entity changed date'), 'changed', $sort_context),
'status' => $this
->t('Status'),
];
$form['channel_wrapper']['entities_wrapper']['entities'] = [
'#type' => 'tableselect',
'#header' => $header,
'#options' => $this->formHelper
->buildEntitiesOptions($json['data'], $selected_remote, $selected_channel),
'#empty' => $this
->t('No entities to be pulled have been found.'),
'#attached' => [
'library' => [
'entity_share_client/admin',
],
],
];
if ($this->moduleHandler
->moduleExists('entity_share_diff')) {
$form['channel_wrapper']['entities_wrapper']['entities']['#attached']['library'][] = 'core/drupal.dialog.ajax';
}
$form['channel_wrapper']['entities_wrapper']['actions_bottom']['#type'] = 'actions';
$synchronize_button = [
'#type' => 'submit',
'#value' => $this
->t('Synchronize entities'),
'#button_type' => 'primary',
'#validate' => [
'::validateSelectedEntities',
],
'#submit' => [
'::submitForm',
'::synchronizeSelectedEntities',
],
];
$form['channel_wrapper']['entities_wrapper']['actions_bottom']['synchronize'] = $synchronize_button;
if (isset($json['meta']['count'])) {
$import_channel_button = [
'#type' => 'submit',
'#value' => $this
->t('Import the whole channel'),
'#button_type' => 'primary',
'#submit' => [
'::submitForm',
'::importChannel',
],
];
$form['channel_wrapper']['entities_wrapper']['actions_bottom']['import_channel'] = $import_channel_button;
$storage = $form_state
->getStorage();
$storage['remote_channel_count'] = $json['meta']['count'];
$form_state
->setStorage($storage);
}
$form['channel_wrapper']['entities_wrapper']['actions_top'] = $form['channel_wrapper']['entities_wrapper']['actions_bottom'];
$form['channel_wrapper']['entities_wrapper']['actions_top']['#weight'] = -1;
}
protected function getHeader(MarkupInterface $header, $header_machine_name, array $context) {
$cell = [];
$title = new TranslatableMarkup('sort by @s', [
'@s' => $header,
]);
if ($header_machine_name == $context['name']) {
$cell['aria-sort'] = $context['sort'] == 'asc' ? 'ascending' : 'descending';
$context['sort'] = $context['sort'] == 'asc' ? 'desc' : 'asc';
$cell['class'][] = 'is-active';
$tablesort_indicator = [
'#theme' => 'tablesort_indicator',
'#style' => $context['sort'],
];
$image = $this->renderer
->render($tablesort_indicator);
}
else {
$context['sort'] = 'asc';
$image = '';
}
$cell['data'] = Link::createFromRoute(new FormattableMarkup('@cell_content@image', [
'@cell_content' => $header,
'@image' => $image,
]), '<current>', [], [
'attributes' => [
'title' => $title,
],
'query' => array_merge($context['query'], [
'sort' => $context['sort'],
'order' => $header_machine_name,
]),
]);
return $cell;
}
protected function getBasicPagerLinks() {
return [
'first' => $this
->t('First'),
'prev' => $this
->t('Previous'),
'next' => $this
->t('Next'),
'last' => $this
->t('Last'),
];
}
public function goToPage(array &$form, FormStateInterface $form_state) {
$triggering_element = $form_state
->getTriggeringElement();
$link_name = current($triggering_element['#parents']);
if ($link_name !== FALSE && array_key_exists($link_name, $this
->getBasicPagerLinks())) {
$this
->pagerRedirect($form_state, $link_name);
}
}
protected function pagerRedirect(FormStateInterface $form_state, $link_name) {
$storage = $form_state
->getStorage();
if (isset($storage['links'][$link_name]['href'])) {
$parsed_url = UrlHelper::parse($storage['links'][$link_name]['href']);
if (isset($parsed_url['query']['page']) && isset($parsed_url['query']['page']['offset'])) {
$additional_query_parameters = [
'offset' => $parsed_url['query']['page']['offset'],
'order' => $this->query
->get('order', ''),
'sort' => $this->query
->get('sort', ''),
];
$this
->setFormRedirect($form_state, $additional_query_parameters);
}
}
}
protected function setFormRedirect(FormStateInterface $form_state, array $additional_query_parameters = []) {
$query_parameters = [
'remote' => $form_state
->getValue('remote'),
'channel' => $form_state
->getValue('channel'),
'import_config' => $form_state
->getValue('import_config'),
'search' => $form_state
->getValue('search', ''),
];
if ($additional_query_parameters) {
$query_parameters = array_merge($query_parameters, $additional_query_parameters);
}
$form_state
->setRedirect('entity_share_client.admin_content_pull_form', [], [
'query' => $query_parameters,
]);
}
public function resetSort(array &$form, FormStateInterface $form_state) {
$this
->setFormRedirect($form_state);
}
}