commit 72c55f35100734cdc4fc21e69a28131de0f113d1
Author: Bart Feenstra <bart@mynameisbart.com>
Date:   Wed Oct 9 14:53:21 2013 +0200

    foo

diff --git a/payment/lib/Drupal/payment/Element/PaymentStatusInput.php b/payment/lib/Drupal/payment/Element/PaymentStatusInput.php
new file mode 100644
index 0000000..6ad98b5
--- /dev/null
+++ b/payment/lib/Drupal/payment/Element/PaymentStatusInput.php
@@ -0,0 +1,111 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment\Element\PaymentStatusInput.
+ */
+
+namespace Drupal\payment\Element;
+
+use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Datetime\DrupalDateTime;
+use Drupal\payment\Plugin\payment\status\PaymentStatusInterface;
+
+/**
+ * Provides form callbacks for the payment_status_input form element.
+ */
+class PaymentStatusInput {
+
+  /**
+   * Implements form #process callback.
+   */
+  public static function process(array $element, array &$form_state, array $form) {
+    // Validate the element configuration.
+    if ($element['#default_value'] && !($element['#default_value'] instanceof PaymentStatusInterface)) {
+      throw new \InvalidArgumentException('The default value does not implement \Drupal\payment\Plugin\payment\status\PaymentStatusInterface.');
+    }
+
+    static::initialize($element, $form_state);
+    $status = static::getStatus($element, $form_state);
+
+    $element['#attributes'] = array(
+      'class' => array('payment-status-input'),
+    );
+    $element['plugin_id'] = array(
+      '#default_value' => $status ? $status->getPluginId() : NULL,
+      '#empty_value' => '',
+      '#options' => \Drupal::service('plugin.manager.payment.status')->options(),
+      '#required' => $element['#required'],
+      '#title' => t('Status'),
+      '#type' => 'select',
+    );
+    if (\Drupal::moduleHandler()->moduleExists('datetime')) {
+      $element['created'] = array(
+        '#default_value' => $status ? new DrupalDateTime($status->getCreated()) : new DrupalDateTime(),
+        '#title' => t('Date and time'),
+        '#type' => 'datetime',
+      );
+    }
+    else {
+      $element['created'] = array(
+        '#default_value' => REQUEST_TIME,
+        '#type' => 'value',
+      );
+    }
+
+    return $element;
+  }
+
+  /**
+   * Implements form #element_validate callback.
+   */
+  public static function validate(array $element, array &$form_state, array &$form) {
+    $values = NestedArray::getValue($form_state['values'], $element['#parents']);
+    $status = \Drupal::service('plugin.manager.payment.status')
+      ->createInstance($values['plugin_id']);
+    if (\Drupal::moduleHandler()->moduleExists('datetime')) {
+      $status->setCreated(strtotime($values['created']['date'] . ' ' . $values['created']['time']));
+    }
+    else {
+      $status->setCreated($values['created']);
+    }
+    static::setStatus($element, $form_state, $status);
+  }
+
+  /**
+   * Stores the status in the form's state.
+   *
+   * @param array $element
+   * @param array $form_state
+   * @param \Drupal\payment\Plugin\payment\status\PaymentStatusInterface $status
+   */
+  protected static function setStatus(array $element, array &$form_state, PaymentStatusInterface $status) {
+    $form_state['payment_status_input'][$element['#name']] = $status;
+  }
+
+  /**
+   * Retrieves the status from the form's state.
+   *
+   * @param array $element
+   * @param array $form_state
+   *
+   * @return \Drupal\payment\Plugin\payment\status\PaymentStatusInterface
+   */
+  public static function getStatus(array $element, array &$form_state) {
+    return $form_state['payment_status_input'][$element['#name']];
+  }
+
+  /**
+   * Check if the form's state has been initialized for an element.
+   *
+   * @param array $element
+   * @param array $form_state
+   *
+   * @return bool
+   */
+  protected static function initialize(array $element, array &$form_state) {
+    if (!(isset($form_state['payment_status_input']) && array_key_exists($element['#name'], $form_state['payment_status_input']))) {
+      $form_state['payment_status_input'][$element['#name']] = $element['#default_value'];
+    }
+  }
+}
diff --git a/payment/lib/Drupal/payment/Tests/Element/PaymentStatusInputWebTest.php b/payment/lib/Drupal/payment/Tests/Element/PaymentStatusInputWebTest.php
new file mode 100644
index 0000000..cbbbe78
--- /dev/null
+++ b/payment/lib/Drupal/payment/Tests/Element/PaymentStatusInputWebTest.php
@@ -0,0 +1,111 @@
+<?php
+
+/**
+ * @file
+ * Contains class \Drupal\payment\Tests\PaymentStatusInputWebTest.
+ */
+
+namespace Drupal\payment\Tests\Element;
+
+use Drupal\payment\Generate;
+use Drupal\payment\Plugin\payment\line_item\PaymentLineItemInterface;
+use Drupal\simpletest\WebTestBase ;
+
+/**
+ * Tests the payment_status_input element.
+ */
+class PaymentStatusInputWebTest extends WebTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = array('payment_test');
+
+  /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * The state service.
+   *
+   * @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface
+   */
+  protected $state;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'description' => '',
+      'name' => 'payment_status_input element',
+      'group' => 'Payment',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+    parent::setUp();
+    $this->moduleHandler = $this->container->get('module_handler');
+    $this->state = $this->container->get('state');
+  }
+
+  /**
+   * Tests the element.
+   */
+  protected function testElement() {
+    $statuses_data = array(
+      'no_default_optional' => array(
+        'plugin_id' => 'payment_success',
+        'created_date' => '1988-09-07',
+        'created_time' => '13:37:00',
+      ),
+      'default_required' => array(
+        'plugin_id' => 'payment_cancelled',
+        'created_date' => '1991-09-06',
+        'created_time' => '22:22:22',
+      ),
+    );
+
+    // Test not setting values for optional elements, and without
+    // datetime.module.
+    $this->drupalGet('payment_test/element/payment_status_input');
+    foreach (array_keys($statuses_data) as $name) {
+      $this->assertFieldByXPath('//select[@name="status_' . $name . '[plugin_id]"]');
+      $this->assertNoFieldByXPath('//input[@name="status_' . $name . '[created]"]');
+    }
+    $name = 'default_required';
+    $post_data = array(
+      'status_' . $name . '[plugin_id]' => $statuses_data[$name]['plugin_id'],
+    );
+    $this->drupalPostForm('payment_test/element/payment_status_input', $post_data, t('Submit'));
+    $status = $this->state->get('payment_test_element_payment_status_input_' . $name);
+    $this->assertEqual($status->getPluginId(), $statuses_data[$name]['plugin_id']);
+
+    // Test with datetime.module.
+    $this->moduleHandler->install(array('datetime'));
+    $this->drupalGet('payment_test/element/payment_status_input');
+    foreach (array_keys($statuses_data) as $name) {
+      $this->assertFieldByXPath('//select[@name="status_' . $name . '[plugin_id]"]');
+      $this->assertFieldByXPath('//input[@name="status_' . $name . '[created][date]"]');
+      $this->assertNoFieldByXPath('//input[@name="status_' . $name . '[created][date]" and @value=""]');
+    }
+    $post_data = array();
+    foreach ($statuses_data as $name => $status_data) {
+      $post_data['status_' . $name . '[plugin_id]'] = $status_data['plugin_id'];
+//      $post_data['status_' . $name . '[created][date]'] = $status_data['created_date'];
+//      $post_data['status_' . $name . '[created][time]'] = $status_data['created_time'];
+    }
+    $this->drupalPostForm('payment_test/element/payment_status_input', $post_data, t('Submit'));
+    foreach ($statuses_data as $name => $status_data) {
+      $status = $this->state->get('payment_test_element_payment_status_input_' . $name);
+      $this->assertEqual($status->getPluginId(), $status_data['plugin_id']);
+      $this->assertEqual($status->getCreated(), strtotime($status_data['created_date'] . ' ' . $status_data['created_time']));
+    }
+  }
+}
diff --git a/payment/payment.module b/payment/payment.module
index 58299c6..c5a0c47 100644
--- a/payment/payment.module
+++ b/payment/payment.module
@@ -279,6 +279,19 @@ function payment_element_info() {
     '#pre_render' => array(array('\Drupal\payment\Element\PaymentStatusesDisplay', 'preRender')),
   );
 
+  // Payment status plugin instance configuration.
+  $elements['payment_status_input'] = array(
+    // An optional
+    // \Drupal\payment\plugin\payment\PaymentStatus\PaymentStatusInterface
+    // object.
+    '#default_value' => NULL,
+    '#element_validate' => array(array('\Drupal\payment\Element\PaymentStatusInput', 'validate')),
+    '#input' => TRUE,
+    '#process' => array('form_process_container', array('\Drupal\payment\Element\PaymentStatusInput', 'process')),
+    '#theme_wrappers' => array('container'),
+    '#tree' => TRUE,
+  );
+
   return $elements;
 }
 
diff --git a/payment_test/lib/Drupal/payment_test/PaymentStatusInputElement.php b/payment_test/lib/Drupal/payment_test/PaymentStatusInputElement.php
new file mode 100644
index 0000000..adf87a6
--- /dev/null
+++ b/payment_test/lib/Drupal/payment_test/PaymentStatusInputElement.php
@@ -0,0 +1,83 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\payment_test\PaymentStatusInputElement.
+ */
+
+namespace Drupal\payment_test;
+
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Entity\EntityManager;
+use Drupal\Core\Form\FormInterface;
+use Drupal\payment\Element\PaymentStatusInput;
+use Drupal\payment\Generate;
+use Drupal\payment\Plugin\payment\type\Manager;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+class PaymentStatusInputElement implements ContainerInjectionInterface, FormInterface {
+
+  /**
+   * The entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManager
+   */
+  protected $entityManager;
+
+  /**
+   * Constructor.
+   */
+  function __construct(EntityManager $entity_manager, Manager $type_manager) {
+    $this->entityManager = $entity_manager;
+    $this->typeManager = $type_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static($container->get('plugin.manager.entity'), $container->get('plugin.manager.payment.type'));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormID() {
+    return 'payment_test_element_payment_status_input';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, array &$form_state) {
+    $form['status_no_default_optional'] = array(
+      '#type' => 'payment_status_input',
+    );
+    $form['status_default_required'] = array(
+      '#default_value' => \Drupal::service('plugin.manager.payment.status')
+        ->createInstance('payment_pending')
+        ->setCreated(123321),
+      '#type' => 'payment_status_input',
+    );
+    $form['submit'] = array(
+      '#type' => 'submit',
+      '#value' => t('Submit'),
+    );
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, array &$form_state) {
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, array &$form_state) {
+    \Drupal::state()->set('payment_test_element_payment_status_input_no_default_optional', PaymentStatusInput::getStatus($form['status_no_default_optional'], $form_state));
+    \Drupal::state()->set('payment_test_element_payment_status_input_default_required', PaymentStatusInput::getStatus($form['status_default_required'], $form_state));
+  }
+}
diff --git a/payment_test/payment_test.routing.yml b/payment_test/payment_test.routing.yml
index 0631b37..828efe7 100644
--- a/payment_test/payment_test.routing.yml
+++ b/payment_test/payment_test.routing.yml
@@ -18,3 +18,10 @@ payment_test.payment_line_item_payment_basic_form_elements:
     _form: '\Drupal\payment_test\PaymentLineItemPaymentBasicFormElements'
   requirements:
     _access: 'TRUE'
+
+payment_test.payment_status_input_element:
+  path: '/payment_test/element/payment_status_input'
+  defaults:
+    _form: '\Drupal\payment_test\PaymentStatusInputElement'
+  requirements:
+    _access: 'TRUE'
