Feeds

Author

Upgrade Your Drupal Skills

We trained 1,000+ Drupal Developers over the last decade.

See Advanced Courses NAH, I know Enough
Oct 12 2020
Oct 12

After a couple of weeks of coding and testing, I've tagged a first alpha release of the ActivityPub module for Drupal! It implements the protocol so that you can communicate with other Drupal sites or platforms which support ActivityPub. Remote accounts on for example Mastodon or Pixelfed can follow any user on a Drupal site now and read content, like posts and reply from their platform. It's only the tip of the iceberg of what's possible with AP, but the main focus at this point is discovery of content and users on remote platforms and performing typical social responses (reply, favorite, announce).

The core of the implementation uses Drupal plugins to map fields and content types to activity types. Being in alpha means that the interfaces will most likely change as bugs are fixed and new features will be implemented, but I'll document those when I tag a new version. For more information, installation and configuration, check the README which I will continue to update as well.

Main features

  • Enable ActivityPub per user, discovery via the Webfinger module
  • Map Activity types and properties to content types and create posts to send out to the Fediverse.
  • Create comments from Create activities from remote users to content
  • Accept follow requests, Undo (follow), Outbox, Inbox and followers endpoints
  • Send posts via drush or cron, with HttpSignature for authorization

Follow me!

Have an account on Mastodon, Pixelfed or Pleroma? Then you can follow me via @[email protected]. Discovery probably works on most other platforms as well, but I haven't interacted with those yet, and I hope other people will download and start testing with those as well.

ActivityPub for Drupal: first alpha release! If you are on a federated platform, you can now follow me via [email protected] :) #drupal #activitypub https://realize.be/blog/activitypub-drupal

Send me a webmention with Drupal!

Dec 05 2018
Dec 05

Your site on the fediverse with Bridgy Fed

Nov 20 2018
Nov 20

Exclude entities which have a redirect from the search api index

Sep 03 2018
Sep 03

Webmention.io integration for Drupal 8

Feb 27 2018
Feb 27

Configuration split: deploy subsets of configuration in Drupal 8

Nov 28 2016
Nov 28

Stop telling users that another user has modified the content in Drupal 8

Aug 05 2016
Aug 05

Taking a (Drupal 8) website offline using AppCache

Apr 11 2016
Apr 11

Drupal 8 Field API series part 4: entity (form) displays and display modes

Dec 02 2013
Dec 02

An open source app for DrupalCon Prague

Sep 19 2013
Sep 19
Aug 07 2013
Aug 07

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 ?

Aug 05 2013
Aug 05

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.

Jul 22 2013
Jul 22

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.

Jun 18 2013
Jun 18

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.

Apr 14 2013
Apr 14
Live commit of the Field API to CMI patch at Frontend United

At BADCamp 2011 in San Francisco, Yves Chedemois, Greg Dunlap and I sat down to talk how we'd have to convert Field API to the configuration system that is now in Drupal 8.

Fast forward to April 13 2013. After months of hard work, with initial work done at DrupalCon Munich, the patch was finally ready to be committed. The Drupal core maintainers agreed that Alex Pott and I could commit this patch live on stage at Frontend United! And I had the honor of pushing the final button. I'll never be closer than this being a core comitter :)

Graham Taylor (@g_taylor) captured it all on video. 5 minutes of Drupal History.

swentel Sun, 14/04/2013 - 11:49
Dec 19 2012
Dec 19

The Field formatter conditions module for Drupal allows you to add conditions and actions per field on the manage display screens of entities (nodes, users etc). For example: you can hide a field on path x. Or hide it when the current user has role 'Administrator'. The module ships with several built-in conditions and actions for common use cases we encountered during projects at work. And thanks to jpstrikesback a couple of more will be added before we hit a 1.0 release.

It's easy to write your own specific use cases for a project, consult the API or look into the code how to built a form, condition and callback yourself in a few minutes. Field API and Display Suite fields are supported, Field API's extra fields are not, and most likely neither in the future.

Rules support

It gets better though. The API is limited to the fact that a callback is both the condition and the action. But what in case you want more conditions ? That's where Rules comes into play. You can build unlimited rules with fancy conditions and then fire off actions on a specific field. Currently, the module ships with two rules actions which can hide a field or change the image style of an image formatter. The event for these rules always needs to be 'A field is rendered'. Depending on the use cases, more will be added later, but it's easy for developers to write their own actions targeted for a specific project.

See it in action

See how it works in a short screencast. Happy conditioning and a very good new year!

Aug 27 2012
Aug 27

Last week, I attended the Field API pre Drupalcon sprint in Munich. My major motivation was to start working on the conversion of Field API to CMI and little than a week later, we have a patch that is green. While there's still a lot of work on it, it's still a good feeling after one week of hacking and discussions. After driving 8 hours home after a fantastic week, I decided to step up as a co-maintainer of Field API. But me stepping up won't make the big difference, we need more people joining us!

Anyone can help in many different ways

  • Gardening the queue: go through bug reports, ask for more info when something is not clear or close duplicates.
  • You can work on an issue by creating patches. Assign an issue to yourself so we know who's working on it.
  • Help by reviewing and testing patches to see if they actually work as intented.
  • Come bring us coffee and cake :)

Where should I start ?

There's a lot of work, that's for sure: CMI, widget and formatter plugins, Field API vs OO and so on. We have a site which lists the most important issues: http://realize.be/field-api - Bookmark that if you're interested!

We are also available on freenode.net on #drupal-fieldapi in case you have any questions.

How much time should I invest in this ?

That is totally up to you. Remember, this is voluntary work, so we do not expect anybody to work 40 hours a week - although that would be more than fantastic of course :) As an example, this is roughly what I think my weekly schedule will look like:

  1. Continue working further on the CMI patch. I have sponsored time from Wunderkraut so I can focus hard on getting this critical piece of functionality in. Goodbye field_config and field_config_instance tables!
  2. Go through the queue twice a week for about an hour and review existing patches from other people.
  3. Go through the queue twice a week for half an hour and close duplicates or ask for more info.
  4. Pick one 'quick win' issue per week to create a patch from it, either a bug or feature request.
  5. Work on Display Suite issue queue, this usually happens once a week for about 3 hours. Been like that since forever.

So wait, I thought you didn't sleep?

Yes I do, and I do other things as well. It's not worth spending all your nights on coding, you'll get burned out before you know it. But wouldn't it be great to go to sleep and someone else was doing some great work ?

So, who's helping along?

Jul 31 2012
Jul 31

In a previous article", I talked about the possibilities that CTools now exposes through its new powerfull drush commands. With the addition of a new alter hook just before writing out the files to a module, the sky is now the limit when it comes to having configuration of a site in code.

Knapsack

Almost two weeks from now, I created a project called 'Drush CTools Export Bonus' which does 2 basic things: writing a lot of new configuration to files and tons of commands to rebuild this configuration on command line. It finally gave me a reason to use the nice logo which was made a few months ago by Leen Van Severen. It pictures a knapsack which was the codename for a project I worked on at the end of last year, but got hold up by various reasons. Parts of that code ended up as the initial drush export patch for CTools.

So what does it do ? Simple: export or rebuild, via drush, any configuration that isn't supported by CTools' exportables, currently including common objects like content types, fields and instances, variables and many more. Entity exportables are on its way as well. See the project page for a full list. In the end, you should get the state of your site configuration in one single module, controllable by various drush commands. Drush being our Swiss army knife, the module our knapsack. In the end, I chose for a more descriptive project name referencing to its master drush command.

The workflow is extremely easy:

  1. drush dl drush_ctex_bonux into your .drush folder
  2. drush @site_alias ctex module_name to create a new or overwrite an existing export module
  3. drush @site_alias cbum module_name (first time) or drush @site_alias cbrm module_name when updating
  4. Sit back and watch the messages on your screen

For more commands, just type drush --filter=ctools

But hey, this sounds like features!

