Author

Jun 19 2018
Jun 19

Only a month has passed since DrupalCamp Transylvania, and already another Drupal Camp has come and gone in Romania. This time it was Drupal HackCamp, organised in the Romanian capital, Bucharest. It was a Drupal Camp with a very specific theme: Security.

 

Throughout the sessions presented at the Camp, one was able to find out what security issues Drupal had experienced in the past, how the Drupal Security team, as well as the Community in general, had dealt with them, what Drupal did to improve the security of the platforms that were developed using the CMS and what can (and should) be done to have a more secure application.

Since I first heard of it, a Camp focused on Drupal security sounded really interesting to me. This is the type of camp every Drupal developer should attend at least once in their career. Actually any web developer for that matter. As we know, security is a very important topic with regards to the web. Even for experienced developers, some things can be very tricky, as an application's security does not only depend on the code. It also depends on how the web server is configured or what kind of third-party libraries your code depends on. Additionally, it also depends on the libraries you are using in development, if they are used to pack or bundle your code, or if they end up touching your code in any other way.

One of the sessions which focused on how Drupal improved its security with each new version, was Peter Wolanin's - 10 Ways Drupal 8 Is More Secure.

10 Ways Drupal is More Secure

In this session, Peter Wolanin first gave a brief introduction to the OWASP Top 10, a list with the top 10 critical security risks that affect a web application. This is not only Drupal related, it applies to any kind of application that is accessible via the web. Next, he pointed out 10 things Drupal 8 implemented that help the developer to avoid those security risks. Among the points he mentioned were, the autoescaping feature implemented in twig (so now everything which gets outputted by twig, is by default, escaped), the automatic CSRF tokens in the route definitions (making it easier for the developer to create links which are valid only for the current user session), the removal of the PHP input filter (which was very dangerous if misused), and the enforcement of trusted host patterns for requests (so that your application will respond only if requested via a host which you actually trust).

As previously mentioned, having a secure app doesn't guarantee that your Drupal is secure. Nowadays, there is a growing interest in having decoupled apps. This means you have a backend which is usually used for content management only (that can be a Drupal site) and a frontend, which is a modern js application, that can be implemented optionally, using a framework like React, Vue.js, and so on. But then you also need to use npm for installing the additional js libraries you need, webpack for creating the javascript bundles for your app, and babel for transpiling your javascript code. So suddenly you start to introduce a ton of other dependencies, which each depend on a lot of other packages. Alexandru Badiu did a presentation called, “JS and Security”, which covered some of those aspects.

JS and security

So, you do the best you can to write secure code, try to evaluate the dependencies of your project, and make sure that they don't introduce critical security issues, but is that enough? There could still be several security issues which you’re unaware of, which will only be discovered while you are using the application. It would be awesome if we're able to do something to proactively protect us against common security risks.

Bastian Widmer (@dasrecht) presented a talk on this subject, entitled “How Open Source will help you to survive the next Drupalgeddon”, where he showed us a few tips that we can use in advance, in order to respond to potential security issues in future. Besides ensuring you do regular updates for all your app’s dependencies, you could also take some measures at the web server level. For example, only allow index.php to be executed, use a web application firewall or make sure that your operating system is configured properly.

How open source will help you to survive the next Drupalgeddon

Of course, there had to be a session about the last Drupalgeddon(s), at a Camp focusing on Security. The event’s keynote was by Jasper Mattsson, who actually discovered Drupalgeddon 2. He shared some tips with us on how to find security breaches. He said that there is no secret 'recipe' for that, but a good starting point, is to look for functions which output data, which can do multiple things, perhaps depending on how they are invoked (in which context or with which parameters) or which can trigger code execution.

Finding Drupalgeddon

There is one very important thing to keep in mind if you discover a security breach: do not post it on the regular Drupal issue queue. Instead, follow the instructions on how to report a security issue when you found one. The implications of reporting a security issue inside the regular Drupal issue queue can be very dangerous, as the attackers will then have plenty of time to create an attack until the issue is fixed.

