Thursday 23 October 2014

Adding a submit callback to an entity form

If you use hook_form_alter(), hook_form_FORM_ID_alter(), or even hook_form_BASE_FORM_ID_alter() to intercept an entity add/edit form and you want to add a #submit callback, then this:

$form['#submit'][] = 'my_module_extra_submit';

won't work. But this will:

$form['actions']['submit']['#submit'][] = 'my_module_extra_submit';

Because the submit callbacks are added to the button and not the form. The same applies to #validate though as usual you're better off adding '#element_validate' to the element you want to check, if possible.

But that probably won't be enough because the default content of the submit callbacks is:

array('::submitForm', '::save')

If you're making a change to the entity you can't add your callback on the end of the array because the entity has already been saved, and your change will make no difference.

Nor can you unshift it on to the start because ::submitForm() overwrites the entity with a new one constructed from the form values. So any change will be wiped out.

You have to insert it just before the ::save(), this code will do the trick:

  // Have to add the submit callback to the button submit on an entity form
  // but it has to go after the '::submitForm()' and before the '::save()'.
  $submits =& $form['actions']['submit']['#submit'];
  $key = array_search('::save', $submits, TRUE);
  array_splice($submits, $key, 0, array('my_module_extra_submit'));

Things to bear in mind here is that the 4th parameter to array_splice() is cast to an array if it isn't one, which could result in strange behaviour if you're using a callback to a method, in which case you need array(array('myClass', 'myMethod')).

BASE_FORM_ID?

The entity form class creates a base_form_id, which is the entity id plus "_form" (e.g. "node_form") which is also called in the form alter sequence. It means you can have a single hook to handle both add and edit forms.

If the base form id would be the same as the form id, it doesn't perform the extra call because that would result in a hook function being called more than once.

Don't forget to sign-up to get information about my Drupal 8 book(s).

Friday 17 October 2014

How to screw up an annotation

When defining plugins in Drupal 8 you use the annotation in the file, like this (example from block_content):


/**
 * Defines the custom block type entity.
 *
 * @ConfigEntityType(
 *   id = "block_content_type",
 *   label = @Translation("Custom block type"),
 *   handlers = {
 *     "form" = {
 *       "default" = "Drupal\block_content\BlockContentTypeForm",
 *       "add" = "Drupal\block_content\BlockContentTypeForm",
 *       "edit" = "Drupal\block_content\BlockContentTypeForm",
 *       "delete" = "Drupal\block_content\Form\BlockContentTypeDeleteForm"
 *     },
 *     "list_builder" = "Drupal\block_content\BlockContentTypeListBuilder"
 *   },
 *   admin_permission = "administer blocks",
 *   config_prefix = "type",
 *   bundle_of = "block_content",
 *   entity_keys = {
 *     "id" = "id",
 *     "label" = "label"
 *   },
 *   links = {
 *     "delete-form" = "entity.block_content_type.delete_form",
 *     "edit-form" = "entity.block_content_type.edit_form"
 *   }
 * )
 */

Which is all great and we love it.

However I was developing a new ConfigEntityType and the plugin discovery system was just refusing to recognise it. So I was tearing my hair out for a while until I discovered this:

Under absolutely no circumstances place a second docblock after the first one, like this:

/**
 * Defines the custom block type entity.
 *
 * @ConfigEntityType(
 *   id = "block_content_type",
etc...
 */
/**
 * Another docblock...
 */

Because the annotation discovery system won't recognise the first one.

Why would you do such a thing? Well, I did it because I wanted to store some of the annotation fields that I wasn't currently implementing as I developed the plugin. And I used a docblock.

Then wasted several hours.

(Don't forget to sign up for information on the Drupal 8 books I'll be releasing - don't worry you won't be spammed.)



Wednesday 10 September 2014

Installing Drupal 8 on Ubuntu

I've been through the process of installing Drupal 8 on an Ubuntu virtual machine running on Windows several times now and I think I've got it down to a series of instructions that works every time.

Where I use the word "Download" you click it to do the downloading.

A. Virtual Machine on Windows

