Upgrade Your Drupal Skills

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

See Advanced Courses NAH, I know Enough
May 18 2019
May 18

So I needed the latest git checkout of the source code from external entities Drupal module. Its latest is 8.x-2.x

Using composer require drupal/external_entities is not enough as that will download the latest released version in this case 8.x-2.0-alpha1

Using composer require drupal/external_entities:8.x-2.x is not a valid version for composer as Drupal versions are not semantic versioned.

We should use as version 2.x which is a Drupal 8 compatible branch . SemVer can work with dev versions using 2.x-dev so using composer require drupal/external_entities:2.x-dev we get the latest dev. But still as zip

By altering composer.json setting a preferred-install for the module we finally get the latest git checkout.

"config": {
    "preferred-install": {
        "drupal/external_entities": "source",
        "*": "dist"
    },
    "autoloader-suffix": "Drupal8"
}

After running composer require drupal/external_entities:2.x-dev we get a git checkout but it is a detached head.

cd modules/contrib/external_entities
git status
HEAD detached at 809e275
nothing to commit, working tree clean

The final step is to do git checkout origin and you can create patches.

Refs

Jun 29 2017
Jun 29

Coding style?

Building software is a complex and sometimes tedious process in which you make errors and mistakes. Testing for errors is mostly done by running your website / code through tests either manually or automatically.

Checking for your code style like formatting and documentation flaws you can use a code sniffer. For PHP you can run phpcs using PHP_CodeSniffer.

Drupal core provides core/phpcs.xml.dist to tell phpcs what to test for.

How to run it

Run all tests on current directory.

cd core
../vendor/bin/phpcs -p -s

where -p shows the progress and -s does a sniff.

It result in

............................................................   60 / 7477 (1%)
............................................................  120 / 7477 (2%)
............................................................  420 / 7477 (6%)
...........................S................................  480 / 7477 (6%)
............................................................  540 / 7477 (7%)

............................................................  780 / 7477 (10%)
...............S......S.....................................  840 / 7477 (11%)
............................................................  900 / 7477 (12%)
............................................................  960 / 7477 (13%)

............................................................ 1320 / 7477 (18%)
............................................................ 1380 / 7477 (18%)
.......................................SSSSSSSSSSSSSSSSSS... 1440 / 7477 (19%)
...

Check for Drupal standards

Using the switch --standard=Drupal you can check your code for Drupal related stuff. This uses coder.

cd core
../vendor/bin/phpcs --standard=Drupal modules/views/views.module

FILE: ...sers/clemens/Sites/drupal/d8/www/core/modules/views/views.module
----------------------------------------------------------------------
FOUND 23 ERRORS AND 8 WARNINGS AFFECTING 27 LINES
----------------------------------------------------------------------
  92 | ERROR   | [x] Inline comments must end in full-stops,
     |         |     exclamation marks, colons, question marks, or
     |         |     closing parentheses
  94 | ERROR   | [ ] If the line declaring an array spans longer than
     |         |     80 characters, each element should be broken
     |         |     into its own line
 116 | ERROR   | [ ] If the line declaring an array spans longer than
     |         |     80 characters, each element should be broken
     |         |     into its own line
 117 | ERROR   | [ ] If the line declaring an array spans longer than
     |         |     80 characters, each element should be broken
     |         |     into its own line
 120 | ERROR   | [x] Each index in a multi-line array must be on a
     |         |     new line
 121 | ERROR   | [x] Each index in a multi-line array must be on a
     |         |     new line
 121 | ERROR   | [x] Each index in a multi-line array must be on a
     |         |     new line
 121 | ERROR   | [x] Each index in a multi-line array must be on a
     |         |     new line
 121 | WARNING | [x] A comma should follow the last multiline array
     |         |     item. Found: ]

etc
----------------------------------------------------------------------
PHPCBF CAN FIX THE 11 MARKED SNIFF VIOLATIONS AUTOMATICALLY
----------------------------------------------------------------------

Note the last line telling some stuff can be automatically fixed. Awesome!

Not interested @ all

You can filter on the sniff you are interested in using the --sniff option.

cd core
../vendor/bin/phpcs --standard=Drupal modules/views/views.module --sniffs=Drupal.Commenting.HookComment

FILE: ...sers/clemens/Sites/drupal/d8/www/core/modules/views/views.module
----------------------------------------------------------------------
FOUND 0 ERRORS AND 6 WARNINGS AFFECTING 6 LINES
----------------------------------------------------------------------
 249 | WARNING | Format should be "* Implements hook_foo_BAR_ID_bar()
     |         | for xyz_bar().", "* Implements hook_foo_BAR_ID_bar()
     |         | for xyz-bar.html.twig.", "* Implements
     |         | hook_foo_BAR_ID_bar() for xyz-bar.tpl.php.", or "*
     |         | Implements hook_foo_BAR_ID_bar() for block
     |         | templates."
 274 | WARNING | Format should be "* Implements hook_foo_BAR_ID_bar()
     |         | for xyz_bar().", "* Implements hook_foo_BAR_ID_bar()
     |         | for xyz-bar.html.twig.", "* Implements
     |         | hook_foo_BAR_ID_bar() for xyz-bar.tpl.php.", or "*
     |         | Implements hook_foo_BAR_ID_bar() for block
     |         | templates."
 287 | WARNING | Format should be "* Implements hook_foo_BAR_ID_bar()
     |         | for xyz_bar().", "* Implements hook_foo_BAR_ID_bar()
     |         | for xyz-bar.html.twig.", "* Implements
     |         | hook_foo_BAR_ID_bar() for xyz-bar.tpl.php.", or "*
     |         | Implements hook_foo_BAR_ID_bar() for block
     |         | templates."
 638 | WARNING | Format should be "* Implements hook_foo()."
 650 | WARNING | Format should be "* Implements hook_foo_BAR_ID_bar()
     |         | for xyz_bar().", "* Implements hook_foo_BAR_ID_bar()
     |         | for xyz-bar.html.twig.", "* Implements
     |         | hook_foo_BAR_ID_bar() for xyz-bar.tpl.php.", or "*
     |         | Implements hook_foo_BAR_ID_bar() for block
     |         | templates."
 827 | WARNING | Format should be "* Implements hook_foo_BAR_ID_bar()
     |         | for xyz_bar().", "* Implements hook_foo_BAR_ID_bar()
     |         | for xyz-bar.html.twig.", "* Implements
     |         | hook_foo_BAR_ID_bar() for xyz-bar.tpl.php.", or "*
     |         | Implements hook_foo_BAR_ID_bar() for block
     |         | templates."
----------------------------------------------------------------------

Time: 157ms; Memory: 12.75Mb

More info

May 09 2016
May 09

Submitted by clemens on Mon, 2016/05/09 - 12:58pm

We could use the several drush + shell commands manually but why not script this? This is how I do this on Mac OSX which has it's own quirks.

Place the follow script into the parent dir of your drupal root as sync2dev. This way you can control all you DEV sites in a similar way. ../sync2dev

Content of sync2dev by https://twitter.com/ClemensTolboom

# Show commands executed
set -x

# --simulate does not work
# DRUSH_OPTS="--yes --verbose --simulate"

# Say yes to the dress
DRUSH_OPTS="--yes"

# --no does not work for registry-rebuild or features-status
# DRUSH_OPTS="--no"

# simulate seems gone as an option
# DRUSH_OPTS="--simulate"

# Source alias
[email protected]
# [email protected]

# Target
[email protected]
DRUPAL_ROOT=/Users/clemens/Sites/my-site/dev/www

# Where to find the files
SITE_DIR=default

# What user and group to set
USER=clemens
GROUP=_www

# Make sure sudo command is executable later on
sudo echo "Thanks."

# Sync database
drush $DRUSH_OPTS $DRUSH_ALIAS_DEV sql-drop
drush $DRUSH_OPTS sql-sync $DRUSH_ALIAS_REMOTE $DRUSH_ALIAS_DEV

# Fix before receiving files
sudo chown -R $USER $DRUPAL_ROOT/sites/$SITE_DIR/files
sudo chown -R $USER $DRUPAL_ROOT/sites/$SITE_DIR/private
sudo chgrp -R $GROUP $DRUPAL_ROOT/sites/$SITE_DIR/files

# Get the files
drush $DRUSH_OPTS rsync $DRUSH_ALIAS_REMOTE:%files $DRUSH_ALIAS_DEV:%files

# Fix after receiving
sudo chown -R $USER $DRUPAL_ROOT/sites/$SITE_DIR/files
sudo chown -R $USER $DRUPAL_ROOT/sites/$SITE_DIR/private
sudo chgrp -R $GROUP $DRUPAL_ROOT/sites/$SITE_DIR/files

# Drupal 7 variant of cache-rebuild
drush $DRUSH_OPTS $DRUSH_ALIAS_DEV registry-rebuild

# Login to site
drush $DRUSH_OPTS $DRUSH_ALIAS_DEV user-login

# Make sure drush finds the features drush extension
drush cc drush

# List features with changes
drush $DRUSH_OPTS $DRUSH_ALIAS_DEV features-list --state=db
Feb 17 2015
Feb 17

