Commit a162b11e authored by Quan Nguyen's avatar Quan Nguyen

add feeds tamper

parent 2ad177c7
This diff is collapsed.
{
"name": "drupal/feeds_tamper",
"type": "drupal-module",
"description": "Modify feeds data before it gets saved.",
"keywords": ["Drupal", "feeds", "tamper"],
"homepage": "http://drupal.org/project/feeds_tamper",
"license": "GPL-2.0+",
"support": {
"issues": "http://drupal.org/project/issues/feeds_tamper",
"source": "http://cgit.drupalcode.org/feeds_tamper"
},
"require": {
"drupal/feeds": "~3.0",
"drupal/tamper": "~1.0"
},
"require-dev": {
"drupal/feeds": "3.x-dev"
}
}
# Third party settings for feed type for Feeds Tamper.
feeds.feed_type.*.third_party.feeds_tamper:
type: mapping
label: 'Feeds Tamper settings'
mapping:
tampers:
type: sequence
sequence:
type: feeds_tamper.[plugin]
# Feeds Tamper instance.
feeds_tamper.*:
type: tamper.[plugin]
mapping:
uuid:
type: uuid
plugin:
type: string
label: 'Tamper plugin ID'
source:
type: string
label: 'Source field'
weight:
type: integer
label: 'Weight'
label:
type: string
label: 'Label'
description:
type: string
label: 'Description'
name: Feeds Tamper
type: module
description: Modify feeds data before it gets saved.
# core: 8.x
package: Feeds
dependencies:
- drupal:system (>=8.5.0)
- feeds:feeds
- tamper:tamper
# Information added by Drupal.org packaging script on 2019-07-20
version: '8.x-2.0-beta1'
core: '8.x'
project: 'feeds_tamper'
datestamp: 1563627493
entity.feeds_feed_type.tamper:
route_name: entity.feeds_feed_type.tamper
base_route: entity.feeds_feed_type.edit_form
title: 'Tamper'
weight: 0.5
<?php
/**
* @file
* Feeds Tamper hook implementations.
*/
use Drupal\Core\Entity\EntityInterface;
use Drupal\feeds\FeedTypeInterface;
/**
* Implements hook_entity_operation().
*/
function feeds_tamper_entity_operation(EntityInterface $entity) {
$operations = [];
if ($entity->getEntityTypeId() != 'feeds_feed_type') {
// Feeds Tamper only provides operations for feed types.
return [];
}
$account = \Drupal::currentUser();
if (!$account->hasPermission('administer feeds_tamper') && !$account->hasPermission('tamper ' . $entity->id())) {
// No access.
return [];
}
return [
'tamper' => [
'title' => t('Tamper'),
'url' => $entity->toUrl('tamper'),
// Appear after operation "mapping".
'weight' => 12,
],
];
}
/**
* Implements hook_menu_links_discovered_alter().
*/
function feeds_tamper_menu_links_discovered_alter(&$links) {
// Add "Tamper" link for each feed type.
foreach (\Drupal::entityTypeManager()->getStorage('feeds_feed_type')->loadMultiple() as $machine_name => $bundle) {
$links['entity.feeds_feed_type.tamper' . $machine_name] = [
'title' => t('Tamper'),
'route_name' => 'entity.feeds_feed_type.tamper',
'menu_name' => 'admin',
'parent' => 'entity.feeds_feed_type.edit_form.' . $machine_name,
'route_parameters' => ['feeds_feed_type' => $machine_name],
'weight' => 0,
];
}
}
/**
* Implements hook_entity_type_alter().
*
* Adds link template for tamper operation to the feed type entity.
*/
function feeds_tamper_entity_type_alter(array &$entity_types) {
if (isset($entity_types['feeds_feed_type'])) {
$entity_types['feeds_feed_type']->setLinkTemplate('tamper', '/admin/structure/feeds/manage/{feeds_feed_type}/tamper');
}
}
/**
* Implements hook_ENTITY_TYPE_presave() for 'feeds_feed_type'.
*
* Remove tamper plugin instances for removed mappers.
*/
function feeds_tamper_feeds_feed_type_presave(FeedTypeInterface $feed_type) {
/** @var \Drupal\feeds_tamper\FeedTypeTamperMetaInterface $feed_type_tamper_meta */
$feed_type_tamper_meta = \Drupal::service('feeds_tamper.feed_type_tamper_manager')
->getTamperMeta($feed_type)
->rectifyInstances();
}
administer feeds_tamper:
title: 'Administer Feeds Tamper'
description: 'Create, edit and delete plugins for any feed type.'
restrict access: TRUE
permission_callbacks:
- \Drupal\feeds_tamper\FeedsTamperPermissions::feedTypeTamperPermissions
entity.feeds_feed_type.tamper:
path: '/admin/structure/feeds/manage/{feeds_feed_type}/tamper'
defaults:
_form: '\Drupal\feeds_tamper\Form\TamperListForm'
_title: 'Tamper'
requirements:
_custom_access: '\Drupal\feeds_tamper\Form\TamperListForm::checkAccess'
options:
parameters:
feeds_feed_type:
type: 'entity:feeds_feed_type'
entity.feeds_feed_type.tamper_add:
path: '/admin/structure/feeds/manage/{feeds_feed_type}/tamper/add/{source_field}'
defaults:
_form: '\Drupal\feeds_tamper\Form\TamperAddForm'
_title_callback: '\Drupal\feeds_tamper\Form\TamperAddForm::tamperTitle'
requirements:
_custom_access: '\Drupal\feeds_tamper\Form\TamperAddForm::checkAccess'
options:
parameters:
feeds_feed_type:
type: 'entity:feeds_feed_type'
entity.feeds_feed_type.tamper_edit:
path: '/admin/structure/feeds/manage/{feeds_feed_type}/tamper/{tamper_uuid}/edit'
defaults:
_form: '\Drupal\feeds_tamper\Form\TamperEditForm'
_title_callback: '\Drupal\feeds_tamper\Form\TamperEditForm::tamperTitle'
requirements:
_custom_access: '\Drupal\feeds_tamper\Form\TamperEditForm::checkAccess'
options:
parameters:
feeds_feed_type:
type: 'entity:feeds_feed_type'
entity.feeds_feed_type.tamper_delete:
path: '/admin/structure/feeds/manage/{feeds_feed_type}/tamper/{tamper_uuid}/delete'
defaults:
_form: '\Drupal\feeds_tamper\Form\TamperDeleteForm'
requirements:
_custom_access: '\Drupal\feeds_tamper\Form\TamperDeleteForm::checkAccess'
options:
parameters:
feeds_feed_type:
type: 'entity:feeds_feed_type'
services:
feeds_tamper.feed_type_tamper_manager:
class: Drupal\feeds_tamper\FeedTypeTamperManager
parent: container.trait
feeds_tamper.feeds_subscriber:
class: Drupal\feeds_tamper\EventSubscriber\FeedsSubscriber
arguments: ['@feeds_tamper.feed_type_tamper_manager']
tags:
- {name: event_subscriber}
<?php
/**
* @file
* Version agnostic parts of feeds_tamper.module.
*/
/**
* Return a machine name safe version of a string.
*
* @param string $string
* String to get machine nameized.
*
* @return string
* A lowercase string with all values not in [a-zA-Z0-9] replaced with an
* underscore and shortened to 128 characters.
*/
function feeds_tamper_make_machine($string) {
return drupal_substr(preg_replace('/[^a-z0-9-]/u', '_', drupal_strtolower($string)), 0, 127);
}
/**
* @} End of "feeds_tamper_api".
*/
/**
* Menu access callback.
*
* @param string|FeedsImporter $importer
* The importer or importer id being tampered with.
* @param string|stdClass $instance
* (optional) If set, the importer attached to $instance will be tried first.
* Defaults to NULL.
*
* @return bool
* TRUE if the user has acces, FALSE if not.
*/
function feeds_tamper_access($importer, $instance = NULL) {
if (isset($instance)) {
if (is_object($instance)) {
$importer = $instance->importer;
}
else {
$importer = feeds_tamper_load_instance($instance)->importer;
}
}
elseif (is_object($importer)) {
$importer = $importer->id;
}
// Verify actual input if above failed.
if ($importer) {
// Check for permissions, otherwise return FALSE.
if (user_access('administer feeds_tamper') || user_access('tamper ' . $importer)) {
return TRUE;
}
}
return FALSE;
}
<?php
/**
* @file
* Forms and their accompanying validation and submit functions for Feeds Tamper
* UI.
*/
/**
* Helper functions for ajax.
*/
function feeds_tamper_machine_name_callback($id, $form, $form_state) {
// Importer id's are machine safe.
$importer_id = $form_state['importer']->id;
$source = feeds_tamper_make_machine($form_state['source']);
return feeds_tamper_load_instance("$importer_id-$source-$id");
}
/**
* Ajax callback for add plugin form.
*/
function feeds_tamper_ajax_callback($form, $form_state) {
// The form has already been submitted and updated. We can return the replaced
// item as it is.
return $form['plugin'];
}
/* Stolen from Rules */
.feeds-tamper-table ul.action-links {
margin: 0;
padding: 0;
}
.feeds-tamper-table li {
list-style-position: inside;
}
.feeds-tamper-table ul.feeds-tamper-add a {
line-height: 1em;
}
tr.feeds-tamper-add td {
padding-top: 2px;
padding-bottom: 2px;
}
.feeds-tamper-table {
margin-top: 3em;
position: relative;
}
/* Fix table drag weights to don't take extra space */
.feeds-tamper-table .tabledrag-toggle-weight-wrapper {
float: right;
position: absolute;
right: 0px;
}
.feeds-tamper-table caption {
font-size: 110%;
font-weight: bold;
padding-bottom: 0.5em;
text-align: left;
}
.feeds-tamper-table tr.disabled td {
color: #aaaaaa;
}
.feeds-tamper-table tr.disabled {
color: #aaaaaa;
background-color: #f5f5f5;
}
/* Style add plugin form. */
/* hide the next button when not degrading to non-javascript browser */
html.js .no-js {
display: none;
}
/* Make Choose button come up next to select. */
.form-item-plugin-id {
display: inline-block;
}
<?php
/**
* @file
* Feeds Tamper UI - Defines the user interface for Feeds Tamper.
*/
/**
* Menu loader callback for grabbing the source from the URL.
*/
function feeds_tamper_ui_source_load($source) {
// We've HEX encoded the source to allow all possible characters.
return pack('H*', $source);
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Modify feeds_ui_overview_form(), adding Tamper links if the user has access.
*/
function feeds_tamper_ui_form_feeds_ui_overview_form_alter(&$form, &$form_state) {
if (!empty($form['enabled'])) {
foreach ($form['enabled'] as $id => &$table) {
if (feeds_tamper_access($id)) {
$table['operations']['#markup'] .= ' | ' . l(t('Tamper'), "admin/structure/feeds/$id/tamper");
}
}
}
}
/**
* Calculate display name for source.
*
* @param stdClass $instance
* A plugin instance object.
*
* @return string
* The unsanitized name to display for a Feeds source.
*/
function feeds_tamper_ui_source_name(stdClass $instance) {
$importer = feeds_importer($instance->importer);
$sources = $importer->parser->getMappingSources();
return !empty($sources[$instance->source]['name']) ? $sources[$instance->source]['name'] : $instance->source;
}
<?php
namespace Drupal\feeds_tamper\Adapter;
use Drupal\feeds\Feeds\Item\ItemInterface;
use Drupal\tamper\TamperableItemInterface;
/**
* Provides an adapter to use the feed item as a tamperable item.
*/
class TamperableFeedItemAdapter implements TamperableItemInterface {
/**
* A feed item.
*
* @var \Drupal\feeds\Feeds\Item\ItemInterface
*/
protected $feedItem;
/**
* Create a new instance of the adapter.
*
* @param \Drupal\feeds\Feeds\Item\ItemInterface $feed_item
* A feed item.
*/
public function __construct(ItemInterface $feed_item) {
$this->feedItem = $feed_item;
}
/**
* {@inheritdoc}
*/
public function getSource() {
return $this->feedItem->toArray();
}
/**
* {@inheritdoc}
*/
public function setSourceProperty($property, $data) {
$this->feedItem->set($property, $data);
}
/**
* {@inheritdoc}
*/
public function getSourceProperty($property) {
$this->feedItem->get($property);
}
}
<?php
namespace Drupal\feeds_tamper\EventSubscriber;
use Drupal\feeds\Event\FeedsEvents;
use Drupal\feeds\Event\ParseEvent;
use Drupal\feeds\Feeds\Item\ItemInterface;
use Drupal\feeds_tamper\Adapter\TamperableFeedItemAdapter;
use Drupal\feeds_tamper\FeedTypeTamperManagerInterface;
use Drupal\tamper\Exception\SkipTamperDataException;
use Drupal\tamper\Exception\SkipTamperItemException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Subscriber to Feeds events.
*
* This is where Tamper plugins are applied to the Feeds parser result, which
* will modify the feed items. This happens after parsing and before going
* into processing.
*/
class FeedsSubscriber implements EventSubscriberInterface {
/**
* A feed type meta object.
*
* @var \Drupal\feeds_tamper\FeedTypeTamperManagerInterface
*/
protected $tamperManager;
/**
* Constructs a new FeedsSubscriber object.
*
* @param \Drupal\feeds_tamper\FeedTypeTamperManagerInterface $tamper_manager
* A feed type meta object.
*/
public function __construct(FeedTypeTamperManagerInterface $tamper_manager) {
$this->tamperManager = $tamper_manager;
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events[FeedsEvents::PARSE][] = ['afterParse', FeedsEvents::AFTER];
return $events;
}
/**
* Acts on parser result.
*/
public function afterParse(ParseEvent $event) {
/** @var \Drupal\feeds\FeedInterface $feed */
$feed = $event->getFeed();
/** @var \Drupal\feeds_tamper\FeedTypeTamperMetaInterface $tamper_meta */
$tamper_meta = $this->tamperManager->getTamperMeta($feed->getType());
// Load the tamper plugins that need to be applied to Feeds.
$tampers_by_source = $tamper_meta->getTampersGroupedBySource();
// Abort if there are no tampers to apply on the current feed.
if (empty($tampers_by_source)) {
return;
}
/** @var \Drupal\feeds\Result\ParserResultInterface $result */
$result = $event->getParserResult();
for ($i = 0; $i < $result->count(); $i++) {
if (!$result->offsetExists($i)) {
break;
}
/** @var \Drupal\feeds\Feeds\Item\ItemInterface $item */
$item = $result->offsetGet($i);
try {
$this->alterItem($item, $event, $tampers_by_source);
}
catch (SkipTamperItemException $e) {
$result->offsetUnset($i);
$i--;
}
}
}
/**
* Alters a single item.
*
* @param \Drupal\feeds\Feeds\Item\ItemInterface $item
* The item to make modifications on.
* @param \Drupal\feeds\Event\ParseEvent $event
* The parse event.
* @param \Drupal\tamper\TamperInterface[][] $tampers_by_source
* A list of tampers to apply, grouped by source.
*/
protected function alterItem(ItemInterface $item, ParseEvent $event, array $tampers_by_source) {
$tamperable_item = new TamperableFeedItemAdapter($item);
foreach ($tampers_by_source as $source => $tampers) {
try {
// Get the value for a source.
$item_value = $item->get($source);
$multiple = is_array($item_value) && !empty($item_value);
/** @var \Drupal\tamper\TamperInterface $tamper */
foreach ($tampers as $tamper) {
$definition = $tamper->getPluginDefinition();
// Many plugins expect a scalar value but the current value of the
// pipeline might be multiple scalars and in this case the current
// value needs to be iterated and each scalar separately transformed.
if ($multiple && !$definition['handle_multiples']) {
$new_value = [];
// @todo throw exception if $item_value is not an array.
foreach ($item_value as $scalar_value) {
$new_value[] = $tamper->tamper($scalar_value, $tamperable_item);
}
$item_value = $new_value;
}
else {
$item_value = $tamper->tamper($item_value, $tamperable_item);
$multiple = $tamper->multiple();
}
}
// Write the changed value.
$item->set($source, $item_value);
}
catch (SkipTamperDataException $e) {
// @todo We would rather unset the source, but that isn't possible yet
// with ItemInterface.
$item->set($source, NULL);
}
}
}
}
<?php
namespace Drupal\feeds_tamper;
use Drupal\feeds\FeedTypeInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
/**
* Manager for FeedTypeTamperMeta instances.
*/
class FeedTypeTamperManager implements FeedTypeTamperManagerInterface {
use ContainerAwareTrait;
/**
* An array of FeedsTamper instances.
*
* @var \Drupal\feeds_tamper\FeedTypeTamperMeta[]
*/
protected $tamperMetas = [];
/**
* {@inheritdoc}
*/
public function getTamperMeta(FeedTypeInterface $feed_type, $reset = FALSE) {
$id = $feed_type->id();
if ($reset || !isset($this->tamperMetas[$id])) {
$this->tamperMetas[$id] = FeedTypeTamperMeta::create($this->container, $feed_type);
}
return $this->tamperMetas[$id];
}
}
<?php
namespace Drupal\feeds_tamper;
use Drupal\feeds\FeedTypeInterface;
/**
* Interface for managing FeedTypeTamperMeta instances.
*/
interface FeedTypeTamperManagerInterface {
/**
* Gets Tamper functionality for a feed type.
*
* @param \Drupal\feeds\FeedTypeInterface $feed_type
* The feed type to manage tamper plugins for.
* @param bool $reset
* Whether to force creating a new instance (useful in automated tests).
* Defaults to false.
*
* @return \Drupal\feeds_tamper\FeedTypeTamperMetaInterface
* A feed type tamper meta object.
*/
public function getTamperMeta(FeedTypeInterface $feed_type, $reset = FALSE);
}
<?php
namespace Drupal\feeds_tamper;
use Drupal\Component\Uuid\UuidInterface;
use Drupal\feeds\FeedTypeInterface;
use Drupal\tamper\SourceDefinition;
use Drupal\tamper\TamperManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Class for managing tamper plugins for a feed type.
*/
class FeedTypeTamperMeta implements FeedTypeTamperMetaInterface {
/**
* The Uuid generator.
*
* @var \Drupal\Component\Uuid\UuidInterface
*/
protected $uuidGenerator;
/**
* The Tamper plugin manager.
*
* @var \Drupal\tamper\TamperManagerInterface
*/
protected $tamperManager;
/**
* The feed type to manage tamper plugins for.
*
* @var \Drupal\feeds\Entity\FeedType
*/
protected $feedType;
/**
* Holds the collection of tampers that are used by the feed type.
*
* @var \Drupal\feeds_tamper\TamperPluginCollection
*/
protected $tamperCollection;
/**
* Constructs a new FeedTypeTamperMeta object.
*
* @param \Drupal\Component\Uuid\UuidInterface $uuid_generator
* The Uuid generator.
* @param \Drupal\tamper\TamperManagerInterface $tamper_manager
* The Tamper plugin manager.
* @param \Drupal\feeds\FeedTypeInterface $feed_type
* The feed type to manage tamper plugins for.
*/
public function __construct(UuidInterface $uuid_generator, TamperManagerInterface $tamper_manager, FeedTypeInterface $feed_type) {
$this->uuidGenerator = $uuid_generator;
$this->tamperManager = $tamper_manager;
$this->feedType = $feed_type;
}
/**
* Creates a new instance.
*
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
* The service container.
* @param \Drupal\feeds\FeedTypeInterface $feed_type
* The feed type to manage tamper plugins for.
*
* @return static
* A new FeedTypeTamperMeta instance.
*/
public static function create(ContainerInterface $container, FeedTypeInterface $feed_type) {
return new static(
$container->get('uuid'),
$container->get('plugin.manager.tamper'),
$feed_type
);
}
/**
* {@inheritdoc}
*/
public function getTamper($instance_id) {
return $this->getTampers()->get($instance_id);
}
/**
* {@inheritdoc}
*/
public function getTampers() {
if (!isset($this->tamperCollection)) {
$tampers = $this->feedType->getThirdPartySetting('feeds_tamper', 'tampers');
$tampers = empty($tampers) ? [] : $tampers;
$this->tamperCollection = new TamperPluginCollection($this->tamperManager, $this->getSourceDefinition(), $tampers);
$this->tamperCollection->sort();
}
return $this->tamperCollection;
}
/**
* {@inheritdoc}
*/
public function getTampersGroupedBySource() {
$grouped_tampers = [];
$this->getTampers()->sort();
foreach ($this->getTampers() as $id => $tamper) {
$grouped_tampers[(string) $tamper->getSetting('source')][$id] = $tamper;
}
return $grouped_tampers;
}
/**
* {@inheritdoc}
*/
public function getPluginCollections() {
return ['tampers' => $this->getTampers()];
}
/**
* {@inheritdoc}
*/
public function addTamper(array $configuration) {
$configuration['uuid'] = $this->uuidGenerator->generate();
$configuration['source_definition'] = $this->getSourceDefinition();
$this->getTampers()->addInstanceId($configuration['uuid'], $configuration);
$this->updateFeedType();
return $configuration['uuid'];
}
/**
* {@inheritdoc}
*/
public function setTamperConfig($instance_id, array $configuration) {
$configuration['uuid'] = $instance_id;
$this->getTampers()->setInstanceConfiguration($instance_id, $configuration);
$this->updateFeedType();
return $this;
}
/**
* {@inheritdoc}
*/
public function removeTamper($instance_id) {
$this->getTampers()->removeInstanceId($instance_id);
$this->updateFeedType();
return $this;
}
/**
* {@inheritdoc}
*/
public function rectifyInstances() {
// Check the difference between the tampers grouped by source and a list of
// all sources used in mapping. By diffing we keep an array of tampers
// belonging to a source that is no longer used in the mapping.
$tampers_by_source_to_remove = array_diff_key($this->getTampersGroupedBySource(), $this->getUniqueSourceList());
// Remove these tamper instances.
foreach ($tampers_by_source_to_remove as $tampers) {
foreach ($tampers as $uuid => $tamper) {
$this->removeTamper($uuid);
}
}
}
/**
* {@inheritdoc}
*/
public function getUniqueSourceList() {
// Extract used sources from mappings.
$sources = [];
foreach ($this->feedType->getMappings() as $mapping) {
foreach ($mapping['map'] as $column => $source) {
if ($source == '') {
continue;
}
$sources[$source] = $source;
}
}
return $sources;
}
/**
* {@inheritdoc}
*/
public function getSourceDefinition() {
$source_list = [];
foreach ($this->feedType->getMappingSources() as $key => $source) {
$source_list[$key] = $key;
}
return new SourceDefinition($source_list);
}
/**
* Writes tampers back on the feed type.
*/
protected function updateFeedType() {
$this->getTampers()->sort();
foreach ($this->getPluginCollections() as $plugin_config_key => $plugin_collection) {
$this->feedType->setThirdPartySetting('feeds_tamper', $plugin_config_key, $plugin_collection->getConfiguration());
}
}
}
<?php
namespace Drupal\feeds_tamper;
use Drupal\Core\Plugin\ObjectWithPluginCollectionInterface;
/**
* Interface for managing tamper plugins for a feed type.
*/
interface FeedTypeTamperMetaInterface extends ObjectWithPluginCollectionInterface {
/**
* Returns a specific Tamper plugin.
*
* @param string $instance_id
* The tamper plugin instance ID.
*
* @return \Drupal\tamper\TamperInterface
* The tamper plugin instance.
*/
public function getTamper($instance_id);
/**
* Returns the tamper plugin instances for this feed type.
*
* @return \Drupal\feeds_tamper\TamperPluginCollection|\Drupal\tamper\TamperInterface[]
* The tamper plugin collection.
*/
public function getTampers();
/**
* Returns the tamper plugin instances for this feed type, keyed by source.
*
* @return \Drupal\tamper\TamperInterface[][]
* An associative array of plugin instances, keyed by source.
*/
public function getTampersGroupedBySource();
/**
* Adds a tamper plugin instance for this feed type.
*
* @param array $configuration
* An array of tamper configuration.
*
* @return string
* The tamper plugin instance ID.
*/
public function addTamper(array $configuration);
/**
* Sets the configuration for a tamper plugin instance.
*
* @param string $instance_id
* The ID of a tamper plugin to set the configuration for.
* @param array $configuration
* The tamper plugin configuration to set.
*
* @return $this
*/
public function setTamperConfig($instance_id, array $configuration);
/**
* Removes a tamper plugin instance from this feed type.
*
* @param string $instance_id
* The ID of a tamper plugin to remove.
*
* @return $this
*/
public function removeTamper($instance_id);
/**
* Removes tamper instances whose source was removed from the mapping.
*/
public function rectifyInstances();
/**
* Returns an unique list of sources used in mappings.
*
* @return string[]
* A list of sources.
*/
public function getUniqueSourceList();
/**
* Returns a definition of sources.
*
* @return \Drupal\tamper\SourceDefinitionInterface
* A source definition, which can be used by Tamper plugins.
*/
public function getSourceDefinition();
}
<?php
namespace Drupal\feeds_tamper;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\feeds\Entity\FeedType;
/**
* Defines a class containing permission callbacks.
*/
class FeedsTamperPermissions {
use StringTranslationTrait;
/**
* Returns an array of feeds tamper permissions.
*
* @return array
* An array of permissions.
*/
public function feedTypeTamperPermissions() {
$perms = [];
foreach (FeedType::loadMultiple() as $type) {
$args = ['%name' => $type->label()];
/** @var \Drupal\feeds\Entity\FeedType $type */
$perms['tamper ' . $type->id()] = [
'title' => $this->t('Tamper %name feed type', $args),
'description' => $this->t('Create, edit and delete plugins for %name feed type', $args),
];
}
return $perms;
}
}
<?php
namespace Drupal\feeds_tamper\Form;
use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\Core\Form\FormStateInterface;
use Drupal\feeds\FeedTypeInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Provides a form to add a tamper plugin to a feed type.
*/
class TamperAddForm extends TamperFormBase {
/**
* The source field on the feed type.
*
* @var string
*/
protected $sourceField;
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'feeds_tamper_add_form';
}
/**
* Makes sure that the source field exists.
*
* @param \Drupal\feeds\FeedTypeInterface $feeds_feed_type
* The feed.
* @param string $source_field
* The source field.
*/
private function assertSourceField(FeedTypeInterface $feeds_feed_type, $source_field) {
$sources = $feeds_feed_type->getMappingSources();
if (!isset($sources[$source_field])) {
throw new NotFoundHttpException();
}
}
/**
* Prepares the tamper plugin.
*
* @param string $tamper_id
* The id of the tamper plugin.
*
* @return \Drupal\tamper\TamperInterface|null
* The tamper plugin instance or null in case the Tamper plugin could not be
* instantiated.
*/
protected function preparePlugin($tamper_id = NULL) {
if (empty($tamper_id)) {
return NULL;
}
$meta = $this->feedTypeTamperManager->getTamperMeta($this->feedsFeedType);
try {
/** @var \Drupal\tamper\TamperInterface $tamper */
$tamper = $this->tamperManager->createInstance($tamper_id, [
'source_definition' => $meta->getSourceDefinition(),
]);
return $tamper;
}
catch (PluginException $e) {
$this->messenger()->addError($this->t('The specified plugin is invalid.'));
}
}
/**
* Form constructor.
*
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param \Drupal\feeds\FeedTypeInterface $feeds_feed_type
* The feed that we are adding a tamper plugin to.
* @param string $source_field
* The source field we are adding the tamper plugin to.
*
* @return array
* The form structure.
*/
public function buildForm(array $form, FormStateInterface $form_state, FeedTypeInterface $feeds_feed_type = NULL, $source_field = NULL) {
$this->assertSourceField($feeds_feed_type, $source_field);
$this->feedsFeedType = $feeds_feed_type;
$this->sourceField = $source_field;
$this->plugin = $this->preparePlugin($form_state->getValue(self::VAR_TAMPER_ID));
return parent::buildForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// Support non-javascript browsers.
if (empty($this->plugin) || $this->plugin->getPluginId() !== $form_state->getValue('tamper_id')) {
$form_state->setRebuild();
return;
}
$config = $this->prepareConfig($this->sourceField, $form_state);
$tamper_meta = $this->feedTypeTamperManager->getTamperMeta($this->feedsFeedType);
$tamper_meta->addTamper($config);
$this->feedsFeedType->save();
$this->messenger()->addStatus($this->t('Plugin %plugin_label was successfully added to %source.', [
'%plugin_label' => $this->plugin->getPluginDefinition()['label'],
'%source' => $this->sourceField,
]));
}
}
<?php
namespace Drupal\feeds_tamper\Form;
use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\feeds\FeedTypeInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Tamper delete form.
*
* @package Drupal\feeds_tamper\Form
*/
class TamperDeleteForm extends ConfirmFormBase {
use TamperFormTrait;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
/** @var self $form */
$form = parent::create($container);
$form->setTamperManager($container->get('plugin.manager.tamper'));
$form->setTamperMetaManager($container->get('feeds_tamper.feed_type_tamper_manager'));
return $form;
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'feeds_tamper_delete_form';
}
/**
* {@inheritdoc}
*/
public function getCancelUrl() {
return new Url('entity.feeds_feed_type.tamper', [
'feeds_feed_type' => $this->feedsFeedType->id(),
]);
}
/**
* {@inheritdoc}
*/
public function getQuestion() {
return $this->t('Are you sure you want to delete the Tamper plugin instance %instance from the source %source?', [
'%source' => $this->plugin->getSetting('source'),
'%instance' => $this->plugin->getPluginDefinition()['label'],
]);
}
/**
* Form constructor.
*
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param \Drupal\feeds\FeedTypeInterface $feeds_feed_type
* The feed that we are adding a tamper plugin to.
* @param string $tamper_uuid
* The tamper uuid.
*
* @return array
* The form structure.
*/
public function buildForm(array $form, FormStateInterface $form_state, FeedTypeInterface $feeds_feed_type = NULL, $tamper_uuid = NULL) {
$this->assertTamper($feeds_feed_type, $tamper_uuid);
$this->feedsFeedType = $feeds_feed_type;
$tamper_meta = $this->feedTypeTamperManager->getTamperMeta($feeds_feed_type);
$this->plugin = $tamper_meta->getTamper($tamper_uuid);
$form = parent::buildForm($form, $form_state);
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$tamper_meta = $this->feedTypeTamperManager->getTamperMeta($this->feedsFeedType);
$uuid = $this->plugin->getSetting('uuid');
$tampers_config = $tamper_meta->getTampers()->getConfiguration();
$tamper_meta->removeTamper($uuid);
$this->feedsFeedType->save();
$this->messenger()->addStatus($this->t('The Tamper plugin instance %plugin has been deleted from %source.', [
'%plugin' => $this->plugin->getPluginDefinition()['label'],
'%source' => $tampers_config[$uuid]['source'],
]));
$form_state->setRedirect('entity.feeds_feed_type.tamper', [
'feeds_feed_type' => $this->feedsFeedType->id(),
]);
}
}
<?php
namespace Drupal\feeds_tamper\Form;
use Drupal\Core\Form\FormStateInterface;
use Drupal\feeds\FeedTypeInterface;
/**
* Tamper edit form.
*
* @package Drupal\feeds_tamper\Form
*/
class TamperEditForm extends TamperFormBase {
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'feeds_tamper_edit_form';
}
/**
* Form constructor.
*
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param \Drupal\feeds\FeedTypeInterface $feeds_feed_type
* The feed that we are adding a tamper plugin to.
* @param string $tamper_uuid
* The tamper uuid.
*
* @return array
* The form structure.
*/
public function buildForm(array $form, FormStateInterface $form_state, FeedTypeInterface $feeds_feed_type = NULL, $tamper_uuid = NULL) {
$this->assertTamper($feeds_feed_type, $tamper_uuid);
$this->feedsFeedType = $feeds_feed_type;
$tamper_meta = $this->feedTypeTamperManager->getTamperMeta($feeds_feed_type);
$this->plugin = $tamper_meta->getTamper($tamper_uuid);
$form = parent::buildForm($form, $form_state);
$form[self::VAR_TAMPER_ID]['#disabled'] = TRUE;
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$tamper_meta = $this->feedTypeTamperManager->getTamperMeta($this->feedsFeedType);
$uuid = $this->plugin->getSetting('uuid');
$tampers_config = $tamper_meta->getTampers()->getConfiguration();
$config = $this->prepareConfig($tampers_config[$uuid]['source'], $form_state);
$tamper_meta->setTamperConfig($uuid, $config);
$this->feedsFeedType->save();
$this->messenger()->addStatus($this->t('The plugin %plugin_label has been updated.', [
'%plugin_label' => $this->plugin->getPluginDefinition()['label'],
]));
}
}
<?php
namespace Drupal\feeds_tamper\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\SubformState;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* The base class for add/edit tamper forms.
*
* @package Drupal\feeds_tamper\Form
*/
abstract class TamperFormBase extends FormBase {
use TamperFormTrait;
// Form fields.
const VAR_TAMPER_ID = 'tamper_id';
const VAR_TAMPER_LABEL = 'label';
const VAR_PLUGIN_CONFIGURATION = 'plugin_configuration';
const VAR_WEIGHT = 'weight';
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
/** @var self $form */
$form = parent::create($container);
$form->setTamperManager($container->get('plugin.manager.tamper'));
$form->setTamperMetaManager($container->get('feeds_tamper.feed_type_tamper_manager'));
return $form;
}
/**
* Page title callback.
*
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match.
*
* @return \Drupal\Core\StringTranslation\TranslatableMarkup|null
* Translated string to use as the title.
*/
public function tamperTitle(RouteMatchInterface $route_match) {
/** @var \Drupal\feeds\Entity\FeedType $feed_type */
$feed_type = $route_match->getParameter('feeds_feed_type');
$source_field = $route_match->getParameter('source_field');
$tamper_uuid = $route_match->getParameter('tamper_uuid');
if ($source_field) {
return $this->t('Add a tamper plugin to @label : @source', [
'@label' => $feed_type->label(),
'@source' => $source_field,
]);
}
elseif ($tamper_uuid) {
$tamper_meta = $this->feedTypeTamperManager->getTamperMeta($feed_type);
$tamper = $tamper_meta->getTamper($tamper_uuid);
return $this->t('Edit @label', [
'@label' => $tamper->getPluginDefinition()['label'],
]);
}
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form[self::VAR_TAMPER_ID] = [
'#type' => 'select',
'#title' => $this->t('The plugin to add'),
'#options' => $this->getPluginOptions(),
'#required' => TRUE,
'#default_value' => $this->plugin ? $this->plugin->getPluginDefinition()['id'] : NULL,
'#ajax' => [
'callback' => '::getPluginForm',
'wrapper' => 'plugin-config',
],
];
$form[self::VAR_PLUGIN_CONFIGURATION] = [
'#type' => 'container',
'#tree' => TRUE,
'#attributes' => ['id' => ['plugin-config']],
];
if ($this->plugin) {
$form[self::VAR_PLUGIN_CONFIGURATION][self::VAR_TAMPER_LABEL] = [
'#type' => 'textfield',
'#title' => $this->t('Label'),
'#maxlength' => '255',
'#description' => $this->t('A useful description of what this plugin is doing.'),
'#required' => TRUE,
'#default_value' => $this->plugin->getSetting(self::VAR_TAMPER_LABEL) ? $this->plugin->getSetting(self::VAR_TAMPER_LABEL) : $this->plugin->getPluginDefinition()['label'],
];
$form[self::VAR_PLUGIN_CONFIGURATION]['description'] = [
'#markup' => $this->plugin->getPluginDefinition()['description'],
];
$subform_state = SubformState::createForSubform($form[self::VAR_PLUGIN_CONFIGURATION], $form, $form_state);
$form[self::VAR_PLUGIN_CONFIGURATION] = $this->plugin->buildConfigurationForm($form[self::VAR_PLUGIN_CONFIGURATION], $subform_state);
}
$form[self::VAR_WEIGHT] = [
'#type' => 'hidden',
'#value' => $this->getWeight(),
];
$cancel_url = Url::fromRoute('entity.feeds_feed_type.tamper', [
'feeds_feed_type' => $this->feedsFeedType->id(),
]);
$form['actions'] = ['#type' => 'actions'];
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Submit'),
'#button_type' => 'primary',
];
$form['actions']['cancel'] = [
'#type' => 'link',
'#title' => $this->t('Cancel'),
'#attributes' => ['class' => ['button']],
'#url' => $cancel_url,
];
return $form;
}
/**
* Ajax callback.
*
* Returns the plugin configuration form from an ajax request.
*
* @param array $form
* Drupal form array.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* Form state interface.
*
* @return array
* Plugin form.
*/
public function getPluginForm(array $form, FormStateInterface $form_state) {
// Update label when selecting an other plugin.
if (!$this->plugin || !$this->plugin->getSetting(self::VAR_TAMPER_LABEL)) {
$form[self::VAR_PLUGIN_CONFIGURATION][self::VAR_TAMPER_LABEL]['#value'] = $form[self::VAR_PLUGIN_CONFIGURATION][self::VAR_TAMPER_LABEL]['#default_value'];
}
return $form[self::VAR_PLUGIN_CONFIGURATION];
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
if (!empty($this->plugin)) {
$subform_state = SubformState::createForSubform($form[self::VAR_PLUGIN_CONFIGURATION], $form, $form_state);
$this->plugin->validateConfigurationForm($form[self::VAR_PLUGIN_CONFIGURATION], $subform_state);
}
}
/**
* Get the tamper plugin options.
*
* @return array
* List of tamper plugin groups, keyed by group, where the value is another
* array of plugin labels keyed by plugin id.
*/
protected function getPluginOptions() {
// @todo Move this logic to the tamper manager interface?
$plugin_options = array_map(function ($grouped_plugins) {
$group_options = [];
foreach ($grouped_plugins as $id => $plugin_definition) {
$group_options[$id] = $plugin_definition['label'];
}
return $group_options;
}, $this->tamperManager->getGroupedDefinitions());
return $plugin_options;
}
/**
* Prepares a configuration array.
*
* @param string $source
* The source.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The form state.
*
* @return array
* The configuration array.
*/
protected function prepareConfig($source, FormStateInterface $form_state) {
$config = [
'plugin' => $this->plugin->getPluginId(),
'source' => $source,
'weight' => $form_state->getValue(self::VAR_WEIGHT),
'label' => $form_state->getValue([self::VAR_PLUGIN_CONFIGURATION, self::VAR_TAMPER_LABEL]),
];
$plugin_config = $form_state->getValue(self::VAR_PLUGIN_CONFIGURATION);
if ($plugin_config) {
$config += $plugin_config;
}
return $config;
}
/**
* Gets the weight to use for the plugin.
*
* @return int
* The plugin's weight.
*/
protected function getWeight() {
$request = $this->getRequest();
if ($request->query->has(self::VAR_WEIGHT)) {
return (int) $request->query->get(self::VAR_WEIGHT);
}
if ($this->plugin) {
return (int) $this->plugin->getSetting(self::VAR_WEIGHT);
}
return 0;
}
}
<?php
namespace Drupal\feeds_tamper\Form;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\feeds\FeedTypeInterface;
use Drupal\feeds_tamper\FeedTypeTamperManagerInterface;
use Drupal\tamper\TamperManagerInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Trait TamperFormTrait.
*
* Provides helper methods for the Tamper forms.
*
* @package Drupal\feeds_tamper\Form
*/
trait TamperFormTrait {
/**
* The feed item we are adding a tamper plugin to.
*
* @var \Drupal\feeds\FeedTypeInterface
*/
protected $feedsFeedType;
/**
* The tamper plugin instance.
*
* @var \Drupal\tamper\TamperInterface
*/
protected $plugin;
/**
* The tamper plugin manager.
*
* @var \Drupal\tamper\TamperManagerInterface
*/
protected $tamperManager;
/**
* The feeds tamper metadata manager.
*
* @var \Drupal\feeds_tamper\FeedTypeTamperManagerInterface
*/
protected $feedTypeTamperManager;
/**
* Sets the tamper manager.
*
* @param \Drupal\tamper\TamperManagerInterface $tamper_manager
* Tamper plugin manager.
*/
public function setTamperManager(TamperManagerInterface $tamper_manager) {
$this->tamperManager = $tamper_manager;
}
/**
* Sets the feed type tamper manager.
*
* @param \Drupal\feeds_tamper\FeedTypeTamperManagerInterface $feed_type_tamper_manager
* Feed type tamper manager.
*/
public function setTamperMetaManager(FeedTypeTamperManagerInterface $feed_type_tamper_manager) {
$this->feedTypeTamperManager = $feed_type_tamper_manager;
}
/**
* Makes sure that the tamper exists.
*
* @param \Drupal\feeds\FeedTypeInterface $feeds_feed_type
* The feed.
* @param string $tamper_uuid
* The tamper uuid.
*
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
* In case a Tamper plugin with the given uuid could not be found.
*/
protected function assertTamper(FeedTypeInterface $feeds_feed_type, $tamper_uuid) {
$tamper_meta = $this->feedTypeTamperManager->getTamperMeta($feeds_feed_type);
try {
$tamper_meta->getTamper($tamper_uuid);
}
catch (PluginNotFoundException $e) {
throw new NotFoundHttpException();
}
}
/**
* Checks access for the form page.
*
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match.
* @param \Drupal\Core\Session\AccountInterface $account
* The current user account.
*
* @return \Drupal\Core\Access\AccessResult
* The access result.
*/
public function checkAccess(RouteMatchInterface $route_match, AccountInterface $account) {
if ($account->hasPermission('administer feeds_tamper')) {
return AccessResult::allowed();
}
/** @var \Drupal\feeds\Entity\FeedType $feed_type */
$feed_type = $route_match->getParameter('feeds_feed_type');
return AccessResult::allowedIf($account->hasPermission('tamper ' . $feed_type->id()))
->addCacheableDependency($feed_type);
}
}
<?php
namespace Drupal\feeds_tamper\Form;
use Drupal\Component\Utility\Html;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\feeds\FeedTypeInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a form to manage tamper plugins for a feed type.
*
* @package Drupal\feeds_tamper\Form
*/
class TamperListForm extends FormBase {
use TamperFormTrait;
/**
* An array of the feed type's tamper plugins.
*
* @var array
*/
protected $tampers;
/**
* An array of the feed type's sources.
*
* @var array
*/
protected $sources;
/**
* An array of the feed type's targets.
*
* @var array
*/
protected $targets;
/**
* An array of the grouped mappings.
*
* @var array
*/
protected $groupedMappings;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
/** @var self $form */
$form = parent::create($container);
$form->setTamperMetaManager($container->get('feeds_tamper.feed_type_tamper_manager'));
return $form;
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'feeds_tamper_list_form';
}
/**
* Form constructor.
*
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param \Drupal\feeds\FeedTypeInterface $feeds_feed_type
* The feed that we are adding a tamper plugin to.
*
* @return array
* The form structure.
*/
public function buildForm(array $form, FormStateInterface $form_state, FeedTypeInterface $feeds_feed_type = NULL) {
$this->feedsFeedType = $feeds_feed_type;
$tamper_meta = $this->feedTypeTamperManager->getTamperMeta($this->feedsFeedType);
$this->tampers = $tamper_meta->getTampersGroupedBySource();
$this->sources = $this->feedsFeedType->getMappingSources();
$this->targets = $this->feedsFeedType->getMappingTargets();
$mappings = $this->feedsFeedType->getMappings();
$args = [
'@url' => Url::fromRoute('entity.feeds_feed_type.mapping', [
'feeds_feed_type' => $this->feedsFeedType->id(),
])->toString(),
];
if (!$mappings) {
$this->messenger()->addWarning($this->t('There are no <a href="@url">mappings</a> defined for this importer.', $args));
return $form;
}
// Help message at the top. I have a seceret, I added the link back to the
// mappings because it makes my life a lot easier while testing this.
$message = $this->t('Configure plugins to modify Feeds data before it gets saved. Each <a href="@url">mapping</a> can be manipulated individually.', $args);
$form['help'] = [
'#type' => 'item',
'#markup' => '<div class="help"><p>' . $message . '</p></div>',
];
// Build mapping grouped by source>targets>columns.
foreach ($mappings as $mapping) {
foreach ($mapping['map'] as $column => $source) {
if ($source == '') {
continue;
}
$this->groupedMappings[$source][$mapping['target']][$column] = $mapping;
}
}
$i = 0;
$form['#tree'] = TRUE;
foreach ($this->groupedMappings as $source => $targets) {
$form[$source] = $this->buildTampersTable($form, $form_state, $source, $targets);
$i++;
}
$form['actions'] = [
'#type' => 'actions',
];
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Save'),
];
return $form;
}
/**
* Builds a table of tampers for the specified source -> targets.
*
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param string $source
* The source name.
* @param array $targets
* An array of the source targets.
*
* @return array
* Form table element.
*/
protected function buildTampersTable(array $form, FormStateInterface $form_state, $source, array $targets) {
$header = [
'label' => $this->t('Label'),
'description' => $this->t('Description'),
'weight' => $this->t('Weight'),
'plugin' => $this->t('Plugin'),
'operations' => $this->t('Operations'),
// @todo Implement enabled.
// 'enabled' => $this->t('Enabled'),
];
$url_parameters = ['feeds_feed_type' => $this->feedsFeedType->id()];
$view_tampers_url = Url::fromRoute('entity.feeds_feed_type.tamper', $url_parameters)->toString();
$destination_query = ['destination' => $view_tampers_url];
$target_labels = [];
$item = [
'#type' => 'table',
'#header' => $header,
'#tabledrag' => [
[
'action' => 'order',
'relationship' => 'sibling',
'group' => 'tamper-weight',
],
],
];
foreach ($targets as $target_key => $columns) {
/* @var \Drupal\feeds\FieldTargetDefinition $target */
$target = $this->targets[$target_key];
$source_label = Html::escape($this->sources[$source]['label']);
$label = Html::escape($target->getLabel()) . ': ';
foreach ($columns as $column => $mapping) {
if (count($mapping['map']) > 1) {
$target_labels[] = $label . $target->getPropertyLabel($column);
}
else {
$target_labels[] = $label . ($target->getDescription() ?: $column);
}
}
}
$item['#caption'] = $source_label . ' -> ' . implode(', ', $target_labels);
$add_plugin_weight = 0;
if (!empty($this->tampers[$source])) {
// Calculate the range (delta) needed for the weight field. By default,
// the range is from -10 to 10, which means 21 slots in total. If there
// are however more than 21 tamper instances, the range should increase in
// order to be able to assign an unique weight to each tamper instance.
$tampers_count = round(count($this->tampers[$source]) / 2);
$tamper_weight_delta = ($tampers_count < 10 ? 10 : $tampers_count);
/** @var \Drupal\tamper\TamperInterface $tamper */
foreach ($this->tampers[$source] as $id => $tamper) {
$row = [
'#attributes' => ['class' => ['draggable']],
'#weight' => $tamper->getSetting('weight'),
];
// Label.
$row['label'] = [
'#plain_text' => $tamper->getSetting('label') ? $tamper->getSetting('label') : '',
];
// Plugin instance description.
$row['description'] = [
'#plain_text' => $tamper->getPluginDefinition()['description'],
];
// Weight field.
$row['weight'] = [
'#title' => $this->t('Weight'),
'#title_display' => 'invisible',
'#type' => 'weight',
'#default_value' => $tamper->getSetting('weight'),
'#attributes' => ['class' => ['tamper-weight']],
'#delta' => $tamper_weight_delta,
];
$row['plugin'] = [
'#plain_text' => $tamper->getPluginDefinition()['label'],
];
$operations_params = $url_parameters + ['tamper_uuid' => $id];
$row['operations'] = [
'#type' => 'operations',
'#links' => [
'edit' => [
'title' => $this->t('Edit'),
'url' => Url::fromRoute('entity.feeds_feed_type.tamper_edit', $operations_params, [
'query' => $destination_query,
]),
],
'delete' => [
'title' => $this->t('Delete'),
'url' => Url::fromRoute('entity.feeds_feed_type.tamper_delete', $operations_params),
],
],
];
// @todo Implement enabled.
// $row['enabled'] = '';
$item[$id] = $row;
$add_plugin_weight = $tamper->getSetting('weight') + 1;
}
}
$add_tamper_url = Url::fromRoute('entity.feeds_feed_type.tamper_add', $url_parameters + [
'source_field' => $source,
], ['query' => array_merge($destination_query, ['weight' => $add_plugin_weight])]);
$item['add']['link'] = [
'#type' => 'link',
'#title' => $this->t('Add plugin'),
'#url' => $add_tamper_url,
'#wrapper_attributes' => ['colspan' => count($header)],
];
return $item;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
foreach ($this->groupedMappings as $source => $targets) {
if ($tampers = $form_state->getValue($source)) {
foreach ($tampers as $tamper_uuid => $values) {
// @todo Implement enabled.
$tamper_meta = $this->feedTypeTamperManager->getTamperMeta($this->feedsFeedType);
$tamper = $tamper_meta->getTamper($tamper_uuid);
$tamper_meta->setTamperConfig($tamper_uuid, array_merge($tamper->getConfiguration(), ['weight' => $values['weight']]));
}
}
}
$this->feedsFeedType->save();
$this->messenger()->addStatus($this->t('Your changes have been saved.'));
}
}
<?php
namespace Drupal\feeds_tamper;
use Drupal\tamper\TamperPluginCollection as TamperPluginCollectionBase;
/**
* Collection of Tamper plugins for a specific Feeds importer.
*/
class TamperPluginCollection extends TamperPluginCollectionBase {
/**
* The key within the plugin configuration that contains the plugin ID.
*
* @var string
*/
protected $pluginKey = 'plugin';
/**
* Provides uasort() callback to sort plugins.
*/
public function sortHelper($aID, $bID) {
$a = $this->get($aID)->getSetting('weight');
$b = $this->get($bID)->getSetting('weight');
if ($a != $b) {
return ($a < $b) ? -1 : 1;
}
return parent::sortHelper($aID, $bID);
}
}
<?php
namespace Drupal\Tests\feeds_tamper\Functional;
/**
* Tests that the Tamper link is shown on the feed type list page.
*
* @group feeds_tamper
*/
class FeedTypeListBuilderTest extends FeedsTamperBrowserTestBase {
/**
* Tests that the tamper operation is displayed on the feed type list page.
*/
public function testUiWithRestrictedPrivileges() {
// Add two feed types.
$this->feedType = $this->createFeedType([
'id' => 'my_feed_type',
'label' => 'My feed type',
]);
$this->feedType = $this->createFeedType([
'id' => 'my_feed_type_restricted',
'label' => 'My feed type (restricted)',
]);
// Add an user who may only tamper 'my_feed_type'.
$account = $this->drupalCreateUser([
'administer feeds',
'tamper my_feed_type',
]);
$this->drupalLogin($account);
// Assert that the tamper operation links is being displayed only for
// my_feed_type .
$this->drupalGet('/admin/structure/feeds');
$session = $this->assertSession();
$session->linkExists('Tamper');
$session->linkByHrefExists('/admin/structure/feeds/manage/my_feed_type/tamper');
$session->linkByHrefNotExists('/admin/structure/feeds/manage/my_feed_type_restricted/tamper');
}
/**
* Tests that the weight range selection increases when having many tampers.
*
* By default, the weight range for a tamper plugin is from -10 to 10. So
* that's room for 21 tamper plugin instances. But when there are more than 21
* tampers, the weight range should become bigger.
*/
public function testDeltaIncreaseWithManyTampers() {
$feed_type_tamper_manager = $this->container->get('feeds_tamper.feed_type_tamper_manager');
$feed_type = $this->createFeedType([
'id' => 'my_feed_type',
'label' => 'My feed type',
]);
// Add a tamper.
$uuid = $feed_type_tamper_manager->getTamperMeta($feed_type)
->addTamper([
'plugin' => 'convert_case',
'operation' => 'strtoupper',
'source' => 'title',
'description' => 'Convert the case to uppercase.',
]);
$feed_type->save();
// Assert that weight selector ranges from -10 to 10.
$this->drupalGet('/admin/structure/feeds/manage/my_feed_type/tamper');
$weight_selector = $this->getSession()
->getPage()
->find('css', '#edit-title-' . $uuid . '-weight');
$this->assertEquals(implode('', range(-10, 10)), $weight_selector->getText());
// Now add 19 more tampers and assert that the weight selector still ranges
// from -10 to 10.
for ($i = 0; $i < 19; $i++) {
$feed_type_tamper_manager->getTamperMeta($feed_type)
->addTamper([
'plugin' => 'convert_case',
'operation' => 'strtoupper',
'source' => 'title',
'description' => 'Convert the case to uppercase.',
]);
}
$feed_type->save();
$this->drupalGet('/admin/structure/feeds/manage/my_feed_type/tamper');
$weight_selector = $this->getSession()
->getPage()
->find('css', '#edit-title-' . $uuid . '-weight');
$this->assertEquals(implode('', range(-10, 10)), $weight_selector->getText());
// Finally, add two more tampers. Assert that weight selector now ranges
// from -11 to 11.
for ($i = 0; $i < 2; $i++) {
$feed_type_tamper_manager->getTamperMeta($feed_type)
->addTamper([
'plugin' => 'convert_case',
'operation' => 'strtoupper',
'source' => 'title',
'description' => 'Convert the case to uppercase.',
]);
}
$feed_type->save();
$this->drupalGet('/admin/structure/feeds/manage/my_feed_type/tamper');
$weight_selector = $this->getSession()
->getPage()
->find('css', '#edit-title-' . $uuid . '-weight');
$this->assertEquals(implode('', range(-11, 11)), $weight_selector->getText());
}
}
<?php
namespace Drupal\Tests\feeds_tamper\Functional;
use Drupal\Tests\feeds\Functional\FeedsBrowserTestBase;
/**
* Provides a base class for Feeds Tamper functional tests.
*/
abstract class FeedsTamperBrowserTestBase extends FeedsBrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = [
'feeds',
'feeds_tamper',
'node',
'user',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Create an user with Feeds admin privileges.
$this->adminUser = $this->drupalCreateUser([
'administer feeds',
'administer feeds_tamper',
]);
$this->drupalLogin($this->adminUser);
}
}
<?php
namespace Drupal\Tests\feeds_tamper\Functional;
/**
* Tests adding/editing/removing tamper plugins using the UI.
*
* @group feeds_tamper
*/
class UiCrudTest extends FeedsTamperBrowserTestBase {
/**
* A feed type entity.
*
* @var \Drupal\feeds\Entity\FeedType
*/
protected $feedType;
/**
* The url to the tamper listing page.
*
* @var string
*/
protected $url;
/**
* The manager for FeedTypeTamperMeta instances.
*
* @var \Drupal\feeds_tamper\FeedTypeTamperManager
*/
protected $feedTypeTamperManager;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Add body field.
node_add_body_field($this->nodeType);
// Add a feed type with mapping to body.
$this->feedType = $this->createFeedType([
'mappings' => array_merge($this->getDefaultMappings(), [
[
'target' => 'body',
'map' => [
'summary' => 'description',
'value' => 'content',
],
],
]),
]);
$this->url = $this->feedType->toUrl('tamper');
$this->feedTypeTamperManager = \Drupal::service('feeds_tamper.feed_type_tamper_manager');
}
/**
* Tests adding a Tamper plugin using the UI.
*/
public function testAddTamperInstance() {
// Go to the tamper listing.
$this->drupalGet($this->url);
// Click link for adding a tamper plugin to the source 'description'.
$this->getSession()
->getPage()
->find('css', '#edit-description-add-link')
->click();
// Select plugin.
$edit = [
'tamper_id' => 'trim',
];
$this->drupalPostForm(NULL, $edit, 'Submit');
// Configure plugin.
$edit = [
'plugin_configuration[label]' => 'Trim test',
'plugin_configuration[side]' => 'ltrim',
];
$this->drupalPostForm(NULL, $edit, 'Submit');
// And assert that the tamper plugin was added.
$this->feedType = $this->reloadEntity($this->feedType);
$plugin_collection = $this->feedTypeTamperManager
->getTamperMeta($this->feedType, TRUE)
->getTampers();
$this->assertCount(1, $plugin_collection);
$tamper = $plugin_collection->getIterator()->current();
$this->assertEquals('trim', $tamper->getPluginId());
$this->assertEquals('Trim test', $tamper->getSetting('label'));
$this->assertEquals('ltrim', $tamper->getSetting('side'));
$this->assertEquals('description', $tamper->getSetting('source'));
}
/**
* Tests editing a Tamper plugin using the UI.
*/
public function testEditTamperInstance() {
// Programmatically add a tamper plugin instance.
$uuid = $this->feedTypeTamperManager
->getTamperMeta($this->feedType)
->addTamper([
'plugin' => 'convert_case',
'operation' => 'strtoupper',
'label' => 'Str to Upper',
'source' => 'title',
'description' => 'Convert the case to uppercase.',
]);
$this->feedType->save();
// Go to the tamper listing.
$this->drupalGet($this->url);
// Click link for editing this tamper plugin.
$this->getSession()
->getPage()
->find('css', '#edit-title ul.dropbutton li.edit a')
->click();
// Change a setting.
$edit = [
'plugin_configuration[operation]' => 'ucfirst',
];
$this->drupalPostForm(NULL, $edit, 'Submit');
// Assert that the tamper instance configuration was updated.
$this->feedType = $this->reloadEntity($this->feedType);
$plugin_collection = $this->feedTypeTamperManager
->getTamperMeta($this->feedType, TRUE)
->getTampers();
$this->assertCount(1, $plugin_collection);
$tamper = $plugin_collection->getIterator()->current();
$this->assertEquals('convert_case', $tamper->getPluginId());
$this->assertEquals($uuid, $tamper->getSetting('uuid'));
$this->assertEquals('ucfirst', $tamper->getSetting('operation'));
$this->assertEquals('title', $tamper->getSetting('source'));
}
/**
* Tests removing a Tamper plugin using the UI.
*/
public function testRemoveTamperInstance() {
// Programmatically add a tamper plugin instance.
$this->feedTypeTamperManager
->getTamperMeta($this->feedType, TRUE)
->addTamper([
'plugin' => 'convert_case',
'operation' => 'strtoupper',
'label' => 'Str to Upper',
'source' => 'title',
'description' => 'Convert the case to uppercase.',
]);
$this->feedType->save();
// Go to the tamper listing.
$this->drupalGet($this->url);
// Click link for removing this tamper plugin.
$this->getSession()
->getPage()
->find('css', '#edit-title ul.dropbutton li.delete a')
->click();
// Confirm.
$this->drupalPostForm(NULL, [], 'Confirm');
// Assert that the tamper instance was removed.
$this->feedType = $this->reloadEntity($this->feedType);
$plugin_collection = $this->feedTypeTamperManager
->getTamperMeta($this->feedType, TRUE)
->getTampers();
$this->assertCount(0, $plugin_collection);
}
/**
* Tests changing weights of Tamper plugins.
*/
public function testChangeTamperOrder() {
// Programmatically add a few tamper plugin instances.
$tamper_meta = $this->feedTypeTamperManager->getTamperMeta($this->feedType, TRUE);
$uuid_content_1 = $tamper_meta->addTamper([
'plugin' => 'explode',
'label' => 'Explode',
'separator' => '|',
'source' => 'content',
]);
$uuid_content_2 = $tamper_meta->addTamper([
'plugin' => 'implode',
'label' => 'Implode',
'glue' => '-',
'source' => 'content',
]);
$uuid_content_3 = $tamper_meta->addTamper([
'plugin' => 'trim',
'label' => 'Trim Content',
'side' => 'trim',
'source' => 'content',
]);
$uuid_title_1 = $tamper_meta->addTamper([
'plugin' => 'trim',
'label' => 'Trim Title',
'side' => 'trim',
'source' => 'title',
]);
$uuid_title_2 = $tamper_meta->addTamper([
'plugin' => 'required',
'label' => 'Required',
'invert' => FALSE,
'source' => 'title',
]);
$this->feedType->save();
// Go to the tamper listing.
$this->drupalGet($this->url);
// Change weights.
$edit = [
"title[$uuid_title_1][weight]" => -9,
"title[$uuid_title_2][weight]" => -10,
"content[$uuid_content_1][weight]" => -10,
"content[$uuid_content_2][weight]" => -8,
"content[$uuid_content_3][weight]" => -9,
];
$this->drupalPostForm(NULL, $edit, 'Save');
// Assert that the weights of all tamper plugins were updated.
$this->feedType = $this->reloadEntity($this->feedType);
$tamper_meta = $this->feedTypeTamperManager->getTamperMeta($this->feedType, TRUE);
$this->assertEquals(-9, $tamper_meta->getTamper($uuid_title_1)->getSetting('weight'));
$this->assertEquals(-10, $tamper_meta->getTamper($uuid_title_2)->getSetting('weight'));
$this->assertEquals(-10, $tamper_meta->getTamper($uuid_content_1)->getSetting('weight'));
$this->assertEquals(-8, $tamper_meta->getTamper($uuid_content_2)->getSetting('weight'));
$this->assertEquals(-9, $tamper_meta->getTamper($uuid_content_3)->getSetting('weight'));
}
}
<?php
namespace Drupal\Tests\feeds_tamper\FunctionalJavascript;
use Drupal\Tests\feeds\FunctionalJavascript\FeedsJavascriptTestBase;
/**
* Base class for Feeds Tamper javascript tests.
*/
abstract class FeedsTamperJavascriptTestBase extends FeedsJavascriptTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = [
'feeds',
'feeds_tamper',
'node',
'user',
'dblog',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalLogout();
// Create an user with Feeds admin privileges.
$this->adminUser = $this->drupalCreateUser([
'administer feeds',
'administer feeds_tamper',
]);
$this->drupalLogin($this->adminUser);
}
/**
* Asserts that the logs do not contain PHP errors.
*/
protected function assertNoPhpErrorsInLog() {
$logs = \Drupal::database()->select('watchdog', 'w')
->fields('w')
->condition('w.type', 'php', '=')
->execute()
->fetchAll();
$message = 'There were no PHP errors.';
if (!empty($logs)) {
$errors = [];
foreach ($logs as $log_entry) {
// Format the error message.
$log_entry->variables = unserialize($log_entry->variables);
$errors[] = strtr($log_entry->message, $log_entry->variables);
}
$message = implode("\n", $errors);
}
$this->assertEmpty($logs, $message);
}
}
<?php
namespace Drupal\Tests\feeds_tamper\FunctionalJavascript;
/**
* Tests adding/editing/removing tamper plugins using the UI.
*
* @group feeds_tamper
*/
class UiCrudTest extends FeedsTamperJavascriptTestBase {
/**
* A feed type entity.
*
* @var \Drupal\feeds\Entity\FeedType
*/
protected $feedType;
/**
* The url to the tamper listing page.
*
* @var string
*/
protected $url;
/**
* The manager for FeedTypeTamperMeta instances.
*
* @var \Drupal\feeds_tamper\FeedTypeTamperManager
*/
protected $feedTypeTamperManager;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Add body field.
node_add_body_field($this->nodeType);
// Add a feed type with mapping to body.
$this->feedType = $this->createFeedType([
'mappings' => array_merge($this->getDefaultMappings(), [
[
'target' => 'body',
'map' => [
'summary' => 'description',
'value' => 'content',
],
],
]),
]);
$this->url = $this->feedType->toUrl('tamper');
$this->feedTypeTamperManager = \Drupal::service('feeds_tamper.feed_type_tamper_manager');
}
/**
* Tests adding a Tamper plugin using the UI using javascript.
*/
public function testAddTamperInstance() {
// Go to the tamper listing.
$this->drupalGet($this->url);
// Click link for adding a tamper plugin to the source 'description'.
$this->getSession()
->getPage()
->find('css', '#edit-description-add-link')
->click();
// Select plugin and wait for config form to show up.
$this->getSession()->getPage()->selectFieldOption('tamper_id', 'trim');
$this->assertSession()->waitForElementVisible('css', '#plugin-config');
// Configure plugin.
$edit = [
'plugin_configuration[label]' => 'Trim test',
'plugin_configuration[side]' => 'ltrim',
];
$this->submitForm($edit, 'Submit');
// And assert that the tamper plugin was added.
$this->feedType = $this->reloadEntity($this->feedType);
$plugin_collection = $this->feedTypeTamperManager
->getTamperMeta($this->feedType, TRUE)
->getTampers();
$this->assertCount(1, $plugin_collection);
$tamper = $plugin_collection->getIterator()->current();
$this->assertEquals('trim', $tamper->getPluginId());
$this->assertEquals('Trim test', $tamper->getSetting('label'));
$this->assertEquals('ltrim', $tamper->getSetting('side'));
$this->assertEquals('description', $tamper->getSetting('source'));
// Assert that no PHP errors were generated.
$this->assertNoPhpErrorsInLog();
}
/**
* Tests that the label gets updated when selecting an other plugin.
*/
public function testLabelUpdateWhenChangingPlugin() {
// Go to the tamper listing.
$this->drupalGet($this->url);
// Click link for adding a tamper plugin to the source 'description'.
$this->getSession()
->getPage()
->find('css', '#edit-description-add-link')
->click();
// Select plugin and wait for config form to show up.
$page = $this->getSession()->getPage();
$page->selectFieldOption('tamper_id', 'required');
$this->assertSession()->waitForElementVisible('css', '#plugin-config');
// Assert that the label got set to 'Required'.
$field = $page->findField('plugin_configuration[label]');
$this->assertEquals('Required', $field->getValue());
// Now select an other plugin.
$page = $this->getSession()->getPage();
$page->selectFieldOption('tamper_id', 'unique');
// We use waitForText() here because '#plugin-config' is already visible.
$this->assertSession()->waitForText('Makes the elements in a multivalued field unique.');
// Assert that the label changed to 'Unique'.
$field = $page->findField('plugin_configuration[label]');
$this->assertEquals('Unique', $field->getValue());
// And save.
$this->submitForm([], 'Submit');
// And assert that the tamper plugin was added.
$this->feedType = $this->reloadEntity($this->feedType);
$plugin_collection = $this->feedTypeTamperManager
->getTamperMeta($this->feedType, TRUE)
->getTampers();
$this->assertCount(1, $plugin_collection);
$tamper = $plugin_collection->getIterator()->current();
$this->assertEquals('unique', $tamper->getPluginId());
$this->assertEquals('Unique', $tamper->getSetting('label'));
$this->assertEquals('description', $tamper->getSetting('source'));
// Assert that no PHP errors were generated.
$this->assertNoPhpErrorsInLog();
}
}
<?php
namespace Drupal\Tests\feeds_tamper\Kernel;
use Drupal\Component\Uuid\UuidInterface;
use Drupal\feeds\FeedTypeInterface;
use Drupal\feeds_tamper\FeedTypeTamperMeta;
use Drupal\tamper\Plugin\Tamper\ConvertCase;
/**
* @coversDefaultClass \Drupal\feeds_tamper\FeedTypeTamperMeta
* @group feeds_tamper
*/
class FeedTypeTamperMetaTest extends FeedsTamperKernelTestBase {
/**
* The Tamper manager for a feed type.
*
* @var \Drupal\feeds_tamper\FeedTypeTamperMeta
*/
protected $feedTypeTamperMeta;
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
$container = \Drupal::getContainer();
// Mock the UUID generator and let it always return 'uuid3'.
$uuid_generator = $this->createMock(UuidInterface::class);
$uuid_generator->expects($this->any())
->method('generate')
->will($this->returnValue('uuid3'));
// Get the tamper manager.
$tamper_manager = $container->get('plugin.manager.tamper');
// Mock the feed type and let it always return two tampers.
$feed_type = $this->createMock(FeedTypeInterface::class);
$feed_type->expects($this->any())
->method('getThirdPartySetting')
->with('feeds_tamper', 'tampers')
->will($this->returnValue([
'uuid1' => [
'uuid' => 'uuid1',
'plugin' => 'explode',
'separator' => '|',
'source' => 'alpha',
'description' => 'Explode with pipe character',
],
'uuid2' => [
'uuid' => 'uuid2',
'plugin' => 'convert_case',
'operation' => 'strtoupper',
'source' => 'beta',
'description' => 'Convert all characters to uppercase',
],
]));
$feed_type->expects($this->any())
->method('getMappingSources')
->will($this->returnValue([
'alpha' => [
'label' => 'Alpha',
],
'beta' => [
'label' => 'Beta',
],
]));
// Instantiate a feeds type tamper meta object.
$this->feedTypeTamperMeta = new FeedTypeTamperMeta($uuid_generator, $tamper_manager, $feed_type);
}
/**
* @covers ::getTamper
*/
public function testGetTamper() {
$tamper = $this->feedTypeTamperMeta->getTamper('uuid2');
$this->assertInstanceOf(ConvertCase::class, $tamper);
}
/**
* @covers ::getTampers
*/
public function testGetTampers() {
$tampers = iterator_to_array($this->feedTypeTamperMeta->getTampers());
// Assert that two tampers exist in total.
$this->assertCount(2, $tampers);
// Assert that tampers with uuid 'uuid1' and 'uuid2' exist.
$this->assertArrayHasKey('uuid1', $tampers);
$this->assertArrayHasKey('uuid2', $tampers);
}
/**
* @covers ::getTampersGroupedBySource
*/
public function testGetTampersGroupedBySource() {
// Add a second tamper to 'alpha' source.
$this->feedTypeTamperMeta->addTamper([
'plugin' => 'convert_case',
'operation' => 'ucfirst',
'source' => 'alpha',
'description' => 'Start text with uppercase character',
]);
$tampers_by_source = $this->feedTypeTamperMeta->getTampersGroupedBySource();
// Assert tampers for two sources.
$this->assertCount(2, $tampers_by_source);
$this->assertArrayHasKey('alpha', $tampers_by_source);
$this->assertArrayHasKey('beta', $tampers_by_source);
// Assert that for the first source two tampers exist.
$this->assertCount(2, $tampers_by_source['alpha']);
// And one for the second.
$this->assertCount(1, $tampers_by_source['beta']);
}
/**
* @covers ::addTamper
*/
public function testAddTamper() {
$uuid = $this->feedTypeTamperMeta->addTamper([
'plugin' => 'convert_case',
'operation' => 'ucfirst',
'source' => 'gamma',
'description' => 'Start text with uppercase character',
]);
$this->assertEquals('uuid3', $uuid);
$tamper = $this->feedTypeTamperMeta->getTamper($uuid);
$this->assertInstanceOf(ConvertCase::class, $tamper);
// Assert that three tampers exist in total.
$this->assertCount(3, $this->feedTypeTamperMeta->getTampers());
}
/**
* @covers ::setTamperConfig
*/
public function testSetTamperConfig() {
$separator = ':';
$description = 'Explode with colon character (updated)';
$this->feedTypeTamperMeta->setTamperConfig('uuid1', [
'separator' => $separator,
'description' => $description,
]);
$tampers_config = $this->feedTypeTamperMeta->getTampers()->getConfiguration();
$config = $tampers_config['uuid1'];
$this->assertEquals($separator, $config['separator']);
$this->assertEquals($description, $config['description']);
}
/**
* @covers ::removeTamper
*/
public function testRemoveTamper() {
$this->feedTypeTamperMeta->removeTamper('uuid1');
$tampers = iterator_to_array($this->feedTypeTamperMeta->getTampers());
// Assert that uuid1 is removed, but uuid2 still exists.
$this->assertArrayNotHasKey('uuid1', $tampers);
$this->assertArrayHasKey('uuid2', $tampers);
// Assert that one tamper exists in total.
$this->assertCount(1, $tampers);
}
}
<?php
namespace Drupal\Tests\feeds_tamper\Kernel;
use Drupal\Tests\feeds\Kernel\FeedsKernelTestBase;
/**
* Provides a base class for Feeds Tamper kernel tests.
*/
abstract class FeedsTamperKernelTestBase extends FeedsKernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = [
'feeds',
'feeds_tamper',
'tamper',
];
}
<?php
namespace Drupal\Tests\feeds_tamper\Kernel;
/**
* Tests removing tampers when mapping is removed.
*
* @group feeds_tamper
*/
class TamperRemoveTest extends FeedsTamperKernelTestBase {
/**
* Tests if the Tamper plugins are removed when the mapping is removed.
*/
public function testRemoveTamperWhenMappingIsRemoved() {
// Create a feed type.
$feed_type = $this->createFeedType();
// Initiate feed type tamper manager.
$feed_type_tamper_manager = \Drupal::service('feeds_tamper.feed_type_tamper_manager');
// Add a tamper plugin for this feed type.
$feed_type_tamper_manager->getTamperMeta($feed_type, TRUE)
->addTamper([
'plugin' => 'convert_case',
'operation' => 'ucfirst',
'source' => 'title',
'description' => 'Start text with uppercase character',
]);
$feed_type->save();
// Assert that the feed type has one tamper plugin when reloaded.
$feed_type = $this->reloadEntity($feed_type);
$this->assertCount(1, $feed_type_tamper_manager->getTamperMeta($feed_type, TRUE)->getTampers());
// Now remove the mapping for 'title' (which is at position 1).
$feed_type->removeMapping(1);
$feed_type->save();
// Assert that the tamper instance no longer exists on the feed.
$this->assertCount(0, $feed_type_tamper_manager->getTamperMeta($feed_type, TRUE)->getTampers());
}
}
<?php
namespace Drupal\Tests\feeds_tamper\Unit\Adapter;
use Drupal\feeds\Feeds\Item\ItemInterface;
use Drupal\feeds_tamper\Adapter\TamperableFeedItemAdapter;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\feeds_tamper\Adapter\TamperableFeedItemAdapter
* @group feeds_tamper
*/
class TamperableFeedItemAdapterTest extends UnitTestCase {
/**
* A feed item.
*
* @var \Drupal\feeds\Feeds\Item\ItemInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $feedItem;
/**
* Wrapper around feed item to use it as a tamperable item.
*
* @var \Drupal\feeds_tamper\Adapter\TamperableFeedItemAdapter
*/
protected $adapter;
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->feedItem = $this->createMock(ItemInterface::class);
$this->adapter = new TamperableFeedItemAdapter($this->feedItem);
}
/**
* @covers ::getSource
*/
public function testGetSource() {
$this->feedItem->expects($this->once())
->method('toArray');
$this->adapter->getSource();
}
/**
* @covers ::getSourceProperty
*/
public function testGetSourceProperty() {
$this->feedItem->expects($this->once())
->method('get')
->with('foo');
$this->adapter->getSourceProperty('foo');
}
/**
* @covers ::setSourceProperty
*/
public function testSetSourceProperty() {
$this->feedItem->expects($this->once())
->method('set')
->with('foo', 'bar');
$this->adapter->setSourceProperty('foo', 'bar');
}
}
<?php
namespace Drupal\Tests\feeds_tamper\Unit;
use Drupal\Component\Uuid\UuidInterface;
use Drupal\feeds\FeedTypeInterface;
use Drupal\feeds_tamper\FeedTypeTamperMeta;
use Drupal\tamper\TamperInterface;
use Drupal\tamper\TamperManagerInterface;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\feeds_tamper\FeedTypeTamperMeta
* @group feeds_tamper
*/
class FeedTypeTamperMetaTest extends UnitTestCase {
/**
* The Tamper manager for a feed type.
*
* @var \Drupal\feeds_tamper\FeedTypeTamperMeta
*/
protected $feedTypeTamperMeta;
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
// Mock the UUID generator and let it always return 'uuid3'.
$uuid_generator = $this->createMock(UuidInterface::class);
$uuid_generator->expects($this->any())
->method('generate')
->will($this->returnValue('uuid3'));
// Get the tamper manager.
$tamper_manager = $this->createMock(TamperManagerInterface::class);
$tamper_manager->expects($this->any())
->method('createInstance')
->will($this->returnValue($this->createMock(TamperInterface::class)));
// Mock the feed type and let it always return two tampers.
$feed_type = $this->createMock(FeedTypeInterface::class);
$feed_type->expects($this->any())
->method('getThirdPartySetting')
->with('feeds_tamper', 'tampers')
->will($this->returnValue([
'uuid1' => [
'uuid' => 'uuid1',
'plugin' => 'explode',
'separator' => '|',
'source' => 'alpha',
'description' => 'Explode with pipe character',
],
'uuid2' => [
'uuid' => 'uuid2',
'plugin' => 'convert_case',
'operation' => 'strtoupper',
'source' => 'beta',
'description' => 'Convert all characters to uppercase',
],
]));
$feed_type->expects($this->any())
->method('getMappingSources')
->will($this->returnValue([
'alpha' => [
'label' => 'Alpha',
],
'beta' => [
'label' => 'Beta',
],
]));
// Instantiate a feeds type tamper meta object.
$this->feedTypeTamperMeta = new FeedTypeTamperMeta($uuid_generator, $tamper_manager, $feed_type);
}
/**
* @covers ::getTamper
*/
public function testGetTamper() {
$tamper = $this->feedTypeTamperMeta->getTamper('uuid2');
$this->assertInstanceOf(TamperInterface::class, $tamper);
}
/**
* @covers ::getTampers
*/
public function testGetTampers() {
$tampers = iterator_to_array($this->feedTypeTamperMeta->getTampers());
// Assert that two tampers exist in total.
$this->assertCount(2, $tampers);
// Assert that tampers with uuid 'uuid1' and 'uuid2' exist.
$this->assertArrayHasKey('uuid1', $tampers);
$this->assertArrayHasKey('uuid2', $tampers);
}
/**
* @covers ::getTampersGroupedBySource
*/
public function testGetTampersGroupedBySource() {
$this->assertInternalType('array', $this->feedTypeTamperMeta->getTampersGroupedBySource());
}
/**
* @covers ::addTamper
*/
public function testAddTamper() {
$uuid = $this->feedTypeTamperMeta->addTamper([
'plugin' => 'convert_case',
'operation' => 'ucfirst',
'source' => 'gamma',
'description' => 'Start text with uppercase character',
]);
$this->assertEquals('uuid3', $uuid);
$tamper = $this->feedTypeTamperMeta->getTamper($uuid);
$this->assertInstanceOf(TamperInterface::class, $tamper);
// Assert that three tampers exist in total.
$this->assertCount(3, $this->feedTypeTamperMeta->getTampers());
}
/**
* @covers ::setTamperConfig
*/
public function testSetTamperConfig() {
$separator = ':';
$description = 'Explode with colon character (updated)';
$this->assertEquals($this->feedTypeTamperMeta, $this->feedTypeTamperMeta->setTamperConfig('uuid1', [
'separator' => $separator,
'description' => $description,
]));
}
/**
* @covers ::removeTamper
*/
public function testRemoveTamper() {
$this->feedTypeTamperMeta->removeTamper('uuid1');
$tampers = iterator_to_array($this->feedTypeTamperMeta->getTampers());
// Assert that uuid1 is removed, but uuid2 still exists.
$this->assertArrayNotHasKey('uuid1', $tampers);
$this->assertArrayHasKey('uuid2', $tampers);
// Assert that one tamper exists in total.
$this->assertCount(1, $tampers);
}
}
<?php
namespace Drupal\Tests\feeds_tamper\Unit;
use Drupal\Tests\feeds\Unit\FeedsUnitTestCase;
/**
* Base class for feeds tamper unit tests.
*/
abstract class FeedsTamperTestCase extends FeedsUnitTestCase {}
vendor/
\ No newline at end of file
This diff is collapsed.
# Tamper #
[![Build Status](https://travis-ci.org/ericgsmith/tamper.svg?branch=master)](https://travis-ci.org/ericgsmith/tamper)
[![Maintainability](https://api.codeclimate.com/v1/badges/494ae79569ca56e13d24/maintainability)](https://codeclimate.com/github/ericgsmith/tamper/maintainability)
Tamper module playground.
[Follow the conversation on Drupal.org](https://www.drupal.org/node/2921727)
{
"name": "drupal/tamper",
"type": "drupal-module",
"description": "Generic plugin to modify data.",
"keywords": ["Drupal", "tamper"],
"license": "GPL-2.0+",
"support": {
"issues": "http://drupal.org/project/issues/tamper",
"source": "http://cgit.drupalcode.org/tamper"
}
}
tamper.*:
type: mapping
label: 'Tamper plugin'
tamper.convert_case:
mapping:
operation:
type: string
label: 'How to convert case'
tamper.default_value:
mapping:
default_value:
type: string
label: 'Value'
only_if_empty:
type: boolean
label: 'Only if empty'
tamper.explode:
mapping:
separator:
type: string
label: 'String separator'
limit:
type: integer
label: 'Limit'
tamper.find_replace:
mapping:
find:
type: string
label: 'Text to find'
replace:
type: string
label: 'Text to replace'
case_sensitive:
type: boolean
label: 'Case sensitive'
word_boundaries:
type: boolean
label: 'Respect word boundaries'
whole:
type: boolean
label: 'Match whole word/phrase'
tamper.find_replace_regex:
mapping:
find:
type: string
label: 'REGEX to find'
replace:
type: string
label: 'Replacement pattern'
limit:
type: integer
label: 'Limit number of replacements'
tamper.hash:
mapping:
override:
type: boolean
label: 'Override set value'
tamper.implode:
mapping:
glue:
type: string
label: 'String glue'
tamper.number_format:
mapping:
decimals:
type: integer
label: 'Decimals'
dec_point:
type: string
label: 'Decimal point'
thousands_sep:
type: string
label: 'Thousands separator'
tamper.required:
mapping:
invert:
type: boolean
label: 'Invert filter'
tamper.rewrite:
mapping:
text:
type: string
label: 'Replacement pattern'
tamper.sprintf:
mapping:
format:
type: string
label: 'Format string'
tamper.str_pad:
mapping:
pad_length:
type: integer
label: 'Pad length'
pad_string:
type: string
label: 'Pad string'
pad_type:
type: string
label: 'Pad type'
tamper.strip_tags:
mapping:
allowed_tags:
type: string
label: 'Allowed tags'
tamper.trim:
mapping:
character:
type: string
label: 'Characters to trim'
side:
type: string
label: 'Side'
tamper.truncate_text:
mapping:
num_char:
type: integer
label: 'Number of characters'
ellipses:
type: boolean
label: 'Ellipses'
wordsafe:
type: boolean
label: 'Truncate on a word boundary'
<?php
namespace Drupal\tamper\Adapter;
use Drupal\Core\TypedData\ComplexDataInterface;
use Drupal\tamper\TamperableItemInterface;
/**
* Provides an adapter to use complex typed data as a tamperable item.
*/
class TamperableComplexDataAdapter implements TamperableItemInterface {
/**
* Typed complex data object.
*
* @var \Drupal\Core\TypedData\ComplexDataInterface
*/
protected $complexData;
/**
* Create a new instance of the adapter.
*
* @param \Drupal\Core\TypedData\ComplexDataInterface $complexData
* Typed complex data object.
*/
public function __construct(ComplexDataInterface $complexData) {
$this->complexData = $complexData;
}
/**
* {@inheritdoc}
*/
public function getSource($include_computed = FALSE) {
return $this->complexData->toArray();
}
/**
* {@inheritdoc}
*/
public function setSourceProperty($property, $data) {
$this->complexData->set($property, $data);
}
/**
* {@inheritdoc}
*/
public function getSourceProperty($property) {
return $this->complexData->get($property);
}
}
<?php
namespace Drupal\tamper\Annotation;
use Drupal\Component\Annotation\Plugin;
/**
* Defines a Tamper annotation object.
*
* Tamperers handle the tampering of data.
*
* @Annotation
*
* @see \Drupal\tamper\TamperPluginManager
* @see \Drupal\tamper\TamperInterface
*/
class Tamper extends Plugin {
/**
* The plugin ID.
*
* @var string
*/
public $id;
/**
* The human-readable name of the tamper plugin.
*
* @var \Drupal\Core\Annotation\Translation
*
* @ingroup plugin_translatable
*/
public $label;
/**
* A short description of the tamper plugin.
*
* @var \Drupal\Core\Annotation\Translation
*
* @ingroup plugin_translatable
*/
public $description;
/**
* The category under which the tamper plugin should be listed in the UI.
*
* @var \Drupal\Core\Annotation\Translation
*
* @ingroup plugin_translatable
*/
public $category = '';
/**
* Whether the plugin handles multiples itself.
*
* Typically plugins that have handle_multiples as TRUE will expect an array
* as input and iterate over it themselves, changing the whole array.
*
* @var bool
*/
public $handle_multiples = FALSE;
}
<?php
namespace Drupal\tamper\Exception;
/**
* Tamper skip data exception.
*
* Thrown when the calling tamper process should be skipped for the given data.
*/
class SkipTamperDataException extends TamperException {
}
<?php
namespace Drupal\tamper\Exception;
/**
* Tamper skip item exception.
*
* Thrown when the calling tamper process should be skipped for the given item.
*/
class SkipTamperItemException extends TamperException {
}
<?php
namespace Drupal\tamper\Exception;
/**
* Defines the tamper exception class.
*/
class TamperException extends \Exception {
}
<?php
namespace Drupal\tamper\Plugin\Tamper;
use Drupal\tamper\Exception\TamperException;
use Drupal\tamper\TamperableItemInterface;
use Drupal\tamper\TamperBase;
/**
* Plugin implementation for filtering data.
*
* @Tamper(
* id = "array_filter",
* label = @Translation("Filter items"),
* description = @Translation("Filter empty items from a list."),
* category = "List",
* handle_multiples = TRUE
* )
*/
class ArrayFilter extends TamperBase {
/**
* {@inheritdoc}
*/
public function tamper($data, TamperableItemInterface $item = NULL) {
if (!is_array($data)) {
throw new TamperException('Input should be an array.');
}
return array_values(array_filter($data));
}
/**
* {@inheritdoc}
*/
public function multiple() {
return TRUE;
}
}
<?php
namespace Drupal\tamper\Plugin\Tamper;
use Drupal\tamper\TamperableItemInterface;
use Drupal\tamper\TamperBase;
/**
* Plugin implementation for casting to integer.
*
* @Tamper(
* id = "cast_to_int",
* label = @Translation("Cast to integer"),
* description = @Translation("This plugin will convert any value to its integer form."),
* category = "Text"
* )
*/
class CastToInt extends TamperBase {
/**
* {@inheritdoc}
*/
public function tamper($data, TamperableItemInterface $item = NULL) {
return (int) $data;
}
}
<?php
namespace Drupal\tamper\Plugin\Tamper;
use Drupal\Core\Form\FormStateInterface;
use Drupal\tamper\Exception\TamperException;
use Drupal\tamper\TamperableItemInterface;
use Drupal\tamper\TamperBase;
/**
* Plugin implementation for converting case.
*
* @Tamper(
* id = "convert_case",
* label = @Translation("Convert case"),
* description = @Translation("Convert case."),
* category = "Text"
* )
*/
class ConvertCase extends TamperBase {
const SETTING_OPERATION = 'operation';
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
$config = parent::defaultConfiguration();
$config[self::SETTING_OPERATION] = 'ucfirst';
return $config;
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form[self::SETTING_OPERATION] = [
'#type' => 'select',
'#title' => $this->t('How to convert case'),
'#options' => $this->getOptions(),
'#default_value' => $this->getSetting(self::SETTING_OPERATION),
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
parent::submitConfigurationForm($form, $form_state);
$this->setConfiguration([self::SETTING_OPERATION => $form_state->getValue(self::SETTING_OPERATION)]);
}
/**
* Get the case conversion options.
*
* @return array
* List of options, keyed by method on Drupal's unicode class.
*/
protected function getOptions() {
return [
'strtoupper' => $this->t('Convert to uppercase'),
'strtolower' => $this->t('Convert to lowercase'),
'ucfirst' => $this->t('Capitalize the first character'),
'lcfirst' => $this->t('Convert the first character to lowercase'),
'ucwords' => $this->t('Capitalize the first character of each word'),
];
}
/**
* {@inheritdoc}
*/
public function tamper($data, TamperableItemInterface $item = NULL) {
if (!is_string($data)) {
throw new TamperException('Input should be a string.');
}
$operation = $this->getSetting(self::SETTING_OPERATION);
switch ($operation) {
case 'strtoupper':
return mb_strtoupper($data);
case 'strtolower':
return mb_strtolower($data);
default:
if (!is_callable(['\Drupal\Component\Utility\Unicode', $operation])) {
throw new TamperException('Invalid operation ' . $operation);
}
return call_user_func(['\Drupal\Component\Utility\Unicode', $operation], $data);
}
}
}
<?php
namespace Drupal\tamper\Plugin\Tamper;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\tamper\Exception\TamperException;
use Drupal\tamper\TamperableItemInterface;
use Drupal\tamper\TamperBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Plugin implementation for converting country to ISO code.
*
* @Tamper(
* id = "country_to_code",
* label = @Translation("Country to ISO code"),
* description = @Translation("Converts this field from a country name string to the two character ISO 3166-1 alpha-2 code."),
* category = "Text"
* )
*/
class CountryToCode extends TamperBase implements ContainerFactoryPluginInterface {
/**
* Holds the CountryManager object so we can grab the country list.
*
* @var \Drupal\Core\Locale\CountryManagerInterface
*/
protected $countryManager;
/**
* {@inheritdoc}
*/
public function tamper($data, TamperableItemInterface $item = NULL) {
if (!is_string($data)) {
throw new TamperException('Input should be a string.');
}
/**
* Holds the list of countries in an array.
* @static
*/
static $countries = [];
if (empty($countries)) {
$countries = $this->countryManager->getList();
foreach ($countries as $country_code => $country_name) {
$countries[$country_code] = mb_strtolower((string) $country_name);
}
$countries = array_flip($countries);
}
// If it's already a valid country code, leave it alone.
if (in_array($data, $countries)) {
return $data;
}
// Trim whitespace, set to lowercase.
$country = mb_strtolower(trim($data));
if (isset($countries[$country])) {
return $countries[$country];
}
else {
throw new TamperException('Could not find country name ' . $country . ' in list of countries.');
}
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
$instance = new static($configuration, $plugin_id, $plugin_definition, $configuration['source_definition']);
$instance->setCountryManager($container->get('country_manager'));
return $instance;
}
/**
* Setter function for the CountryManagerInterface.
*
* @param object $country_manager
* The country manager used to grab country list.
*/
public function setCountryManager($country_manager) {
$this->countryManager = $country_manager;
}
}
<?php
namespace Drupal\tamper\Plugin\Tamper;
use Drupal\Core\Form\FormStateInterface;
use Drupal\tamper\TamperableItemInterface;
use Drupal\tamper\TamperBase;
/**
* Plugin implementation for setting a value or default value.
*
* @Tamper(
* id = "default_value",
* label = @Translation("Set value or default value"),
* description = @Translation("Set value or default value."),
* category = "Text"
* )
*/
class DefaultValue extends TamperBase {
const SETTING_DEFAULT_VALUE = 'default_value';
const SETTING_ONLY_IF_EMPTY = 'only_if_empty';
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
$config = parent::defaultConfiguration();
$config[self::SETTING_DEFAULT_VALUE] = '';
$config[self::SETTING_ONLY_IF_EMPTY] = FALSE;
return $config;
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form[self::SETTING_DEFAULT_VALUE] = [
'#type' => 'textarea',
'#title' => $this->t('Value'),
'#default_value' => $this->getSetting(self::SETTING_DEFAULT_VALUE),
'#description' => $this->t('This field will be set to the value specified.'),
];
$form[self::SETTING_ONLY_IF_EMPTY] = [
'#type' => 'checkbox',
'#title' => $this->t('Only if empty'),
'#default_value' => $this->getSetting(self::SETTING_ONLY_IF_EMPTY),
'#description' => $this->t('This field will be set to the value specified only if the imported field is empty.'),
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
parent::submitConfigurationForm($form, $form_state);
$this->setConfiguration([self::SETTING_DEFAULT_VALUE => $form_state->getValue(self::SETTING_DEFAULT_VALUE)]);
$this->setConfiguration([self::SETTING_ONLY_IF_EMPTY => $form_state->getValue(self::SETTING_ONLY_IF_EMPTY)]);
}
/**
* {@inheritdoc}
*/
public function tamper($data, TamperableItemInterface $item = NULL) {
// Setting a default value.
$only_if_empty = $this->getSetting(self::SETTING_ONLY_IF_EMPTY);
if (!empty($only_if_empty) && !$data) {
$data = $this->getSetting(self::SETTING_DEFAULT_VALUE);
}
elseif (empty($only_if_empty)) {
$data = $this->getSetting(self::SETTING_DEFAULT_VALUE);
}
return $data;
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<?php
namespace Drupal\tamper\Plugin\Tamper;
use Drupal\tamper\Exception\TamperException;
use Drupal\tamper\TamperableItemInterface;
use Drupal\tamper\TamperBase;
/**
* Plugin implementation for html entity decode.
*
* @Tamper(
* id = "html_entity_decode",
* label = @Translation("HTML entity decode"),
* description = @Translation("Convert all HTML entities such as &amp;amp; and &amp;quot; to &amp; and &quot;."),
* category = "Text"
* )
*/
class HtmlEntityDecode extends TamperBase {
/**
* {@inheritdoc}
*/
public function tamper($data, TamperableItemInterface $item = NULL) {
if (!is_string($data)) {
throw new TamperException('Input should be a string.');
}
return html_entity_decode($data, ENT_QUOTES, 'UTF-8');
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
services:
plugin.manager.tamper:
class: Drupal\tamper\TamperManager
parent: default_plugin_manager
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment