Commit c5e5ad4a authored by alexpott's avatar alexpott

Issue #2204509 by klausi, Xano, fago: Allow context definitions to specify default values

parent 69ce3554
......@@ -54,11 +54,15 @@ public function getContextValue() {
// Support optional contexts.
if (!isset($this->contextValue)) {
$definition = $this->getContextDefinition();
if ($definition->isRequired()) {
$default_value = $definition->getDefaultValue();
if (!isset($default_value) && $definition->isRequired()) {
$type = $definition->getDataType();
throw new ContextException(sprintf("The %s context is required and not present.", $type));
}
return NULL;
// Keep the default value here so that subsequent calls don't have to look
// it up again.
$this->contextValue = $default_value;
}
return $this->contextValue;
}
......
......@@ -109,6 +109,24 @@ public function isRequired();
*/
public function setRequired($required = TRUE);
/**
* Provides the default value for this context definition.
*
* @return mixed
* The default value or NULL if no default value is set.
*/
public function getDefaultValue();
/**
* Sets the default data value.
*
* @param mixed $default_value
* The default value to be set or NULL to remove any default value.
*
* @return $this
*/
public function setDefaultValue($default_value);
/**
* Returns an array of validation constraints.
*
......
......@@ -50,6 +50,16 @@
* }
* @endcode
*
* Specifying a default value for the context definition:
* @code
* context = {
* "message" = @ContextDefinition("string",
* label = @Translation("Message"),
* default_value = @Translation("Checkout complete! Thank you for your purchase.")
* )
* }
* @endcode
*
* @see annotation
*
* @}
......@@ -85,6 +95,8 @@ class ContextDefinition extends Plugin {
* - required: (optional) Whether the context definition is required.
* - multiple: (optional) Whether the context definition is multivalue.
* - description: (optional) The UI description of this context definition.
* - default_value: (optional) The default value in case the underlying
* value is not set.
* - class: (optional) A custom ContextDefinitionInterface class.
*
* @throws \Exception
......@@ -95,6 +107,7 @@ public function __construct(array $values) {
$values += array(
'required' => TRUE,
'multiple' => FALSE,
'default_value' => NULL,
);
// Annotation classes extract data from passed annotation classes directly
// used in the classes they pass to.
......@@ -111,7 +124,7 @@ public function __construct(array $values) {
throw new \Exception('ContextDefinition class must implement \Drupal\Core\Plugin\Context\ContextDefinitionInterface.');
}
$class = isset($values['class']) ? $values['class'] : 'Drupal\Core\Plugin\Context\ContextDefinition';
$this->definition = new $class($values['value'], $values['label'], $values['required'], $values['multiple'], $values['description']);
$this->definition = new $class($values['value'], $values['label'], $values['required'], $values['multiple'], $values['description'], $values['default_value']);
}
/**
......
......@@ -40,11 +40,18 @@ class Context extends ComponentContext implements ContextInterface {
public function getContextValue() {
if (!isset($this->contextData)) {
$definition = $this->getContextDefinition();
if ($definition->isRequired()) {
$default_value = $definition->getDefaultValue();
if (isset($default_value)) {
// Keep the default value here so that subsequent calls don't have to
// look it up again.
$this->setContextValue($default_value);
}
elseif ($definition->isRequired()) {
$type = $definition->getDataType();
throw new ContextException(SafeMarkup::format("The @type context is required and not present.", array('@type' => $type)));
}
return NULL;
return $default_value;
}
return $this->getTypedDataManager()->getCanonicalRepresentation($this->contextData);
}
......@@ -72,6 +79,15 @@ public function getConstraints() {
* {@inheritdoc}
*/
public function getContextData() {
if (!isset($this->contextData)) {
$definition = $this->getContextDefinition();
$default_value = $definition->getDefaultValue();
if (isset($default_value)) {
// Store the default value so that subsequent calls don't have to look
// it up again.
$this->contextData = $this->getTypedDataManager()->create($definition->getDataDefinition(), $default_value);
}
}
return $this->contextData;
}
......
......@@ -56,6 +56,13 @@ class ContextDefinition implements ContextDefinitionInterface {
*/
protected $isRequired = TRUE;
/**
* The default value.
*
* @var mixed
*/
protected $defaultValue;
/**
* An array of constraints.
*
......@@ -90,15 +97,18 @@ public static function create($data_type = 'any') {
* Whether the context definition is required.
* @param bool $multiple
* Whether the context definition is multivalue.
* @param mixed string|null $description
* @param string|null $description
* The description of this context definition for the UI.
* @param mixed $default_value
* The default value of this definition.
*/
public function __construct($data_type = 'any', $label = NULL, $required = TRUE, $multiple = FALSE, $description = NULL) {
public function __construct($data_type = 'any', $label = NULL, $required = TRUE, $multiple = FALSE, $description = NULL, $default_value = NULL) {
$this->dataType = $data_type;
$this->label = $label;
$this->isRequired = $required;
$this->isMultiple = $multiple;
$this->description = $description;
$this->defaultValue = $default_value;
}
/**
......@@ -176,6 +186,21 @@ public function setRequired($required = TRUE) {
return $this;
}
/**
* {@inheritdoc}
*/
public function getDefaultValue() {
return $this->defaultValue;
}
/**
* {@inheritdoc}
*/
public function setDefaultValue($default_value) {
$this->defaultValue = $default_value;
return $this;
}
/**
* {@inheritdoc}
*/
......
......@@ -7,6 +7,7 @@
namespace Drupal\Tests\Component\Plugin\Context;
use Drupal\Component\Plugin\Context\Context;
use Drupal\Tests\UnitTestCase;
/**
......@@ -86,4 +87,20 @@ public function testGetContextValue($expected, $context_value, $is_required, $da
}
}
/**
* @covers ::getContextValue
*/
public function testDefaultValue() {
$mock_definition = $this->getMockBuilder('Drupal\Component\Plugin\Context\ContextDefinitionInterface')
->setMethods(array('getDefaultValue'))
->getMockForAbstractClass();
$mock_definition->expects($this->once())
->method('getDefaultValue')
->willReturn('test');
$context = new Context($mock_definition);
$this->assertEquals('test', $context->getContextValue());
}
}
......@@ -7,6 +7,7 @@
namespace Drupal\Tests\Core\Plugin\Context;
use Drupal\Core\Plugin\Context\ContextDefinition;
use Drupal\Tests\UnitTestCase;
/**
......@@ -205,4 +206,15 @@ public function testGetConstraint($expected, $constraint_array, $constraint) {
$this->assertEquals($expected, $mock_context_definition->getConstraint($constraint));
}
/**
* @covers ::getDefaultValue
* @covers ::setDefaultValue
*/
public function testDefaultValue() {
$context_definition = new ContextDefinition();
$this->assertNull($context_definition->getDefaultValue());
$context_definition->setDefaultValue('test');
$this->assertSame('test', $context_definition->getDefaultValue());
}
}
<?php
/**
* @file
* Contains \Drupal\Tests\Core\Plugin\Context\ContextTest.
*/
namespace Drupal\Tests\Core\Plugin\Context;
use Drupal\Core\Plugin\Context\Context;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\Core\Plugin\Context\Context
* @group Plugin
*/
class ContextTest extends UnitTestCase {
/**
* The mocked context definition object.
*
* @var \Drupal\Core\Plugin\Context\ContextDefinitionInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $contextDefinition;
/**
* The mocked Typed Data manager.
*
* @var \Drupal\Core\TypedData\TypedDataManager|\PHPUnit_Framework_MockObject_MockObject
*/
protected $typedDataManager;
/**
* The mocked Typed Data object.
*
* @var \Drupal\Core\TypedData\TypedDataInterface|\PHPUnit_Framework_MockObject_MockObject
*/
protected $typedData;
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
$mock_data_definition = $this->getMock('Drupal\Core\TypedData\DataDefinitionInterface');
$this->contextDefinition = $this->getMockBuilder('Drupal\Core\Plugin\Context\ContextDefinitionInterface')
->setMethods(array('getDefaultValue', 'getDataDefinition'))
->getMockForAbstractClass();
$this->contextDefinition->expects($this->once())
->method('getDefaultValue')
->willReturn('test');
$this->contextDefinition->expects($this->once())
->method('getDataDefinition')
->willReturn($mock_data_definition);
$this->typedData = $this->getMock('Drupal\Core\TypedData\TypedDataInterface');
$this->typedDataManager = $this->getMockBuilder('Drupal\Core\TypedData\TypedDataManager')
->disableOriginalConstructor()
->setMethods(array('create'))
->getMock();
$this->typedDataManager->expects($this->once())
->method('create')
->with($mock_data_definition, 'test')
->willReturn($this->typedData);
}
/**
* @covers ::getContextValue
*/
public function testDefaultValue() {
$context = new Context($this->contextDefinition);
$context->setTypedDataManager($this->typedDataManager);
$this->assertEquals('test', $context->getContextValue());
}
/**
* @covers ::getContextData
*/
public function testDefaultDataValue() {
$context = new Context($this->contextDefinition);
$context->setTypedDataManager($this->typedDataManager);
$this->assertEquals($this->typedData, $context->getContextData());
}
}
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