Submitted by clemens on Tue, 2015/02/17 - 4:29pm

Node closed comment delete

Having a forum you needs quick deletions of improper comments.

In Drupal 7 and Drupal 8 you have to visit admin/content/comments to do so. But then you loose the thread.

You could review and use this patch or add this to your custom module. The first needs review and testing. The later needs a Drupal coder.

/**
 * Implements hook_entity_prepare_view().
 */
function module_custom_entity_prepare_view($entities, $type, $langcode) {
  // Store nodes to use with comments later on.
  static $nodes = array();

  if (user_access('administer comments')) {
    if ($type == 'node') {
      $nodes = $nodes + $entities;
    }
    elseif ($type == 'comment') {
      foreach ($entities as $cid => $comment) {
        if (isset($nodes[$comment->nid])) {
          $node = $nodes[$comment->nid];
          if ($node->comment == COMMENT_NODE_CLOSED) {
            $node->comment = COMMENT_NODE_OPEN;
          }
        }
      }
    }
  }
}
Oct 28 2014
Oct 28

Inspired by @fgm http://blog.riff.org/2014_10_05_drupal_8_tip_of_the_day_check_menu_links_consistency I tried to make it a little bigger and not using a file scan but just 'core'.

# routes.php
$nodes = array();
$links = array();

$routes = db_query('select * from router');
foreach($routes as $route) {
  $route->route = unserialize($route->route);
  $nodes[$route->name] = array(
    'path' => $route->name,
    'type' => 'route',
  );

  $nodes[$route->path] = array(
    'path' => array_pop(explode('/', $route->path)),
  );

  // Join id and path
  $links[$route->path][$route->name] = $route->name;

  // Add all path fragments
  $segments = explode("/", $route->path);

  // Drop of first /
  array_shift($segments);
  $path = '';
  foreach($segments as $index => $segment) {
    if (empty($path)) {
      $links['/'][$path . '/' . $segment] = $route->name;
    }
    else {
      $links[$path][$path . '/' . $segment] = $route->name;
    }
    $path.= '/' . $segment;
    $nodes[$path] = array(
      'path' => $segment,
    );
  }
}
# some magic code follows

With $nodes and __ $links__ it is 'easy' to generate the needed data structures for GraphViz and d3js.

GraphViz tree

$ drush @drupal.d8 php-script routes.php | dot -T svg -o ~/Downloads/drupal-8-menu-tree.svg

which gives

Drupal menu tree with routers.

Thats great but can we do better with d3js?

D3JS flat tree

D3JS radial tree

When clicking on the open aka end points something interesting happens

Conclusion

There is too much data to visualize the menu tree at once. Using d3js with its animations it's possible to hide some parts to get to the good parts.

Having a filter to quickly find and hide others may be the next step.

Oct 09 2014
Oct 09

One of the Drupal 8 initiatives was to make "headless" Drupal core work out of the box. Drupal now has a ReST API.

This allows you to use all the Drupal 8 features to edit your content but present the content not only on a Drupal frontend but also on mobile apps or anything else that can work with JSON data.

ReST

Drupal 8 has two modules that provide a ReST api. The rest module provides the simplest responses and can be used by requesting pages with the Accept: application/json header (AngularJS does this). The output of this api is just the result of the entity serializer in Drupal 8.

This is an example of a JSON response:

{
    "nid": [{
        "value": "1"
    }],
    "uuid": [{
        "value": "c0de5b48-d165-4970-8e05-5fccc692845f"
    }],
    "type": [{
        "target_id": "article"
    }],
    "langcode": [{
        "value": "und"
    }],
    "title": [{
        "value": "Rusticus Vulpes"
    }],
    "uid": [{
        "target_id": "0"
    }],
    "status": [{
        "value": "1"
    }],
    "body": [{
        "value": "This is the body of this node",
        "format": "plain_text",
        "summary": "And this is the summary"
    }],
    "comment": [{
        "status": "2",
        "cid": "5",
        "last_comment_timestamp": "1411546941",
        "last_comment_name": "",
        "last_comment_uid": "1",
        "comment_count": "5"
    }],
    "field_image": [{
        "target_id": "28",
        "display": null,
        "description": null,
        "alt": "Facilisi gemino nisl probo veniam.",
        "title": "",
        "width": "399",
        "height": "308"
    }],
    "field_tags": [{
        "target_id": "4"
    }]
}

HAL

When you enable the hal module and configur it you can request pages with the Accept: application/hal+json header. The HAL responses are the same as the normal JSON responses with an added _links and _embedded key that contains relation information about the requested entity.

This is an example of a hal+json response:

{
    "_links": {
        "self": {
            "href": "http:\/\/d8.dev\/node\/1"
        },
        "type": {
            "href": "http:\/\/d8.dev\/rest\/type\/node\/article"
        },
        "http:\/\/d8.dev\/rest\/relation\/node\/article\/uid": [{
            "href": "http:\/\/d8.dev\/user\/0",
            "lang": "und"
        }],
        "http:\/\/d8.dev\/rest\/relation\/node\/article\/revision_uid": [{
            "href": "http:\/\/d8.dev\/user\/0"
        }],
        "http:\/\/d8.dev\/rest\/relation\/node\/article\/field_image": [{
            "href": "http:\/\/d8.dev\/sites\/default\/files\/field\/image\/generateImage_Af38aP.jpg",
            "lang": "und"
        }],
        "http:\/\/d8.dev\/rest\/relation\/node\/article\/field_tags": [{
            "href": "http:\/\/d8.dev\/taxonomy\/term\/4",
            "lang": "und"
        }]
    },
    "uuid": [{
        "value": "c0de5b48-d165-4970-8e05-5fccc692845f"
    }],
    "type": [{
        "target_id": "article"
    }],
    "langcode": [{
        "value": "und"
    }],
    "title": [{
        "value": "Rusticus Vulpes",
        "lang": "und"
    }],
    "_embedded": {
        "http:\/\/d8.dev\/rest\/relation\/node\/article\/uid": [{
            "_links": {
                "self": {
                    "href": "http:\/\/d8.dev\/user\/0"
                },
                "type": {
                    "href": "http:\/\/d8.dev\/rest\/type\/user\/user"
                }
            },
            "uuid": [{
                "value": "aa69dd40-a5ad-4346-8d07-7c01f3e9e726"
            }],
            "lang": "und"
        }],
        "http:\/\/d8.dev\/rest\/relation\/node\/article\/revision_uid": [{
            "_links": {
                "self": {
                    "href": "http:\/\/d8.dev\/user\/0"
                },
                "type": {
                    "href": "http:\/\/d8.dev\/rest\/type\/user\/user"
                }
            },
            "uuid": [{
                "value": "aa69dd40-a5ad-4346-8d07-7c01f3e9e726"
            }]
        }],
        "http:\/\/d8.dev\/rest\/relation\/node\/article\/field_image": [{
            "_links": {
                "self": {
                    "href": "http:\/\/d8.dev\/sites\/default\/files\/field\/image\/generateImage_Af38aP.jpg"
                },
                "type": {
                    "href": "http:\/\/d8.dev\/rest\/type\/file\/file"
                }
            },
            "uuid": [{
                "value": "84c9914b-d5e1-4235-b911-17e0381948bd"
            }],
            "uri": [{
                "value": "http:\/\/d8.dev\/sites\/default\/files\/field\/image\/generateImage_Af38aP.jpg"
            }],
            "lang": "und"
        }],
        "http:\/\/d8.dev\/rest\/relation\/node\/article\/field_tags": [{
            "_links": {
                "self": {
                    "href": "http:\/\/d8.dev\/taxonomy\/term\/4"
                },
                "type": {
                    "href": "http:\/\/d8.dev\/rest\/type\/taxonomy_term\/tags"
                }
            },
            "uuid": [{
                "value": "069bc39f-a41a-4fc6-a404-4df9e92e7b0b"
            }],
            "lang": "und"
        }]
    },
    "status": [{
        "value": "1",
        "lang": "und"
    }],
    "body": [{
        "value": "This is the body of this node",
        "format": "plain_text",
        "summary": "And this is the summary",
        "lang": "und"
    }],
    "comment": [{
        "status": "2",
        "cid": "5",
        "last_comment_timestamp": "1411546941",
        "last_comment_name": "",
        "last_comment_uid": "1",
        "comment_count": "5",
        "lang": "und"
    }]
}

A HAL response differs from a JSON response in replacing ids for relational data like image, user, tags and more by a entity in the _links key with a type, URI and a documentation link.

How to get to this point

Note: Most good demos need fixes described in rest meta issue

The easiest way to setup Drupal 8 for ReST development is using the drupal-rest-test tool. This tool uses drush to set up a Drupal installation, install the modules, configure the modules and set up the permissions. The project readme contains the information to set up the environment.

The missing pieces

Getting information from the hal or json api is pretty easy and the response is almost self-documenting. The big problems start when trying to add or modify nodes through the api. The documentation for this is non-existant and mostly reverse-engineered from the code. With this patch it's possible to POST new nodes to Drupal using the application/json format. This is implemented in the drupal-rest-test project in the post-rest.php file. The same thing using the application/hal+json format is implemented in the post-hal.php file.

