From 9ebb46eeca913f359e87052a90a2f366e5525e32 Mon Sep 17 00:00:00 2001
From: spiff <spiff@167954.no-reply.drupal.org>
Date: Fri, 18 Apr 2014 15:03:11 +0200
Subject: [PATCH] Issue #1796606 by kerasai, marvil07, Enxebre, Mile23, spiffl:
 D8 Port of field_example

---
 .../config/schema/field_example.schema.yml         |  10 ++
 field_example/field_example.info.yml               |   5 +
 field_example/field_example.js                     |  24 ++++
 field_example/field_example.module                 | 125 +++++++++++++++++++++
 field_example/field_example.routing.yml            |   7 ++
 .../Controller/FieldExampleController.php          |  24 ++++
 .../field_example/FieldExampleWebTestBase.php      | 123 ++++++++++++++++++++
 .../FieldFormatter/ColorBackgroudFormatter.php     |  51 +++++++++
 .../Field/FieldFormatter/SimpleTextFormatter.php   |  50 +++++++++
 .../Plugin/Field/FieldType/RgbItem.php             |  60 ++++++++++
 .../Plugin/Field/FieldWidget/ColorPickerWidget.php |  46 ++++++++
 .../Plugin/Field/FieldWidget/Text3Widget.php       |  65 +++++++++++
 .../Plugin/Field/FieldWidget/TextWidget.php        |  44 ++++++++
 .../Drupal/field_example/Tests/Text3WidgetTest.php | 110 ++++++++++++++++++
 .../Drupal/field_example/Tests/TextWidgetTest.php  | 103 +++++++++++++++++
 15 files changed, 847 insertions(+)
 create mode 100644 field_example/config/schema/field_example.schema.yml
 create mode 100644 field_example/field_example.info.yml
 create mode 100644 field_example/field_example.js
 create mode 100644 field_example/field_example.module
 create mode 100644 field_example/field_example.routing.yml
 create mode 100644 field_example/lib/Drupal/field_example/Controller/FieldExampleController.php
 create mode 100644 field_example/lib/Drupal/field_example/FieldExampleWebTestBase.php
 create mode 100644 field_example/lib/Drupal/field_example/Plugin/Field/FieldFormatter/ColorBackgroudFormatter.php
 create mode 100644 field_example/lib/Drupal/field_example/Plugin/Field/FieldFormatter/SimpleTextFormatter.php
 create mode 100644 field_example/lib/Drupal/field_example/Plugin/Field/FieldType/RgbItem.php
 create mode 100644 field_example/lib/Drupal/field_example/Plugin/Field/FieldWidget/ColorPickerWidget.php
 create mode 100644 field_example/lib/Drupal/field_example/Plugin/Field/FieldWidget/Text3Widget.php
 create mode 100644 field_example/lib/Drupal/field_example/Plugin/Field/FieldWidget/TextWidget.php
 create mode 100644 field_example/lib/Drupal/field_example/Tests/Text3WidgetTest.php
 create mode 100644 field_example/lib/Drupal/field_example/Tests/TextWidgetTest.php

diff --git a/field_example/config/schema/field_example.schema.yml b/field_example/config/schema/field_example.schema.yml
new file mode 100644
index 0000000..6b63036
--- /dev/null
+++ b/field_example/config/schema/field_example.schema.yml
@@ -0,0 +1,10 @@
+field.field_example_rgb.value:
+  type: sequence
+  label: 'Default value'
+  sequence:
+    - type: mapping
+      label: 'Default'
+      mapping:
+        value:
+          type: string
+          label: 'Value'
diff --git a/field_example/field_example.info.yml b/field_example/field_example.info.yml
new file mode 100644
index 0000000..2ba15b6
--- /dev/null
+++ b/field_example/field_example.info.yml
@@ -0,0 +1,5 @@
+name: Field Example
+type: module
+description: An implementation of a field to show the Field API
+package: Example modules
+core: 8.x
diff --git a/field_example/field_example.js b/field_example/field_example.js
new file mode 100644
index 0000000..58e5339
--- /dev/null
+++ b/field_example/field_example.js
@@ -0,0 +1,24 @@
+/**
+ * @file
+ * Javascript for Field Example.
+ */
+
+/**
+ * Provides a farbtastic colorpicker for the fancier widget.
+ */
+(function($) {
+    Drupal.behaviors.field_example_colorpicker = {
+        attach: function() {
+            $(".edit-field-example-colorpicker").on("focus", function(event) {
+                var edit_field = this;
+                var picker = $(this).closest('div').parent().find(".field-example-colorpicker");
+                // Hide all color pickers except this one.
+                $(".field-example-colorpicker").hide();
+                $(picker).show();
+                $.farbtastic(picker, function(color) {
+                    edit_field.value = color;
+                }).setColor(edit_field.value);
+            });
+        }
+    };
+})(jQuery);
diff --git a/field_example/field_example.module b/field_example/field_example.module
new file mode 100644
index 0000000..31de5a8
--- /dev/null
+++ b/field_example/field_example.module
@@ -0,0 +1,125 @@
+<?php
+
+/**
+ * @file
+ * An example field using the Field Types API.
+ */
+/**
+ * @defgroup field_example Example: Field Types API
+ * @ingroup examples
+ * @{
+ * Examples using Field Types API.
+ *
+ * Providing a field requires:
+ * - Defining a field:
+ *   - config/schema/[module_name].schema.yml
+ *   - A field_type plugin
+ *   - One or more widget plugins
+ *   - One or more formatter plugins
+ *
+ * - Defining a formatter for the field (the portion that outputs the field for
+ *   display):
+ *   - hook_field_formatter_info()
+ *   - hook_field_formatter_view()
+ *
+ * - Defining a widget for the edit form:
+ *   - hook_field_widget_info()
+ *   - hook_field_widget_form()
+ *
+ * Our module defines the field in field_example_field_info(),
+ * field_example_field_validate() and field_example_field_is_empty().
+ * field_example_field_schema() is implemented in field_example.install.
+ *
+ * Our module sets up a formatter in field_example_field_formatter_info() and
+ * field_example_field_formatter_view(). These are the API hooks that present
+ * formatted and themed output to the user.
+
+ * And finally, our module defines the widet in
+ * field_example_field_widget_info() and field_example_field_widget_form().
+ * The widget is the form element used to receive input from the user
+ * when the field is being populated.
+ *
+ * @see field_types
+ * @see field
+ */
+
+/**
+ * Implements hook_menu().
+ *
+ * We will define a single menu route, so that we can give the user some
+ * helpful introductory information.
+ *
+ * @return array
+ *   Complex array describing our menu item.
+ */
+function field_example_menu() {
+  $items['examples/field_example'] = array(
+    'title' => 'Field Example',
+    'route_name' => 'field_example_page',
+    'expanded' => TRUE,
+  );
+  return $items;
+}
+
+/**
+ * Validate and clean up the input on single text field entry.
+ */
+function field_example_text_validate($element, &$form_state) {
+  $value = $element['#value'];
+  // Nothing to do if input is empty.
+  if (empty($value)) {
+    \Drupal::formBuilder()->setValue($element, '', $form_state);
+    return;
+  }
+
+  // Strip preceding pound sign for analysis.
+  if (substr($value, 0, 1) == '#') {
+    $value = substr($value, 1);
+  }
+
+  // Convert to lower-case for consistency.
+  $value = strtolower($value);
+
+  // Keeping it simple, only 6-digit hex values accepted.
+  if (strlen($value) != 6) {
+    \Drupal::formBuilder()->setError($element, $form_state, t("Please enter a 6-digit hexadecimal value."));
+    return;
+  }
+
+  // Validate that this is a legit hex value.
+  if (!ctype_xdigit($value)) {
+    \Drupal::formBuilder()->setError($element, $form_state, t("Please enter a valid hexadecimal value."));
+  }
+
+  // Add a preceding pound sign.
+  $value = "#$value";
+  \Drupal::formBuilder()->setValue($element, $value, $form_state);
+}
+
+/**
+ * Validate the individual fields and then convert them into a single HTML RGB
+ * value as text.
+ */
+function field_example_3text_validate($element, &$form_state) {
+  // Validate each of the textfield entries.
+  foreach (array('r', 'g', 'b') as $colorfield) {
+    $values[$colorfield] = $element[$colorfield]['#value'];
+    // If they left any empty, we'll set the value empty and quit.
+    if (strlen($values[$colorfield]) == 0) {
+      \Drupal::formBuilder()->setValue($element, '', $form_state);
+      return;
+    }
+    // If they gave us anything that's not hex, reject it.
+    if ((strlen($values[$colorfield]) != 2) || !ctype_xdigit($values[$colorfield])) {
+      \Drupal::formBuilder()->setError($element[$colorfield], $form_state, t("Saturation value must be a 2-digit hexadecimal value between 00 and ff."));
+    }
+  }
+
+  // Set the value of the entire form element.
+  $value = strtolower(sprintf('#%02s%02s%02s', $values['r'], $values['g'], $values['b']));
+  \Drupal::formBuilder()->setValue($element, $value, $form_state);
+}
+
+/**
+ * @} End of "defgroup field_example".
+ */
diff --git a/field_example/field_example.routing.yml b/field_example/field_example.routing.yml
new file mode 100644
index 0000000..35225a6
--- /dev/null
+++ b/field_example/field_example.routing.yml
@@ -0,0 +1,7 @@
+# Provides a simple user interface that tells the developer where to go.
+field_example_page:
+  path: 'examples/field_example'
+  defaults:
+    _content: '\Drupal\field_example\Controller\FieldExampleController::page'
+  requirements:
+    _access: 'TRUE'
diff --git a/field_example/lib/Drupal/field_example/Controller/FieldExampleController.php b/field_example/lib/Drupal/field_example/Controller/FieldExampleController.php
new file mode 100644
index 0000000..df3877f
--- /dev/null
+++ b/field_example/lib/Drupal/field_example/Controller/FieldExampleController.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\field_example\Controller\FieldExampleController.
+ */
+
+namespace Drupal\field_example\Controller;
+
+use Drupal\Core\Controller\ControllerBase;
+
+/**
+ * Returns responses for dblog routes.
+ */
+class FieldExampleController extends ControllerBase {
+
+  /**
+   * A simple page to explain to the developer what to do.
+   */
+  public function page() {
+    return t("The Field Example provides a field composed of an HTML RGB value, like #ff00ff. To use it, add the field to a content type.");
+  }
+
+}
diff --git a/field_example/lib/Drupal/field_example/FieldExampleWebTestBase.php b/field_example/lib/Drupal/field_example/FieldExampleWebTestBase.php
new file mode 100644
index 0000000..315ed26
--- /dev/null
+++ b/field_example/lib/Drupal/field_example/FieldExampleWebTestBase.php
@@ -0,0 +1,123 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\field_example\Tests\FieldWebTestBase.
+ */
+
+namespace Drupal\field_example;
+
+use Drupal\Core\Session\AccountInterface;
+use Drupal\simpletest\WebTestBase;
+
+class FieldExampleWebTestBase extends WebTestBase {
+  /**
+   * @var string
+   */
+  protected $contentTypeName;
+
+  /**
+   * @var AccountInterface
+   */
+  protected $administratorAccount;
+
+  /**
+   * @var AccountInterface
+   */
+  protected $authorAccount;
+  /**
+   * @var string
+   */
+  protected $fieldName;
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('node', 'field_ui', 'field_example');
+
+  /**
+   * {@inheritdoc}
+   *
+   * Once installed, a content type with the desired field is created
+   */
+  protected function setUp() {
+    // Install Drupal.
+    parent::setUp();
+
+    // Create and login a user that creates the content type.
+    $permissions = array(
+      'administer content types',
+      'administer node fields',
+      'administer node form display',
+    );
+    $this->administratorAccount = $this->drupalCreateUser($permissions);
+    parent::drupalLogin($this->administratorAccount);
+
+    // Prepare a new content type where the field will be added.
+    $this->contentTypeName = strtolower($this->randomName(10));
+    $this->drupalGet('admin/structure/types/add');
+    $edit = array(
+      'name' => $this->contentTypeName,
+      'type' => $this->contentTypeName,
+    );
+    $this->drupalPostForm(NULL, $edit, t('Save and manage fields'));
+    $this->assertText(t('The content type @name has been added.', array('@name' => $this->contentTypeName)));
+
+    // Reset the permission cache.
+    $create_permission = 'create ' . $this->contentTypeName . ' content';
+    $this->checkPermissions(array($create_permission), TRUE);
+
+    // Now that we have a new content type, create a user that has privileges
+    // on the content type.
+    $this->authorAccount = $this->drupalCreateUser(array($create_permission));
+  }
+
+  /**
+   * Create a field on the content type created during setUp().
+   *
+   * @param string $type
+   *   The storage field type to create
+   * @param string $widget_type
+   *   The widget to use when editing this field
+   * @param int|string $cardinality
+   *   Cardinality of the field.
+   *
+   * @return string
+   *   Name of the field, like field_something
+   */
+  protected function createField($type = 'field_example_rgb', $widget_type = 'field_example_text', $cardinality = 'number') {
+    $this->drupalGet('admin/structure/types/manage/' . $this->contentTypeName . '/fields');
+
+    $field_name = strtolower($this->randomName(10));
+    // Add a singleton field_example_text field.
+    $edit = array(
+      'fields[_add_new_field][label]' => $field_name,
+      'fields[_add_new_field][field_name]' => $field_name,
+      'fields[_add_new_field][type]' => $type,
+    );
+    $this->drupalPostForm(NULL, $edit, t('Save'));
+
+    $edit = array('field[cardinality]' => (string) $cardinality);
+
+    // Using all the default settings, so press the button.
+    $this->drupalPostForm(NULL, $edit, t('Save field settings'));
+    debug(t('Saved settings for field %field_name with widget %widget_type and cardinality %cardinality',
+        array(
+          '%field_name' => $field_name,
+          '%widget_type' => $widget_type,
+          '%cardinality' => $cardinality,
+        )
+      ));
+    $this->assertText(t('Updated field @name field settings.', array('@name' => $field_name)));
+
+    // Set the widget type for the newly created field.
+    $this->drupalGet('admin/structure/types/manage/' . $this->contentTypeName . '/form-display');
+    $edit = array(
+      'fields[field_' . $field_name . '][type]' => $widget_type,
+    );
+    $this->drupalPostForm(NULL, $edit, t('Save'));
+
+    return $field_name;
+  }
+}
diff --git a/field_example/lib/Drupal/field_example/Plugin/Field/FieldFormatter/ColorBackgroudFormatter.php b/field_example/lib/Drupal/field_example/Plugin/Field/FieldFormatter/ColorBackgroudFormatter.php
new file mode 100644
index 0000000..7e1e2ec
--- /dev/null
+++ b/field_example/lib/Drupal/field_example/Plugin/Field/FieldFormatter/ColorBackgroudFormatter.php
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\field_example\Plugin\field\formatter\ColorBackgroudFormatter.
+ */
+
+namespace Drupal\field_example\Plugin\field\formatter;
+
+use Drupal\Core\Field\FormatterBase;
+use Drupal\Core\Field\FieldItemListInterface;
+
+/**
+ * Plugin implementation of the 'field_example_color_background' formatter.
+ *
+ * @FieldFormatter(
+ *   id = "field_example_color_background",
+ *   label = @Translation("Change the background of the output text"),
+ *   field_types = {
+ *     "field_example_rgb"
+ *   }
+ * )
+ */
+class ColorBackgroudFormatter extends FormatterBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function viewElements(FieldItemListInterface $items) {
+    $elements = array();
+
+    foreach ($items as $delta => $item) {
+      $elements[$delta] = array(
+        '#type' => 'html_tag',
+        '#tag' => 'p',
+        '#value' => t('The content area color has been changed to @code', array('@code' => $item->value)),
+        '#attached' => array(
+          'css' => array(
+            array(
+              'data' => 'main { background-color:' . $item->value . ';}',
+              'type' => 'inline',
+            ),
+          ),
+        ),
+      );
+    }
+
+    return $elements;
+  }
+
+}
diff --git a/field_example/lib/Drupal/field_example/Plugin/Field/FieldFormatter/SimpleTextFormatter.php b/field_example/lib/Drupal/field_example/Plugin/Field/FieldFormatter/SimpleTextFormatter.php
new file mode 100644
index 0000000..2d65454
--- /dev/null
+++ b/field_example/lib/Drupal/field_example/Plugin/Field/FieldFormatter/SimpleTextFormatter.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\field_example\Plugin\field\formatter\SimpleTextFormatter.
+ */
+
+namespace Drupal\field_example\Plugin\Field\FieldFormatter;
+
+use Drupal\Core\Field\FormatterBase;
+use Drupal\Core\Field\FieldItemListInterface;
+
+/**
+ * Plugin implementation of the 'field_example_simple_text' formatter.
+ *
+ * @FieldFormatter(
+ *   id = "field_example_simple_text",
+ *   module = "field_example",
+ *   label = @Translation("Simple text-based formatter"),
+ *   field_types = {
+ *     "field_example_rgb"
+ *   }
+ * )
+ */
+class SimpleTextFormatter extends FormatterBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function viewElements(FieldItemListInterface $items) {
+    $elements = array();
+
+    foreach ($items as $delta => $item) {
+      $elements[$delta] = array(
+        // We create a render array to produce the desired markup,
+        // "<p style="color: #hexcolor">The color code ... #hexcolor</p>".
+        // See theme_html_tag().
+        '#type' => 'html_tag',
+        '#tag' => 'p',
+        '#attributes' => array(
+          'style' => 'color: ' . $item->value,
+        ),
+        '#value' => t('The color code in this field is @code', array('@code' => $item->value)),
+      );
+    }
+
+    return $elements;
+  }
+
+}
diff --git a/field_example/lib/Drupal/field_example/Plugin/Field/FieldType/RgbItem.php b/field_example/lib/Drupal/field_example/Plugin/Field/FieldType/RgbItem.php
new file mode 100644
index 0000000..f7096b3
--- /dev/null
+++ b/field_example/lib/Drupal/field_example/Plugin/Field/FieldType/RgbItem.php
@@ -0,0 +1,60 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\field_example\Plugin\field\field_type\RgbItem.
+ */
+
+namespace Drupal\field_example\Plugin\Field\FieldType;
+
+use Drupal\Core\Field\FieldItemBase;
+use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\TypedData\DataDefinition;
+
+/**
+ * Plugin implementation of the 'field_example_rgb' field type.
+ *
+ * @FieldType(
+ *   id = "field_example_rgb",
+ *   label = @Translation("Example Color RGB"),
+ *   module = "field_example",
+ *   description = @Translation("Demonstrates a field composed of an RGB color."),
+ *   default_widget = "field_example_text",
+ *   default_formatter = "field_example_simple_text"
+ * )
+ */
+class RgbItem extends FieldItemBase {
+  /**
+   * {@inheritdoc}
+   */
+  public static function schema(FieldStorageDefinitionInterface $field_definition) {
+    return array(
+      'columns' => array(
+        'value' => array(
+          'type' => 'text',
+          'size' => 'tiny',
+          'not null' => FALSE,
+        ),
+      ),
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isEmpty() {
+    $value = $this->get('value')->getValue();
+    return $value === NULL || $value === '';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
+    $properties['value'] = DataDefinition::create('string')
+      ->setLabel(t('Hex value'));
+
+    return $properties;
+  }
+}
diff --git a/field_example/lib/Drupal/field_example/Plugin/Field/FieldWidget/ColorPickerWidget.php b/field_example/lib/Drupal/field_example/Plugin/Field/FieldWidget/ColorPickerWidget.php
new file mode 100644
index 0000000..bf86a62
--- /dev/null
+++ b/field_example/lib/Drupal/field_example/Plugin/Field/FieldWidget/ColorPickerWidget.php
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\field_example\Plugin\Field\FieldWidget\ColorPickerWidget.
+ */
+
+namespace Drupal\field_example\Plugin\Field\FieldWidget;
+
+use Drupal\Core\Field\FieldItemListInterface;
+/**
+ * Plugin implementation of the 'field_example_colorpicker' widget.
+ *
+ * @FieldWidget(
+ *   id = "field_example_colorpicker",
+ *   module = "field_example",
+ *   label = @Translation("Color Picker"),
+ *   field_types = {
+ *     "field_example_rgb"
+ *   }
+ * )
+ */
+class ColorPickerWidget extends TextWidget {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, array &$form_state) {
+    $element = parent::formElement($items, $delta, $element, $form, $form_state);
+    $element['value'] += array(
+      '#suffix' => '<div class="field-example-colorpicker"></div>',
+      '#attributes' => array('class' => array('edit-field-example-colorpicker')),
+      '#attached' => array(
+        // Add Farbtastic color picker.
+        'library' => array(
+          'core/jquery.farbtastic',
+        ),
+        // Add javascript to trigger the colorpicker.
+        'js' => array(drupal_get_path('module', 'field_example') . '/field_example.js'),
+      ),
+    );
+
+    return $element;
+  }
+
+}
diff --git a/field_example/lib/Drupal/field_example/Plugin/Field/FieldWidget/Text3Widget.php b/field_example/lib/Drupal/field_example/Plugin/Field/FieldWidget/Text3Widget.php
new file mode 100644
index 0000000..37e9ca7
--- /dev/null
+++ b/field_example/lib/Drupal/field_example/Plugin/Field/FieldWidget/Text3Widget.php
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\field_example\Plugin\field\widget\Text3Widget.
+ */
+
+namespace Drupal\field_example\Plugin\Field\FieldWidget;
+
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Field\WidgetBase;
+
+/**
+ * Plugin implementation of the 'field_example_3text' widget.
+ *
+ * @FieldWidget(
+ *   id = "field_example_3text",
+ *   module = "field_example",
+ *   label = @Translation("RGB text field"),
+ *   field_types = {
+ *     "field_example_rgb"
+ *   }
+ * )
+ */
+class Text3Widget extends WidgetBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, array &$form_state) {
+    $value = isset($items[$delta]->value) ? $items[$delta]->value : '';
+    // Parse the single hex string into RBG values.
+    if (!empty($value)) {
+      preg_match_all('@..@', substr($value, 1), $match);
+    }
+    else {
+      $match = array(array());
+    }
+
+    // Set up the form element for this widget.
+    $element += array(
+      '#type' => 'fieldset',
+      '#element_validate' => array('field_example_3text_validate'),
+    );
+
+    // Add in the RGB textfield elements.
+    foreach (array('r' => t('Red'), 'g' => t('Green'), 'b' => t('Blue')) as $key => $title) {
+      $element[$key] = array(
+        '#type' => 'textfield',
+        '#title' => $title,
+        '#size' => 2,
+        '#default_value' => array_shift($match[0]),
+        '#attributes' => array('class' => array('rgb-entry')),
+        '#description' => t('The 2-digit hexadecimal representation of @color saturation, like "a1" or "ff"', array('@color' => $title)),
+      );
+      // Since Form API doesn't allow a fieldset to be required, we
+      // have to require each field element individually.
+      if ($element['#required']) {
+        $element[$key]['#required'] = TRUE;
+      }
+    }
+    return array('value' => $element);
+  }
+
+}
diff --git a/field_example/lib/Drupal/field_example/Plugin/Field/FieldWidget/TextWidget.php b/field_example/lib/Drupal/field_example/Plugin/Field/FieldWidget/TextWidget.php
new file mode 100644
index 0000000..695dc99
--- /dev/null
+++ b/field_example/lib/Drupal/field_example/Plugin/Field/FieldWidget/TextWidget.php
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\field_example\Plugin\field\widget\TextWidget.
+ */
+
+namespace Drupal\field_example\Plugin\Field\FieldWidget;
+
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Field\WidgetBase;
+
+/**
+ * Plugin implementation of the 'field_example_text' widget.
+ *
+ * @FieldWidget(
+ *   id = "field_example_text",
+ *   module = "field_example",
+ *   label = @Translation("RGB value as #ffffff"),
+ *   field_types = {
+ *     "field_example_rgb"
+ *   }
+ * )
+ */
+class TextWidget extends WidgetBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, array &$form_state) {
+    $value = isset($items[$delta]->value) ? $items[$delta]->value : '';
+    $element += array(
+      '#type' => 'textfield',
+      '#default_value' => $value,
+      // Allow a slightly larger size that the field length to allow for some
+      // configurations where all characters won't fit in input field.
+      '#size' => 7,
+      '#maxlength' => 7,
+      '#element_validate' => array('field_example_text_validate'),
+    );
+    return array('value' => $element);
+  }
+
+}
diff --git a/field_example/lib/Drupal/field_example/Tests/Text3WidgetTest.php b/field_example/lib/Drupal/field_example/Tests/Text3WidgetTest.php
new file mode 100644
index 0000000..691a703
--- /dev/null
+++ b/field_example/lib/Drupal/field_example/Tests/Text3WidgetTest.php
@@ -0,0 +1,110 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\field_example\Tests\Text3WidgetTest.
+ */
+
+namespace Drupal\field_example\Tests;
+
+use Drupal\field_example\FieldExampleWebTestBase;
+
+class Text3WidgetTest extends FieldExampleWebTestBase {
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Field Example with Text3Widget',
+      'description' => 'Create a content type with a example_field_rgb field, configure it with the field_example_text-widget, create a node and check for correct values.',
+      'group' => 'Examples',
+    );
+  }
+
+  /**
+   * Test basic functionality of the example field.
+   *
+   * - Creates a content type.
+   * - Adds a single-valued field_example_rgb to it.
+   * - Adds a multivalued field_example_rgb to it.
+   * - Creates a node of the new type.
+   * - Populates the single-valued field.
+   * - Populates the multivalued field with two items.
+   * - Tests the result.
+   */
+  public function testSingleValueField() {
+    // Add a single field as administrator user.
+    $this->drupalLogin($this->administratorAccount);
+    $this->fieldName = $this->createField('field_example_rgb', 'field_example_3text', 'number');
+    // Post-condition: Content type now has the desired field.
+
+    // Switch to the author user to create content with this type and field.
+    $this->drupalLogin($this->authorAccount);
+    $this->drupalGet('node/add/' . $this->contentTypeName);
+
+    // Fill the create form.
+    $title = $this->randomName(20);
+    $edit = array(
+      'title[0][value]' => $title,
+      'field_' . $this->fieldName . '[0][value][r]' => '00',
+      'field_' . $this->fieldName . '[0][value][g]' => '0a',
+      'field_' . $this->fieldName . '[0][value][b]' => '01',
+    );
+
+    // Create the content.
+    $this->drupalPostForm(NULL, $edit, t('Save'));
+    $this->assertText(t('@type @title has been created', array('@type' => $this->contentTypeName, '@title' => $title)));
+
+    // Verify the value is shown when viewing this node.
+    $output_strings = $this->xpath("//div[contains(@class,'field-type-field-example-rgb')]/div/div/p/text()");
+    $this->assertEqual((string) $output_strings[0], "The color code in this field is #000a01");
+  }
+
+  /**
+   * Test basic functionality of the example field.
+   *
+   * - Creates a content type.
+   * - Adds a single-valued field_example_rgb to it.
+   * - Adds a multivalued field_example_rgb to it.
+   * - Creates a node of the new type.
+   * - Populates the single-valued field.
+   * - Populates the multivalued field with two items.
+   * - Tests the result.
+   */
+  public function testMultiValueField() {
+    // Add a single field as administrator user.
+    $this->drupalLogin($this->administratorAccount);
+    $this->fieldName = $this->createField('field_example_rgb', 'field_example_3text', '-1');
+    // Post-condition: Content type now has the desired field.
+
+    // Switch to the author user to create content with this type and field.
+    $this->drupalLogin($this->authorAccount);
+    $this->drupalGet('node/add/' . $this->contentTypeName);
+
+    // Fill the create form.
+    $title = $this->randomName(20);
+    $edit = array(
+      'title[0][value]' => $title,
+      'field_' . $this->fieldName . '[0][value][r]' => '00',
+      'field_' . $this->fieldName . '[0][value][g]' => 'ff',
+      'field_' . $this->fieldName . '[0][value][b]' => '00',
+    );
+
+    // Add a 2nd item to the multivalue field, so hit "add another".
+    $this->drupalPostForm(NULL, $edit, t('Add another item'));
+    $edit = array(
+      'field_' . $this->fieldName . '[1][value][r]' => 'ff',
+      'field_' . $this->fieldName . '[1][value][g]' => 'ff',
+      'field_' . $this->fieldName . '[1][value][b]' => 'ff',
+    );
+
+    // Create the content.
+    $this->drupalPostForm(NULL, $edit, t('Save'));
+    $this->assertText(t('@type @title has been created', array('@type' => $this->contentTypeName, '@title' => $title)));
+
+    // Verify the values are shown when viewing this node.
+    $output_strings = $this->xpath("//div[contains(@class,'field-type-field-example-rgb')]/div/div/p/text()");
+    $this->assertEqual((string) $output_strings[0], "The color code in this field is #00ff00");
+    $this->assertEqual((string) $output_strings[1], "The color code in this field is #ffffff");
+  }
+}
diff --git a/field_example/lib/Drupal/field_example/Tests/TextWidgetTest.php b/field_example/lib/Drupal/field_example/Tests/TextWidgetTest.php
new file mode 100644
index 0000000..f10e82c
--- /dev/null
+++ b/field_example/lib/Drupal/field_example/Tests/TextWidgetTest.php
@@ -0,0 +1,103 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\field_example\Tests\TextWidgetTest.
+ */
+
+namespace Drupal\field_example\Tests;
+
+use Drupal\field_example\FieldExampleWebTestBase;
+
+class TextWidgetTest extends FieldExampleWebTestBase {
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'Field Example with Text Widget',
+      'description' => 'Create a content type with a example_field_rgb field, configure it with the field_example_text-widget, create a node and check for correct values.',
+      'group' => 'Examples',
+    );
+  }
+
+  /**
+   * Test basic functionality of the example field.
+   *
+   * - Creates a content type.
+   * - Adds a single-valued field_example_rgb to it.
+   * - Adds a multivalued field_example_rgb to it.
+   * - Creates a node of the new type.
+   * - Populates the single-valued field.
+   * - Populates the multivalued field with two items.
+   * - Tests the result.
+   */
+  public function testSingleValueField() {
+    // Add a single field as administrator user.
+    $this->drupalLogin($this->administratorAccount);
+    $this->fieldName = $this->createField('field_example_rgb', 'field_example_text', 'number');
+
+    // Now that we have a content type with the desired field, switch to the author user to create content with it.
+    $this->drupalLogin($this->authorAccount);
+    $this->drupalGet('node/add/' . $this->contentTypeName);
+
+    // Add a node.
+    $title = $this->randomName(20);
+    $edit = array(
+      'title[0][value]' => $title,
+      'field_' . $this->fieldName . '[0][value]' => '#000001',
+    );
+
+    // Create the content.
+    $this->drupalPostForm(NULL, $edit, t('Save'));
+    $this->assertText(t('@type @title has been created', array('@type' => $this->contentTypeName, '@title' => $title)));
+
+    // Verify the value is shown when viewing this node.
+    $output_strings = $this->xpath("//div[contains(@class,'field-type-field-example-rgb')]/div/div/p/text()");
+    $this->assertEqual((string) $output_strings[0], "The color code in this field is #000001");
+  }
+
+  /**
+   * Test basic functionality of the example field.
+   *
+   * - Creates a content type.
+   * - Adds a single-valued field_example_rgb to it.
+   * - Adds a multivalued field_example_rgb to it.
+   * - Creates a node of the new type.
+   * - Populates the single-valued field.
+   * - Populates the multivalued field with two items.
+   * - Tests the result.
+   */
+  public function testMultiValueField() {
+    // Add a single field as administrator user.
+    $this->drupalLogin($this->administratorAccount);
+    $this->fieldName = $this->createField('field_example_rgb', 'field_example_text', '-1');
+
+    // Now that we have a content type with the desired field, switch to the author user to create content with it.
+    $this->drupalLogin($this->authorAccount);
+    $this->drupalGet('node/add/' . $this->contentTypeName);
+
+    // Add a node.
+    $title = $this->randomName(20);
+    $edit = array(
+      'title[0][value]' => $title,
+      'field_' . $this->fieldName . '[0][value]' => '#00ff00',
+    );
+
+    // We want to add a 2nd item to the multivalue field, so hit "add another".
+    $this->drupalPostForm(NULL, $edit, t('Add another item'));
+
+    $edit = array(
+      'field_' . $this->fieldName . '[1][value]' => '#ffffff',
+    );
+
+    // Now we can fill in the second item in the multivalue field and save.
+    $this->drupalPostForm(NULL, $edit, t('Save'));
+    $this->assertText(t('@type @title has been created', array('@type' => $this->contentTypeName, '@title' => $title)));
+
+    // Verify the value is shown when viewing this node.
+    $output_strings = $this->xpath("//div[contains(@class,'field-type-field-example-rgb')]/div/div/p/text()");
+    $this->assertEqual((string) $output_strings[0], "The color code in this field is #00ff00");
+    $this->assertEqual((string) $output_strings[1], "The color code in this field is #ffffff");
+  }
+}
-- 
1.8.1.3