Assuming you're running Windows 7/8 machine the first thing you need is a Ubuntu virtual machine, you will need a decent Internet connection for this:

  1. Download and install the VMware player software;
  2. Download the XUbuntu virtual machine image (it's a compressed file);
    WARNING: This file is huge and the download process could take hours, depending on how good your connection is. If you have a bad connection then you may as well forget it right now.
  3. Unpack the XUbuntu 14.04t image into a folder where you want to keep your VMs (you can have more than one);
  4. Open up the folders and find the Xubuntu.vmx file and double-click it.
The virtual machine will run and everything will set-up. By the end of the process you will have a copy of XUbuntu running. And you will be filled with joy-joy feelings.

B. Adding the software

The next stage is to add all the software you want to use. Some items are essential, some are a good idea, others are entirely optional. You must have an Internet connection for this
  1. Click on the icon in the bottom left (it's the sam sort of thing as the Windows "Start" menu) and click Terminal Emulator;
    Just in case you don't know: a lot of Linux work is done on the command line.
  2. Type: sudo apt-get update
    This updates all the information about what packages are available for installing. The first time you do this (and if you leave it a while between commands) you will be asked for the password. it's password.
  3. Type: java -version
    It will tell you there isn't a version of java loaded and will then offer you some choices.
  4. Type: sudo apt-get install openjdk-7-jre-headless -y
    If you don't add the -y you'll be asked to confirm. Java will now be installed.
  5. The VM comes with Firefox, if you want Chrome (I prefer it) do this:
    a. Type: sudo apt-get install chromium-browser -y
    b. Launch Chrome from the menu under "Internet", sync with your G+ account as needed.
  6. I use Eclipse, so if you want it follow the instructions on this site. Select the PHP-specific version of Luna (at time of writing). Also in the instructions on that page use the original Exec=/opt/eclipse/eclipse command, and not the suggested change.
  7. Type: sudo apt-get install git -y
  8. Type: sudo apt-get install lamp-server^ -y
    The ^ is not a mistake, you need it. You will also need to enter the MySQL password.
  9. Type: sudo a2enmod rewrite
    To enable the apache2 rewrite module.
  10. Type: sudo service apache2 restart
    To restart apache2 (surprise!)
  11. Now you need to enable the PHP extensions you need. The only one that Drupal will actually tell you it wants is GD, but that is not in the system so it has to be loaded. There's a fairly awesome new method of doing this with PHP5.4 which we will now demonstrate:

    a. Type: 
    sudo apt-get install php5-<name> -y
    where <name> can be cgi, curl, gd, json, mysql, tidy, mcrypt, memcached, oauth, xdebug, xhprof and more. You can put them all on one line, one after the other with a space between, but don't forget to prefix with php5- in each case.

    b. Type: sudo php5enmod gd
    To enable the GD extension. So much better than fiddling with php.ini.

    You can find out more information about these here and here.
  12. Type: cd /var/www
    To move to the web directory.
  13. Type: sudo git clone --branch 8.0.x http://git.drupal.org/project/drupal.git
    To bring down this version of Drupal 8, you can substitute the branch you want. This gives you a directory called 'drupal' you may want to change that.
  14. Finally you need to configure the virtual host in apache2 follow the instructions here.

C. Installing Drupal

This process is pretty much the same as for D6 and D7 but before you start there are a couple of things to do:
  1. Type: cd /var/www/drupal
    Or whatever you called your Drupal 8 directory in the end.
  2. Type: sudo chmod a+w -R sites/default
    This makes the settings directory and its contents read/writable which is necessary when going through the initial installation of Drupal.
  3. Type: cd ../..
    Go back up two directory levels.
  4. Type: chmod 777 drupal
    This allows your IDE (such as eclipse) to create its management files in the directory.
  5. Finally, go to your browser, type in the web address of your site (as configured in apache2 and the hosts file in step B.14) and you should be greeted with the Drupal install screen.

D. Relax

Job well done.

Drupal 8 (mostly)

It's customary to do a quick introduction to one's blog.

I've been earning money from Drupal for the last 8 years - almost every day of my working life has been about coding Drupal 5, 6, 7 and now 8.

My job as a contract web developer (working exclusively in Drupal) has taken to me to many different companies, large (NBC, BBC) and small (tiny), covering a wide range of subjects from control of broadcast TV to stock markets and healthcare.

I also have various contrib modules on drupal.org.

So I have a pretty wide knowledge of Drupal as well as lots of varied experience.

Which is where this blog comes in. I'll be using it for various hints and tips and bits of knowledge (the same as I do for my Drupal7-ish blog).

I am also in the process writing a series of books about using and developing for Drupal 8. If you want to know when they're available join my mailing list (on the right, just up there). Thanks.

Right well, that's enough of that.