diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/views/argument_validator/Term.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/views/argument_validator/Term.php
index ea1fa3a..56a4701 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/views/argument_validator/Term.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/views/argument_validator/Term.php
@@ -9,19 +9,12 @@
use Drupal\views\ViewExecutable;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
-use Drupal\views\Annotation\ViewsArgumentValidator;
-use Drupal\Core\Annotation\Translation;
-use Drupal\views\Plugin\views\argument_validator\ArgumentValidatorPluginBase;
+use Drupal\views\Plugin\views\argument_validator\Entity;
/**
- * Validate whether an argument is an acceptable node.
- *
- * @ViewsArgumentValidator(
- * id = "taxonomy_term",
- * title = @Translation("Taxonomy term")
- * )
+ * Adds legacy vocabulary handling to standard Entity Argument validation..
*/
-class Term extends ArgumentValidatorPluginBase {
+class Term extends Entity {
/**
* Overrides \Drupal\views\Plugin\views\Plugin\views\PluginBase::init().
@@ -40,166 +33,4 @@ public function init(ViewExecutable $view, DisplayPluginBase $display, array &$o
}
}
}
-
- protected function defineOptions() {
- $options = parent::defineOptions();
- $options['vids'] = array('default' => array());
- $options['type'] = array('default' => 'tid');
- $options['transform'] = array('default' => FALSE, 'bool' => TRUE);
-
- return $options;
- }
-
- public function buildOptionsForm(&$form, &$form_state) {
- $vocabularies = entity_load_multiple('taxonomy_vocabulary');
- $options = array();
- foreach ($vocabularies as $voc) {
- $options[$voc->id()] = $voc->label();
- }
-
- $form['vids'] = array(
- '#type' => 'checkboxes',
- '#prefix' => '
',
+ // Sanitize ID for js.
+ $sanitized_id = static::encodeValidatorId($id);
+ $form['validate']['options'][$sanitized_id] = array(
+ '#prefix' => '
',
'#suffix' => '
',
'#type' => 'item',
// Even if the plugin has no options add the key to the form_state.
@@ -338,14 +342,14 @@ public function buildOptionsForm(&$form, &$form_state) {
'#states' => array(
'visible' => array(
':input[name="options[specify_validation]"]' => array('checked' => TRUE),
- ':input[name="options[validate][type]"]' => array('value' => $id),
+ ':input[name="options[validate][type]"]' => array('value' => $sanitized_id),
),
),
- '#id' => 'edit-options-validate-options-' . $id,
+ '#id' => 'edit-options-validate-options-' . $sanitized_id,
'#default_value' => array(),
);
- $plugin->buildOptionsForm($form['validate']['options'][$id], $form_state);
- $validate_types[$id] = $info['title'];
+ $plugin->buildOptionsForm($form['validate']['options'][$sanitized_id], $form_state);
+ $validate_types[$sanitized_id] = $info['title'];
}
}
}
@@ -387,10 +391,12 @@ public function validateOptionsForm(&$form, &$form_state) {
$plugin->validateOptionsForm($form['summary']['options'][$summary_id], $form_state, $form_state['values']['options']['summary']['options'][$summary_id]);
}
- $validate_id = $form_state['values']['options']['validate']['type'];
+ $sanitized_id = $form_state['values']['options']['validate']['type'];
+ // Correct ID for js sanitized version.
+ $validate_id = static::decodeValidatorId($sanitized_id);
$plugin = $this->getPlugin('argument_validator', $validate_id);
if ($plugin) {
- $plugin->validateOptionsForm($form['validate']['options'][$default_id], $form_state, $form_state['values']['options']['validate']['options'][$validate_id]);
+ $plugin->validateOptionsForm($form['validate']['options'][$default_id], $form_state, $form_state['values']['options']['validate']['options'][$sanitized_id]);
}
}
@@ -420,11 +426,13 @@ public function submitOptionsForm(&$form, &$form_state) {
$form_state['values']['options']['summary_options'] = $options;
}
- $validate_id = $form_state['values']['options']['validate']['type'];
+ $sanitized_id = $form_state['values']['options']['validate']['type'];
+ // Correct ID for js sanitized version.
+ $form_state['values']['options']['validate']['type'] = $validate_id = static::decodeValidatorId($sanitized_id);
$plugin = $this->getPlugin('argument_validator', $validate_id);
if ($plugin) {
- $options = &$form_state['values']['options']['validate']['options'][$validate_id];
- $plugin->submitOptionsForm($form['validate']['options'][$validate_id], $form_state, $options);
+ $options = &$form_state['values']['options']['validate']['options'][$sanitized_id];
+ $plugin->submitOptionsForm($form['validate']['options'][$sanitized_id], $form_state, $options);
// Copy the now submitted options to their final resting place so they get saved.
$form_state['values']['options']['validate_options'] = $options;
}
@@ -886,7 +894,7 @@ public function summarySort($order, $by = NULL) {
* @param $data
* The query results for the row.
*/
- public function summaryArgument($data) {
+ public function summaryArgument($data) {
return $data->{$this->base_alias};
}
@@ -1119,6 +1127,35 @@ public static function processContainerRadios($element) {
return $element;
}
+ /**
+ * Sanitize validator options including derivatives with : for js.
+ *
+ * Reason and alternative: http://drupal.org/node/2035345
+ *
+ * @param string $id
+ * The identifier to be sanitized.
+ *
+ * @return string
+ * The sanitized identifier.
+ *
+ * @see decodeValidatorId().
+ */
+ public static function encodeValidatorId($id) {
+ return str_replace(':', '---', $id);
+ }
+
+ /**
+ * Revert sanititized validator options.
+ *
+ * @param string $id
+ * The santitized identifier to be reverted.
+ *
+ * @return string
+ * The original identifier.
+ */
+ public static function decodeValidatorId($id) {
+ return str_replace('---', ':', $id);
+ }
}
/**
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/argument_validator/Entity.php b/core/modules/views/lib/Drupal/views/Plugin/views/argument_validator/Entity.php
new file mode 100644
index 0000000..b647691
--- /dev/null
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/argument_validator/Entity.php
@@ -0,0 +1,214 @@
+entityManager = $entity_manager;
+ $this->multipleCapable = TRUE;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) {
+ return new static(
+ $configuration,
+ $plugin_id,
+ $plugin_definition,
+ $container->get('plugin.manager.entity')
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function defineOptions() {
+ $options = parent::defineOptions();
+
+ $options['bundles'] = array('default' => array());
+ $options['access'] = array('default' => FALSE, 'bool' => TRUE);
+ $options['operation'] = array('default' => 'view');
+ $options['multiple'] = array('default' => FALSE, 'bool' => TRUE);
+
+ return $options;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function buildOptionsForm(&$form, &$form_state) {
+ parent::buildOptionsForm($form, $form_state);
+
+ $entity_type = $this->definition['entity_type'];
+ // Derivative IDs are all entity:entity_type. Sanitized for js.
+ // The ID is converted back on submission.
+ $sanitized_id = ArgumentPluginBase::encodeValidatorId($this->definition['id']);
+ $entity_definitions = $this->entityManager->getDefinitions();
+ $bundle_type = $entity_definitions[$entity_type]['entity_keys']['bundle'];
+
+ // If the entity has bundles, allow option to restrict to bundle(s).
+ if ($bundle_type) {
+ $bundles = entity_get_bundles($entity_type);
+ $bundle_options = array();
+ foreach ($bundles as $bundle_id => $bundle_info) {
+ $bundle_options[$bundle_id] = $bundle_info['label'];
+ }
+ $bundles_title = empty($entity_definitions[$entity_type]['bundle_label']) ? t('Bundles') : $entity_definitions[$entity_type]['bundle_label'];
+ if (in_array('Drupal\Core\Entity\ContentEntityInterface', class_implements($entity_definitions[$entity_type]['class']))) {
+ $fields = $this->entityManager->getFieldDefinitions($entity_type);
+ }
+ $bundle_name = (empty($fields) || empty($fields[$bundle_type]['label'])) ? t('bundles') : $fields[$bundle_type]['label'];
+ $form['bundles'] = array(
+ '#title' => $bundles_title,
+ '#default_value' => $this->options['bundles'],
+ '#type' => 'checkboxes',
+ '#options' => $bundle_options,
+ '#description' => t('Restrict to one or more %bundle_name. If none selected all are allowed.', array('%bundle_name' => $bundle_name)),
+ );
+ }
+
+ // Offer the option to filter by access to the entity in the argument.
+ $form['access'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Validate user has access to the %name', array('%name' => $entity_definitions[$entity_type]['label'])),
+ '#default_value' => $this->options['access'],
+ );
+ $form['operation'] = array(
+ '#type' => 'radios',
+ '#title' => t('Access operation to check'),
+ '#options' => array('view' => t('View'), 'update' => t('Edit'), 'delete' => t('Delete')),
+ '#default_value' => $this->options['operation'],
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="options[validate][options][' . $sanitized_id . '][access]"]' => array('checked' => TRUE),
+ ),
+ ),
+ );
+
+ // If class is multiple capable give the option to validate single/multiple.
+ if ($this->multipleCapable) {
+ $form['multiple'] = array(
+ '#type' => 'radios',
+ '#title' => t('Multiple arguments'),
+ '#options' => array(
+ 0 => t('Single ID', array('%type' => $entity_definitions[$entity_type]['label'])),
+ 1 => t('One or more IDs separated by , or +', array('%type' => $entity_definitions[$entity_type]['label'])),
+ ),
+ '#default_value' => (string) $this->options['multiple'],
+ );
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function submitOptionsForm(&$form, &$form_state, &$options = array()) {
+ // Filter out unused options so we don't store giant unnecessary arrays.
+ $options['bundles'] = array_filter($options['bundles']);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function validateArgument($argument) {
+ $entity_type = $this->definition['entity_type'];
+
+ if ($this->options['multiple']) {
+ // At this point only interested in individual IDs no matter what type,
+ // just splitting by the allowed delimiters.
+ $ids = array_filter(preg_split('/[,+ ]/', $argument));
+ }
+ elseif ($argument) {
+ $ids = array($argument);
+ }
+ // No specified argument should be invalid.
+ else {
+ $ids = array();
+ return FALSE;
+ }
+
+ $entities = $this->entityManager->getStorageController($entity_type)->loadMultiple($ids);
+ // Validate each id => entity. If any fails break out and return false.
+ foreach ($ids as $id) {
+ // There is no entity for this ID.
+ if (!isset($entities[$id])) {
+ return FALSE;
+ }
+ if (!$this->validateEntity($entities[$id])) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+ }
+
+ /**
+ * Validates an individual entity against class access settings.
+ *
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *
+ * @return bool
+ * True if validated.
+ */
+ protected function validateEntity(EntityInterface $entity) {
+ // If access restricted by entity operation.
+ if ($this->options['access'] && ! $entity->access($this->options['operation'])) {
+ return FALSE;
+ }
+ // If restricted by bundle.
+ $bundles = $this->options['bundles'];
+ if (count($bundles) && empty($bundles[$entity->bundle()])) {
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+}
diff --git a/core/modules/views/tests/Drupal/views/Tests/Plugin/argument_validator/EntityTest.php b/core/modules/views/tests/Drupal/views/Tests/Plugin/argument_validator/EntityTest.php
new file mode 100644
index 0000000..6d45f18
--- /dev/null
+++ b/core/modules/views/tests/Drupal/views/Tests/Plugin/argument_validator/EntityTest.php
@@ -0,0 +1,219 @@
+ 'Argument validator: Entity',
+ 'description' => 'Tests the generic entity argument validator.',
+ 'group' => 'Views Plugin',
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp() {
+ parent::setUp();
+
+ $this->entityManager = $this->getMockBuilder('Drupal\Core\Entity\EntityManager')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $mock_entity = $this->getMockBuilder('Drupal\Core\Entity\Entity')
+ ->disableOriginalConstructor()
+ ->setMethods(array('bundle', 'access'))
+ ->getMock();
+ $mock_entity->expects($this->any())
+ ->method('bundle')
+ ->will($this->returnValue('test_bundle'));
+ $mock_entity->expects($this->any())
+ ->method('access')
+ ->will($this->returnValueMap(array(
+ array('test_op', NULL, TRUE),
+ array('test_op_2', NULL, FALSE),
+ array('test_op_3', NULL, TRUE),
+ )));
+
+ $mock_entity_bundle_2 = $this->getMockBuilder('Drupal\Core\Entity\Entity')
+ ->disableOriginalConstructor()
+ ->setMethods(array('bundle', 'access'))
+ ->getMock();
+ $mock_entity_bundle_2->expects($this->any())
+ ->method('bundle')
+ ->will($this->returnValue('test_bundle_2'));
+ $mock_entity_bundle_2->expects($this->any())
+ ->method('access')
+ ->will($this->returnValueMap(array(
+ array('test_op_3', NULL, TRUE),
+ )));
+
+
+ $storage_controller = $this->getMock('Drupal\Core\Entity\EntityStorageControllerInterface');
+
+ // Setup values for IDs passed as strings or numbers.
+ $value_map = array(
+ array(array(), array()),
+ array(array(1), array(1 => $mock_entity)),
+ array(array('1'), array(1 => $mock_entity)),
+ array(array(1, 2), array(1 => $mock_entity, 2 => $mock_entity_bundle_2)),
+ array(array('1', '2'), array(1 => $mock_entity, 2 => $mock_entity_bundle_2)),
+ array(array(2), array(2 => $mock_entity_bundle_2)),
+ array(array('2'), array(2 => $mock_entity_bundle_2)),
+ );
+ $storage_controller->expects($this->any())
+ ->method('loadMultiple')
+ ->will($this->returnValueMap($value_map));
+
+ $this->entityManager->expects($this->any())
+ ->method('getStorageController')
+ ->will($this->returnValue($storage_controller));
+
+ $this->executable = $this->getMockBuilder('Drupal\views\ViewExecutable')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->display = $this->getMockBuilder('Drupal\views\Plugin\views\display\DisplayPluginBase')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $definition = array(
+ 'entity_type' => 'entity_test',
+ );
+
+ $this->argumentValidator = new Entity(array(), 'entity_test', $definition, $this->entityManager);
+ }
+
+ /**
+ * Tests the validate argument method with no access and bundles.
+ *
+ * @see \Drupal\views\Plugin\views\argument_validator\Entity::validateArgument()
+ */
+ public function testValidateArgumentNoAccess() {
+ $options = array();
+ $options['access'] = FALSE;
+ $options['bundles'] = array();
+ $this->argumentValidator->init($this->executable, $this->display, $options);
+
+ $this->assertFalse($this->argumentValidator->validateArgument(3));
+ $this->assertFalse($this->argumentValidator->validateArgument(''));
+
+ $this->assertTrue($this->argumentValidator->validateArgument(1));
+ $this->assertTrue($this->argumentValidator->validateArgument(2));
+ $this->assertFalse($this->argumentValidator->validateArgument('1,2'));
+ }
+
+ /**
+ * Tests the validate argument method with access and no bundles.
+ *
+ * @see \Drupal\views\Plugin\views\argument_validator\Entity::validateArgument()
+ */
+ public function testValidateArgumentAccess() {
+ $options = array();
+ $options['access'] = TRUE;
+ $options['bundles'] = array();
+ $options['operation'] = 'test_op';
+ $this->argumentValidator->init($this->executable, $this->display, $options);
+
+ $this->assertFalse($this->argumentValidator->validateArgument(3));
+ $this->assertFalse($this->argumentValidator->validateArgument(''));
+
+ $this->assertTrue($this->argumentValidator->validateArgument(1));
+
+ $options = array();
+ $options['access'] = TRUE;
+ $options['bundles'] = array();
+ $options['operation'] = 'test_op_2';
+ $this->argumentValidator->init($this->executable, $this->display, $options);
+
+ $this->assertFalse($this->argumentValidator->validateArgument(3));
+ $this->assertFalse($this->argumentValidator->validateArgument(''));
+
+ $this->assertFalse($this->argumentValidator->validateArgument(1));
+ $this->assertFalse($this->argumentValidator->validateArgument(2));
+ }
+
+ /**
+ * Tests the validate argument method with bundle checking.
+ */
+ public function testValidateArgumentBundle() {
+ $options = array();
+ $options['access'] = FALSE;
+ $options['bundles'] = array('test_bundle' => 1);
+ $this->argumentValidator->init($this->executable, $this->display, $options);
+
+ $this->assertTrue($this->argumentValidator->validateArgument(1));
+ $this->assertFalse($this->argumentValidator->validateArgument(2));
+ }
+
+ /**
+ * Tests the validate argument method with multiple argument splitting.
+ */
+ public function testValidateArgumentMultiple() {
+ $options = array();
+ $options['access'] = TRUE;
+ $options['bundles'] = array();
+ $options['operation'] = 'test_op';
+ $options['multiple'] = TRUE;
+ $this->argumentValidator->init($this->executable, $this->display, $options);
+
+ $this->assertTrue($this->argumentValidator->validateArgument('1'));
+ $this->assertFalse($this->argumentValidator->validateArgument('2'));
+
+ $this->assertFalse($this->argumentValidator->validateArgument('1,2'));
+ $this->assertFalse($this->argumentValidator->validateArgument('1+2'));
+
+ $options = array();
+ $options['access'] = TRUE;
+ $options['bundles'] = array();
+ $options['operation'] = 'test_op_3';
+ $options['multiple'] = TRUE;
+ $this->argumentValidator->init($this->executable, $this->display, $options);
+
+ $this->assertTrue($this->argumentValidator->validateArgument('1,2'));
+ $this->assertTrue($this->argumentValidator->validateArgument('1+2'));
+ }
+
+}