You're right, both projects (and maybe others out there) try do the same thing: make configuration available in code, but there's a couple of reasons why I chose to go down this road instead of using the features module. In a non-prioritized by importance order, this is why I've been thinking and coding about this the last year (probably even longer, I can't even recall anymore):

  1. Full control: Damian 'damiankloip' Lee and I are the main contributors with access to the CTools project regarding everything related with CTools' drush commands and bulk exporting. That gives us enough flexibility, also because the exportables in CTools just work™. By adding the alter mentioned above, new projects can easily be created and we don't pollute the main ctools project with configuration that it doesn't support out of the box. This new project is mine, but I'm open to any contributors out there, so ping me if you want access to make this rock beyond imagination.
  2. Killing module overhead: with features, you at least need 2 modules enabled. The exported module and features itself. If you want variables, you need strongarm - although that can be disabled and there's other modules out there exposing more configuration on top of that really shouldn't be enabled on your production environment at all. CTools is now slowly becoming a de facto contrib that you install on your Drupal 7 environment - think views and many others out there - which makes the choice very easy. With this project, you only need one drush file and the ctools commands to handle the configuration, including variables.
  3. It's CTools man! I admit, I'm a fan, and not only because of the exportables, which in my opinion everyone should simply implement as it will save you tons of code and maintenance nightmare. Do not try to write your own logic for code vs overridden vs database version of configuration. It's all there. And that's only the top of the iceberg of fine developer tools inside that project.
  4. It's about the state of your site. In its essence, you can export a complete site with both projects to one single module, commit that and update various environments. This is probably the workflow many developers have adopted using features, but there's also the tendency to split up use cases in various pieces. The philosophy of this project is simple: one site, one configuration. There's is absolutely no intention or need to split this up in individual pieces for flexibility. While it's possible, just don't do it, you don't need it.
  5. Dependency on modules: as already mentioned, you need features enabled to run its exported modules. Another key difference in this project is how we handle content types by not implementing hook_node_info(), but simply exporting the content type info to a separate file and a call to node_type_save() when updating. We're still implementing hook_image_default_styles, but that will soon disappear as we'll be adding an option to save every exportable, even ctools', to the database, so you can disable an exported module generated by this project.

Meet us at DrupalCon Munich

We're holding a bof session, currently scheduled on thursday, about all these fine goodies. We'll give you demo on how these commands work, answer your questions and talk about the future.

Will this solve all my problems ?

Maybe. Maybe not. There's no guarantee this approach is better than others, but there's one thing I do now though: typing drush @site_alias cbum module_name on a minimal Drupal install and then looking at the screen is one of the finer things I've seen in months which I wrote myself - but that's not a guarantee either. However, it's getting close to a point I've been dreaming for at least two years, having full control of a D7 configuration in code without any issues - so far. Anyone interested, please test and help along. And be very descriptive in case you post issues.

May 31 2012
May 31

Facet API for Drupal is an amazing module that enables you to easily create and manage faceted search interfaces. The UI is fantastic and easy and it works perfectly together with either Search API or Apache Solr. It also changes the breadcrumb as soon as you start filtering further, which in many (probably most) occasions, is a nice default behavior. But not every project wants these kind of breadcrumbs. In a project we tried first to override the breadcrumb with hook_breadcrumb_alter(), but as we have a lot of search pages, the code was getting ridiculously ugly.

So we looked at where exactly Facet API is changing the breadcrumbs. This happens in a method called setBreadcrumb in a url processor class. So, we need to create our own processor and override the default behavior. First of all, we need to let Facet API know we have our own url processor:

<?php
/**
* Implements hook_facetapi_url_processors().
*/
function yourmodule_facetapi_url_processors() {
  return array(
   
'standard' => array(
     
'handler' => array(
       
'label' => t('Your module URL processor'),
       
'class' => 'FacetApiYourClass',
      ),
    ),
  );
}
?>


