An open source app for DrupalCamps

On september 14 and 15, Leuven will host the annual Belgium DrupalCamp. During those two days, people come together learning and discussing the open-source content management system Drupal. The program will be available on the website, but we decided to also create an application this year. We've tried to make it abstract as possible, so other Drupal events can easily built from the source code which is available online.

The apps will be available for Android and iOS. As soon as the program is ready, we'll publish them, so keep an eye out for the camp website, twitter or, of course, this article. The Android version is available on Play Store.

Features include:

  • Built with speed in mind: native app
  • Works offline
  • Sessions and speakers
  • Mark your favorite sessions
  • Maps integration when online

Collaborate

The code is freely available on GitHub, so anyone can easily send in bug reports, interface translations or create pull requests to make the applications even better.

Proudly built by @swentel, @TimLeytens and @leenvs.

So, who will make the Windows mobile and Firefox version ?

Drupal 8 Field API series part 2: field widgets

In the first article of the Drupal 8 Field API series, we saw how field formatters are written in Drupal 8. Now it's time for widgets. You might get a déjà vu when reading as a lot resemble to formatters.

Plugins

Creating field widgets in Drupal 7 was done by implementing four hooks. In Drupal 8, widgets are now plugins using the new Plugin API. Hooks are replaced by methods in classes, which means that your module file will be empty if you only provide a widget, unless you also implement one of the (new) widget alter hooks. Being classes, this means that field widgets can now extend on each other. A good example in core is the image field widget extending the file field widget class. Discovery and class instantiation is managed by the new widget plugin manager.

Create a file like '{your_module}/lib/Drupal/{your_module}/Plugin/field/FieldWidget/{NameOfYourWidget}.php. That's a lot of directories right ? Welcome to the world of PSR-0, namespaces and plugins in D8. This is most likely going to change, feel free to read, or even help along in https://drupal.org/node/1971198. Also, plugin managers can control where plugins reside, see https://drupal.org/node/2043379, so we'll probably change this at some point.

In most cases, you will want to extend the WidgetBase class which does most of the heavy lifting for you (holds the code that was in the field_default_*() functions in Drupal 7). Following classes will usually be imported at the top of your file depending on which methods you override:

<?php
// WidgetBase class.
use Drupal\Core\Field\WidgetBase;
// FieldItemListInterface
use Drupal\Core\Field\FieldItemListInterface;
// Symfone violation interface
use Symfony\Component\Validator\ConstraintViolationInterface;
?>

1. hook_field_widget_info() are now annotations

hook_field_widget_info() is replaced by annotation-based plugin discovery, using the \Drupal\field\Annotation\FieldWidget annotation class. As for other plugin types, the accepted properties are documented in the annotation class. Other modules can extend this by implementing hook_field_widget_info_alter(). Note that some property names have changed since Drupal 7 (spaces replaces by underscores). This is how an annotation looks like, which is placed right above the class keyword.

<?php
/**
* Plugin implementation of the 'foo_widget' widget
*
* @FieldWidget(
*   id = "foo_widget",
*   label = @Translation("Foo widget"),
*   field_types = {
*     "text",
*     "text_long"
*   },
*   settings = {
*     "size" = "600",
*   }
* )
*/
class FooWidget extends WidgetBase { }
?>

2. hook_field_widget_settings_form() becomes WidgetInterface::settingsForm()

Next up is to create a settingsForm() method. If you have an old settings form, you can simply move the code to this method. The calling code (typically Field UI) takes care of saving the settings on form submit. Remember to always start with an empty $elements array and not with the $form argument from the function arguments.

Side note: in all methods in the widget class:

  • the settings values currently configured for the widget can be accessed with $this->getSetting('settings_key'), or $this->getSettings()
  • the settings values currently configured for the field on which the widget is being used can be accessed with $this->getSetting('settings_key'), or $this->getSettings(). Those methods return both field level settings and instance level settings, merged.
  • If access to field properties other than field settings is needed, the field definition can be accessed with $this->getFieldDefinition(). This returns an object implementing \Drupal\Core\Entity\Field\FieldDefinitionInterface, which unifies the separate $field and $instance structures D7 coders are familiar with. More details on this will come in a following post.

<?php
 
/**
   * {@inheritdoc}
   */
 
public function settingsForm(array $form, array &$form_state) {
   
$element = array();

   
$element['size'] = array(
     
'#type' => 'number',
     
'#title' => t('Size of textfield'),
     
'#default_value' => $this->getSetting('size'),
     
'#required' => TRUE,
     
'#min' => 1,
    );

    return
$element;
  }
?>

3. hook_field_widget_form becomes WidgetInterface::formElement()

This is where you return the widget form. Also, the methods now receive the field values as a \Drupal\Core\Field\FieldItemListInterface object, rather than an $items array in Drupal 7. More information can be found about Drupal 8 Entity API and the syntax around field values in the handbook. Simply put, FieldInterface objects can be accessed and iterated on like an array of items keyed by delta, and properties in each item can be accessed by simple object syntax.

<?php
 
/**
   * {@inheritdoc}
   */
 
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, array &$form_state) {
   
$main_widget = $element + array(
     
'#type' => 'textfield',
     
'#default_value' => isset($items[$delta]->value) ? $items[$delta]->value : NULL,
     
'#size' => $this->getSetting('size'),
     
'#placeholder' => $this->getSetting('placeholder'),
     
'#maxlength' => $this->getSetting('max_length'),
     
'#attributes' => array('class' => array('text-full')),
    );

    if (
$this->getSetting('text_processing')) {
     
$element = $main_widget;
     
$element['#type'] = 'text_format';
     
$element['#format'] = isset($items[$delta]->format) ? $items[$delta]->format : NULL;
     
$element['#base_type'] = $main_widget['#type'];
    }
    else {
     
$element['value'] = $main_widget;
    }

    return
$element;
  }
?>

4. hook_field_widget_error becomes WidgetInterface::errorElement()

The second parameter, $violation, contains the list of constraint violations reported during the validation phase. In Drupal 8, the Sympony contraints class is used to validate objects, whether doing this programmatically or through a form. We won't go deeper into this for now. In the next article, when we'll talk about the field types, this will become much clearer. Just remember for now that validation should not happen in your widgets, but in constraints.

<?php
 
/**
   * {@inheritdoc}
   */
 
public function errorElement(array $element, ConstraintViolationInterface $violation, array $form, array &$form_state) {
    return
$element[$violation->arrayPropertyPath[0]];
  }
?>

5. WidgetInterface::settingsSummary()

This is a new method which resembles FormatterInterface::settingsSummary(). With the introduction of form modes in D8, a given field can be displayed with different widgets in the different form modes used by the entity type. This method is used to output the settings currently configured for the widget in Field UI screens, just like you do with field formatters - try to keep it short :-).

<?php
 
/**
   * {@inheritdoc}
   */
 
public function settingsSummary() {
   
$summary = array();

   
$summary[] = t('Textfield size: !size', array('!size' => $this->getSetting('size')));
   
$placeholder = $this->getSetting('placeholder');
    if (!empty(
$placeholder)) {
     
$summary[] = t('Placeholder: @placeholder', array('@placeholder' => $placeholder));
    }

    return
$summary;
  }
?>

6. WidgetInterface::massageFormValues()

This is a new method which you should only implement if you need todo complex FAPI tricks when a form is submitted. This lets you turn the raw submitted form values produced by your widget into the "regular" format expected for field values. Examples in core are the taxonomy autocomplete widget and the file widget which need todo additional processing when a form is submitted.

Alter hooks

Besides the existing hook_field_widget_info_alter, two new hooks are introduced allowing you to alter widget settings form or summary on Field UI, which behave almost the same like field formatter alter hooks: hook_field_widget_settings_form and hook_field_widget_settings_summary_alter.

Resources

Conclusion

Like formatters, writing and maintaining field widgets for Drupal 8 is not hard. In most cases, when porting, it's simply moving the contents of your old hooks to the methods in a class. In the next part, we will see how you write field type plugins in Drupal 8.

Drupal 8 Field API series part 1: field formatters

The Drupal 8 cycle has entered the API freeze since the 1st of July, which means it's time to start porting modules or simply play around with the API. While there are exceptions to change the API, you can safely assume that 95% (or even more) will remain as it is today.

This is the first article which will be part of a series of changes in Field API for Drupal 8: field formatters. In case there are updates to the API, we will update the articles, but also the change records on drupal.org. Watch out for the next couple of weeks and months and hopefully you're prepared for Drupal 8 and Field API.

Plugins

Creating field formatters in Drupal 7 was done by implementing four hooks. In Drupal 8, formatters are now plugins using the new Plugin API. Hooks are replaced by methods in classes, which means that your module file will be empty if you only provide a formatter (unless you also implement one of the field formatter alter hooks). Being classes, this means that field formatters can now extend on each other. A good example in core is the image field formatter extending the file field formatter class. Discovery and class instantiation is managed by the new formatter plugin manager.

Create a file like '{your_module}/lib/Drupal/{your_module}/Plugin/field/FieldFormatter/{NameOfYourFormatter}.php. That's a lot of directories right ? Welcome to the world of PSR-0, namespaces and plugins in D8. This is most likely going to change, feel free to read, or even help along in https://drupal.org/node/1971198. Also, plugin managers can now control where plugins reside, see https://drupal.org/node/2043379, so we'll probably change this at some point.

In most cases, you will want to extend the FormatterBase class which does most of the heavy lifting for you. Following classes will usually be imported at the top of your file:

<?php
// FormatterBase class.
Drupal\Core\Field\FormatterBase;
// FieldItemInterface
use Drupal\Core\Field\FieldItemListInterface;
?>

1. hook_field_formatter_info() are now annotations

hook_field_formatter_info() is replaced by annotation-based plugin discovery, using the \Drupal\field\Annotation\FieldFormatter annotation class. As for other plugin types, the accepted properties are documented in the annotation class. Other modules can extend this by implementing hook_field_formatter_info_alter(). In core, the edit module adds the edit property so it knows which in-place editor it has to use. Note that some property names have changed since Drupal 7 (spaces replaces by underscores). This is how an annotation looks like, which is placed right above the class keyword.

<?php
/**
* Plugin implementation of the 'foo_formatter' formatter
*
* @FieldFormatter(
*   id = "foo_formatter",
*   label = @Translation("Foo formatter"),
*   field_types = {
*     "text",
*     "text_long"
*   },
*   settings = {
*     "trim_length" = "600",
*   },
*    edit = {
*      "editor" = "form"
*    }
* )
*/
class FooFormatter extends FormatterBase { }
?>

2. hook_field_formatter_settings_form() becomes FormatterInterface::settingsForm()

Next up is to create a settingsForm() method. If you have an old settings form, you can simply move the code to this method. Settings are automatically saved and can be accessed by calling $this->getSetting('settings_key');. Remember to always start with an empty $elements array and not with the $form argument from the function arguments.

<?php
 
/**
   * {@inheritdoc}
   */
 
public function settingsForm(array $form, array &$form_state) {
   
$element = array();

   
$element['trim_length'] = array(
     
'#title' => t('Trim length'),
     
'#type' => 'number',
     
'#default_value' => $this->getSetting('trim_length'),
     
'#min' => 1,
     
'#required' => TRUE,
    );

    return
$element;
  }
?>

3. hook_field_formatter_settings_summary() becomes FormatterInterface::settingsSummary()

Settings are accessed by calling $this->getSetting('settings_key');. Another change is that the summary now needs to return an array instead of a string.

<?php
 
/**
   * {@inheritdoc}
   */
 
public function settingsSummary() {
   
$summary = array();
   
$summary[] = t('Trim length: @trim_length', array('@trim_length' => $this->getSetting('trim_length')));
    return
$summary;
  }
?>

4. hook_field_formatter_prepare_view becomes FormatterInterface::prepareView() and hook_field_formatter_view() becomes FormatterInterface::viewElements()

The first method allows you to add additional information on the items, the second is where the actual formatting happens. Settings are accessed by calling $this->getSetting('settings_key');. Also, the methods now receive the field values as a \Drupal\Core\Field\FieldItemListInterface object, rather than an $items array in Drupal 7. More information can be found about Drupal 8 Entity API and the syntax around field values in the handbook. Simply put, FieldItemListInterface objects can be accessed and iterated on like an array of items keyed by delta, and properties in each item can be accessed by simple object syntax.

<?php
 
/**
   * {@inheritdoc}
   */
 
public function viewElements(FieldItemListInterface $items) {
   
$elements = array();

   
$text_processing = $this->getSetting('text_processing');
    foreach (
$items as $delta => $item) {
      if (
$this->getPluginId() == 'text_summary_or_trimmed' && !empty($item->summary)) {
       
$output = $item->summary_processed;
      }
      else {
       
$output = $item->processed;
       
$output = text_summary($output, $text_processing ? $item->format : NULL, $this->getSetting('trim_length'));
      }
     
$elements[$delta] = array('#markup' => $output);
    }

    return
$elements;
  }
