View source
<?php
namespace Drupal\feeds\Form;
use Drupal\Component\Utility\Html;
use Drupal\Core\Config\Entity\ConfigEntityStorageInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\PluginFormInterface;
use Drupal\feeds\Exception\MissingTargetException;
use Drupal\feeds\FeedTypeInterface;
use Drupal\feeds\MissingTargetDefinition;
use Drupal\feeds\Plugin\Type\MappingPluginFormInterface;
use Drupal\feeds\Plugin\Type\Target\ConfigurableTargetInterface;
use Drupal\feeds\Plugin\Type\Target\TargetInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class MappingForm extends FormBase {
protected $feedType;
protected $feedTypeStorage;
protected $mappings;
public function __construct(ConfigEntityStorageInterface $feed_type_storage) {
$this->feedTypeStorage = $feed_type_storage;
}
public static function create(ContainerInterface $container) {
return new static($container
->get('entity_type.manager')
->getStorage('feeds_feed_type'));
}
public function getFormId() {
return 'feeds_mapping_form';
}
public function buildForm(array $form, FormStateInterface $form_state, FeedTypeInterface $feeds_feed_type = NULL) {
$feed_type = $this->feedType = $feeds_feed_type;
$this->targets = $targets = $feed_type
->getMappingTargets();
$this->sourceOptions = [];
foreach ($feed_type
->getMappingSources() as $key => $info) {
$this->sourceOptions[$key] = $info['label'];
}
$this->sourceOptions = $this
->sortOptions($this->sourceOptions);
$this->sourceOptions = [
'__new' => $this
->t('New source...'),
'----' => '----',
] + $this->sourceOptions;
$target_options = [];
foreach ($targets as $key => $target) {
$target_options[$key] = $target
->getLabel() . ' (' . $key . ')';
}
$target_options = $this
->sortOptions($target_options);
if ($form_state
->getValues()) {
$this
->processFormState($form, $form_state);
$triggering_element = $form_state
->getTriggeringElement() + [
'#op' => '',
];
switch ($triggering_element['#op']) {
case 'cancel':
case 'configure':
break;
default:
$this
->messenger()
->addWarning($this
->t('Your changes will not be saved until you click the <em>Save</em> button at the bottom of the page.'));
break;
}
}
$form['#tree'] = TRUE;
$form['#prefix'] = '<div id="feeds-mapping-form-ajax-wrapper">';
$form['#suffix'] = '</div>';
$form['#attached']['library'][] = 'feeds/feeds';
$table = [
'#type' => 'table',
'#header' => [
$this
->t('Source'),
$this
->t('Target'),
$this
->t('Summary'),
$this
->t('Configure'),
$this
->t('Unique'),
$this
->t('Remove'),
],
'#sticky' => TRUE,
];
foreach ($feed_type
->getMappings() as $delta => $mapping) {
$table[$delta] = $this
->buildRow($form, $form_state, $mapping, $delta);
}
$table['add']['source']['#markup'] = '';
$table['add']['target'] = [
'#type' => 'select',
'#title' => $this
->t('Add a target'),
'#title_display' => 'invisible',
'#options' => $target_options,
'#empty_option' => $this
->t('- Select a target -'),
'#parents' => [
'add_target',
],
'#default_value' => NULL,
'#ajax' => [
'callback' => '::ajaxCallback',
'wrapper' => 'feeds-mapping-form-ajax-wrapper',
'effect' => 'none',
'progress' => 'none',
],
];
$table['add']['summary']['#markup'] = '';
$table['add']['configure']['#markup'] = '';
$table['add']['unique']['#markup'] = '';
$table['add']['remove']['#markup'] = '';
$form['mappings'] = $table;
$form['legendset'] = $this
->buildLegend($form, $form_state);
$form['actions'] = [
'#type' => 'actions',
];
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this
->t('Save'),
'#button_type' => 'primary',
];
foreach ($feed_type
->getPlugins() as $plugin) {
if ($plugin instanceof MappingPluginFormInterface) {
$plugin
->mappingFormAlter($form, $form_state);
}
}
return $form;
}
protected function buildRow(array $form, FormStateInterface $form_state, array $mapping, $delta) {
try {
$plugin = $this->feedType
->getTargetPlugin($delta);
} catch (MissingTargetException $e) {
$this
->messenger()
->addWarning($e
->getMessage());
watchdog_exception('feeds', $e);
$plugin = NULL;
}
if (!empty($this->targets[$mapping['target']])) {
$target_definition = $this->targets[$mapping['target']];
}
else {
$target_definition = MissingTargetDefinition::create();
}
$ajax_delta = -1;
$triggering_element = (array) $form_state
->getTriggeringElement() + [
'#op' => '',
];
if ($triggering_element['#op'] === 'configure') {
$ajax_delta = $form_state
->getTriggeringElement()['#delta'];
}
$row = [
'#attributes' => [
'class' => [
'draggable',
'tabledrag-leaf',
],
],
];
$row['map'] = [
'#type' => 'container',
];
$row['targets'] = [
'#theme' => 'item_list',
'#items' => [],
'#attributes' => [
'class' => [
'target',
],
],
];
if ($target_definition instanceof MissingTargetDefinition) {
$row['#attributes']['class'][] = 'missing-target';
$row['#attributes']['class'][] = 'color-error';
}
foreach ($mapping['map'] as $column => $source) {
if (!$target_definition
->hasProperty($column)) {
unset($mapping['map'][$column]);
continue;
}
$row['map'][$column] = [
'select' => [
'#type' => 'select',
'#options' => $this->sourceOptions,
'#default_value' => $source,
'#empty_option' => $this
->t('- Select a source -'),
'#attributes' => [
'class' => [
'feeds-table-select-list',
],
],
],
'__new' => [
'#type' => 'container',
'#states' => [
'visible' => [
':input[name="mappings[' . $delta . '][map][' . $column . '][select]"]' => [
'value' => '__new',
],
],
],
'value' => [
'#type' => 'textfield',
'#states' => [
'visible' => [
':input[name="mappings[' . $delta . '][map][' . $column . '][select]"]' => [
'value' => '__new',
],
],
],
],
'machine_name' => [
'#type' => 'machine_name',
'#machine_name' => [
'exists' => [
$this,
'customSourceExists',
],
'source' => [
'mappings',
$delta,
'map',
$column,
'__new',
'value',
],
'standalone' => TRUE,
'label' => '',
],
'#default_value' => '',
'#required' => FALSE,
'#disabled' => '',
],
],
];
$label = Html::escape($target_definition
->getLabel() . ' (' . $mapping['target'] . ')');
if (count($mapping['map']) > 1) {
$desc = $target_definition
->getPropertyLabel($column);
}
else {
$desc = $target_definition
->getDescription();
}
if ($desc) {
$label .= ': ' . $desc;
}
$row['targets']['#items'][] = $label;
}
$default_button = [
'#ajax' => [
'callback' => '::ajaxCallback',
'wrapper' => 'feeds-mapping-form-ajax-wrapper',
'effect' => 'fade',
'progress' => 'none',
],
'#delta' => $delta,
];
$row['settings']['#markup'] = '';
$row['configure']['#markup'] = '';
if ($plugin && $this
->pluginHasSettingsForm($plugin, $form_state)) {
if ($delta == $ajax_delta) {
$row['settings'] = $plugin
->buildConfigurationForm([], $form_state);
$row['settings']['actions'] = [
'#type' => 'actions',
'save_settings' => $default_button + [
'#type' => 'submit',
'#button_type' => 'primary',
'#value' => $this
->t('Update'),
'#op' => 'update',
'#name' => 'target-save-' . $delta,
],
'cancel_settings' => $default_button + [
'#type' => 'submit',
'#value' => $this
->t('Cancel'),
'#op' => 'cancel',
'#name' => 'target-cancel-' . $delta,
'#limit_validation_errors' => [
[],
],
],
];
$row['#attributes']['class'][] = 'feeds-mapping-settings-editing';
}
else {
$row['settings'] = [
'#parents' => [
'config_summary',
$delta,
],
] + $this
->buildSummary($plugin);
$row['configure'] = $default_button + [
'#type' => 'image_button',
'#op' => 'configure',
'#name' => 'target-settings-' . $delta,
'#src' => 'core/misc/icons/787878/cog.svg',
];
}
}
elseif ($plugin instanceof ConfigurableTargetInterface) {
$summary = $this
->buildSummary($plugin);
if (!empty($summary)) {
$row['settings'] = [
'#parents' => [
'config_summary',
$delta,
],
] + $this
->buildSummary($plugin);
}
}
$mappings = $this->feedType
->getMappings();
foreach ($mapping['map'] as $column => $source) {
if ($target_definition
->isUnique($column)) {
$row['unique'][$column] = [
'#title' => $this
->t('Unique'),
'#type' => 'checkbox',
'#default_value' => !empty($mappings[$delta]['unique'][$column]),
'#title_display' => 'invisible',
];
}
else {
$row['unique']['#markup'] = '';
}
}
if ($delta != $ajax_delta) {
$row['remove'] = $default_button + [
'#title' => $this
->t('Remove'),
'#type' => 'checkbox',
'#default_value' => FALSE,
'#title_display' => 'invisible',
'#parents' => [
'remove_mappings',
$delta,
],
'#remove' => TRUE,
];
}
else {
$row['remove']['#markup'] = '';
}
return $row;
}
protected function pluginHasSettingsForm(TargetInterface $plugin, FormStateInterface $form_state) {
if (!$plugin instanceof ConfigurableTargetInterface) {
return FALSE;
}
if (!$plugin instanceof PluginFormInterface) {
return FALSE;
}
$settings_form = $plugin
->buildConfigurationForm([], $form_state);
return !empty($settings_form);
}
protected function buildSummary(ConfigurableTargetInterface $plugin) {
$summary = $plugin
->getSummary();
if (!empty($summary)) {
if (!is_array($summary)) {
$summary = [
$summary,
];
}
return [
'#type' => 'inline_template',
'#template' => '<div class="plugin-summary">{{ summary|safe_join("<br />") }}</div>',
'#context' => [
'summary' => $summary,
],
'#cell_attributes' => [
'class' => [
'plugin-summary-cell',
],
],
];
}
return [];
}
protected function buildLegend(array $form, FormStateInterface $form_state) {
$element = [
'#type' => 'details',
'#title' => $this
->t('Legend'),
'sources' => [
'#caption' => $this
->t('Sources'),
'#type' => 'table',
'#header' => [
$this
->t('Name'),
$this
->t('Machine name'),
$this
->t('Description'),
],
'#rows' => [],
],
'targets' => [
'#caption' => $this
->t('Targets'),
'#type' => 'table',
'#header' => [
$this
->t('Name'),
$this
->t('Machine name'),
$this
->t('Description'),
],
'#rows' => [],
],
];
foreach ($this->feedType
->getMappingSources() as $key => $info) {
$element['sources']['#rows'][$key] = [
'label' => $info['label'],
'name' => $key,
'description' => isset($info['description']) ? $info['description'] : NULL,
];
}
asort($element['sources']['#rows']);
foreach ($this->targets as $key => $definition) {
$element['targets']['#rows'][$key] = [
'label' => $definition
->getLabel(),
'name' => $key,
'description' => $definition
->getDescription(),
];
}
return $element;
}
public function customSourceExists($name, array $element, FormStateInterface $form_state) {
$unchanged_feed_type = $this->feedTypeStorage
->loadUnchanged($this->feedType
->getOriginalId());
if ($unchanged_feed_type && $unchanged_feed_type
->customSourceExists($name)) {
return TRUE;
}
$element_delta = $element['#array_parents'][1];
$element_column = $element['#array_parents'][3];
foreach ($form_state
->getValue('mappings') as $delta => $mapping) {
foreach ($mapping['map'] as $column => $value) {
if ($delta == $element_delta && $element_column == $column) {
continue;
}
if ($value['select'] == '__new') {
$map_name = $mappings[$delta]['map'][$column] = $value['__new']['machine_name'];
if ($name == $map_name) {
return TRUE;
}
}
}
}
return FALSE;
}
protected function processFormState(array $form, FormStateInterface $form_state) {
$triggering_element = $form_state
->getTriggeringElement() + [
'#op' => '',
];
if ($triggering_element['#op'] === 'update') {
$this->feedType
->getTargetPlugin($triggering_element['#delta'])
->submitConfigurationForm($form, $form_state);
}
$mappings = $this->feedType
->getMappings();
foreach (array_filter((array) $form_state
->getValue('mappings', [])) as $delta => $mapping) {
foreach ($mapping['map'] as $column => $value) {
if ($value['select'] == '__new') {
$this->feedType
->addCustomSource($value['__new']['machine_name'], [
'label' => $value['__new']['value'],
] + $value['__new']);
$mappings[$delta]['map'][$column] = $value['__new']['machine_name'];
}
else {
$mappings[$delta]['map'][$column] = $value['select'];
}
}
if (isset($mapping['unique'])) {
$mappings[$delta]['unique'] = array_filter($mapping['unique']);
}
}
$this->feedType
->setMappings($mappings);
foreach (array_keys(array_filter($form_state
->getValue('remove_mappings', []))) as $delta) {
$this->feedType
->removeMapping($delta);
}
if ($new_target = $form_state
->getValue('add_target')) {
$map = array_fill_keys($this->targets[$new_target]
->getProperties(), '');
$this->feedType
->addMapping([
'target' => $new_target,
'map' => $map,
]);
}
$input =& $form_state
->getUserInput();
unset($input['add_target']);
}
public function validateForm(array &$form, FormStateInterface $form_state) {
if (isset($form_state
->getTriggeringElement()['#delta'])) {
$delta = $form_state
->getTriggeringElement()['#delta'];
$this->feedType
->getTargetPlugin($delta)
->validateConfigurationForm($form, $form_state);
$form_state
->setRebuild();
}
else {
foreach ($this->feedType
->getPlugins() as $plugin) {
if ($plugin instanceof MappingPluginFormInterface) {
$plugin
->mappingFormValidate($form, $form_state);
}
}
}
}
public function submitForm(array &$form, FormStateInterface $form_state) {
$this
->processFormState($form, $form_state);
foreach ($this->feedType
->getPlugins() as $plugin) {
if ($plugin instanceof MappingPluginFormInterface) {
$plugin
->mappingFormSubmit($form, $form_state);
}
}
$this->feedType
->save();
}
protected function sortOptions(array $options) {
$result = [];
foreach ($options as $k => $v) {
if (is_array($v) && !empty($v['label'])) {
$result[$k] = $v['label'];
}
elseif (is_array($v)) {
$result[$k] = $k;
}
else {
$result[$k] = $v;
}
}
asort($result);
return $result;
}
public static function ajaxCallback(array $form, FormStateInterface $form_state) {
return $form;
}
public function mappingTitle(FeedTypeInterface $feeds_feed_type) {
return $this
->t('Mappings @label', [
'@label' => $feeds_feed_type
->label(),
]);
}
}