We are doing something wrong here: the standard key is also used in facetapi_facetapi_url_processors(), so we should use another name, because we are now overriding the default class. The trick is to extend on that class so the other methods will still do the heavy lifting. Oh module_invoke_all, we sometimes love your merge behavior (although technically here it's a CTools plugin, but the result is the same for both).

<?php
class FacetApiYourClass extends FacetapiUrlProcessorStandard {
  public function
setBreadcrumb() {
   
// Keep default behavior.
 
}
}
?>


If this class is not defined in your .module, make sure you add it to the .info file so the registry picks it up.

That's it. Again, this post should probably also be tagged with '

You're doing it wrong

', but it works perfectly for our current use case.

Mar 24 2012
Mar 24

With the newest release of ctools, a new command was made available for drush to export all objects to code with one simple command to a module. Instead of having to copy and paste all code via the bulk export module to your custom module, a simple drush command now saves you a lot of time. But it doesn't stop there. Damian 'damiankloip' Lee started a sandbox to add more powerful funtionality and this has now been merged in the 7.x branch which will be available in the (soon) next release of ctools. An initial patch to select the exportables by hand has also been committed, but could need some more love on the UX side. Apart from that, the goodies that are in already, should make any developer extremely happy and opens up new possibilities in so many ways. So what commands can you use now and what do they do ?

  • drush ctools-export-disable: disable one or more exportables
  • drush ctools-export-enable: enable one or more exportables
  • drush ctools-export-revert: revert one or more exportables
  • drush ctools-export-info: get an overview of all possible exportables
  • drush ctools-export-view: view one or more exportables
  • drush ctools-export: export all exportables to a module

Excited yet ? We are, so we made a screencast, we're pretty sure you'll love it. Damian and I are planning more things for the future, so anyone who wants to help can post issues to the sandbox. Once they're done, we can commit these easily now Damian has access. Let's make ctools drush extremely powerful!

Mar 19 2012
Mar 19

There are several ways to install XHProf on your mac in a MAMP environment. After a lot fails, the easiest way in my opinion is using Homebrew. The rest of the article assumes you already have this installed, so let's get to the XHProf install.

  1. Fire 'brew install autoconf' to make sure autoconf is installed.
  2. Fire 'brew install xhprof' on the command line. You might get an error downloading the pcre package (depending on your homebrew version):
    Error: Failure while executing: /usr/bin/curl -f#LA Homebrew\ 0.8\ (Ruby\ 1.8.7-249;\ Mac\ OS\ X\ 10.7.3) ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.12.tar.bz2 -o /Users/swentel/Library/Caches/Homebrew/pcre-8.12.tar.bz2
    In that case, go to https://github.com/mxcl/homebrew/blob/master/Library/Formula/pcre.rb and download that file into /usr/local/Library/Formula/ and run the command again from shell.
  3. The extension is now built and can be copied to your MAMP installation:
    cp /usr/local/Cellar/xhprof/0.9.2/xhprof.so /Applications/MAMP/bin/php/php5.3.6/lib/php/extensions/no-debug-non-zts-20090626/ 
  4. Go to your php.ini file in your MAMP installation and paste following code and restart MAMP.
    [xhprof]
    extension=xhprof.so
    ;This is the directory that XHProf stores it's profile runs in.
    xhprof.output_dir=/tmp

That's it. Should take you about 5 minutes. Took me a couple of wasted hours, but it's worth doing. And now you can finally toggle the XHProf option on the devel settings page in case you're working with Drupal.

Other resources about installing XHprof:

Jan 18 2012
Jan 18

I've blogged about this topic almost more than 3 years ago how I used a couple of drupal modules to send mails with images from my iPhone which automatically got posted to my site. But times change. I now own a Nexus One and one of my goals for 2012 is to write at least one decent mobile application. I experimented with Titanium first, but decided to go native for a couple of reasons, performance and size of app being the main reasons.

While I already have an official go for an application I can develop, I started with a simple use case to learn android application development: uploading images to my personal blog saving a new node. Especially the java part is as good as new to me, so starting simple is always the best advice. After 3 hours of Drupal hacking and a lot more Java reading and debugging, my first application is happily working on my own phone and I have cute Druplicon on my desktop.

The code is freely available (see below) consisting of 2 parts.

The Android part

The application is build at SDK version 10, so it should work on any Android 2.3 or higher. It might possibly work on lower versions as well. After installation of the application and the first run on your telephone, you will need to login. The app authenticates with a Drupal user and only stores the endpoint URL and the session cookie. The session cookie is send when uploading an image so we know exactly who's uploading. Besides selecting from the app, you can also go to your Image gallery, select an image and use the Share menu to drop the image to the application.

The layout of the application as the code is relatively simple, it probably doesn't follow the best practices of Java programming, so be gentle in case you start reviewing, I'm still learning :)

The Drupal part

The Drupal module - D7 only - is simple as well, but was less hard to develop. While I could 've used a combination of services and other modules, I decided to write a simple module with a single menu callback that accepts a request that can either authenticate a user or create a node with title, image and other keys. Once enabled, you need to go to 'admin/config/media/drupapp' and start configure following items:

  • The endpoint. Defaults to 'drupapp', but you can change this to your likings.
  • The image field name: this will immediately select the content type that will be used to save the node.
  • Published status: default status, handy if you want to review after upload.
  • Debug option: log the complete $_POST variable through watchdog for testing purposes.

You will need to grant permission on the permissions page as well. Best practice is to create a new role which only has the "upload images via app" permission, and that role does not necessarily need a permission to create nodes.

Combine the 2 technologies and you get Drupoid. What's in name right ? You can browse, fork and/or download the code at http://github.com/swentel/Drupoid. Feel free to modify it to your own needs. The app in its current version will never be made available on the Android market as it's really a personal project. But it might serve as a nice start example for your own adventures in mobile application land.

Jan 05 2012
Jan 05