?>

Alter hooks

The alter hooks are still the same for Drupal 8, with one small API change in hook_field_formatter_settings_summary_alter() which is invoked by the Field UI module. The summary is now an array of strings instead of a single string. <br/> will be automatically inserted between the strings when the summary is displayed.

<?php
/**
* Implements hook_field_formatter_settings_summary_alter().
*/
function my_module_field_formatter_settings_summary_alter(&$summary, $context) {
 
// Append a message to the summary when an instance of foo_formatter has
  // mysetting set to TRUE for the current view mode.
 
if ($context['formatter']->getPluginId() == 'foo_formatter') {
    if (
$context['formatter']->getSetting('mysetting')) {
     
$summary[] = t('My setting enabled.');
    }
  }
}
?>

The other two hooks are hook_field_formatter_info_alter() allowing you to make changes to the formatter definitions and hook_field_formatter_settings_form_alter() which is invoked from by the Field UI module when displaying the summary of the formatter settings for a field.

Resources

Conclusion

Writing and maintaining field formatters for Drupal 8 is not hard. In most cases, when porting, it's simply moving the contents of your old hooks to the methods in a class. In the next part, we will see how you write widget plugins in Drupal 8.

An open source app for the 'Gentse Feesten'

Every year, my hometown Ghent, hosts its annual festival called the Gentse Feesten. 10 days - actually, 11, if you count the unofficial pre-friday day - the city will barely sleep.

One of the hardest things is to actually figure out where you want to go during one of these days. There is the official website and there is also an official app available, but we decided, for several reasons, to write our own, open sourced and native application. The data is grabbed and available at http://data.gent.be. Open data and open source, a perfect match.

After a couple of brainstorms and iterations on the design, we released the first versions so you can start planning your events and have them available at any time.

You can install the Android version from Google Play, the iOS version from the iTunes store, the Firefox OS app in the Firefox Market (make sure you go Worldwide), the Windows mobile from Windows mobile store and Windows 8 in Windows 8 store.

Features include:

  • Built with speed in mind: native written application.
  • Works offline after initially downloading the program (around 2.5MB).
  • List of favorites: handy list so you can easily see what todo on which day.
  • Free text and facet style search.
  • Share functionality: integrates with installed applications on your platform.
  • Maps integration when online.
  • Location - what's going on around me
  • Parking availability

In case you have an Android device, the program will be downloaded in the background on the first start. On iOS, you can start browsing immediately.

Collaborate

The code is freely available on GitHub, so anyone can easily send bug reports, interface translations or create pull requests to make the applications even better. The services are currently our own, so you need to setup those yourself first. Note that there are also some implementation differences between the platforms.

Proudly built by @swentel, @TimLeytens, @leenvs, @janyves and @AppCreativity.

So, who will make the Windows mobile (done!) and Firefox (Done too!) version ?

Why you should come to DrupalCon Prague

First of all: I'm a featured speaker. I'll be hosting a session called 'Drupal 8 for Site Builders'. Come and watch to get an overview of all the wonders and power Drupal 8 has for creating a site. However, there are other reasons to attend DrupalCon Prague, and they are not Drupal related at all.

Kutná Hora

Kutná Hora is a little town, about 80 kilometers away from Prague, world known for its Sedlec ossuary which contains a lot of human bones, used for decorating the inside. While this may sound luguber, a visit to this small chapel is not something you will ever forget. And if you don't go inside, the top of the chapel has a skull, they really thought about everything. The easiest way to get there is by train and even arriving in the small station is worth travelling. I have seen it already, but will be going again, so feel free to join me.

CERN opendays

CERN, the European Organization for Nuclear Research, is opening its doors on 28th and 29th of september for the public for it so called CERN opendays. CERN lies in Geneva, Switserland, about 8 hours driving from Prague, or maybe 1 hour flying by plane. You'll be able to go down 100 meters underground and look at the Large Hadron collider and all other amazingly cool experiments scientists and engineers perform. Unless you are Daniel "dawehner" Wehner, you don't get that many chances to visit this place in your lifetime, especially since they only open it up publicly every 4 years. Tickets are for sale starting August, so keep an eye on the website if you want to go. That means I won't be attending the post-sprints, but honestly, I can live with that.

Pages

Subscribe to realize.be RSS