Being in a city with such a rich history, we could certainly not miss the walking tour that the organisers had prepared for us on the Saturday afternoon. During the tour, we saw Bucharest’s most iconic buildings, which have survived all the great historical periods over the last 200 years - the monarchy, two world wars, communism and now democracy.

Atheneul roman

Old Church

Old Monastery

Drupal HackCamp Bucharest was a really great event, and I hope it takes place next year. It is of great value to all web developers, especially those at the beginning of their careers, as it prepares them for the dangers of the wild world wide web and equips them with the required knowledge to guard against any that may pop up along the way.

May 11 2018
May 11

If you were in the city of Cluj-Napoca between 4 and 6 May 2018 and walked around The Office, you probably saw over a 100 people from all over the world, wearing the same t-shirts, talking about Drupal. That's because DrupalCamp Transylvania was in town.

If you know a bit of Romanian history and have heard about Transylvania, you probably know about Vlad the Impaler. If not, then you've probably heard about Dracula. Either way, they're the same person. You may be asking yourself, "What has Dracula got to do with Drupal?". Well, the answer is in the picture below:

Immortal Drupal

We all want Drupal to be immortal. Because we love developing awesome websites with it.  That said, we must remember one thing, it's not all about work and making money, it's also about having fun using Drupal. That was one of the key points of Robert Douglass' keynote - "My Drupal Mid-Life Crisis".

One of the most interesting sessions was Larry Garfield's - "The container is a lie!". On reading the title, you'll probably want to check that out, since you most probably use containers (not necessarily Docker containers, although Docker is probably the most used these days) in your everyday work. He spoke of how software runs on modern Linux systems, that we should not think of boats, whales or shipping or even Docker when we hear the word container, and why it is actually useful that modern software is built (runs) on these "lies". These "lies" form part of our everyday work, and more importantly, the deployment to different environments makes it so much easier.

Larry Garfield Docker

Another very important topic, not only in the Drupal community but in technology in general, is GDPR (General Data Protection Regulation). Balu Ertl had a great session entitles, Overview of GDPR modules for Drupal, in which he provided an overview of all the modules in Drupal that can help your Drupal site achieve GDPR compliance.

Is Drupal ready for GDPR?

The conclusion was that we have quite a few modules (9) in this category, some of them available on both Drupal 7 and 8. Some of them implement a small part of the regulations (like the consent for using personal data, the possibility to delete or download all the personal data of a user, the possibility to anonymize user information when dumping a production database, etc.) and many of them implement overlapping features.

Drupal GDPR modules list

But there seems to be one module, General Data Protection Regulation, which tries to bring all these modules together under one umbrella so that we can have a unified and clear solution for making a site GDPR compliant.

Another thing that came up during the discussions about this subject, was that this is a really complicated subject for both technical and legal minds, and as such, you'll most probably not be fined immediatly if you're not 100% GDPR compliant on the 26th of May 2018. The most likely scenario is that the authorities will be there to help at first, and only fine you as a last resort. That said, this cannot be confirmed and everything should still be done to be GDPR compliant by the deadline.

Wait, there's more! While attending Lenard Palko's presentation, we saw this:

Auditing PHP Apps

No, we did not watch an episode from Doc McStuffins. This was about Auditing PHP Applications, a session in which Lenard Palko showed us how his team is dealing with auditing PHP applications and what things should we look for when having to do such an audit. He also shared some helpful tools that you should use and how should you structure the report.

As you can see it was a great DrupalCamp. Nice location, great presenters, lots of people and a dedicated sprint room. So, did we have any time for doing other stuff than coding and talking about Drupal? Yes, we did! We had some great parties each evening and a brave few of us even went for a morning run on Saturday.

Morning Run

I'm already looking forward to the next DrupalCamp Transylvania in 2019. See you there!

Aug 02 2017
Aug 02