By default it's not possible to override existing paths with Page manager, the excellent module that is bundled in the CTools project. The Page manager existing pages module now allows you to do that. Technically, this module defines one abstract task and one content type plugin, so menu items can be overridden and the original page callback can be called through the content type plugin. This project comes with one default existing page, which is 'node', the default Drupal frontpage.

Basically, you are now able to override any Drupal path in your installation and create variants for it. The module comes with one default existing page (although new ones might be added in future). Default contexts for entities is possible as well. I've created a screencast so you can see the module in action at http://www.youtube.com/watch?v=W-4g01WjwI4.

Oct 15 2011
Oct 15

Little than a week ago, I committed Renderable elements which enables you to register any piece of build in your Drupal 7 installation and manage that through Field UI. It will make additional fields available of existing entities on the manage forms/display screens or you can for example register the contact form, webform or (let's go crazy) the exposed filter form of a view and rearrange the fields with Field UI. Essentialy, the idea is that you can rip out all elements which are nested, also those inside the vertical tab on the node edit form for instance.

How it works

Currently, only forms are supported, support for any kind of other non entity display is coming soon. Once you enable the module, you can go to admin/config/system/rel/config and enable the registration link on top of forms. Now go to any form in your installation and you'll see a 'Manage form display' link on top. Clicking on the link will make the form available to manage. An overview can be found at admin/config/system/rel. In case you're registering a non entity form, this build will be inserted as a bundle into a new entity that's exposed by the module. This way, we can profit from hook_field_extra_fields to register any kind of element. Tricky ? Sure. Cool, oh yeah!

Any kind of custom registration is exportable thanks to CTools as well.

The power

The idea from the module came after the initial proof of concepts while working on forms support for Display suite. Soon after, an issue appeared as well in the Field group issue queue to make field groups available in other contexts than entities and the ability to rip out the elements that are nested inside vertical tabs. Of course, anything can be done with form alter, but our goal was to make this possible through Field UI. Now, we don't want to force people Field group and/or DS, so we decided to create a separate project, which also means there are no dependencies. If offers a lot benefits and the possibilities you have now are huge:

  1. Install this as a stand alone module and it will make any element on the form available like the Save/Preview/Delete button, vertical tabs etc.
  2. Install Field group and you can take control of existing field groups or add new ones on say the contact form.
  3. Install Display suite and you can now select a template file to manage the layout of registered forms, like webform etc.

Both modules (rel and ds_forms) still need cleanup, but also a lot of testing, so please test as much edge cases as you can and report back into the issue queue!

This might all seem a bit cryptic, so, we've recorded a screencast showing you the power of Renderable elements together with Field group and Display suite on node, contact, webform and views exposed filter forms.

Jul 30 2011
Jul 30

This is probably the number one feature request during the D6 cycle of Display suite: limit the number of items on a multiple value field, usually on images. This involves custom coding in the form of creating new formatters over and over again in code or using the Custom formatters module. The upcoming release of Display Suite for Drupal 7 now puts an end to this annoying limitation. All Field API fields with a cardinality set to unlimited or more than one get an extra textfield on the manage display screens to limit the output per field. I've recorded a short screencast to demonstrate that exciting new suble feature. A new release is scheduled this wednesday, so test and report any errors when found!

There are also 2 issues on d.o to get this feature into other modules:

Jun 18 2011
Jun 18

Ever since Display Suite came out, people always thought it was a competitor for Panels, which really isn't the case. I'm not going into that debate, both me and merlinofchaos know the facts. I've got better news instead. Apart from the fact that Panel layouts are already available through the Field UI, the Panel editor can now be used as well. That's right, if you are a Panels/Page manager user, you can now use that interface to manage the layout of any view mode of any entity in your Drupal 7 installation. Drupal display managemement just got slick!

The code is available in the 7.x-1.x version of Display suite as a submodule called Panel view modes. This is a call for testers to play around with the initial implementation to make sure this works well. Post features, bugs into the issue queue. Make sure you have the latest dev versions installed of CTools, Panels and Display Suite and post as much debug information and/or steps to reproduce for any problem you might find.

In the meantime, you can drool over the screencast, the real magic starts around 1:35 :)

May 23 2011
May 23

Display Suite has the ability to create custom fields, either through code or via the UI. Those fields can hold custom content or even PHP code. It is somewhat limited since one field can only have one type of content, unless your field has some crazy PHP code which is not easy to maintain. Those days are over now. You want to add custom content, a node, the logo or any other type on the Field UI screens? The upcoming release - 7.x-1.2 - comes with a new field called ctools field, which can hold any sort of content that is available through ctools' plugin system and which is configurable on the "Manage display" screens per entity, bundle and view mode. Think as of Panels, but in Field UI. And they are of course all exportable. This may all sound a bit cryptic to you, so I've recorded a screencast showing you how this works. Be amazed and watch out for the upcoming release of Display Suite somewhere next week!