And example for posting a node using application/json:

POST /entity/node
Content-type: application/json

{
    "title": [
        "test"
    ],
    "type": [
        {
            "value": "article"
        }
    ]
}

And a example for posting a node using application/hal+json:

POST /entity/node Content-type: application/hal+json

{
    "title": [
        "test"
    ],
    "type": [
        {
            "value": "article"
        }
    ],
    "_links": {
        "type": {
            "href": "http:\/\/d8.dev\/rest\/type\/node\/article"
        }
    }
}

Creating entities with relations

One important thing in the API is creating entities that have relations to other entities. For example the Comment entity. This contains a relation to the Node entity.

Creating a comment using application/json (thanks @bertramakers):

POST /entity/comment
Content-type: application/json

{
    "entity_type": "node",
    "field_name": "comment",
    "entity_id": [
        {
            "target_id": 1
        }
    ],
    "comment_body": [
        {
            "value": "Example comment message."
        }
    ]
}

And doing the same using application/hal+json:

POST /entity/comment
Content-type: application/hal+json

{
    "entity_type": [
        "node"
    ],
    "field_name": [
        "comment"
    ],
    "comment_body": [
        "test"
    ],
    "_links": {
        "http:\/\/d8.dev\/rest\/relation\/comment\/comment\/entity_id": [
            {
                "href": "http:\/\/d8.dev\/node\/1"
            }
        ],
        "type": {
            "href": "http:\/\/d8.dev\/rest\/type\/comment\/comment"
        }
    },
    "_embedded": {
        "http:\/\/d8.dev\/rest\/relation\/comment\/comment\/entity_id": [
            {
                "uuid": "af3710e7-fec3-4064-b500-b30d838236f5"
            }
        ]
    }
}

And yes: This is the minimum amount of data to create a comment using HAL

Building a frontend using ReST and Drupal

To test the API from an usability perspective we've started the drupal-8-rest-angular project. This projects tries to reimplement the Bartik theme as an AngularJS app. The project uses some custom views in Drupal 8 to get the required information from the API. Currently its build on the application/json format.

The project demonstrates everything that is missing from the API endpoints to recreate the complete Drupal frontend. There is no way to get the basic site information like the site name. Its impossible to get the content for the blocks and There are on average 5 json requests needed to present a single article to the user.

So if you're building a headless Drupal site or if you're doing something else with ReST. See the meta issue and report issues

Oct 01 2014
Oct 01

Submitted by clemens on Wed, 2014/10/01 - 8:30am

dreditor triageFor the mentoring sprint on friday @ https://amsterdam2014.drupal.org we'd like to use the dreditor tool plus one of its new pull request feature. To install this just visit https://dreditor.org/development/build and pick your browser icon from #93 Add support for stock responses (macro/templates). When it states 'Rebase needed' just ping ClemensTolboom @ irc as dreditor is still evolving. Installing on Chrome needs 3 additional steps. Next visit an issue and see the new blue buttons at the comment field. It now contains stock responses to insert.

Editing stock response

When editing the stock response use the clear cache.
Sep 05 2014
Sep 05
Looking for a HTML range field in Drupal 8 core I did not found one. First I tried to alter Number Widget but that was wrong. Just extend a suitable widget. The code is very short. But the display is not as good as one needs. Where is the min and max value? And the current value? The specs @ w3c html5 forms input range has some user agent rendering which does not give min, max or current value hints.

number field.

range field.

with datalist trying to add ticks and labels.

Checkout the difference between your browsers and platforms.

Please help with https://www.drupal.org/node/2332833 to add min,max and current value while editing.

Jul 25 2014
Jul 25
Experimenting with the index file of https://github.com/clemens-tolboom/uml-generator-php lead to a nice animation. Click the image below using Chrome to see Drupal 8 in it's full glory (only if you have the power to consume a hefty animation). graph of drupal dependencies Or just a self portrait of uml-generator-php on http://clemens-tolboom.github.io/uml-generator-php/animations/self/
Jul 23 2014
Jul 23

Submitted by clemens on Wed, 2014/07/23 - 4:42pm

http://www.droidforums.net/galleries/photos/hal-9000.4475/
  1. Checkout the issue queue for HAL and ReST.
  2. Use the quickstart tool: https://github.com/build2be/drupal-rest-test.

Need HAL?

  1. Install HAL Browser on your site to see what we got till now.
  2. cd drupal-root
    git clone https://github.com/mikekelly/hal-browser.git
  3. then visit http://drupal.d8/hal-browser/browser.html
drupal-8planetplanet drupalresthal

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