There are 3 things you'll need to build this: Drupal, React and something that can bind those two together, meaning that it can fetch the data from Drupal and make it available in React. For this third thing, we will use GraphQL and Apollo.

Drupal Setup

As mentioned, the first thing you'll need to do is to install Drupal. Nothing special to mention here, just do a plain Drupal 8 installation. Additionally, you should also download the GraphQL module or clone it from https://github.com/fubhy/graphql-drupal. For now, we will just install the GraphQL Content and the GraphQL Views modules (they will automatically install the GraphQL and GraphQL Core dependencies).

We want to list some articles, so just go ahead and create a few (or use the Devel generate module if you want). Be sure to also create a simple view that lists articles (don't worry about displays right now, just list the title of them).

So now you have the Article content type (which was already there) and the Articles view which you just created, and you want to expose them via GraphQL. For this, you need a schema. You don't have to write it yourself, you just have to expose your configuration.

To expose content, just go to /admin/config/graphql/content and there you should see a list with all your entity types and bundles that you can expose. We want to expose the Article content type, so just click on Content and then Article.

Graphql Expose Content

Then you have the possibility to choose which fields to expose. And you do that by selecting a View mode. So before actually exposing the Article bundle, we need to configure a view mode. I recommend creating a new one, let's call it graphql, so that you can easily see it is used to expose data to GraphQL.

Go to admin/structure/display-modes/view and create the new view mode. Now you can go back to admin/config/graphql/content, click again on Content and Article and you should be able to select GraphQL from the view modes list. Don't forget to click on the Save configuration button.

Next you can go to the Manage display tab of the Article content type (/admin/structure/types/manage/article/display), and configure the GraphQL display.

Let's say we want to expose the body field(the title is automatically exposed).

Graphql View Mode
 

The second thing we want to expose is the Articles view. To do that, just go to edit your view and add a GraphQL display. Let's change the machine name to graphql_articles. Rebuild the cache and that's it. You just exposed your view to the GrapqQL schema.

Now, how can you be sure that what you did has really changed the schema. There is a very easy way to check this. There is a tool called GraphiQL which you can use to run GraphQL queries and also check the schema. For this, just install the GraphQL Explorer module, and then navigate to /graphql/explorer on your site. On the top right you should have a link called Docs which will expand the Documentation Explorer on click.

In the search box, just input Article and you should see two entries: NodeArticle (this is the article content type you exposed) and articlesGraphqlArticlesView which is your view. If you see those, then you properly configured the schema.


Graphql Explorer

As mentioned, with GraphiQL you can run GraphQL queries. It's time to see if we get any data.

So for the article view, just use this query:

{
  articlesGraphqlArticlesView {
    ...on NodeArticle {
      entityId
      title
      body
    }
  }
}

Put this in the left panel, click the run button and you should see the results in the right panel. The above query can be translated like this: give me the results from the articlesGraphqlArticlesView view, and when the type of the object is NodeArticle, give me the title, the body and the entityId fields.

With the query above we just tested both things we exposed: the Articles view and NodeArticle content type.


React

The second thing to do is to prepare your frontend. In this example, we'll use React. Probably the easiest is to use create-react-app. So just go ahead and create a new app and make sure you can start it and have the home screen displaying properly.

Let's build now an ArticlesView component which can display a list of ArticleTeaser components.

Here is the ArticlesView component:

import React from 'react';
import ArticleTeaser from '../ArticleTeaser';

const ArticlesView = ({articles}) => (
  <ul>
    {articles.map(article => <li key={article.id}><ArticleTeaser article={article} /></li>)}
  </ul>
);

export default ArticlesView;


And here is the ArticleTeaser component:
 

import React from 'react';

const ArticleTeaser = ({ article }) => (
  <div>
    <h3>{article.title}</h3>
    <div>{article.body}</div>
  </div>
);

export default ArticleTeaser;

Finally, the App.js can look something like this for now (with a dummy list of articles):

import React, { Component } from 'react';
import ArticlesView from './ArticlesView';

const articles = [
  {
    id: 1,
    title: 'First article',
    body: 'This is the first article',
  },
  {
    id: 2,
    title: 'Second article',
    body: 'This is the second article',
  }
]

const App = () => (
  <div className="App">
    <ArticlesView articles={articles} />
  </div>
);

export default App;

If you run this you should see a list of two articles with their title and body. This is pretty much the pure frontend you have to do. The rest is just supplying the real data to the frontend. This needs some additional packages to be installed. So here we go!
 

GraphQL and React Apollo

To connect to our GraphQL server from React, we will use the React Apollo library. To install it just run:

yarn add react-apollo

The first thing we will do is to update our ArticlesView component. We want to get the list of the articles injected into the component. So we will use the graphql Higher Order Component provided by the react-apollo library and run a basic query to get the results. 

Here is the updated code for the ArticlesView component:

import React from 'react';
import { gql, graphql } from 'react-apollo';
import ArticleTeaser from '../ArticleTeaser';

const query = gql`
  query articlesQuery {
     articlesGraphqlArticlesView {
      ...on NodeArticle {
        id:entityId
        title
        body
      }
    }
  }
`;

const withQuery = graphql(query, {
  props: ({ data: { loading, articlesGraphqlArticlesView } }) => ({
    loading,
    articles: articlesGraphqlArticlesView
  }),
});

const ArticlesView = ({ loading, articles }) => {
  if (loading) {
    return null;
  }
  return (
    <ul>
      {articles.map(article => <li key={article.id}><ArticleTeaser article={article} /></li>)}
    </ul>
  )
};

export default withQuery(ArticlesView);

The first thing to do is to build the GraphQL query that we'll use to fetch our data. We can do this using the gql function. Then we wrap our initial ArticlesView component with the Higher Order Component returned by grapqhl. The GraphQL HOC will get our query as a parameter, as well as a config object where we can specify among other things the props that our ArticlesView component will get.

In our case, the ArticlesView component will receive a loading flag which can be used to check if the data is still loading from the server and the articles which are basically the articlesGraphqlArticlesView result from the GraphQL request.

We also need to update a bit the App component, because we need to wrap everything into an ApolloProvider component.

Here is the code:

import React, { Component } from 'react';
import ArticlesView from './ArticlesView';
import { ApolloClient, createNetworkInterface } from 'apollo-client';
import { ApolloProvider } from 'react-apollo';

const client = new ApolloClient({
  networkInterface: createNetworkInterface({
    uri: 'http://url_to_drupal_site/graphql'
  }),
});

const App = () => (
  <ApolloProvider client={client}>
    <ArticlesView />
  </ApolloProvider>
);

export default App;

As you can see, the App component wraps the ArticlesView component inside ApolloProvider (which is initialized with an ApolloClient) and this means that any component bellow ApolloProvider can use the HOC returned by graphql to make requests to the GraphQL server using the ApolloClient which we instantiate in the client parameter.

This is a very important thing to keep in mind. If you move the ArticlesView component outside of the ApolloProvider, your app will not work anymore.

At this point, you can try to run your app, but you may receive a few js errors.

The first may be this one:

Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

This means that your backend server does not allow sending content to a different domain than its own. So we'd need to enable the cors.config in your services.yml file (in sites/default). More about that here: https://www.drupal.org/node/2715637

Here's a possible configuration:

cors.config:
    enabled: true
    # Specify allowed headers, like 'x-allowed-header'.
    allowedHeaders: ['x-csrf-token','authorization','content-type','accept','origin','x-requested-with', 'access-control-allow-origin']
    # Specify allowed request methods, specify ['*'] to allow all possible ones.
    allowedMethods: ['*']
    # Configure requests allowed from specific origins.
    allowedOrigins: ['*']
    # Sets the Access-Control-Expose-Headers header.
    exposedHeaders: false
    # Sets the Access-Control-Max-Age header.
    maxAge: false
    # Sets the Access-Control-Allow-Credentials header.
    supportsCredentials: false

Rebuild the drupal cache and this error should dissapear.

A second error you could get is a 403 for the request to /graphql. The problem is that the anonymous user does not have access to run GraphQL queries. Just go to the administer permissions page and give the Execute arbitrary GraphQL requests permission to the anonymous role. Reload the page and you should see a list of articles (which of course you have to create first in Drupal).

This is the most simple listing app that you can do with Drupal, GraphQL, React and Apollo. However, there are many other features you may need. In the next section, we'll discuss pagination and filtering (including contextual arguments).

Filtering

For this example, we need to add a new field on our Article content type. Let's say we add a Year field on our articles, which is of type List (integer) and can have for now 3 possible values: 2017, 2016 and 2015.

It may look something like this:

Year field.

Then add the Year field as an exposed filter.

Year filter

And you can also expose the year field in the GraphQL display mode of the Article content type so that you can also fetch it for displaying in the frontend.

And now all you have to do in the frontend is to adjust the query and use the filter, like this:

const query = gql`
  query articlesQuery {
     articlesGraphqlArticlesView(filter: {field_year_value: "2015"}) {
      ...on NodeArticle {
        id:entityId
        title
        body
      }
    }
  }
`;

One important thing to keep in mind for now: if you have a filter on a view, you must provide a value for it in the query, otherwise the empty string will be used and most probably you will not get any results. And in this case, if you just want to print all the articles, use 'All' for the field_year_value filter.

Now, of course, having that year value hardcoded in the query is good enough for a demo, but in real apps you will probably want to have that variable. For this, we have to update our query to contain variables and our config object which is used by the graphql HOC to inject that variable into our query.

First, let's see the new query:

const query = gql`
  query articlesQuery($year: String!) {
     articlesGraphqlArticlesView(filter: {field_year_value: $year}) {
      ...on NodeArticle {
        id:entityId
        title
        body
        fieldYear
      }
    }
  }
`;

And second, let's see the new GraphQL call:

const withQuery = graphql(query, {
  options: () => ({
    variables: {
      year: 2016,
    },
  }),
  props: ({ data: { loading, articlesGraphqlArticlesView } }) => ({
    loading,
    articles: articlesGraphqlArticlesView
  }),
});

What we did so far was to just move the hard-coded value from the query into the config object. It's a step forward, but it is still kind of hard-coded. Ideally, we'd have the year specified as a prop on the ArticlesView component.

And in fact we can do that, because the options function can get the component props as a parameter, like this:

options: (props) => ({
  variables: {
    year: props.year,
  },
})


Which means you will use the ArticlesView component in App.js like this:

<ArticlesView year={2016} />


If you try now the app and replace the year with different values you should get different results.

Contextual filters are pretty much the same as filters. So, for example, if we want to add the author of the articles as a contextual filter to the view, to query that in fronted just add the contextual_filterI argument to the articlesGraphqlArticlesView field, like this:

articlesGraphqlArticlesView(filter: {field_year_value: $year}, contextual_filter: {uid:"2"}) {
  ...
}

And just like for the year filter, you can use a variable for the uid filter.


Pagination

To use pagination, first, you have to update the view itself. It is not important how many items per page you set, it is just important that the view has a page. The number of items per page will be specified in the query.

If you do that the articlesGraphqlArticlesView field will get two additional arguments: page and pageSize.

You will have something like this:

articlesGraphqlArticlesView(page: 0, pageSize: 2, filter: {field_year_value: $year}, contextual_filter: {uid:"2"}) {
  ...
}

And if you run now your app you will actually see that there are no results returned. The reason is that the articlesGraphqlArticlesView will now return a different structure. It will be an object with two attributes: count that represents the total number of results and results which are the results of the current page.

The new query therefore is (for simplicity, the filters and contextual filters are removed):

const query = gql`
  query articlesQuery {
     articlesGraphqlArticlesView(page: 0, pageSize: 2) {
      count
      results {
        ...on NodeArticle {
          id:entityId
          title
          body
          fieldYear
        }
      }
    }
  }
`;

Of course, now we have to update the props of the graphql config object.

props: ({ data: { loading, articlesGraphqlArticlesView } }) => ({
  loading,
  articles: articlesGraphqlArticlesView && articlesGraphqlArticlesView.results,
}),

If you reload the page, you should see now 2 articles. The last thing to do is to have a link or a button that when clicked it will load more entries.

Here is the LoadMore component:

import React from 'react';

const LoadMore = ({ loadMoreHandler }) => (
  <a onClick={(e) => loadMoreHandler(); e.preventDefault()} href="https://www.amazeelabs.com/en">Load more</a>
);

export default LoadMore;

And add the LoadMore component in the ArticlesView component:

<div>
  <ul>
    {articles.map(article => <li key={article.id}><ArticleTeaser article={article} /></li>)}
  </ul>
  <LoadMore />
</div>

Now we have to supply a loadMoreHandler to the LoadMore component. Luckily, when using the config object of the grapqhl we have access to a function called fetchMore() which we can use to rerun the query and fetch more (or other) results. Using that function, we will add a new prop which will be injected into our ArticlesView component, which will be the loadMoreHandler.

Here is the updated query (which now contains the page and pageSize as variables) and the updated graphql call:

const query = gql`
  query articlesQuery($page: Int!, $pageSize: Int!, $year: String!) {
     articlesGraphqlArticlesView(page: $page, pageSize: $pageSize, filter: {field_year_value: $year}) {
      count
      results {
        ...on NodeArticle {
          id:entityId
          title
          body
          fieldYear
        }
      }
    }
  }
`;

const withQuery = graphql(query, {
  options: (props) => ({
    variables: {
      year: props.year,
      page: 0,
      pageSize: 2,
    },
  }),
  props: ({ data: { loading, articlesGraphqlArticlesView, fetchMore } }) => ({
    loading,
    articles: articlesGraphqlArticlesView && articlesGraphqlArticlesView.results,
    total: articlesGraphqlArticlesView && articlesGraphqlArticlesView.count,
    loadMoreEntries() {
      return fetchMore({
        variables: {
          // The page number will start with 0, so the next page is basically
          // the number of current results divided by the page size.
          page:
            articlesGraphqlArticlesView.results &&
            Math.ceil(
              articlesGraphqlArticlesView.results.length / 2,
            ),
        },
        updateQuery: (previousResult, { fetchMoreResult }) => {
          // If there are no new results, then just return the previous result.
          if (!fetchMoreResult.articlesGraphqlArticlesView.results) {
            return previousResult;
          }
          return {
            articlesGraphqlArticlesView: {
              ...previousResult.articlesGraphqlArticlesView,
              count: fetchMoreResult.articlesGraphqlArticlesView.count,
              results: [
                ...previousResult.articlesGraphqlArticlesView.results,
                ...fetchMoreResult.articlesGraphqlArticlesView.results,
              ],
            },
          };
        },
      });
    },
  }),
});

So fetchMore will rerun the query. The config object it gets as a parameter has two main properties: the variables which will be used to merge new variables into the existing ones (in our case we just want to get the next page) and updateQuery, which is actually a function that can access the previous result as well as the new result and has to return the new articlesGraphqlArticlesView field.  

As seen above, we use that to merge the new results into the existing ones. And it's now time to use the new props that we inject into our ArticlesView component.

Here is the updated component:

const ArticlesView = ({ loading, articles, total, loadMoreEntries }) => {
  if (loading) {
    return null;
  }
  return (
    <div>
      <ul>
        {articles.map(article => <li key={article.id}><ArticleTeaser article={article} /></li>)}
      </ul>
      { total > articles.length ? <LoadMore loadMoreHandler={loadMoreEntries}/> : null}
    </div>
  )
};

export default withQuery(ArticlesView);

And that's all I wrote, folks! You now have a view built in Drupal that you expose in GraphQL and use Apollo and React to display it in frontend, with pagination and filters.

Apr 13 2017
Apr 13

Having menus rendered on a site in Drupal 8 is pretty simple. Most of the time this can be accomplished with site building, in a few clicks. And if you need some more advanced features on top of what the Drupal 8 core has, you can also have a look at modules like Menu Block, or have a look at this (a bit outdated) contributed modules for menus page.

However, you may have some special requirements, for example, to display some small portion of a menu inside the template of a node. There is no simple 'site building' solution for that. You most probably need to code a bit.

Let's say you have these requirements: a node which can be part of the main menu, and which could also have menu items bellow it, should display the first level of those menu items somewhere inside its template (so just as you would display a regular field).

The image above shows a very simple visual representation of what we'd like to achieve.

Now let's break this down into smaller and independent pieces. You can also skip to the entire code. Basically, you have to:

  • Load and render a menu (or parts of a menu).
  • Have a custom field available on the Manage display page for a content type, so that you can place it on the node page.

Let's actually start with the second part, as it is simpler.

Custom (extra) fields

There is the hook_entity_extra_fields_info hook which can be used to expose custom or extra fields on the entities. We'll use this hook to provide a custom field on a node.

The visible flag represents the default visibility setting of the field. Usually, you want this to be false, so you decide when the element is displayed and not when it is NOT displayed.

Now clear the cache and go to Structure >> Content types >> YourContentType, click on the Manage display tab and you should see your new field available there.

The next thing is to implement the hook_ENTITY_TYPE_view and particularly, in this case, hook_node_view. Here it is:

Save it, clear the cache, make sure that the field is visible in the display setting for that content type and visit a node of that type. You should see the custom text displayed.

So, we're done with the second part. Let's go and actually load the menu for this node and display the first level of menu items.

Render the menu

For this, we'll have to load a menu using a menu tree service and a set of parameters which define the root to start with, how many levels to load, and so on, then to apply a set of manipulators on the loaded tree that would check the access, do the sorting, etc, and finally to build the menu tree.

The root menu item

One of the tricky things is to get the correct root menu item. We know that the node can be part of a menu. It would be great if we could load a menu link based on the route name and some route parameters. For that, you can use the plugin.manager.menu.link service.

Menu tree parameters

When loading a menu, we have to provide some parameters. For example, what the root of the menu you want to load (if you want to load a subtree, which is our case) is, or how many levels to load. These parameters are specified using a \Drupal\Core\Menu\MenuTreeParameters object.

More details about the MenuTreeParameters can be found here.

Load the menu

To load the menu, we will use the menu.link_tree service.

Menu tree manipulators

After we have the menu loaded, it is time to actually apply some manipulators on its menu items. These manipulators will deal with access checking, sorting according to the weights, or whatever other things you want to do with the menu tree. The code looks like that:

So, in this case, we want to check for the access and sort the items. There's a default tree manipulator service in Core which is Drupal\Core\Menu\DefaultMenuLinkTreeManipulators. That already provides a few basic manipulators you can apply to a menu tree, which is enough in our case. But of course, you can add any other callable to that array and use the implementation from Core as a guideline.

Build the menu

Finally, we have to build the menu. This is as simple as:

This would replace the

$build['my_custom_menu'] = [
  '#markup' => 'Some custom menu',
]

that you used as a placeholder in the custom_node_view() hook.

And that's it. The first level of navigation, having the current node as root, will be displayed inside the template of your node.

Here is the entire code:

Jun 30 2015
Jun 30

Solr is great! When you have a site even with not so much content and you want to have a full text search, then using Solr as a search engine will improve a lot the speed of the search itself and the accuracy of the results. But, as most of the times happen, all the good things also come with a drawback too. In this case, we talk about a new system which our web application will communicate to. This means that, even if the system is pretty good by default, you have to be able in some cases to understand more deeply how the system works.This means that, besides being able to configure the system, you have to know how you can debug it. We'll see in the following how we can debug the Solr queries which our applications use for searching, but first let’s think of a concrete example when we need to debug a query.

An example use case

Let’s suppose we have 2 items which both contain in the title a specific word (let’s say ‘building’). And we have a list where we show search results ordered by their score first, and when they have equal scores by the creation date, desceding. At a first sight, you would say that, because both of them have the word in the title, they have the same score, so you should see the newest item first. Well, it could be that this is not true, and even if they have the word in the title, the scores are not the same.

Preliminaries

Let’s suppose we have a system which uses Solr as a search server. In order to be able to debug a query, we first have to be able to run it directly on Solr. The easiest is when Solr is accessible via http from your browser. If not, the Solr must be reached from the same server where your application sits, so you call it from there. I will not insist on this thing, if you managed to get the Solr running for you application you should be able to call it.

Getting your results

The next thing you do is to try to make a query with the exact same parameters as your application is doing. To have a concrete example, we will consider here that we have a Drupal site which uses the Search API module with the Apache Solr as the search server. One of the possibilities to get the exact query which is made is to check the SearchApiSolrConnection::makeHttpRequest() method which makes a call to drupal_http_request() using an URL. You could also use the Solr logs to check the query if it is easier. Let's say we search for the word “building”. An example query should look like this:

http://localhost:8983/solr/select?fl=item_id%2Cscore&qf=tm_body%24value%...

If you take that one and run it in the browser, you should see a JSON output with the results, something like:

solr json output

To make it look nicer, you can just remove the “wt=json” (and optionally “json.nl=map”) from your URL, so it becomes something like:

http://localhost:8983/solr/select?fl=item_id%2Cscore&qf=tm_body%24value^5.0&qf=tm_title^13.0&fq=index_id%3A"articles"&fq=hash%3Ao47rod&start=0&rows=10&sort=score desc%2C ds_created desc&q="building"

which should result in a much nicer, xml output:

solr xml output

List some additional fields

So now we have the results from Solr, but all they are containing are the internal item id and the score. Let's add some fields which will help us to see exactly what texts do the items contain. The fields you are probably more interested in are the ones which are in the “qf” variable, in your URL. In this case we have:

qf=tm_body%24value^5.0&qf=tm_title^13.0

which means we are probably interested in the “tm_body%24value” and the “ tm_title” fields. To make them appear in the results, we add them to the “fl” variable, so the URL becomes something like:

http://localhost:8983/solr/select?fl=item_id%2Cscore%2Ctm_body%24value%2...^5.0&qf=tm_title^13.0&fq=index_id%3A%22articles%22&fq=hash%3Ao47rod&start=0&rows=10&sort=score%20desc%2C%20ds_created%20desc&q=%22building%22

And the result should look something like:

solr full xml output

Debug the query

Now everything is ready for the final step in getting the debug information: adding the debug flag. It is very easy to do that, all you have to do is to add the “debugQuery=true” to your URL, which means it will look like this:

http://localhost:8983/solr/select?fl=item_id%2Cscore%2Ctm_body%24value%2...^5.0&qf=tm_title^13.0&fq=index_id%3A%22articles%22&fq=hash%3Ao47rod&start=0&rows=10&sort=score%20desc%2C%20ds_created%20desc&q=%22building%22&debugQuery=true

You should see now more debug information, like how the query is parsed, how much time does it take to run, and probably the most important one, how the score of each result is computed. If your browser does not display the formula in an easy-readable way, you can copy and paste it into a text editor, it should look something like:

solr debug output

As you can see, computing the score of an item is done using a pretty complex formula, with many variables as inputs. A few more details about these variables you can find here: Solr Search Relevancy

Further reading and useful links

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