May 09 2011
May 09

The 7.x-1.1 release of Display Suite comes with a couple of new exciting features:

  • Fivestar
  • More hooks and better views extendibility
  • Ability to hide the page title on full node view
  • Custom field templates via the UI

The last feature is better known as the feature for the field system. Or you can also call it semantic fields. The possibilities to theme entities through the Field UI have now become endless. I'll explain those changes with code and screenshots, which should convince you how powerfull and easy Display Suite can be for your website. And best of all, this is all exportable!

Display Suite Extras

The field templates feature is located in the Display suite extras module, so it isn't enabled by default. But once configured to run, all fields, in every entity/bundle/view mode, on the "Manage display" screens get the ability to change following properties of their output:

  • Label: a custom label, wrapper and classes
  • Styles: extra styles on the outer wrapper (except reset or expert)
  • Field template: select the default output function which can be registered with hook_ds_field_theme_functions_info(). 3 options are already available
    1. Default: this is the default Drupal core field output.
    2. Reset: a function which strips all wrappers.
    3. Expert: a function which enables you to have full control over
      • Outer wrapper, element and classes
      • Field items wrapper, element and classes
      • Field item wrapper, element and classes

Fancy pictures usually say more than words, so just take a look at the screenshots underneath to see how this looks in reality.

In case you're not satisfied with those 3 options, you can always create your own output function. It's a simple hook returning an array which keys are the function and values are used for the label in the select box on the Field UI screen. The function takes 2 parameters, namely the $variables from the field the $config variable which holds specific information about the field itself.

<?php
/**
* Implements ds_field_theme_functions_info().
*/
function example_ds_field_theme_functions_info() {
  return array(
'theme_example_field' => t('Example theme field'));
}
/**
* Field theming function.
*/
function theme_example_field($variables, $config) {
 
$output = ''// Render the items without wrappers and label.
 
foreach ($variables['items'] as $delta => $item) {
   
$output .= drupal_render($item);
  }  return
$output;
}
?>

And that's it. Theming your entities has never been so easy in Drupal 7. If you want to try out yourself, go to http://drupal.org/project/ds and download the latest stable release. Happy theming! If you're interested, please support our session at the upcoming DrupalCon London and talk with us maintainers for tips and tricks!

Feb 19 2011
Feb 19

Drupal 7 introduced entities. It's such a great concept, I'm able to easily port Views displays - part of Display suite - to Drupal 7. It is now part of the main package, bundled in a sub module called Display suite Extras, which also includes the 'region to block' and 'switch view mode on a per node basis' features. More goodies, less code, even more power. But back to Views, entities and overriding templates.

For those who don't know exactly what I'm talking about, a quick summary. In the Drupal 6 version of Views displays, you're able to select a single display of a view and rearrange the template variables so you don't have to override the views-view.tpl.php template file. It uses the drag and drop screen that Display suite comes with, but for D7 we decided to use the manage display screens that Field UI provides. This is great because it supports all entities, so any entity that is fieldable use the same forms in which we simply inject our extra regions. However, Views is not an entity, but in comes hook_entity_info to the rescue!

To make sure we can use the Field UI forms, we declare our own entity called 'ds_views', which is not fieldable and only has one default bundle.

<?php
/**
* Implements hook_entity_info().
*/
function ds_extras_entity_info() {  // Register a views entity on behalf of Views.
 
$return = array(
   
'ds_views' => array(
     
'label' => t('Display suite Views'),
     
'bundles' => array(),
    ),
  );
  return
$return;
}
?>

So now that we have an entity, we create our own menu callback function which will ask for the field ui form. Display Suite will call all additional fields and add the layout options too.

<?php
function ds_extras_vd_field_ui() {
 
module_load_include('inc', 'field_ui', 'field_ui.admin');
 
$build = drupal_get_form('field_ui_display_overview_form', 'ds_views', 'views', 'default');
  return
$build;
}
?>

However, when calling the Field UI form, we'll get a message saying this entity does not have not any fields. To solve that problem, we can implement hook_field_extra_fields and return one field. In this case, the title will be overridden later on because we also implement hook_ds_fields() which will return our fields for views. 

<?php
/**
* Implements hook_field_extra_fields().
*/
function ds_extras_field_extra_fields() {  // Register a single field so fields for vd be picked
  // up nicely in the display overview form.
 
$extra['ds_views']['views'] = array(
   
'display' => array(
     
'title' => array(
       
'label' => t('Title'),
       
'description' => t('Title'),
       
'weight' => 10,
      ),
    ),
  );  return
$extra;
}
?>

In the screenshot you see a couple template variables which you can now put into the regions of the chosen layout.

Impressive isn't ? Hacky ? Well .. maybe a little bit. There is even more code, because now the combination of a view and a display is recorded as a bundle within the entity I just created above. I've recorded a short screencast so you can see it in action. If you want to try it yourself, download the second alpha release of Display suite for D7 and have fun!

Jan 26 2011
Jan 26

Drupal 7 is out. Even before the release, the community already started thinking what should happen for Drupal 8, we're just too excited to keep on working! Keeping track of which bugs, tasks and feature requests should be developed is gathered from a lot of sources and eventually - not always - created as an issue in the Drupal Core issue queue. Looking at the size of that queue, we already have a lot of work, most of which are tasks/bugs/follow ups which didn't make into Drupal 7.

New features will obviously come up: proposals and discussions are found on blogs, groups.d.o and Community initiatives. Brainstorm sessions happen at Drupalcons, IRC, local gatherings of Drupal enthousiasts or even surveys. Tracking all this activity and the current work is really hard, probably nearly impossible for every single person on this planet. Also, issues marked as critical give a false sense to me sometimes since the spectrum of things to fix is simply to wide and not focused on making sure a single piece of code is 100% A-OK. You can bookmark several URL's which lists issues based on tags (performance, theme, favorite-of-dries ..), components (node module, etc), priorities (major, critical) and so on. Simply too much to handle for one person.

What's next ?

The biggest personal issue I have, is that I'm not certain by any means what will be in the next release. As an example, take a look at the feature list for Fedora Core 15. It gives me a clear overview what I can expect the next time I'll be updating my development machine(s). I'm able to read a full description, track changes and see the progress of every single feature. That's nice and comforting for me. I would really like to see such an overview for Drupal core too. It would allow me to choose in which areas I want to be involved in and I can rely others to work on area's I'm less familiar with. A nice list like this would finally bring an end to the phrase "when it's ready" on the question when a new Drupal core sees the light.

So what if we started developing Drupal 8 in an agile way, using Scrum or - our favorite at work nowadays - Kanban ? The latter allows us to bend the rules much more in a way that it would fit our situation, in our case, a gigantic community spread all over the world. What do we need and how could we possibly achieve this so we can throw out another kick ass release ?

Roles

In an agile process, several roles are defined each having their responsibilities. Most are known, but we'll define them for our excercise.

  1. Product owner: In some way, everybody owns the product as we all have our ideas. But Dries, at the very end, decides which new features need to be in the next major release and what's left out.
  2. Project managers: Together with Dries, the core committers who test the stories, make sure sprints are delivered on time, discuss and resolve problems towards developers, consulting both the latter as the PO.
  3. Scrum master(s): This is the hardest task. They are responsible to solve problems on the story level, report to PM and PO, guide developers and testers, solve interrelational problems. Experienced people who know how Drupal Core works already, not necessarily on on every level, should be appointed for this role.
  4. Developers: the community. And boy, are we lucky, we have a gigantic pool of resources. But this does not only include hard core coders. People who like design, user experience, information architecture etc. must be involved at all times too. We've seen a lot of good input from a lot of those types, but we need more, a lot more!
  5. Testers: We have a great test bot. But it can't beat every day point and clicking by the average user who will use the product on a daily basis. However, it's ridiculously easy to come up with use cases nobody has never thought of during the process, the clue will be to filter out the critical ones. 

Release planning

Before we all start coding on new things, let's try and find out what the product owner wants to see in the next release. Obviously the PO will have some wishes and as identified above, we already have a big backlog, but priorities are not always clear. In this planning, this should be made clear what is really necessary to be picked up first. Because, the sooner this is done, the better we can focus on the nice to haves. A closing deadline always brings bad advice!

Planning this meeting is not going to be easy for the scrum master. A workable solution is an IRC session on a time it fits for the biggest amount of people, and still, even then some will not get up at night to participate. This can be tackled that already a number of spoke persons stand up voluntarily so they can express the wishes and conceirns from the community. Important is that there can't be a big discussion on technical level, there's just need to be an agreement on a fairly abstract level. Stories are written with a rough estimation, tasks can be assigned, but this not necessary during this meeting. 

Backlog filling

Sprint planning

This meeting should define the primary goal to achieve towards the end of the sprint. It's important that stories in this sprint which do not lead to the success of the sprint are picked up later. This means that a sprint can contain critical, normal and low priority stories! A Drupal core sprint should at least be 2 months, we're still working with volunteers using their free time, and sometimes company time to make things better. At this point, issues can be assigned to a story which make sure we have a deliverable in which the PO, but in the end everyone is comfortable with. Once all is decided, we can start coding and try to make sure we keep up with what is planned. Change requests can occur - read, will happen -  but they should be put in our backlog and be picked up into the following sprint.

Moving stories to a sprint

Adding tasks to the Entity story

Adding tasks to the story

Tools

The screenshots above are taken from the great http://kanbantool.com/ website, which we introduced at Krimson - after a great learning process during my period at One Agency - use on a daily basis for clients, but also for our community work, whether it's maintaining or porting our Drupal modules, organising events etc. The project module already does an amazing job and I think with some really - relatively simple - changes we could make a more visualized and targeted picture of the path Drupal is taking. A board like I mentioned aboved for the Fedora Core release would be the great step. Being able to create meta-issues (stories) and reference other issues (tasks) with eachother is something I would gladly help to write patches for - in an agile fashion of course .. ;)

Will this work ?

To be honest, I think it could. But it won't be easy, but with the right spirit and good leadership, something like this could be achievable. Besides, how extremely cool would it be to tell we're the first community that would work like this - unless there are others, feel free to correct me. But in the end, just knowing what to expect in the next release would make a lot of people feel a lot more at ease.

Dec 21 2010
Dec 21

Let's start with a little Jeoporday quiz!

Answer: There's a module for that, just look it up on d.o.
Question: How can I get this functionality on my website ?

More and more posts on the web are showing up with 'The top x modules you should always install', someone else even created a website for the answer. It's hard to keep up with the new releases everyday, it's sometimes even harder to find out the exact use case why a new module even gets released - although I respect the author because he's contributing, don't get me wrong on that one.

The selection criteria

There are many reasons to pick a module:

  • Deadlines: the functionality is there and the project has to be delivered on time.
  • Workflow: development teams try to work as uniform as possible on every project.
  • Lazyness: why bother writing a functionality yourself (even it you only need one function from a giant module).
  • Strong API: it helps the developer in custom modules or in deployment strategies.
  • PM has found it on the web. I'm dead serious on this one. I've had cases where I had to start on a site where I got the modules picked for me, without even consulting me.

There are other reasons obviously, but what strikes me is that from experience (I'm guilty too) and from a lot of discussions with all kinds of people (developers, project managers, etc), the gap between what the developer needs and the client actually wants has become bigger and bigger. I'm not sure why this is exactly happening, but it can have serious consequences when going live with a project.

The kitten paradox

The Drupal world has this paradigm that you shouldn't hack Drupal core or God kills a kitten. Even worse, Dries too nowadays. This somehow extends to Contrib too, since well, everytime you need to update, you'll have to make sure you have your patches ready. But what about this: every time you're using more than a 100 modules, mankind has killed a lot of kittens by having to install additional load balancers, reverse proxies, web and database servers for keeping the site up if it's a really busy one, especially with authenticated users. Adding hardware because the site is not performing well is - in a lot of cases - not the right answer.

Back to basics

Every project is unique and building a big project should really be evaluated before and - even more important - during the development process, with great care on how the site behaves in a real life situation, investigating the amount of queries, PHP memory, generation time and so on. That's not something you can do at the end. It's simply too late at that point and a lot of kittens have died already. There are great profiling tools in Devel and Xdebug should be your friend all the time. Be sure to use them all the time.

So, depending on the needs, it might be more interesting to write a simple custom module and write PHP yourself again, using either PHP or API functions available in Drupal core. Every developer should've least opened up the includes folder and scanned every file in it. I'm still amazed now and then, even after all those years. Even with Drupal 7 almost out, a lot of people seem scared because Contrib is not entirely ready yet. That's a pity, because there is some amazing stuff under the hood and you'd be pretty surprised how much you can pull off with only Drupal core, PHP and - especially - a lot of developing fun.

Answer: That should be api.drupal.org and php.net.
Question: Which bookmarks should I really have ?

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web