Dec 18 2014
Dec 18

As the Drupal market continues to rock and roll, more and more clients need "Drupal Developers". But what exactly is a Drupal Developer? A Drupal Developer is someone who knows Drupal right? Right?!

There always has been some confusion around job titles and skills in the Drupal world. This is especially true with some recruiters and even managers and clients that are hiring. In effect, there are three main areas of expertise in the Drupal world: site building, backend/module development and theming. The skills required for each are quite different.

Drupal Site Builder

A Drupal site builder is someone who builds Drupal sites with point and click in the admin UI without writing much or any custom code. I say much, because they might implement the odd hook here and there. But most of the functionality of the site/application comes from configuring Drupal core and contributed modules. Site Builders will have experience with a wide range of contributed modules and will understand how they work together to solve a particular problem. They will understand the limitations of modules and should be able to provide a concise argument of the pros and cons of various solutions. Site Builders will build the content types, taxonomy, navigation, image presets, rules etc. One of the magnificent things about Drupal is that it does not exclude non-developers. The Drupal community and the platform provides a very powerful tool set for people to build innovative and complex sites without the requirement to be a programmer. And mastering this is a very valuable skill in itself.

Drupal Themer / Frontend developer

A Drupal Themer is the specialist front end developer. They are experts in HTML, CSS and Javascript. They are also experts in the Drupal theme layer. They should be able to take a design and turn it into the working theme. Ideally they will be well versed in implementing responsive design.

Drupal Module Developer / Backend developer

A Drupal developer is someone who writes a lot of PHP and other server side languages. They write custom modules, automated tests, consume web services, automate deployment etc. They may also be know as “backend Drupal developers”. They may also get involved in some of the more advanced side of the theme layer as well. Often they will set up automated deployment.

A note on contributing and collaboration

Drupal is inherently a collaborative project. Site Builders, Module Developers and Themers will often contribute their work back to the community and collaborate with others. It is common for module developers to share and collaborate on contributed modules, themers on contributed themes and site builders on site building recipes and other forms of documentation.

The Three Disciplines

What is a drupal developer?

Drupal Generalist /Jack of All trades

It is very common to do all three. You may be more advanced in one area or another, but still act in a general capacity.

In most of the projects I have worked on, there has not been a dedicated site builder. Both backend developers and fronted developers will do elements of site building.

A Drupal developer who is a jack of all trades

How a business hire and use Drupal people varies. In one extreme, a business may hire specialists. To deliver a particular piece of functionality or feature, the work may have to go through each of the specialities before it is done. One of the other extremes, is a business may assume that a Drupal person should do all three specialities. In this case, it is common for the team to be a team of “Drupal Developers” and the role encompasses site building, backend development and theming. Both approaches have their pros and cons, but that is for another day!

Other roles

Just like any other web development setup, there is a range of other roles included in the process of building and support Drupal applications. This includes:

Sysadmin / Devops - run the live stack, will often deploy Drupal sites to the live environment, deal with performance issues, setup a CDN, Varnish, Memcache etc.

QA - test all changes to ensure quality and that they meet the requirements. Setup automated tests.

Project Manager / Scrum Master - run the scrum team, remove impediments to progress, ensure delivery of the project on time and budget.

Product owner - comes up with the requirements. Works closely with the project manager to prioritise the backlog. Normally has final sign off of all changes.

Design / UX - comes up with the design and user experience. They might build prototypes that can then be converted into a Drupal theme.

Who is not a Drupal developer

This advice should be given to any recruiter or hirer in the Drupal space. A PHP developer is not necessarily a Drupal developer. It does not matter how good the PHP developer is at PHP, unless they have decent Drupal experience and understand its API’s and, dare I say it, “the Drupal way”, they will make many mistakes. You will then need to hire an experienced Drupal developer to clean up the mess left behind. Of course, I am being a bit simplistic here. In current times where demand far out strips supply, it as not as simple as that. You might be forced to hire a PHP developer who doesn’t have much (or any) Drupal experience. That is fine, but the minimum requirement should be that the developer wants to learn the Drupal standards, understand the Drupal culture and try and do it the Drupal way. You don’t want someone who treats it as just another framework that can be bent and broken to the developer's way of thinking. That is a recipe for creating a maintenance nightmare that will cost the business in the long run.

Where do you go from here?

When I first launched the early edition of my book, Master Drupal Module Development, I asked people on my mailing list whether they were Site Builders, Backend Developers or Themers. The split was mostly between Site Builders and Backend Developers but most of the developers were people who developed in tech outside of Drupal and were looking to find the best way to learn Drupal development.

Developer from another technology

If you are a developer from another technology, there are a couple of routes available. One is to build a couple of sites using pure site building. And then, once you are happy with the basics of Drupal, you could dive into module development. Being an experienced developer will almost certainly give you a leg up, because you obviously already know how to do development. Now you need to learn the Drupal APIs and the Drupal way.

Site Builder

If you are a site builder, you might want to move into module/backend development. Because you already know your way around Drupal, you also have a leg up. However, you still need to learn the Drupal APIs and the Drupal way of developing. In addition, if you don't have any or much programming experience, you will need to learn some of that to. You don't need to be a programming genius to get started with Drupal development. You can get started with just the basics of PHP in addition to something like my book.

Themer

If you are a themer, you might not want to do anything else but get better at theming and frontend development. After all, there is a ton to master there, especially when you include CSS and Javascript. But you might want to learn some module development. It will certainly help you broaden your horizons and achieve more.

Module Developer

If you are a Module/Backend Developer, then where do you go? You can always learn more about theming. But might also consider learning more about being a solutions architect. If you are happy being a backend developer, then getting more experience in other PHP frameworks (such as Laravel or Symfony2) is very beneficial to your career prospects and capabilities as a developer.

Where do you fit in?

Do you consider yourself a Site Builder, Themer, Module Developer or something else? Are you looking to move into another area? If so, what are your main obstacles? I'd love to hear from you in the comments below.

Dec 18 2014
Dec 18

Got some Drupal 7 modules that use the Form API lying around? Want to learn how to port them to Drupal 8? The process could just be the crash course you've been looking for to learn Drupal 8 object-oriented module development and Drupal 8's Form API.

Back in the day, Lullabot trainers produced a module to demonstrate how to use Drupal's Form API. The module was called Form Fun and today, I'm going to tell you a little bit about the "fun" I had porting the Drupal 7 version of this module to Drupal 8.

In this blog post, you'll learn:

  • How to use Drupal Module Upgrader to upgrade a D7 module
  • How hook_menu was replaced with routes and controllers
  • How to find information about API changes in Drupal 8

To get a jumpstart on porting this Drupal 7 module to Drupal 8, I decided to give the Drupal Module Upgrader module a try. I saw a demonstration of it by Angie Byron at the Pacific Northwest Drupal Summit in October and I have been determined to try it out ever since.

Getting Ready to Port

To start, I created a Drupal 8 site on my local machine. At the time, this was Drupal 8, Beta 3. For information about how to install Drupal 8, read this Installation Guide.

I also have Drush 7 installed. You will need this, too, if you want to use the Drupal Module Upgrader. In Terminal, or your command-line interface of choice, type drush --version to see which version of Drush you have installed. If it returns 7.0-dev, you can move on. (Likely any 7.x version will work.) But if you find you need to upgrade Drush, check out my blog post on Upgrading Drush to Work with Drupal 8 for some tips.

Both Drush 7 and Drupal Module Upgrader require Composer. If you are brand new to Composer and would like to learn about what the heck it is, check out this free video on Drupalize.Me from our partners at KnpUniversity: The Wonderful World of Composer. If you don't remember if you've installed Composer already, if you're on a Mac, fire up Terminal and type which composer. If a path is returned, then you're all set. If you need to get Composer, see this doc page.

Drupal Module Upgrader

I recommend installing the latest dev version of Drupal Module Upgrader (DMU). I ran into some problems just installing the stable version which were fixed in the latest dev. So, save yourself some troubleshooting and go ahead and install the latest dev using drush by running this command in your command-line interface (CLI): drush dl drupalmoduleupgrader --select and select [1]. For this tutorial, I downloaded the 8.x-1.x-dev | 2014-Nov-27 | Dev version.)

Follow the instructions in Drupal Module Upgrader's README.txt file. If you run into errors with any of the dependencies that Composer is updating, like Pharborist, try running composer update in the /modules/drupalmoduleupgrader directory. For other troubleshooting help, search the Drupal Module Upgrader Issue Queue.

Now we're almost ready to port this thing, but first we need to copy the D7 module into the modules/ directory.

Back in your CLI, with the DMU module installed, we now have some new Drush commands. Run drush help --filter=drupalmoduleupgrader to see the shiny new commands provided by DMU module.

Analyzing the API Changes

Now run drush dmu-analyze. This command creates an HTML file in your module's directory with a breakdown of every API change that affects your module with links to the change records. (Note: I discovered that this page renders much better in Chrome than in Firefox, on a Mac anyway, so take that for what it's worth.) Right away, I can see that form_set_error() is now a method of FormStateInterface. but there are many more changes listed, with links to the change records containing more information about the API change.

form_set_error() is now a method of FormStateInterface.

Running the Upgrade

After perusing the changes that will need to be made, we're about ready to run the upgrade process. But first, make sure you've got a backup of your module before proceeding.

My module's machine name is form_fun. To have DMU attempt to upgrade it to D7, I run drush dmu-upgrade form_fun (replace "form_fun" with the machine name of your module, if you're playing along).

In the case of my module, it was a bit anti-climatic. But, navigating to modules/form_fun, I can see a bunch of new files! I'm excited! (Or maybe I should be afraid!?)

More Than Just Form API Changes

Even though my module was all about demonstrating Drupal 7's Form API, it used other Drupal 7 APIs and function calls to create menu links, URLs, and page content. This means that in this process of porting this D7 module to D8 using Drupal Module Upgrader, I'm also learning about how Drupal 8 replaced hook_menu and other functions. I'm also getting a crash course on object-oriented PHP and using controllers, classes, and methods instead of as a bunch of procedural functions inside .inc files.

Hook_Menu's New Split Personality

hook_menu has been removed from Drupal 8

hook_menu has been removed from Drupal and in its place, routes and controllers. A route defines what happens at a certain URL and a controller determines what content or functionality should be found at a certain URL.

So, for purposes of comparison, here's a snippet of the D7 version of Form Fun's hook_menu implementation that defines pages, their paths, access, files, functions or "page callbacks", related pages, and in what order those sub-pages should be listed in a menu:

D7 example usage of hook_menu

/**
 * A super-simple menu hook that tells Drupal about our Fun Module's page.
 */
function form_fun_menu() {

  $items['form_fun'] = array(
    'title' => 'Fun with FormAPI',
    'page callback' => 'form_fun_page',
    'access arguments' => array('access content'),
  );

  $items['form_fun/cake'] = array(
    'title' => 'Death, or cake?',
    'page callback' => 'form_fun_cake_page',
    'access arguments' => array('access content'),
    'file' => 'form_fun.cake.inc',
    'weight' => 1,
  );

  $items['form_fun/existential'] = array(
    'title' => 'Existential questions',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('form_fun_existential'),
    'access arguments' => array('access content'),
    'file' => 'form_fun.existential.inc',
    'weight' => 2,
  );

...

  /**
   * These menu callbacks should be ignored! They're secret. Suuuuuuper secret.
   */

  $items['form_fun/death_image'] = array(
    'page callback' => 'form_fun_death_image',
    'access callback' => TRUE,
    'file' => 'form_fun.cake.inc',
    'type' => MENU_CALLBACK,
  );

...

  return $items;
}

In the Drupal 8 version of Form Fun, created by DMU, the functionality in the D7 Form Fun hook_menu has now been placed in the following three files:

  • modules/form_fun/form_fun.routing.yml
  • modules/form_fun/form_fun.links.menu.yml
  • modules/form_fun/src/Controller/DefaultController.php

So now if you wanted to tell Drupal to do something at a certain URL, like build a page, display a form, or create some menu links programmatically, you will need a YAML file (perhaps more than one) and potentially a Controller php file. In the case of form_fun, DMU created src/DefaultController.php to output content for one of the pages in the module, created form_fun.routing.yml to tell Drupal about the names of the "routes" declared in this module, and form_fun.links.menu.yml to tell Drupal about how a menu of links should be built for this module.

Defining Routes with YAML

The form_fun.routing.yml file is a YAML file that describes route names, like form_fun.page. It describes any necessary related information like the path, title, what type of page it is, and where to find the method that builds whatever it is. That could be a controller definining page content, or a class handing a form. Think of the structure of a YAML file as an array, but with colons instead of commas and meaningful indentations.

Here's a snippet of the form_fun.routing.yml file created by DMU in the upgrade process:

form_fun.routing.yml (snippet)

form_fun.page:
  path: /form_fun
  defaults:
    _title: 'Fun with FormAPI'
    _content: '\Drupal\form_fun\Controller\DefaultController::form_fun_page'
  requirements:
    _permission: 'access content'
form_fun.cake_page:
  path: /form_fun/cake
  defaults:
    _title: 'Death, or cake?'
    _content: '\Drupal\form_fun\Controller\DefaultController::form_fun_cake_page'
  requirements:
    _permission: 'access content'
form_fun.existential:
  path: /form_fun/existential
  defaults:
    _title: 'Existential questions'
    _form: \Drupal\form_fun\Form\FormFunExistential
  requirements:
    _permission: 'access content'
 

Note that in Beta 4 of Drupal, you won't have to specify that a route uses a _content controller. You will just need to specify _controller. (See this change record: Routes use _controller instead of _content.)

As you can see, if a route is a form, you specify a _form key with the value being the name of the class that defines your form and its workflow.

Defining Menu Links for the Module

There's now a separate place for defining menu links in your modue. In my Form Fun module, these are contained in the new form_fun.links.menu.yml file created by DMU. Notice the parent and weight keys that create a hierarchy and order menu links.

form_fun.links.menu.yml (snippet)

form_fun.page:
  route_name: form_fun.page
  title: 'Fun with FormAPI'
form_fun.cake_page:
  route_name: form_fun.cake_page
  title: 'Death, or cake?'
  weight: 1
  parent: form_fun.page
form_fun.existential:
  route_name: form_fun.existential
  title: 'Existential questions'
  weight: 2
  parent: form_fun.page

Controllers

Finally, the Controller. A Controller is the place to put methods that output some content to the page. It replaces the page callback procedural function. In the case of the Form Fun module, it's the page that defines a method that returns a list of links to the other forms. It's also the home of several methods that output some images to certain pages. These pages will serve as redirect locations in one of Form Fun's forms. We'll even output a form to a page in a Controller, replacing Drupal 7's drupal_get_form procedural function. I found this handbook page a helpful resource in deciphering the changes that DMU made in replacing hook_menu: An introductory example to Drupal 8 routes and controllers.

src/Controller/DefaultController.php (snippet)


/**
 * @file
 * Contains \Drupal\form_fun\Controller\DefaultController.
 */

namespace Drupal\form_fun\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Url;

/**
 * Default controller for the form_fun module.
 */
class DefaultController extends ControllerBase {


  public function form_fun_page() {
  /* Returns a render array that produces an HTML list of links. */

}

  public function form_fun_cake_page() {
    $form = \Drupal::formBuilder()->getForm('Drupal\form_fun\Form\FormFunCake');
    return $form;
  }
...

So, wow! The changes to hook_menu are really significant. I found it really helpful to learn about those changes in a super-practical way but going through this process of having Drupal Module Upgrader port my D7 module to D8.

@FIXME: Where DMU Only Gets You So Far

Now, DMU did a pretty good job of creating these files and refactoring functions into classes and such, but where it failed, it added comments with @FIXME. For example, I had several @FIXMEs related to building a $links array and displaying those links as a list using a theme function. Here's the comment left by DMU:


// @FIXME
// l() expects a Url object, created from a route name or external URI.
// $links[] = l(t('Death, or cake? (The basics)'), 'form_fun/cake');

Ok. So what to do? What I found the most helpful in this case was to head over to the change records on Drupal.org. There were some direct links to change records provided by the upgrade-info.html file generated by drush dmu-analyze. But where I didn't find a reference there, I simply searched for the function name in question.

So, for this @FIXME, on the change records page I filtered by keywords l() and found this change record: l() and url() are removed in favor of a routing based URL generation API. Usefully, on this change record, there was an example for how to create a Url object. So...

$links[] = l(t('Death, or cake? (The basics)'), 'form_fun/cake');

...is refactored to...


  // Death or Cake? (The basics)
  $url = Url::fromRoute('form_fun.cake_page');
  $text = 'Death, or cake? (The basics)';
  $links[] = \Drupal::l(t($text), $url);

The other thing that was refactored by DMU was the theme function that produced an HTML list of links contained in the $links array, but I found this caused many errors and obviously needed to be changed. I finally found an example of an item_list theme function and refactored DMU's output: return _theme('item_list', array('items' => $links)); to this:


  // Preparing a render array of a HTML item list of the $links array.
  // @FIXME
  // #title value outputs twice on page, as H1 and H3. Fix in twig file.

  $item_list = array(
    '#theme' => 'item_list',
    '#items' => $links,
    '#title' => t('Fun with FormAPI!'),
  );

  // Returning the render array that produces an HTML list of links.
  return $item_list;

You'll notice that I left a @FIXME for myself to fix the duplicated title output, but the takeaway is that Controllers expect render arrays, response objects, or html fragments—not strings. At least that's what the error messages in the "Recent log messages" insisted upon. Also, I discovered that _theme used to be theme() and shouldn't be called directly, so as to encourage the consistent building of renderable arrays. As this is a custom module, I thought I had better refactor. See theme() renamed to _theme() and should not be called directly.

Lessons Learned and What's Next

When I started this process of porting the D7 Form Fun module to D8, I naïvely thought that I would primarily be learning about changes to Drupal's Form API. But what I discovered was equally valuable: lessons on object-oriented PHP, how a major function like hook_menu was replaced with YAML files and Controller php files, new types of files and structure in modules, an invaluable resource in change records, and a great helper in Drupal Module Upgrader.

In my next post on upgrading the Form Fun module to Drupal 8, we'll explore interfaces, a bit more with a Controller, and we'll define a class that defines, builds, validates, and submits a form. Throughout this series on the Drupal 8 Form API, we'll dive into various types of form handling including multi-step and AJAX forms. Stay tuned!

Resources

Dec 18 2014
Dec 18

Kate Marshalkina, Drupal ExpertThis post is part of an ongoing series detailing the new personas that have been drawn up as part of our Drupal.org user research.

Kate Marshalkina has been using Drupal for three and a half years. A web developer by trade, Kate was approached by a friend who wanted her to do Drupal work with him. After doing some research on the system, Kate agreed.

“It’s quite difficult to learn Drupal without paid work because it requires a lot of time and experience to learn the Drupal way of doing things,” Kate said. “I had joined a security startup, and a security company obviously cares about security on the web. So we decided to use Drupal because it’s a safe, well known open source system. I learned a lot while I was working on my tasks, but I spent a lot of my free time to learn Drupal. Once I started learning, I couldn’t stop— I’d previously worked with other content management with less documentation and information and then I started learning Drupal and... because of the community, and all of the learning resources and videos that are available, I was hooked."

“After working with Drupal for three months, I started my blog and not long after that I presented a session at DrupalCamp Moscow. Now, I’m a Drupal lover after three and a half years working with the platform."

Drupal.org: A Valuable Resource

Every day, Kate checks in to Drupal.org: she says she visits the site to find new modules, check the issue queues, and check API documentation. “I’m very comfortable with Drupal.org, but it was hard getting used to it when I started. Initially, it was a question for me why I should even use Drupal.org, and I didn’t know what the benefits are.

"I really like my dashboard on Drupal.org,” said Kate. “It’s a great page where I can see daily updates on my issues — and of course I follow a lot. It’s nice that I can also easily view updates on issues in critical bugs in core and so on, see crucial updates, core releases, and of course I also follow the Drupal Planet RSS feed."

Drupal Planet is one of the most helpful tools for Kate when it comes to getting new Drupal knowledge, and she often encourages her colleagues to follow it. "I think Drupal Planet is an exciting part of Drupal.org. It’s a great resource for Drupal related articles for everyone; beginner to expert, frontend to backend to sysadmin, the information for all these people is usually very high quality on Drupal Planet. When I’m working with fellow developers who have questions, I always ask them to look on Drupal Planet because I know that the information there is of a high quality, and that anyone can find the knowledge they need in there."

It's About the People

Some of the recent changes made on Drupal.org, including the addition of user pictures to the issue queue, have made Kate’s Drupal experience vastly better.

“[The pictures] are great because it makes Drupal.org more personalized, and you can more easily remember the people you talked to because of their photos. And, it reminds people that Drupal isn’t just a CMS, it’s a community, and the people are important.”

“It’s a big question for me how to enroll younger developers,” said Kate. “Looking at the contribution opportunities, [new people] may feel like they can’t be a contributor. So sometimes, they may encounter a bug they don’t know how to fix and think, “oh no, a bug!” instead of recognizing it as an opportunity to learn and grow. If we can encourage more people to become contributors, they will benefit from it and Drupal will benefit from them."

Kate’s advice for new Drupalers is to “start right out and register on Drupal.org. Share modules, create patches, learn how to use git and so on… it’s not easy, but it’s worth it."

Growing With the Project

As for herself, Kate hopes to increase her skill level by contributing to Drupal 8 core.

"I participated in DrupalCon Amsterdam, and really liked what Dries said about getting more benefits to small companies who contribute so that it will be easier for employers to understand why they spent their time and pay for developers on core. I would be much more experienced if I could participate in Drupal core development."

"I also want to someday give a session at DrupalCon,” Kate added. "I give a lot of sessions in my local community, camps and so on. I’ll be speaking at Moscow Drupal Camp in November, but hope to speak at a DrupalCon soon."

We all wish you the best of luck, Kate, and hope to see you on a stage at DrupalCon soon!

Dec 18 2014
Dec 18

Intro

Solr is an open source search engine with many powerful features.

I encounter a lot of experienced developers that either don’t know how to use it or avoid it as it’s in the to hard/scary basket. In this introductory article we’ll talk briefly about how to install Solr in order to use it for development.

There are many ways to install and configure Solr but in this article I’ll show you how to set it up quickly so you can get started developing with it. While the installation and setup will be generic to any framework you want to develop with, I’ll also, show you a couple of extra steps if you’re using Drupal.

Requirements

The only real hard requirement/prerequisite for running Solr is Java. Version 1.6 is recommended for Solr version 4 and upward. Ubuntu and Mac should come with Java pre-installed.

I’m not a windows guy so sorry you guys are on your own There’s pleant of resources out there.

You can find out the version of Java you are running with the following command.

$ java -version

java version "1.7.0_72"
Java(TM) SE Runtime Environment (build 1.7.0_72-b14)
Java HotSpot(TM) 64-Bit Server VM (build 24.72-b04, mixed mode)

I am using Ubuntu 14.04 but the instructions in this article should work if you’re on a Mac or another variant of *nix

Download

Open a terminal and make a folder we can use for working in, and change to that directory:

$ mkdir solr
$ cd solr

Navigate to the Solr download page and find the closest mirror for you. At the time of this article the latest version of Solr is 4.10.2.

Copy the download link for either the solr-VERSION.tar.gz or the solr-VERSION.zip. You don’t want the solr-VERSION-src.tgz (this is the source code and will need to be compiled) and download it with wget.

$ wget -c http://mirrors.sonic.net/apache/lucene/solr/4.10.2/solr-4.10.2.tgz

Unpack

Once downloaded (it’ll be about 150M) we can un-compress it and change into the directory.

$ tar -zxvf solr-4.10.2.tgz
$ cd solr-4.10.2/

Make a working copy

In the current directory there is a folder called example we want to make a copy of this folder.

We could just use the example folder but it’s nice to leave that clean on case you want to use this copy of Solr for other sites as well. So we’ll make a copy and then change directory into the newly created copy.

$ cp -prv example my_solr
$ cd my_solr

Make it work

Now we’re ready to run it for the first time. To run Solr it’s really simple. Simply run:

$ java -jar start.jar

You should see a whole bunch of output (to stop solr press CTRL^C). After a few seconds if you open your browser and navigate to http://0.0.0.0:8983/solr/ you should see something similar to the following (the actual screen may differ depending on your version)

Solr dashboard

That’s it. Solr is now set up and ready to use. Depending on your client frame work you may need to makes some config changes to Solr itself. Consult the installation instructions of your chosen framework. If you’re using Drupal keep reading and I’ll show you the steps required to make Solr ready for Drupal integration. First lets stop SOlr from running by hitting CTRL^C in your terminal.

Modules

There are a couple of modules you can use for Drupal integration with Solr. I wont go into the Drupal configuration side of things (I’ll leave that for another day) but will talk about the steps required to get the Solr server we’ve set up ready for Drupal usage depending on the Solr module you’ve chosen.

Search API and ApacheSolr

If you’re using the search_api you will need to ensure you have the search_api_solr module installed. Otherwise the [apachesolr] module is the way to go.

In both the search_api_solr and [apachesolr] modules, you’ll find a folder called solr-conf in this folder there will be version folders 4.x, 3.x etc. Choose the version of Solr you downloaded. This folder contains all the config files you need to install in your Solr install. I could probably write a whole bunch of articles about the contents of these files but since this is a beginner tutorial we’ll just take the easiest route.

You want to copy the contents of the solr-conf/4.x/ folder into your solder core. We can do this with the following, go back to your terminal, and run (change the path to your Drupal module):

$ cp -v **/path/to/apachesolr/or/search_api_solr/**solr-conf/4.x/* solr/collection1/conf/

That will copy the config for your Drupal site into the my_solr/solr/collection1/conf/ directory.

Conclusion

Solr is now ready for use by your Drupal install. You can run it whenever you like by changing into the my_solr directory and starting it.

$ java -jar start.jar

I wouldn’t recommend using this setup in production. However, for developing on your local machine, it’s perfectly fine.

In the next article, I’ll talk about how to configure the search_api and search_api_solr to use Solr as a search engine for your Drupal site.

Please enable JavaScript to view the comments powered by Disqus.

Dec 17 2014
Dec 17

Nathaniel Catchpole , the Drupal 8 release maintainer and Tag1 Senior Performance Engineer, suggested that Drupal shops everywhere could support the release of Drupal 8 by upgrading their own sites and addressing the issues they find along the way. This series chronicles the journey from Drupal 6 to Drupal 8.

Part 1: Readiness Assessment

Before performing a major version upgrade, I usually go through a series of steps that help me determine:

  1. What compelling business needs or new functionality are prompting the move? In this case, the site builder view on the status of D8 will help us identify and hopefully address real barriers to adoption, as well as to prepare materials that help document the journey.
  2. What does the site do? If there are functional tests or test plans, I review those. Then, I manually review the site, paying attention to functionality for particular roles. I also check with at least one or two of the stakeholders with each role to learn what features they regularly use, what improvements would better support them, and what they’d really like to see. I don’t normally act on that wish list answer as part of the upgrade proper, but knowing what new functionality they desire can help make decisions about the new modules to use.
  3. Are there features that are no longer in use? I’m always surprised, though by now I really shouldn’t be, by the number of things that seemed like a good idea at the time to someone which were never used at all or which are no longer in use.
  4. Is now the right time? Are the contributed modules on which the site depends ready or is there a new way to achieve the required functionality.

1- Compelling Functionality

As noted above, this particular journey is compelled by the desire to identify and fix bugs during the Beta period of Drupal 8. That doesn’t mean we’re not excited by:

  • WYSIWYG in Core- I am ecstatic at the prospect of NOT having to choose and configure basic WYSIWYG functionality.
  • Views in Core - I pay a lot of attention to the experience of content contributors. They’re the everyday users that Dries talked about back at DrupalCon San Francisco, people who use the interface of Drupal as their day job. What a site builder does for them (or fails to do) can make or break the success of the site as well as cement the organizational satisfaction with Drupal itself.

I want Tag1’s busy staff to find it a pleasure to keep their blogs, community contributions, and case studies up-to-date.

2 - Current Functionality

Our speciality is performance and scalability for complex websites, which never necessitated building a complex external site for ourselves. We have internal sites that put Drupal 6 though its paces, but the main Tag1 site is a combination of blogs and brochure:

  • Review what the current site does
    I start any readiness evaluation looking at the site from the user perspective, clicking around, then reviewing any documentation. The site revealed no surprises. I jotted notes about those user-facing features, mapped to Drupal functionality, and stored them as a sheet in the Readiness spreadsheet. For a basic site, the notes aren’t too impressive, but the process was a valuable refresher on what the site does.
  • Inventory the enabled modules
    After looking at the site from the user interface, I make an inventory of enabled modules. The following Drush commands make nice spreadsheets:
    drush pm-list --no-core
    drush pm-list --core
    

    You can take a look on Sheets 2 and 3 of the Readiness spreadsheet to see the entirety of what was and and wasn’t available.

    In our case there are just two areas of concern:

    Blogs have been removed from core, but with views moved in, this won’t be daunting to rebuild. They may work a little differently, but odds are that will be for the better!

    The only missing module that had no alternative and looked like it might hurt was Pathauto. It’s nice to keep URLs tidy and hard to remember to set them, but wouldn’t be strictly necessary for our purposes and there were were no other glaring show stoppers.

    Note: It looks like upgrade work is happening on Github so I updated the spreadsheet to make note and will evaluate the status as part of the process.

  • Look for customizations
    In addition to custom and contributed modules, it’s good to check for inappropriate customizations done directly to core or contrib. Whatever reason they had for being done back then may need to be accounted for, hopefully in a best-practices way, on the new site.

    Although it seemed unlikely that any relevant changes had been done, for good measure, I always run Hacked. It’ll let you know about files that have been changed from the source.

    There were some theme customizations and a .gitignore file was missing, so as suspected, nothing to worry about.

3 - Features no longer in use

I’ve learned over the years that not every module that is enabled is in use! I try to find a link that illustrates somewhere on the site that the module is visibly in play. Even when there is a place in production where the module is used, it’s entirely possible that the production feature itself doesn’t make sense to upgrade. A serious investigation of analytics and logs can be insightful. Data can guide conversations with product owners about what’s still valuable and support decision-making.

On our straightforward site, there’s not much to not use. As I learned running the first migration, though, even features that were briefly explored but never used can come into play. More on this later.

4 - The right time

Once you’ve completed your archaeology, you’ll need to find the right time for your organization. Talk with stakeholders about the change and be on the lookout for in-process or upcoming feature requests that you might not be prepared to accommodate.

In our case, because the motivation for this upgrade is process, not product, we’ll proceed and go as far as we reasonably can with the upgrade. At the time of this writing, I’ve done a D8 install and begun running a migration. (That’s how I know to be on the lookout for certain disabled modules!) I’m feeling confident that we can get a long way.

Unless the migration process is flawless and the Drupal 8 to Drupal 8 update path is in place I'd be unlikely to recommend deployment of the work, especially with an impending re-design. While re-entering some data manually has always been a part of previous major version upgrades, I’d be loathe to commit to beta adoption if I thought we’d find ourselves stranded in the latest version. Fortunately, all the preparatory work can be done without the upgrade path in place!

Coming next in this blog series, Part 2: Preparing to Migrate.

What modules and processes do you use to prepare for a major version upgrade?

jam
Dec 17 2014
Dec 17

Part 1 – Larry Garfield and I had a long chat in front of my camera at DrupalCon Amsterdam. Part of the idea was to help Larry prepare his thoughts for writing the blog post that has turned into "Building Bridges: Linking Islands" in the Future of PHP guest blog series on Acquia.com. In this part we cover Larry's start in Drupal, some project history, what Drupal can teach (and needs to learn) about contribution and community ("celebrating the newbie" and the power of "thank you"), The New PHP, fuzzy project boundaries and inter-project collaboration.

Building Bridges

Larry explains, "The PHP ecosystem is coming together. The web ecosystem is coming together. It's a lot more integrated. It's a lot more collaborative. So projects that are collaborating need to ask themselves 'What is our value-add?'. Drupal's value-add over the PHP baseline is not that it does dependency-injection. It's not that it can serve HTML. It's not that it has forms. It's value-add is entities, views, and the community. We are a content management system and a very good one. By that I mean a structured system to manage content, not a tool for building pages. And we have a community behind it that has your back, that you can rely on, that is not going away any time soon. That's Drupal's value-add. So put our emphasis and effort there; on building a really solid, flexible CMS platform with a community that can support it."

"Those other things that get us there? That's not our value-add. People don't use Drupal for hooks. They use Drupal for nodes. They use Drupal for Views. The more we can outsource our irrelevancies and focus on core competencies, the more we can say, 'The important things that make Drupal worth using, let's focus on those.' And the things that are not the reason people come to Drupal, we can save time by outsourcing that. It may not be perfect ... Could we write something better [than the Symfony routing component] ourselves? Probably. Would it be done right now? Not even close. But we brought in all this code and got 3/4 of the way we wanted to be by adding one, single library."

I proposed one more aspect to Drupal's value-add, "Drupal is architected in a way that is extensible and flexible. You get some more baggage, but its advantage over a specialized system is when your next requirement comes in, we're ready to incorporate it or communicate with it." Larry adds, "That goes to the whole framework versus application debate we've been having since there's been a Drupal. In Drupal 8, in some ways, we became more of a framework, in other ways, more of an application." Pointing to himself, "As one of the framework people for a long time, I think the [framework side] actually lost that battle and we lost it to modern PHP."

"Meanwhile, the application has evolved to being a platform and we should focus on thinking of Drupal as a PHP-powered content management platform. And think of it not in terms of, 'What is the canned user experience we can offer?' But [rather] how can we make a toolchain that let's you build a great user experience. How can we build a toolchain that let's you do all the content modeling shenanigans for whatever site or task you're doing and have reasonable defaults. Look at it as not an application, but as the core of a platform ecosystem. That means designing things and planning things in different ways than you would a pure application. You need to think about extensibility in a a different way between a framework and an application and a platform. I think at this point platform is the right way to think at the high level ... with some framework stuff alongside it."

Links/References

Interview video

[embedded content]

Image credit

linking_islands_1.jpg - Image by Jay-P - https://www.flickr.com/photos/esqenzo/248879957/ License https://creativecommons.org/licenses/by-nd/2.0/

Dec 17 2014
Dec 17

How do you insert a text field into a rendered entity, all within a field collection? This question came to mind on a recent Drupal 7 build. What at first sounded complicated turned into a concise and elegant solution.

The basis of the requirement broke down into the need for events to list those participating in the event. The build already had existing Event and Profile content types, so the new functionality would only require implementing the list of Profiles participating in an event. Each participant would include their role at the event, along with links to contact them.

blog

To implement this, an Event Participant field collection was added as a field to the Event content type. Each instance of the field collection included a single Profile content type and a single text field for their role at the Event. The Profile would then be displayed as a rendered entity within the field collection so that the Profile’s image and contact links could be displayed. The point at issue then arose. How would the Participant’s role at the event be placed within the rendered Profile teaser, along with the other Profile fields? To the user it needed to look like a single unified entity, with the Profile’s image, role at the Event, Profile name, and contact links all included.

Screen Shot 2014-12-11 at 4.27.43 PM.png

After some initial brainstorming a handful of different approaches came to mind. The initial impression was to use template_preprocess_field() since it allows the alteration of values in the theme layer. The participant role field would be removed from it’s initial location and inserted into the participant profile object. This did not work though. Despite the repositioning of elements within the render array, the changes occur too late in the rendering execution. The render array had already been rendered and thus the display remained the same.

After quickly realizing a theme function wasn’t the answer, hook_node_view() came to mind. The hook allows for the alteration of a node before it is rendered and so all that would be needed would be to shift the location of the Participant Role field within the $node array. The issue with this implementation and the reason it wasn’t chosen, is that it would require reaching out to the global state to determine what changes are needed. In other words hook_node_view() doesn’t realize what context it is called under and would thus require a hack of sorts to determine when it was being called under an Event node. This could be worked around by checking the URL path, but this is not best practice as it is brittle and prone to breaking with future site updates.

As they say the third time’s the charm. The final solution was to use hook_field_collection_item_view(). This hook allows for the alteration of a field collection item before rendering. Unlike many of the other options, this hook is called for each field collection item, allowing one to both check the field collection name and adjust it’s internal field ordering, before any rendering occurs. Using this hook, each Event Participant field collection item is altered, removing the Role field and inserting it into the Profile rendered entity. Simple as that. Now each Event Participant is correctly displayed as a single unified entity, with an image, name, and a set of contact links.

function hook_event_field_collection_item_view($field_collection_item, $view_mode, $langcode) { if ($field_collection_item->field_name == "field_event_participants") { $role = $field_collection_item->content['field_event_participants_role']; $profile_id = $field_collection_item->field_event_participants_profile[LANGUAGE_NONE][0]['target_id']; // Remove Participant Role from previous location in field collection output unset($field_collection_item->content['field_event_participants_role']); // Insert Participant Role into the Participant Profile output $field_collection_item->content['field_event_participants_profile'][0]['node'][$profile_id]['field_event_participants_role'] = $role; } } 1 2 3 4 5 6 7 8 9 10 11 12 functionhook_event_field_collection_item_view($field_collection_item,$view_mode,$langcode){  if($field_collection_item->field_name=="field_event_participants"){    $role=$field_collection_item->content['field_event_participants_role'];    $profile_id=$field_collection_item->field_event_participants_profile[LANGUAGE_NONE][0]['target_id'];       // Remove Participant Role from previous location in field collection output    unset($field_collection_item->content['field_event_participants_role']);       // Insert Participant Role into the Participant Profile output    $field_collection_item->content['field_event_participants_profile'][0]['node'][$profile_id]['field_event_participants_role']=$role;  }}

Through several quick, small, iterations an elegant, straight-forward solution was found to what at first sounded like a complicated feature request. Now users viewing an Event can see a list of Participants, with each Participant displayed as a single item. Check out more on field collections on the Phase2 blog!

Dec 17 2014
Dec 17

Chris Ohmstede, Skilled Drupal UserThis post is part of an ongoing series detailing the new personas that have been drawn up as part of our Drupal.org user research.

Chris Ohmstede is based out of Los Angeles, California. An experienced programmer, Chris is new to Drupal but already identifies as a skilled persona. Several months ago, Chris discovered Drupal as he was looking for solutions to build a website for hosting a program he wrote.

"I spent a number of years in the banking industry, and in that industry banks are constantly making connections to everything. I was always running in to problems when things couldn’t connect— it was always an issue trying to figure out what was actually going on. Over the years, I wrote a bunch of applets here and there to figure out what the problems were, and my program whycanticonnect is a conglomeration of those applets that work across operating systems, mobile— I’ve got cloud services approaching me about it, too.

Wanted: Custom Functionality

“Originally, I was hosting my project for download using a different CMS option, but things weren't working properly. Finally, I hit a point where I said “I’ve got to make a change.” I initially went to Drupal because I use Linux as my OS, and Drupal is one of the only CMSs available out of the repositories. I saw that, went and did some research, and Drupal looked big and well organized. One of the cool things that caught my attention is that I could Google around and see that everything I needed was already built. I never had to ask for any help — I was able to roll on through and put my site together easily."

“Also, one of the reasons why I selected Drupal was that I've released my product in 12 different languages, and Drupal looks like it has some decent translation services coming in. I want my site to be usable in all 12 languages I support."

After downloading Drupal, Chris relied on his technical knowledge to carry him through. Though he initially encountered some difficulty getting the modules to do what he wanted, he found after some searching that deriving his theme would fix his problems.

"Once I derived the theme, everything got easier— the way drupal derives themes is perfect. It’s very object oriented, which is fantastic. The nice thing about Drupal was that I could figure out what I wanted to mess with, and I could build variables on the local scope and carry them through the whole page and process, which is surprisingly difficult to do with other systems."

"It gives me everything I need"

“The great thing about Drupal is that you're not limited in how to inject code,” said Chris. "I like my download module because it gives me everything I need. It figures out intelligently what’s the best download to go with, it builds a link and launches the javalaunch, so it’ll work whether java is enabled or not— which was a problem I’d had before. I’m very happy with it, and very happy with Drupal."

For the time being, Chris doesn’t plan to put together more Drupal sites — rather, he’s going to focus on maintaining the one he has.

"I have no desire to become a web designer,” Chris said. “I built my site because I know I'll need to change it often, and with Drupal it doesn’t cost me anything. As far as giving back to the community, I’m mostly focused on working with my product. I need to write an Android, Mac, and iOS version. I’m planning on submitting some of my modules to Drupal.org once I’ve got them in shape, and I’ve made posts for people asking the same questions and encountering the same problem as I have. Maybe someday down the road I’ll have time to do more, though I certainly wouldn’t make any promises about it."

Dec 17 2014
Dec 17

Global #SprintWeekend will be 17-18 January 2015.

Please encourage contributors you know to host a small local sprint near them.

Post an announcement in your local meetup groups please; ask if anyone is interested in hosting a sprint and wants some support organizing it.

List sprints on the wiki: https://groups.drupal.org/node/447258
That wiki also has more information about organizing a sprint.

We will have a couple meetings to support organizers (doodle for organizer support meetings: http://doodle.com/uutcp2ge7gt3a8ed )
and also have mentors in irc over the sprint days (doodle for mentoring during the sprint: http://doodle.com/5vr54mpvxq4k7x29 ).

Dec 17 2014
Dec 17

Attention Michigan Drupalers! Mark your calendars for DrupalCamp Michigan, Saturday January 31 at Henry Ford College in Dearborn. For only $15, you get a full day of Drupal presentations, discussions and lunch!

Online registration/tickets will be available soon, and we’re currently accepting session proposals. If you’ve got an interesting presentation to share, please post it to the website here: http://2015camp.michigandrupal.com. Session selections will be made in early January.

Interested is sponsoring the camp and getting your name out to the local Drupal community? Sponsorship information is available here: http://2015camp.michigandrupal.com/sponsors.

Dec 17 2014
Dec 17

The Password Reset Landing Page or PRLP module is honestly one of the simplest modules you have come across. The sole purpose of the module is to provide the ability for users who request a new password to be able to reset their password on the password reset landing page instead of having to do so on the user edit page.

The main use case for this module is to limit users who request a new password and then once they click on the one time login they forget to set their password. If they forget to do this they would again need to request a new password.

Configuring the PRLP module is easy. With the configuration settings you can set if you want to require them to enter a password on the landing page, the page they will be redirected to after they login and also the fields that appear on the landing page. In this Daily Dose of Drupal I walk through these options and also explain how the process works with and without the module.

Every module has a place and this module definitely falls into the category of "nice to have", but not essential. Go ahead and try this out on your site and let me know what you think.

Ben
Dec 17 2014
Dec 17

It's the final week of work before Christmas and Code Drop have sponsored me to work on core for the whole week :). I've been working towards a successful end to end migration from Drupal 6 to Drupal 8 and after debugging 5 core bugs, I finally have a working solution. Checkout the video!

[embedded content]

The video shows an out the box Drupal 6 website with a bunch of fields, taxonomies and settings created. Most of the stuff comes across to Drupal 8 but there are still a few issues that need work.

Current Core Bugs

These are the core bugs that are required to get this demo working.

Dec 17 2014
Dec 17

Keiko Kanda, Drupal Learner This post is part of an ongoing series detailing the new personas that have been drawn up as part of our Drupal.org user research.

In April of 2014, the Japanese Drupal community gathered in Kyoto for their first ever DrupalCamp. Individuals came from other countries to attend the conference, and that was how Keiko Kanda was first introduced to the project.

“I think it might be a bit unusual to get involved the way I did,” Keiko said. “My cousin who lives in Sydney, Australia, married an Australian man. He came to Japan to attend the DrupalCamp in Kyoto and he asked me to accompany him… though some people were presenting in English, he was concerned that he might need a translator.”

“I knew almost nothing about Drupal, even though he had introduced me to what he was working on at that time. But I thought, I would be glad if I could help him out. So I went to the DrupalCamp in Kyoto and it turned out to be a very interesting experience for me.”

“To be perfectly honest with you, when I decided to tag along with my cousin’s husband, Colin Watson, I didn’t realize how fun it would be. I never thought my involvement would last this long,” Keiko said.

Serving an Immediate Need

At the camp, Keiko quickly found herself enlisted as a translator for some of the sessions, and was surprised by the number of foreign attendees at the conference.

“They were all very nice people, and there was much more international attendance than I expected. There were maybe fifteen or twenty international attendees there, and probably ten or fewer were giving presentations in English. Some were able to give parts of their Drupal explanations in Japanese, but they needed a translator to help introduce themselves. So when I arrived at the venue, the camp organizer asked me to translate the introductions from English to Japanese."

That was how Keiko met Chris Luckhardt, a Drupal Master that we will hear from later in the week.

"Chris was one of the speakers... though he had another translator to translate the technical parts of his presentation, when he spoke about himself and other innocent, casual stuff, he needed someone to translate. So that's how I got involved."

Stayed For the Community

"After the Drupal camp in Kyoto, the organizers — Satoshi Kino and Kyoto Ohtagaki — they asked me to do the translation stuff not only at DrupalCamp but at the meet-ups and meetings. According to them, many Japanese Drupal developers can understand when they read English, but when they have something to express it’s very hard to write or speak whole thing in English.

"Kino-san says it’s hard for Japanese developers to catch up with the latest information on Drupal.org,” Keiko continued, “So he and some other Japanese community members are trying to translate some of the useful English information into Japanese, though it takes time.

"I was — still am — studying to be a translator,” Keiko added, "and I thought it would be a good experience for me to help them. But many of Drupal technical terms... for me, they are gibberish. It’s not a language problem, it’s a problem with Drupal technical terms. So even though at DrupalCamp Kyoto they were talking in Japanese, I often couldn’t understand what they were talking about."

"I refer to Drupal.org occasionally when the community members talk about something very complicated— something about Drupal or what they were doing with Drupal… [translation is] hard for me but it’s fun, and in the process I can learn not only Drupal technical terms, but also what is going on in the Drupal world, learn about the problems the Drupal Japanese community has, and so on."

Ways of Mastering Drupal

When it comes to her current work with Drupal, Keiko is more focused on supporting the CMS than using it.

“When Kino-san organizes Drupal meetings, like monthly meetings, he will post the venue and time and date on Facebook in Japanese, and I will translate that into English and spread the word. When Kino-san or Kyoto-san works with Chris [or other English-speaking Drupal users], I facilitate the meeting on Skype. Though they understand English, it is hard for them to speak in English so I translate from Japanese to English for them."

Keiko has plans to help with translating quite a bit of technical material in the future. She has already agreed to assist Chris Luckhardt with translating a Drupal 8 tutorial video into Japanese, and has volunteered to help translate several books as well. In the meantime, she wishes there was a better way to get ahold of other translators like herself.

“I’m not sure because I’ve never thoroughly searched around Drupal.org, but it would be great if there were resources for people who aren't developers,” Keiko said. “I wish there was a better way to find translators, or event organizers— maybe people who don't create websites but instead help to build the project and the community — not only local communities but worldwide communities too. It would be good if there was a place I could go to find people with experience that might be helpful when organizing DrupalCamps, DrupalCons, and Drupal meetups."

Finding a Common Vocabulary

Keiko has a few other pain points, but they mostly revolve around language.

“As a community member, I think so many challenges I have, are… well, first I’m trying to understand what other community members are talking about as accurately, correctly as I can. That’s the first challenge. And I’m trying to learn more Drupal technical terms so that I can understand people's conversations more easily, but oftentimes… it might appear to be an innocent conversation, but behind the ordinary conversation sometimes there are subtexts… you imagine that someone is talking about an innocent topic but when you try to read between the lines there are implications of bigger problems they have.

“For instance, many people in the Japanese Drupal community are spending incredible amounts of time trying to understand what’s written on Drupal.org, because it’s written in English. It’s my impression — and it might be wrong — that the Japanese Drupal community falls into one of two categories: either they catch up with latest information using Drupal.org frequently, or they are totally left behind, staying within only Japanese-written world, because it is so difficult to read English.

"It’s my impression and opinion too that the Japanese community members are trying to attract more people who maybe did not have anything to do with Drupal, but are good with English. For me, I would be happy to find someone who speaks English and Japanese better than I do... the workload can be overwhelming sometimes, but if I could share the task for someone, that would be very helpful for me. "

Dec 17 2014
Dec 17

Imagine somewhere deep in your site that you have a page with the alias:

/how-to-build-drupal-site.

Now, let’s imagine this page is not a link in your site’s menu. Your visitor remembers that they have seen this page on your site and starts typing in the address line:

http://yoursite.com/how-to-make-drupal-site

What will they get? They may get a 404 error page or perhaps a sitemap with a lot of links which could confuse the user. What is their most likely path at this point? Will they start searching interesting pages in the sitemap or just go away and Google it? Answer: Neither.

Chances are, your visitor will leave your site and may not ever come back. Wouldn’t it be nice if on ‘page not found’ your visitors could search the words in the path ‘how to make a drupal site’ and it could present search results with the intended result at the top? Meet Search404.

This post is related to one of the Top 100 Drupal Modules (as on Sep 9, 2012) Search 404 and its work with Apache Solr Search on a Drupal 7 base.

First we’re going to assume that you have installed and configured Apache Solr Search and is working on your site.  The next step will be to download and enable the Search 404 module. After that, you'll be ready to configure their work as partners.

Step One. Site page 404 settings

On Configuration -> System -> Site information page set default 404 (not found) page to search404 (Fig. 1).

C:\Users\User\Documents\Blog\Search404\Fig01.png

Fig. 1 Default 404 page

Step Two. New Apache Solr Search page

On Apache Solr Search configuration page (Configuration -> Search and metadata -> Apache Solr search) go to PAGES/BLOCKS tag (Fig. 2).

Fig. 2 Apache Solr Search configuration page

And add a new search page (Fig. 3) with desired settings.

C:\Users\User\Documents\Blog\Search404\Fig02.png

Fig. 3 Add search page

Make sure Path in your new search page is different from default 404 site page, not equal to search404 (Fig. 4). Also use a custom filter to filter content types that are used as pages on your site. Custom filter must be like bundle:{content_type} without braces.

Fig. 4 Apache Solr search page settings

Step Three. Search404 settings

Go to Configuration -> Search and metadata -> Search 404 settings. Enable Do a "Search" with custom path instead of a Drupal Search when a 404 occurs checkbox and fill Custom search path with your Apache Solr search page, created on previous step, and /@keys argument (Fig. 5)

Fig. 5 Search 404 settings

The rest of the settings can be set based on required behavior.

Ok, we’re almost all done.

Step Five. Status 404 Not Found

For this moment you must have a search404 not found page that will make search with Apache Solr search. But this page won’t set page status 404 (https://drupal.org/node/1852240#comment-6944804). That is why it was written ‘almost all done’. To do this you will need to add some code. You can use hook_preprocess_html_()  to set page 404 status, like in an next example:

function MODULE_preprocess_html(&$variables) {

 // ‘page-not-found’ path was set in step two.

 if (arg(0) && arg(0) == ‘page-not-found’) {

   drupal_add_http_header('Status', '404 Not Found');

 }

}

or you can use your option to set status 404.

Now we’re all done and you have a 404 page with search results gathered by keywords from the wrong link that the visitor originally visited.

Dec 17 2014
Dec 17

Imagine somewhere deep in your site that you have a page with the alias:

/how-to-build-drupal-site.

Now, let’s imagine this page is not a link in your site’s menu. Your visitor remembers that they have seen this page on your site and starts typing in the address line:

http://yoursite.com/how-to-make-drupal-site

What will they get? They may get a 404 error page or perhaps a sitemap with a lot of links which could confuse the user. What is their most likely path at this point? Will they start searching interesting pages in the sitemap or just go away and Google it? Answer: Neither.

Chances are, your visitor will leave your site and may not ever come back. Wouldn’t it be nice if on ‘page not found’ your visitors could search the words in the path ‘how to make a drupal site’ and it could present search results with the intended result at the top? Meet Search404.

This post is related to one of the Top 100 Drupal Modules (as on Sep 9, 2012) Search 404 and its work with Apache Solr Search on a Drupal 7 base.

First we’re going to assume that you have installed and configured Apache Solr Search and is working on your site.  The next step will be to download and enable the Search 404 module. After that, you'll be ready to configure their work as partners.

Step One. Site page 404 settings

On Configuration -> System -> Site information page set default 404 (not found) page to search404 (Fig. 1).

C:\Users\User\Documents\Blog\Search404\Fig01.png

Fig. 1 Default 404 page

Step Two. New Apache Solr Search page

On Apache Solr Search configuration page (Configuration -> Search and metadata -> Apache Solr search) go to PAGES/BLOCKS tag (Fig. 2).

Fig. 2 Apache Solr Search configuration page

And add a new search page (Fig. 3) with desired settings.

C:\Users\User\Documents\Blog\Search404\Fig02.png

Fig. 3 Add search page

Make sure Path in your new search page is different from default 404 site page, not equal to search404 (Fig. 4). Also use a custom filter to filter content types that are used as pages on your site. Custom filter must be like bundle:{content_type} without braces.

Fig. 4 Apache Solr search page settings

Step Three. Search404 settings

Go to Configuration -> Search and metadata -> Search 404 settings. Enable Do a "Search" with custom path instead of a Drupal Search when a 404 occurs checkbox and fill Custom search path with your Apache Solr search page, created on previous step, and /@keys argument (Fig. 5)

Fig. 5 Search 404 settings

The rest of the settings can be set based on required behavior.

Ok, we’re almost all done.

Step Five. Status 404 Not Found

For this moment you must have a search404 not found page that will make search with Apache Solr search. But this page won’t set page status 404 (https://drupal.org/node/1852240#comment-6944804). That is why it was written ‘almost all done’. To do this you will need to add some code. You can use hook_preprocess_html_()  to set page 404 status, like in an next example:

function MODULE_preprocess_html(&$variables) {

 // ‘page-not-found’ path was set in step two.

 if (arg(0) && arg(0) == ‘page-not-found’) {

   drupal_add_http_header('Status', '404 Not Found');

 }

}

or you can use your option to set status 404.

Now we’re all done and you have a 404 page with search results gathered by keywords from the wrong link that the visitor originally visited.

Dec 16 2014
Dec 16

Row of seedsSometimes CSS feels like it stands for “Complete Sh*t Show.”

Lucky for us, there’s a tool to help get our CSS under control. This tool is Sass; Simply Awesome Style Sheets. Well, actually: Syntactically Awesome Stylesheets.

Sass is a preprocessor for CSS. Just like PHP & Drupal create Drupal’s Very Rich and Enhanced Markup™, Sass does the same for CSS, but properly, by taking your .scss (sass) files and turning them into super .css files, with variables, functions, and other fancy powers.

.sass files -> [magic thingie] -> .css

Install Sass

Installing Sass on your machine is straightforward, if you’re not afraid of the terminal. Simply do a "sudo gem install sass".

Boom.

You’re ready to get Sassy.

But if the terminal gives you agita, you can install Sass with a GUI, a complete list can be found on sass-lang.com/install or LiveReload.com.

Here’s a video showing how this process works: http://wdog.it/4/1/video

Setup your Drupal theme

How Sass works with multiple files
In order to make Sass work in your theme, you need:

  • a Ruby config.rb file, for configuration;
  • a Sass folder for all your Sass files;
  • a CSS folder where all your compiled CSS files will be created.

Say Good-bye to Your CSS Files

First, accept that once you convert your CSS files to Sass, you will never again have to look into the CSS folder.

Your days of fiddling directly with CSS are over, and everything is gonna be OK.

Setup the Sass File Structure

Screen Cap
One of the cool things about Sass is being able to split up all your files and let the preprocessor do the heavy lifting. Sass collates all your files into one minified and ready-to-go file, which grants you a bit of sanity inside the [themename].info file.

Instead of creating each of your Sass files (fx foo.scss) to become a .css file (foo.css), add a "_" to the files you don't want to have rendered fx: _menu.scss, _blocks.scss,
and add the partials to your main theme Sass file (theme_name.scss).

Theme Structure

The theme structure can be found here http://wdog.it/4/1/mdk.

Drupal has a tendency to add at least one CSS file per module, which can be a PITA. Attack that the Angry Themer™ way; put the css files into your theme and let Sass take care of it.

By following a combination of sensible file organizing – and competently navigating Drupalisms (i.e., all the dumb rigmarole you have to account for in Drupal that doesn’t happen anywhere else) – you can structure the sass so that you can easily find them again.

Sass folder:

 
/sass/base/
  _base.scss
  _image.scss
...
/sass/drupal/
 _forms.scss
 _forms-login.scss
...
/sass/layout/
 _grid.scss
 _page-layout.scss
...
/sass/design/
 _colors.scss
 _typography.scss
 -fancy.scss
...
/sass/style.scss
buttermuffin.scss

Inside the main sass file for your theme, import all the files you need:

@import "drupal/forms"
@import "drupal/forms-login"

That is the file that will be finally rendered (remember the underscore trick) into style.css.

As the next step we add the style.css file to the theme's info file:

buttermuffin.info
....
 
;-------------- C S S -------------
stylesheets[all][] = css/buttermuffun.css
stylesheets[all][] = dragons.css

In the .info file, we point the theme to the compiled .css file.

Now that I have ranted about the awesomeness of Sass, you might ask: What is that dragons.css file doing there in my theme?

Here’s why: I use the dragons.css for my cute developers – who shouldn’t be meddling with my Sass files – or for when I need a quick ‘n’ dirty fix on a live server. Use the dragons.css file to add stuff in without having to re-roll the sass to css files.

Conquer

Other articles from this issue:

Columns

Doug Green

Familiarity breeds ease-of-use.

Coming Soon

Articles

Karoly (Chx) Negyesi

The power of process plugins.

Coming Soon

Melissa Anderson

Migrating. Now. Here's how.

Coming Soon

Dec 16 2014
Dec 16

INTRODUCTION

Faceted Searching is a method that allows a user to apply multiple filters of varying dimensions to a list of items on your website. This is a great method for quickly filtering a long list of information down to narrower results that are relevant to what you’re searching for.

For example, on Amazon.com you may filter your product lists by “department”, “rating” or “format” or on Zappos.com you may filter by “shoe size”, “shoe width” and “shoe color” to narrow the list of shoes. Faceted searches are a common and effective method for simplifying the searching of items on your website. But how do you achieve this in Drupal? In this tutorial we’ll show you how to create faceted searches that you can use on your Article listing page created using a Drupal View. This method can also be used for Drupal Commerce product pages and almost any other type of data you may be displaying on your site.

Drupal Search API Module Exmplae

VIDEO AVAILABLE: Please note that there is a video version of this tutorial available at the end of this page.

MODULE SETUP

Install the following modules:

Enable the following modules:

  • Current Search Blocks
  • Database Search
  • Search APi
  • Search Facets
  • Search Views

Drupal Search API Module Settings

NOTE: The Search API system that we’ll be using does not require Drupal’s core Search module. If you’re not using a site-wide system search you can safely disable this module.

Dependencies:

The above modules have the following required modules, if there are not already then they will also need to be installed.

NOTE: For this tutorial we’re going to use Drupal’s built-in Article content type. However, you can use any Drupal entity that can be displayed in a View to create your search facets. You can also chain referenced entities so, for example, you can show Drupal Commerce "Display Nodes" while pulling information from the product entities attached to them into your search facets.

CONFIGURATION

SERVER SETUP

The first thing we’ll need to do is create the ‘Server’ that will host your search index that we’ll be using.

  1. Navigate to CONFIGURATION->SEARCH AND META DATA->SEARCH API (YOURSITE/admin/config/search/search_api)
  2. Click the “Add Server” link.
  3. Enter a name and description for your server for your reference. For example, “Search API Server”
  4. From the “Service Class” drop-down, select the “Database Server” option.
  5. Click the “Create Server” button.

Drupal Search API Create DB Server

INDEX SETUP

The next step is to create the index that will hold your search index information. This index will reside within the server object created in the previous step.

  1. Navigate back to to CONFIGURATION->SEARCH AND META DATA->SEARCH API (YOURSITE/admin/config/search/search_api)
  2. Click the “Add Index” link.
  3. Give your index a name, for example (“Article Index”)
  4. In the “Item Type” dropdown, select “Node”.
  5. In the “Server” dropdown, select the server you created in the previous step.
  6. If your index is going to be large then do not select “Index items immediately”. Since our site will not be very large we will check this option so that node changes are updated in the index right away instead of having to wait for the next cron run.
  7. Click the “Create Index” button.

Drupal Search API Index Settings

FIELD SETUP

The next step is to select the fields that will be indexed. This is a basic and arbitrary list for this example but should give you an idea of how the field selection works to get you started. First, under the “Add Related Fields” section near the bottom of the page, add the “Author” and “Main Body Text” to our index. This will give us access to ‘related’ fields to use in our index. After adding these two related-fields you’ll see more fields available to add to your index.

For this example, we’re going to select the following fields:

  1. Title – Even though this isn’t going to be a search facet, we may want to use this for text searches.
  2. Author > Name
    • IMPORTANT: Change the ‘type’ from ‘Full Text’ to ‘String’. This will index the all words of this field as one string instead of individual words.
  3. Tags
  4. The main body text » Text – Even though this isn’t going to be a search facet, we may want to use this for text searches.
  5. Click the “Save Changes” button.

Drupal Search API Field Settings

NOTE: You can adjust the ‘boost’ level as need to increase or decrease the importance the index will give to that field.

FILTERS SETUP

Now we need to set up the fields for our index. You should already be on the “Filters” tab for your index.

  1. Click the “Bundle filter” checkbox so we can limit our index to the Article content type.
  2. Click the “Exclude unpublished nodes” checkbox if you do not want your unpublished nodes to be indexed.
  3. At the button of the form you should now see of list of “Bundles” (exposed by checking the “Bundle Filter” option). Select the “Article” bundle and select the option to ‘only [index] those from the selected bundles’
  4. Click the “Save Configuration” button.

NOTE: For this example we’ve created a few fake Article nodes and populated them some titles, body text, taxonomy tags and authors (Users).

Drupal Search API Filter Settings

FACET SETUP

Now click the “Facets” tab in your Search Index and select the following fields to create facets out of:

  1. Tags
  2. Author > Name

Drupal Search API Facet Settings

NOTE: If your Index is set to "Index Items Immediately" then your Entities will be added to your Index as your create or update them. If this setting is not enabled then your Index will be updated on each Cron run. Either way though, if you make changes to your Search Index settings you will need to rebuild your Index after making the changes. You do this by clicking the "Index Now" button on your Index's View tab.

Drupal Search API Index Now

FACET BLOCKS SETUP

For each Facet created (above) there will be a corresponding Drupal Block available. We'll want to configure these block to display on your search page.

  1. Navigate to the Block configuration page (admin/structure/block)
  2. Find your Search API Blocks (there will be one block for each facet you created earlier and the Block names will start with 'Facet API')
  3. Assign the blocks to the region that you want them to display one. This is usually either Right Sidebar, Left Sidebar, First Sidebar or Second Sidebar.
  4. NOTE: You do not need to assign visibility to the Blocks. They will automatically be displayed only when you ware on a Search API Views page

OPTIONAL: Although optional, you'll most likely want to also enable the "Current search: Standard" to display the result count and parameters of the current search. To do so, simply find the block title "Current search: Standard" and assign it a region. This block usually works best above the main content so the "Content" region with a low weight value should work well.

Drupal Search API Block Settings

VIEW SETUP

Now we'll create a View to display our indexed items and search facets.

  1. Add a new View (admin/structure/views/add)
    • IMPORTANT: Be sure to select the search index name that you created earlier. In this example it is called “Article Index”.
  2. You can create your View however you’d like it to display. In this example, we’ve created our View to display using a Field Display type and the fields we’re using are Node Title, Author Name and Node Tags (taxonomy).
  3. Optional – If you’d like to create a search box for your page add the filter called “Search: Fulltext search”, Expose it and select the text fields to search on. In this example we’re going to allow search on the Title and Body fields so select those two fields.
  4. Save your View once it is setup how you’d like it.

Drupal Search API View Setup

The Magic

Now, when you navigate to your new, Search Index View you'll see the Search Facets appear beside your View. These Facets will automatically display the Facet Fields that you configured and will only display the fields that are contained in your current View.They will also display the Facet count (number of each field contained in your View) and, the best part, they can be combined to produce a very specific list of results!

Drupal Search API View Output

CONCLUSION

This was a very basic example to get you started but, with this very powerful system, you can create complex Search Facets to filter lists using ranges, multiple taxonomy terms, field values, referenced Entities, and much more.

There are additional Facet options available through the Facet's Block configuration options, including, controlling how many items to display, the display type (Check boxes instead of links) and much more. Be sure to explore these options.

NOTE: The Search API module has many Extension Modules available to add additional functionality. A popular Extension Module is the Search API Ranges Module which allow creating slider facets for a high/low range. For example, creating a facet between $100 and $500.

VIDEO VERSION OF THIS TUTORIAL TOPIC

[embedded content]

Dec 16 2014
Dec 16

The world of PHP development is changing. The past few years have seen the rise of a new wave of frameworks, the birth of component-based architectures, the advent of Composer and Packagist.org, and even our own standards body, the PHP-Framework Interoperability Group. A new "Proudly Invented Elsewhere" culture (because everyone loves PIE, right?) has grown exponentially on the reusable community engine of PHP. The future of PHP is now unquestionably in assembling and using decoupled components, from a variety of different sources.

Glasgow Bridge by Moyan Brenn

Specializing and cooperating

For PHP developers this evolution means building with tools from a variety of different sources, not just from Drupal.

When I first started using Drupal nearly a decade ago, PHP was a very different place. Every framework or application was its own universe, and the differences between projects made it normal to buy into one universe or another and hard to get to know many of them.

Drupal, my universe of choice, prided itself on being not really an application nor just a framework. This allowed it to be a "jack of all trades", even if master of none. Getting really, really good at Drupal could serve a developer very well because it let them find creative solutions to many different challenges. At Palantir.net, we had a saying for a while that "Drupal may not always be the best solution, but it is almost always a solution." We could take on almost any project, CMS-ish or framework-ish, knowing that one really good tool really well.

That's not the case anymore. The PHP world – Drupal included – has come together dramatically in recent years and the market has shifted. Drupal itself has evolved, too, to be less a framework and more a first-class Content Management Platform. As it's done so, it has ceded some "pure framework" use cases while pure frameworks have had a renaissance in the form of Symfony2, Zend Framework 2, Aura, Laravel, and other younger projects. The days when knowing just Drupal (or Joomla, or WordPress, or Symfony, or whatever) was all one needed to know are fading. Instead, the future is purpose-built tools born out of common components.

Calgary Peace Bridge by Dave Bloggs

I can work with that!

So where does that leave Drupal 7 developers? It turns out it leaves them with Drupal 8, a child of this new era of PHP. Drupal 8's architectural redesign was driven in a large part, very deliberately, by a desire to bring it more in line with The New PHP, a robust library of tools ready for developers to remix them. The conceptual difference between Drupal, as embodied in Drupal 8, and other major PHP projects is vastly smaller than ever before, providing PHP coders two key advantages: it makes it easier for non-Drupal developers to learn Drupal and it makes it easier for Drupal developers to learn non-Drupal systems.

That's good for everyone for many reasons. For one, it can serve as a solution to Drupal's talent shortage: the easier it is to train experienced, accomplished developers on Drupal, the easier it is for organizations with Drupal sites to find and hire people to maintain them. The most common feedback I get from other PHP developers upon seeing Drupal 8 is "Huh, cool, I can work with that!" I've even gotten that feedback from non-PHP developers, who are another untapped (but now tappable) resource to grow the pool of Drupal service providers the market has been asking for.

On the flipside, this all opens up new opportunities for Drupal developers outside of Drupal. Heretical as it may be to say, Drupal is not the answer to all problems. As software developers, our job is to solve problems, not to solve problems only with Drupal. The more tools we have in our kit, the more problems we can solve effectively. When Drupal isn't the right tool, we need to be able to pivot quickly to the one that fits the job. Pivoting from Drupal 8 and back again will be far easier than ever before.

Navajo Bridge by Glyn Lowe

The future is now

At Palantir, we've already started that process. Last year, we built a REST-centric decoupled CMS platform for a media client using Drupal 7 and the Silex micro-framework, which is based on the same Symfony components as Drupal 8. Earlier this year we built a low-touch site for a client using Sculpin, a PHP-based static site generator that uses some Symfony components but more importantly Twig, the same templating engine used by Drupal 8 (and Symfony, and Oro Platform, Sylius, Foxycart, eZ Publish, phpBB, Piwik, and others). As I write this, my main client project is a two-install all-Symfony project.

Could Drupal have been used for those cases? Quite possibly, but it would not have been a good fit. And that's OK. Look at the commonalities of those projects: Drupal 8, Symfony, Silex, and Sculpin. All four can use Twig templates. All four are built on similar service architectures. All four use, or can use, any of the 30,000 PHP packages available on Packagist. Three of them use the same request/response/routing pipeline. That means any of our engineers can move with relative ease between those systems with only limited retraining. We can use the right tool for the job without having to learn several completely different tools. To do all this all developers only have to learn one main tool: Modern PHP.

The future of PHP is much more heterogeneous than it has been in the past. Of course, such potential is only useful if we’re prepared for it. Every platform has its strengths and weaknesses, and as professional developers we have a responsibility – to ourselves, our clients, and our employers – to know what those are so we can recognize and use the right tool for the job. Modern PHP ties the whole population of solutions together.

Sunset bridge by F Mira

Be the bridges connecting the islands

Two years ago I called on developers to Get Off the Island and attend conferences outside of their comfort space: to meet new faces, learn new things, and open their minds. I've been pleased to see many doing so, with Drupal developers showing up and presenting at general PHP conferences and general PHP developers attending and presenting at DrupalCons and Drupal Camps.

In 2015, let's take that a step further: Don't just learn; Build.

Make it your goal in 2015 to build and launch something meaningful with a new PHP tool. If you're traditionally a Drupal developer, build an all-Symfony project. If you normally use Symfony, build a Zend Framework-based project. If Zend Framework is your home turf, build and ship a project using Aura. If Aura is your happy place, see what Laravel has to offer. If Laravel is your go-to tool, launch a site with Drupal 8. Or even combine pieces of all of them – you can do that now – each playing to its strengths. Get out of your comfort zone … discover a bigger comfort zone!

But don't just build; Teach. Document what you learn by going through the new process. Blog your experiences with your new tools. Share your new insights with your colleagues at your company, at your user group, at conferences.

And don’t just teach; Help. Be a part of The New PHP by helping to build it and the community around it. Be the bridges that are bringing the islands of PHP together, and become a better developer in the process.

Let's learn, let’s teach, let’s help and let’s all build something good together.

Guest Dossier

Image credits

bridge_navajo.jpg - Image by Glyn Lowe - https://www.flickr.com/photos/glynlowe/7183121469 - License https://creativecommons.org/licenses/by/2.0/

bridge_peace_calgary.jpg - Image by Dave Bloggs - https://www.flickr.com/photos/davebloggs007/15267799517 - License https://creativecommons.org/licenses/by/2.0/

bridge_glasgow.jpg - Image by Moyan Brenn - https://www.flickr.com/photos/aigle_dore/4019285756 - License https://creativecommons.org/licenses/by-nd/2.0/

bridge_sunset.jpg - Image by F Mira https://www.flickr.com/photos/fhmira/5019343521 - License https://creativecommons.org/licenses/by-sa/2.0/

Dec 16 2014
Dec 16

The world of PHP development is changing. The past few years have seen the rise of a new wave of frameworks, the birth of component-based architectures, the advent of Composer and Packagist.org, and even our own standards body, the PHP-Framework Interoperability Group. A new "Proudly Invented Elsewhere" culture (because everyone loves PIE, right?) has grown exponentially on the reusable community engine of PHP. The future of PHP is now unquestionably in assembling and using decoupled components, from a variety of different sources.

Glasgow Bridge by Moyan Brenn

Specializing and cooperating

For PHP developers this evolution means building with tools from a variety of different sources, not just from Drupal.

When I first started using Drupal nearly a decade ago, PHP was a very different place. Every framework or application was its own universe, and the differences between projects made it normal to buy into one universe or another and hard to get to know many of them.

Drupal, my universe of choice, prided itself on being not really an application nor just a framework. This allowed it to be a "jack of all trades", even if master of none. Getting really, really good at Drupal could serve a developer very well because it let them find creative solutions to many different challenges. At Palantir.net, we had a saying for a while that "Drupal may not always be the best solution, but it is almost always a solution." We could take on almost any project, CMS-ish or framework-ish, knowing that one really good tool really well.

That's not the case anymore. The PHP world – Drupal included – has come together dramatically in recent years and the market has shifted. Drupal itself has evolved, too, to be less a framework and more a first-class Content Management Platform. As it's done so, it has ceded some "pure framework" use cases while pure frameworks have had a renaissance in the form of Symfony2, Zend Framework 2, Aura, Laravel, and other younger projects. The days when knowing just Drupal (or Joomla, or WordPress, or Symfony, or whatever) was all one needed to know are fading. Instead, the future is purpose-built tools born out of common components.

Calgary Peace Bridge by Dave Bloggs

I can work with that!

So where does that leave Drupal 7 developers? It turns out it leaves them with Drupal 8, a child of this new era of PHP. Drupal 8's architectural redesign was driven in a large part, very deliberately, by a desire to bring it more in line with The New PHP, a robust library of tools ready for developers to remix them. The conceptual difference between Drupal, as embodied in Drupal 8, and other major PHP projects is vastly smaller than ever before, providing PHP coders two key advantages: it makes it easier for non-Drupal developers to learn Drupal and it makes it easier for Drupal developers to learn non-Drupal systems.

That's good for everyone for many reasons. For one, it can serve as a solution to Drupal's talent shortage: the easier it is to train experienced, accomplished developers on Drupal, the easier it is for organizations with Drupal sites to find and hire people to maintain them. The most common feedback I get from other PHP developers upon seeing Drupal 8 is "Huh, cool, I can work with that!" I've even gotten that feedback from non-PHP developers, who are another untapped (but now tappable) resource to grow the pool of Drupal service providers the market has been asking for.

On the flipside, this all opens up new opportunities for Drupal developers outside of Drupal. Heretical as it may be to say, Drupal is not the answer to all problems. As software developers, our job is to solve problems, not to solve problems only with Drupal. The more tools we have in our kit, the more problems we can solve effectively. When Drupal isn't the right tool, we need to be able to pivot quickly to the one that fits the job. Pivoting from Drupal 8 and back again will be far easier than ever before.

Navajo Bridge by Glyn Lowe

The future is now

At Palantir, we've already started that process. Last year, we built a REST-centric decoupled CMS platform for a media client using Drupal 7 and the Silex micro-framework, which is based on the same Symfony components as Drupal 8. Earlier this year we built a low-touch site for a client using Sculpin, a PHP-based static site generator that uses some Symfony components but more importantly Twig, the same templating engine used by Drupal 8 (and Symfony, and Oro Platform, Sylius, Foxycart, eZ Publish, phpBB, Piwik, and others). As I write this, my main client project is a two-install all-Symfony project.

Could Drupal have been used for those cases? Quite possibly, but it would not have been a good fit. And that's OK. Look at the commonalities of those projects: Drupal 8, Symfony, Silex, and Sculpin. All four can use Twig templates. All four are built on similar service architectures. All four use, or can use, any of the 30,000 PHP packages available on Packagist. Three of them use the same request/response/routing pipeline. That means any of our engineers can move with relative ease between those systems with only limited retraining. We can use the right tool for the job without having to learn several completely different tools. To do all this all developers only have to learn one main tool: Modern PHP.

The future of PHP is much more heterogeneous than it has been in the past. Of course, such potential is only useful if we’re prepared for it. Every platform has its strengths and weaknesses, and as professional developers we have a responsibility – to ourselves, our clients, and our employers – to know what those are so we can recognize and use the right tool for the job. Modern PHP ties the whole population of solutions together.

Sunset bridge by F Mira

Be the bridges connecting the islands

Two years ago I called on developers to Get Off the Island and attend conferences outside of their comfort space: to meet new faces, learn new things, and open their minds. I've been pleased to see many doing so, with Drupal developers showing up and presenting at general PHP conferences and general PHP developers attending and presenting at DrupalCons and Drupal Camps.

In 2015, let's take that a step further: Don't just learn; Build.

Make it your goal in 2015 to build and launch something meaningful with a new PHP tool. If you're traditionally a Drupal developer, build an all-Symfony project. If you normally use Symfony, build a Zend Framework-based project. If Zend Framework is your home turf, build and ship a project using Aura. If Aura is your happy place, see what Laravel has to offer. If Laravel is your go-to tool, launch a site with Drupal 8. Or even combine pieces of all of them – you can do that now – each playing to its strengths. Get out of your comfort zone … discover a bigger comfort zone!

But don't just build; Teach. Document what you learn by going through the new process. Blog your experiences with your new tools. Share your new insights with your colleagues at your company, at your user group, at conferences.

Be a part of The New PHP by helping to build it and the community around it. Be the bridges that are bringing the islands of PHP together, and become a better developer in the process.

Let's learn, let’s teach, and let’s all build something good together.

Guest Dossier

Image credits

bridge_navajo.jpg - Image by Glyn Lowe - https://www.flickr.com/photos/glynlowe/7183121469 - License https://creativecommons.org/licenses/by/2.0/

bridge_peace_calgary.jpg - Image by Dave Bloggs - https://www.flickr.com/photos/davebloggs007/15267799517 - License https://creativecommons.org/licenses/by/2.0/

bridge_glasgow.jpg - Image by Moyan Brenn - https://www.flickr.com/photos/aigle_dore/4019285756 - License https://creativecommons.org/licenses/by-nd/2.0/

bridge_sunset.jpg - Image by F Mira https://www.flickr.com/photos/fhmira/5019343521 - License https://creativecommons.org/licenses/by-sa/2.0/

Dec 16 2014
Dec 16
Using Bootstrap as Drupal base theme

There are so many website themes, frameworks and opinions out there ... how do you decide which is the best foundation for your next project?

You can purchase a ready to use theme, start from a base theme with as much or as little foundation as you like or you can build your own HTML from scratch and then add Wordpress or Drupal API code to your HTML.

I have personally used all of these approaches across various projects, but I have a strong preference these days for Bootstrap as the foundation for every website I build on Drupal, Wordpress and small static pages.

Why I don't use the other approaches any longer

Of course, there are more reasons than I can list here, but a few notes on why I don't start projects without Bootstrap:

  • Ready to use purchased theme: There are many thousands of Wordpress ready to go themes and fewer (but still many) Drupal ready-to-go themes. My biggest reason to avoid these has to do with the design being mostly locked in already. These themes are usually complete designs just waiting for a logo, copy and images. I find I have to argue with the theme too much to bend it away from the built-in design. Some of these themes are so complex they actually change the CMS core functions, but I need consistency across projects in the CMS so my content editors can work across client sites.
  • Build your own HTML: For years, my preferred approach was to build my own perfectly clean HTML/CSS. This has the benefit of being clean and exactly suited to the needed design. In recent years, though, I have fallen out of love with this approach due to the need to reinvent the wheel with every project. Now that every website needs to be responsive and work across browsers and devices and websites requirements include sophisticated CSS3 effects, jQuery magic and HTML5 sophistication—it just doesn't make sense to start over with each project.
  • Wonderful frameworks like 960 grid and others don't include all the LESS/SASS mix-ins that I have at my fingertips with Bootstrap.

Why I use Bootstrap

Before I begin, I should include a quick disclaimer—I have used and also love Zurb Foundation. Bootstrap and Foundation have pretty good feature parity these days. We chose Bootstrap when Foundation didn't have as much Drupal support, but Foundation has pretty robust Drupal support now so in the end you can choose by features meaningful to you.

  • Boostrap includes all the documentation and code to work across browsers and devices.
  • Bootstrap includes tons of mix-ins that allow sophisticated content even inside the WYSIWYG. I also use it in template code and event in Drupal views and Drupal Display Suite.
  • Bootstrap has mobile functionality built in. Whether I'm mobile-first or desktop-first, I am pleasantly surprised when I'm styling a page and then check other breakpoints and almost every time, it is already 95% there ... I just need to add a few adjustments here and there.
  • Many common advanced widgets are already included: buttons, carousels, accordion lists, tabs, and more are already included and just require you to add some classes to your html output.
  • Lots of documentation already exists. If I run into an issue, I can check the Bootstrap website, Stack Overflow and Drupal theme support issue queues.

Drupal-specific modules used to help create a Drupal Bootstrap site

What do you think?

If you've tried one of these methods and have other opinions, please chime in. I seem to check out themes and frameworks like a hobby. I often find myself downloading and playing with a new framework or theme and constantly evaluating it against what I do now. Have you found something better?

Dec 16 2014
Dec 16

YouTube is a great service for storing and managing your videos. While this is handy, many people want to be able to display their videos within their own website as well. You can easily copy the embed code from YouTube and paste that into your content body field, but that only works properly if you have your text formats set up correctly, and you may not want to open that up to just anyone. You also have no way of keeping track of the videos from within your site. This is where the Media, Media Internet Sources (part of the Media package), and Media: YouTube modules can help give you a nice, seamless way to integrate YouTube videos into your site, and give really nice control over how those videos look, along with some built-in media management tools.

This tutorial, based on the free Embed YouTube Videos video from the Working with Media Module in Drupal 7 series, expects a basic understanding of the 1.x Media module. We're using 1.x because that is the current stable release, and version 2 is still in an alpha state. We're starting off with the assumption that you have a Drupal 7 site up and running, with the Media and Media: YouTube modules already downloaded and ready to go. At the end of this tutorial you will have a new video content type created, with a YouTube field that lets you paste in a YouTube video URL or embed code. We'll be diving into Media's file display settings to also make sure the embedded video displays we way we want in different contexts.

The key to making all of this work is the Media Internet Sources module, provided as part of the Media package. Without getting into lots of details, this module provides a standard mechanism for handling stream wrappers, which means you can ingest a lot of different internet services. This is a base module that you use combined with specific provider modules, which extend Media Internet Sources for use with a particular service. You can see a list of the various providers you can use on the Modules that Extend the Media Module page, under the Provider Projects section. We're focused on YouTube videos here, but you can use this same base to integrate many different services into your site.

Hands-on

So let's get started! We're going to enable the modules we need, and then build out the content type. Once that's in place, we'll grab a video URL and make our first video node. To clean things up, we'll dive into Media file display settings and make sure the video displays just the way we like it.

Create the Video Content Type

  1. In the Administrative menu, go to Modules (admin/modules).
  2. Enable the Media, Media Internet Sources, and Media: YouTube modules (located under the Media section).
  3. In the Administrative menu, go to Structure > Content types (admin/structure/types), and click on the "Add content type" link.
  4. Type Video in the Name field, and leave all other defaults. Click the "Save and add fields" button.
  5. Add a new field called YouTube video with the following settings:
    Field name Value Label YouTube video Field Type File Widget Media file selector
  6. Click "Save" and then accept the defaults for the Field Settings screen, and click "Save field settings." Note that we are not going to actually upload any files to this field since we're using an embed, so the upload settings can be ignored.
  7. Complete the Video field settings using the following values, leaving any others at their defaults:
    Field name Value Label YouTube video Required Checked Allowed remote media types Video Allowed URI schemes youtube:// (YouTube videos)
  8. Click the "Save settings" button.

Create a Video Node
Now that we have a content type, go ahead and create a new video node as you normally would, entering whatever text you want for the Title and Body fields. When you come to the YouTube video field:

  1. Go to YouTube.com, find a video you like, and copy either the URL or the embed code from underneath the video player, under the "Share" tab. (You can also just grab the URL from your browser's address bar.)
    YouTube video links
  2. Back on your node creation form, click the "Select media" button. This will pop open a media selector.
  3. Click on the "Web" tab, and paste your YouTube video information into the URL or Embed code field. Click "Submit."
  4. Save the node.

Fix the Video Field Display
You'll see that our video is not actually showing up as a video player, but as a link instead, so we need to go tweak the field display.

  1. In the Administrative menu, go to Structure > Content types (admin/structure/types), and click on the "manage display" link for the Video content type.
  2. Update the settings for the YouTube video field as follows:
    Field name Value Label hidden Format Rendered file
  3. Click "Save" and close the overlay. Our video is now displaying as a playable video player.

Modify the Media File Display
The content type is now working as expected, and displaying my video properly, but if you go back and edit this node, you'll notice that we have the same large player displaying on the edit form, and this is actually covering up some important functions, like being able to remove the video from the field. We'll finish this up by fixing that, and getting to understand how you can manipulate the video file display from within your own site. Much like content types, Media field types have different view modes. On a content type, these are things like Default, Full node, or Teaser. On a field type, you have Default, Preview, Large, etc. We need to fix the Preview view mode that is being shown on the node edit screen, as right now it is just falling back to the Default mode that the node is using.

One important thing to note is that the "file display" is what we are looking for here, not "manage display." The file display digs into the file itself, while manage display for the file types lets us control meta information about the file.

  1. In the Administrative menu, go to Configuration (admin/config) and click the "File types" link (admin/config/media/file-types), in the Media section.
  2. Click "manage file display" for the Video file type.
  3. Click the "Preview" sub-tab to get to the settings for the Preview mode.
  4. Check the YouTube Preview Image option in the Enabled displays field, which will open up a Display settings section at the bottom of the page.
  5. Select square_thumbnail for the Image style field, and click "Save settings."

Now if you go back and edit the video node you created earlier, you'll see that the YouTube video field is showing a nice, tame square thumbnail instead of the big video player. If you wanted to change the settings for the video display on the node, you can always go into the Video file display settings and configure the Default, or other file view modes, to the size you prefer.

Summary

In this short tutorial you've built a new video content type that lets you easily embed YouTube videos, using the Media, Media Internet Sources, and Media: YouTube modules. The advantage of using Media for this is that in addition to a handy field for embedding your video, you also get the media management tools to keep track of and reuse these videos on your site, in addition to having nice, fine-grained control over the output. Media gives you display options for different kinds of files, in different contexts, or view modes, including embedded YouTube videos through the File Display settings. Hopefully this introduction not only showed you how to use embedded videos very quickly and easily, but also gave a look at different ways you can use and customize the Media package for your next project.

Dec 16 2014
Dec 16
function mymodule_views_query_alter(&$view, &$query) {

  global $user;
        
  if (($view->name === 'coaches') || ($view->name === 'trainers')) { 
   
    if (!in_array('trainer', $user->roles) && !in_array('admin', $user->roles)) {
  
      $view->query->fields['field_data_field_profile_hidden'] = array(
        'field' => 'field_profile_hidden_value',
        'table' => 'field_data_field_profile_hidden',
        'alias' => 'field_data_field_profile_hidden'
      );
      
      $join = new views_join;
      $join->table ='field_data_field_profile_hidden';
      $join->left_table = 'users';
      $join->left_field = 'uid';
      $join->field = 'entity_id';
      $join->extra = array(
        0 => array('field' => 'entity_type', 'value' => 'user'),
      );
      $join->type = "LEFT";
      $join->extra_type = 'AND';
      $join->adjusted = 'TRUE';
      
      // add the join
      $view->query->table_queue['field_data_field_profile_hidden'] = array(
        'table' => 'field_data_field_profile_hidden',
        'num' => 1,
        'alias' => 'field_data_field_profile_hidden',
        'join' => $join,
        'relationship' => 'users'
      );
  
      $view->query->tables['node']['field_data_field_profile_hidden'] = array(
        'count' => 1,
        'alias' => 'field_data_field_profile_hidden'
      );
  
      $view->query->where[2]['conditions'][] = array(
        'field' => 'field_profile_hidden_value',
        'value' => 0,
        'operator' => '='
      );
  
    }
  }
}
/**
 * Implements hook_update_N().
 */
function mymodule_update_7001(&$sandbox) {

  $uids = db_select('users', 'u')
    ->fields('u', array('uid'))
                ->execute()
                ->fetchCol();
                
  foreach ($uids as $uid) {
  
    db_insert('field_data_field_profile_hidden')
      ->fields(array(
        'entity_type' => 'user',
        'bundle' => 'user',
        'entity_id' => $uid,
        'revision_id' => $uid,
        'language' => 'und',
        'delta' => 0,
        'field_profile_hidden_value' => 0,
    ))
    ->execute();
  
  }             
}
Sam
Dec 16 2014
Dec 16

Earlier this week I had the opportunity to be the first developer at Code Drop to sit an exam to become an "Acquia Certified Developer". I managed to clear the exam with the following results:

  • Section 1 - Fundamental Web Development Concepts: 87%
  • Section 2 - Site Building: 87%
  • Section 3 - Front end development (Theming) : 92%
  • Section 4 - Back end development (Coding) : 81%

Like many others who have done the exam, I will briefly run over my experience and thoughts on the whole process.

Setup & Software

The testing software provided by Kryterion had some serious rough edges and inconsistencies. All in all, I probably spent 20 minutes chatting to support to try and iron out issues with my account and to clarify inconsistencies with their instructions. Amongst the issues were:

  • Total meltdown of my computer after unplugging my external monitors to center the camera on my face during the Biometric Profile.
  • No clear status indicating if the Biometric profile was successful after my computer restarted.
  • General confusion as to why the character "?" was not included as one of the “special characters” required to make up passwords (a trivial point I'll admit).
  • Inconsistent information about what kind of camera and environment is required for the examination.
  • Inconsistent information about software and hardware requirements, specifically around requiring several network ports to be open?
  • The software simply shutting off completely twice during the exam with no feedback as to why (I now suspect it was due to my internet connection dropping momentarily and I was able to resume the test where I left off by relaunching from the online portal).
  • A general lack of polish when being guided through the setup and purchasing of the examination.

All of those quarms aside, once things were churning along, the software did allow me to select answers to questions and it did spit out a score at the end. Ironically, if you excluded all of the faux security around cheating, Drupal would have been an ideal platform for the e-commerce and testing features.

Getting prepared

I read over the Get Ready guide provided by Acquia a few hours before taking the exam.

  • Some of the documentation linked to was for Drupal 6, which did throw me initially. There wasn’t anything that stuck out to me as being D6 specific in the exam.
  • Skimming some of the documentation I was less familiar with was very valuable, especially when I knew I leaned on documentation and my IDE for certain things frequently.
  • A lot of the material linked didn’t come up in any form in the exam, but I imagine my 60 questions were selected randomly from a pool.
  • The relevant training videos from Drupalize.me was a nice touch. Code Drop are an avid subscriber to the website already!

I think in a lot of cases, the questions did heavily lend themselves to someone who had been getting their hands dirty with Drupal as opposed to someone who had memorised the documentation (which is a great sign in my opionion).

Evaluating the test

My thoughts on the usefulness of the test probably echo those of others who have written about it:

Certification isn’t meant to be used as a stand alone method of evaluating candidates.
Heather James

There are many other aspects to recruitment for which certification does not provide a substitute; it is only one piece of the puzzle.
Dries Buytaert

As the above quotes concisely sum up, I would be hesitant to say scoring a passing grade of 65% would be an automatic qualifier for any Drupal development role, but I think passing the exam and spending the time and money to take the exam are good qualifiers for developers who are invested in Drupal. Being invested in Drupal along with skills and experience are all part of the DNA of an excellent developer.

At Code Drop, the following factors come into play when we are looking for Drupal developers:

  • Involvement in the Drupal community (contrib, core, support, conferences, stack exchange, whatever).
  • A hands on interview with coding challenges (we have kicked around the idea of formalising these challenges into an interactive Drupal module which verifies the list of challenges have been completed).
  • The desire to continue to learn and improve their Drupal skills over time.

I imagine most Drupal agencies have some similar set of rules for evaluating candidates. 

Summing up

I would highly recommend taking the certification exam if you are a Drupal developer. If you want to be prequalified as someone who has worked with Drupal before and is interested in the platform, it's a no-brainer.

Dec 15 2014
Dec 15

Earlier this year we undertook a project to upgrade a client's infrastructure to all new servers including a migration from old Puppet scripts which were starting to show their age after many years of server and service changes. During this process, we created a new set of Puppet scripts using Hiera to separate configuration data from modules. The servers in question were all deployed with CentOS, and it soon became obvious that we needed a modular way in Puppet to install and configure yum package repositories from within our various Puppet modules.

Searching through the Puppet Forge uncovered a handful of modules created to deal with yum repos, however most were designed to implement a single repository, or were not easily configurable via Hiera, which was one of our main goals for the new Puppet scripts. So, we decided to create our own module, and the yumrepos Puppet module was born.

The idea behind the module is to provide a clean and easy way to pull in common CentOS/RHEL yum repos from within Puppet. By wrapping each repo with its own class (e.g. yumrepos::epel and yumrepos::ius), we gain the ability to override default class parameters with Hiera configuration, making the module easy to use in most any environment. For example, if you have your own local mirror of a repo, you can override the default URL parameter either in Hiera, or from the class declaration without having to directly edit any files within the yumrepos module. This is as easy as:

  1. In your calling class, declare the yumrepo class you need. In this example, we'll use EPEL: class { 'yumrepos::epel': }
  2. In your Hiera configuration, you can configure the repo URL with: yumrepos::epel::epel_url: http://your.local.mirror/path/to/epel/

Currently the yumrepos module provides classes for the following yum repos:

  • Drupal Drush 5
  • Drupal Drush 6
  • EPEL
  • IUS Community (Optionally: IUS Community Archive)
  • Jenkins
  • Jpackage
  • Percona
  • PuppetLabs
  • RepoForge
  • Varnish 3
  • Zabbix 2.4

Each repo contains the GPG key for the repo (where available) and defaults to enabling GPG checks. Have another repo you'd like to see enabled? Feel free to file an issue or pull request at https://github.com/tag1consulting/puppet-yumrepos

Additionally, yumrepos classes accept parameters for package includes or excludes so that you can limit packages on a per-repo basis. These translate to the includepkgs and exclude options within a yum repo configuration. Similar to overriding the default repo URL, these options can be overridden by passing parameters within the class declaration or by setting the appropriate variables within Hiera.

Once we had the yumrepos module in place, we had an easy way to configure yum repos from within our other modules. Stay tuned to the blog; we'll have more information about the overall Puppet scripts coming soon.

Dec 15 2014
Dec 15

Bronwen Buswell, Drupal Newcomer This post is part of an ongoing series detailing the new personas that have been drawn up as part of our Drupal.org user research.

Bronwen Buswell is a newcomer to Drupal. Based out of Colorado Springs, Colorado, Bronwen works as a Conference and Communications Coordinator at a nonprofit called PEAK Parent Center, which is dedicated to supporting the families of children with disabilities. While Bronwen’s role isn’t technical, she needs to use her company’s website as part of getting her work done.

“We’re federally designated by the US Department of Education, so we try to be a total one-stop shop information and referral center,” Bronwen said. “Families can call us about any situation related to their child, and we will either refer them to the right agency or provide what they need. We’re focused on helping families navigate the education and special education systems, and we serve families with children ages birth through 26, with all sorts of disabilities, including autism, down syndrome, learning disabilities, and so on."

Keeping Up With Technology

In the past few years, PEAK Parent Center’s website became very outdated, and this was a problem. Bronwen’s clients were very dependent on being able to receive assistance over the phone, as many of the resources that the center provides are not readily available online. When updates needed to be made, Bronwen and her company were forced to rely on their tech vendors to make changes to the website, as they were working with a custom solution rather than a CMS.

“Our website was pre-cutting edge, made by local vendors, all in HTML code and SQL database. We had excellent tech vendors who helped us create what we needed, and this was before the CMS options came along so it was really good at first. However, in the past 5 to 6 years, it has gotten really archaic, and we’re super reliant upon our vendors for updating our website. What’s simple in a CMS is complex for us,” Bronwen said.

After doing lots of research and working with the federal government to find the best solution for PEAK Parent Center and other centers like it, Bronwen and her colleagues decided to explore using Drupal to create a site template that could be deployed for PEAK Parent Center  and for other similar centers that it supports across the country.

“We're the technical assistance center for parent centers like ours in a 12 state region,” said Bronwen. “When [Drupal Association Executive Director] Holly Ross was at NTEN we started going to their conferences, which led us to launch a tech leadership initiative where we supported participating parent centers across the nation. As part of that, we got connected with great consultants and thinkers in tech, and we were asked by the US Department of Education to participate in the creation of website templates in 2 content management systems — Wordpress and Drupal — that could be used in other parent centers in the future."

Getting Experienced Assistance

With help from Aaron Pava and Nikki Pava at Alegria Partners, the staff at PEAK Parent Center has been learning to use their new Drupal website. Aaron has advised Bronwen and her colleagues every step of the way, from proposing solutions in the discovery process to walking Bronwen and her coworkers through specific tasks.

Occasionally, Bronwen encounters small problems due to updates or little glitches with distributions, which is why Aaron has encouraged her to get involved and do some training on Drupal. Unfortunately, most of Bronwen’s time is spent trying to get the website ready to launch, as she’s under pressure from the federal government and her board of directors to deploy the new site. Though Bronwen isn’t working on the technical side of the website, she’s busy populating it with content and making sure that it will be a useful tool for her clients.

“What I haven’t done is specific Drupal training,” said Bronwen. “I know about Lynda and Build A Module, but I’ve only had time to do sessions one-on-one with Aaron, for example, ‘Here’s how to upload content in this template.’

"I have learned a lot on Drupal.org, but it’s been primarily through Aaron sending me a link— for example, he’ll send me links about Red Hen since we’re exploring our CRM options— but I haven’t surfed around it much,” Bronwen added.

Areas For Improvement

Bronwen wishes there was a recommended Drupal 101 section on Drupal.org, something that would help content editors like herself learn to use the CMS better, but for now, she is limited to relying on more educated ambassadors for Drupal to point her in the right direction.

“It’s delicate to recommend vendors,” said Bronwen, "but it seems that the community is really powerful, and is certainly one of the most unique aspects that sets Drupal aside from other CMS options. Even a few vendors recommended by the community, or a recommend Drupal 101 lesson where you can go through it, go off and work in Drupal, and come back and get Drupal 201 would be really valuable for me.

“I know that there are local Drupal meet-ups that happen all over the country” Bronwen added. “[One group we talked with] told us that nonprofits can go to these events and say “I need this or that,” and some hardcore Drupal techie will take the work on pro bono. That was another factor that helped draw us to using Drupal — the availability of the community. It would be useful if there was more information on how to tap into those meetups, perhaps, when they’re happening."

Bronwen knows that the Drupal community is really powerful, and considers it one of the most unique aspects that sets Drupal aside from other CMS options. She is excited by the availability of the Drupal community, and is looking forward to interacting with it and working with them as she continues to run and improve PEAK Parent Center’s website.

Dec 15 2014
Dec 15

I just posted a large excerpt from Ansible for DevOps over on the Server Check.in blog: Highly-Available Infrastructure Provisioning and Configuration with Ansible. In it, I describe a simple set of playbooks that configures a highly-available infrastructure primarily for PHP-based websites and web applications, using Varnish, Apache, Memcached, and MySQL, each configured in a way optimal for high-traffic and highly-available sites.

Here's a diagram of the ultimate infrastructure being built:

Highly Available Infrastructure

The configuration is similar to what many larger Drupal sites would use, and with the exception of the varnish default.vcl and the actual PHP script being deployed (in the example, it's just a PHP file that tests the rest of the infrastructure and outputs success/fail statuses), you could drop a Drupal site on the Apache servers and immediately start scaling up your traffic!

The example highlights the powerful simplicity of Ansible as a tool for not only configuration management (like Puppet, Chef, etc.), but also for provisioning and managing servers in different cloud providers. With under a hundred lines of YAML configuration, I can spin up the exact same infrastructure locally with Vagrant and VirtualBox, on DigitalOcean droplets, or on AWS EC2 instances!

Dec 15 2014
Dec 15

Angular.js is the hot new thing right now for designing applications in the client. Well, it’s not so new anymore but is sure as hell still hot, especially now that it’s being used and backed by Google. It takes the idea of a JavaScript framework to a whole new level and provides a great basis for developing rich and dynamic apps that can run in the browser or as hybrid mobile apps.

logo_drupal

In this article I am going to show you a neat little way of using some of its magic within a Drupal 7 site. A simple piece of functionality but one that is enough to demonstrate how powerful Angular.js is and the potential use cases even within heavy server-side PHP frameworks such as Drupal. So what are we doing?

We are going to create a block that lists some node titles. Big whoop. However, these node titles are going to be loaded asynchronously using Angular.js and there will be a textfield above them to filter/search for nodes (also done asyncronously). As a bonus, we will also use a small open source Angular.js module that will allow us to view some of the node info in a dialog when we click on the titles.

So let’s get started. As usual, all the code we write in the tutorial can be found in this repository.

Ingredients

In order to mock this up, we will need the following:

  • A custom Drupal module
  • A Drupal hook_menu() implementation to create an endpoint for querying nodes
  • A Drupal theme function that uses a template file to render our markup
  • A custom Drupal block to call the theme function and place the markup where we want
  • A small Angular.js app
  • For the bonus part, the ngDialog Angular module

The module

Let us get started with creating a custom module called Ang. As usual, inside the modules/custom folder create an ang.info file:

name = Ang
description = Angular.js example on a Drupal 7 site.
core = 7.x

…and an ang.module file that will contain most of our Drupal related code. Inside this file (don’t forget the opening tag), we can start with the hook_menu() implementation:

/**
 * Implements hook_menu().
 */
function ang_menu() {
  $items = array();

  $items['api/node'] = array(
    'access arguments' => array('access content'),
    'page callback'     => 'ang_node_api',
    'page arguments' => array(2),
    'delivery callback' => 'drupal_json_output'
  );

  return $items;
}
/**
 * API callback to return nodes in JSON format
 *
 * @param $param
 * @return array
 */
function ang_node_api($param) {

  // If passed param is node id
  if ($param && is_numeric($param)) {
    $node = node_load($param);
    return array(
      'nid' => $param,
      'uid' => $node->uid,
      'title' => check_plain($node->title),
      'body' => $node->body[LANGUAGE_NONE][0]['value'],
    );
  }
  // If passed param is text value
  elseif ($param && !is_numeric($param)) {
    $nodes = db_query("SELECT nid, uid, title FROM {node} n JOIN {field_data_body} b ON n.nid = b.entity_id WHERE n.title LIKE :pattern ORDER BY n.created DESC LIMIT 5", array(':pattern' => '%' . db_like($param) . '%'))->fetchAll();
    return $nodes;
  }
  // If there is no passed param
  else {
    $nodes = db_query("SELECT nid, uid, title FROM {node} n JOIN {field_data_body} b ON n.nid = b.entity_id ORDER BY n.created DESC LIMIT 10")->fetchAll();
    return $nodes;
  }
}

In hook_menu() we declare a path (api/node) which can be accessed by anyone with permissions to view content and which will return JSON output created in the callback function ang_node_api(). The latter gets passed one argument, that is whatever is found in the URL after the path we declared: api/node/[some-extra-param]. We need this argument because of we want to achieve 3 things with this endpoint:

  1. return a list of 10 most recent nodes
  2. return a node with a certain id (api/node/5 for example)
  3. return all the nodes which have the passed parameter in their title (api/node/chocolate for example, where chocolate is part of one or more node titles)

And this is what happens in the second function. The parameter is being checked against three cases:

  • If it exists and it’s numeric, we load the respective node and return an array with some basic info form that node (remember, this will be in JSON format)
  • If it exists but it is not numeric, we perform a database query and return all the nodes whose titles contain that value
  • In any other case (which essentially means the lack of a parameter), we query the db and return the latest 10 nodes (just as an example)

Obviously this callback can be further improved and consolidated (error handling, etc), but for demonstration purposes, it will work just fine. Let’s now create a theme that uses a template file and a custom block that will render it:

/**
 * Implements hook_theme().
 */
function ang_theme($existing, $type, $theme, $path) {
  return array(
    'angular_listing' => array(
      'template' => 'angular-listing',
      'variables' => array()
    ),
  );
}

/**
 * Implements hook_block_info().
 */
function ang_block_info() {

  $blocks['angular_nodes'] = array(
    'info' => t('Node listing'),
  );

  return $blocks;
}

/**
 * Implements hook_block_view().
 */
function ang_block_view($delta = '') {

  $block = array();

  switch ($delta) {
    case 'angular_nodes':
      $block['subject'] = t('Latest nodes');
      $block['content'] = array(
        '#theme' => 'angular_listing',
        '#attached' => array(
          'js' => array(
            'https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js',
            'https://ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular-resource.js',
            drupal_get_path('module', 'ang') . '/lib/ngDialog/ngDialog.min.js',
            drupal_get_path('module', 'ang') . '/ang.js',
          ),
          'css' => array(
            drupal_get_path('module', 'ang') . '/lib/ngDialog/ngDialog.min.css',
            drupal_get_path('module', 'ang') . '/lib/ngDialog/ngDialog-theme-default.min.css',
          ),
        ),
      );
      break;
  }

  return $block;
}

/**
 * Implements template_preprocess_angular_listing().
 */
function ang_preprocess_angular_listing(&$vars) {
  // Can stay empty for now.
}

There are four simple functions here:

  1. Using hook_theme() we create our angular_listing theme that uses the angular-listing.tpl.php template file we will create soon.
  2. Inside the hook_block_info() we define our new block, the display of which is being controlled inside the next function.
  3. Using hook_block_view() we define the output of our block: a renderable array using the angular_listing theme and which has the respective javascript and css files attached. From the Google CDN we load the Angular.js library files, inside ang.js we will write our JavaScript logic and in the /lib/ngDialog folder we have the library for creating dialogs. It’s up to you to download the latter and place it in the module following the described structure. You can find the files either in the repository or on the library website.
  4. The last function is a template preprocessor for our template in order to make sure the variables are getting passed to it (even if we are actually not using any).

As you can see, this is standard boilerplate Drupal 7 code. Before enabling the module or trying out this code, let’s quickly create the template file so Drupal doesn’t error out. Inside a file called angular-listing.tpl.php, add the following:

        
        
           
        
             

Filter

  • Open {{ node.title }}

Here we have some simple HTML pimped up with Angular.js directives and expressions. Additionally, we have a tag used by the ngDialog module as the template for the dialog. Before trying to explain this, let’s create also our ang.js file and add our javascript to it (since the two are so connected):

angular.module('nodeListing', ['ngResource', 'ngDialog'])

  // Factory for the ngResource service.
  .factory('Node', function($resource) {
    return $resource(Drupal.settings.basePath + 'api/node/:param', {}, {
      'search' : {method : 'GET', isArray : true}
    });
  })

  .controller('ListController', ['$scope', 'Node', 'ngDialog', function($scope, Node, ngDialog) {
    // Initial list of nodes.
    $scope.nodes = Node.query();

    // Callback for performing the search using a param from the textfield.
    $scope.doSearch = function() {
      $scope.nodes = Node.search({param: $scope.search});
    };

    // Callback to load the node info in the modal
    $scope.open = function(nid) {
      $scope.loadedNode = Node.get({param: nid});
      ngDialog.open({
        template: 'loadedNodeTemplate',
        scope: $scope
      });
    };

}]);

Alright. Now we have everything (make sure you also add the ngDialog files as requested in the #attached key of the renderable array we wrote above). You can enable the module and place the block somewhere prominent where you can see it. If all went well, you should get 10 node titles (if you have so many) and a search box above. Searching will make AJAX calls to the server to our endpoint and return other node titles. And clicking on them will open up a dialog with the node title and body on it. Sweet.

But let me explain what happens on the Angular.js side of things as well. First of all, we define an Angular.js app called nodeListing with the ngResource (the Angular.js service in charge communicating with the server) and ngDialog as its dependencies. This module is also declared in our template file as the main app, using the ng-app directive.

Inside this module, we create a factory for a new service called Node which returns a $resource. The latter is in fact a connection to our data on the server (the Drupal backend accessed through our endpoint). In addition to the default methods on it, we define another one called .search() that will make a GET request and return an array of results (we need a new one because the default .get() does not accept an array of results).

Below this factory, we define a controller called ListController (also declared in the template file using the ng-controller directive). This is our only controller and it’s scope will apply over all the template. There are a few things we do inside the controller:

  1. We load nodes from our resource using the query() method. We pass no parameters so we will get the latest 10 nodes on the site (if you remember our endpoint callback, the request will be made to /api/node). We attach the results to the scope in a variable called nodes. In our template, we loop through this array using the ng-repeat directive and list the node titles. Additionally, we create a button for each with an ng-click directive that triggers the callback open(node.nid) (more on this at point 3).
  2. Looking still at the template, above this listing, we have an input element whose value will be bound to the scope using the ng-model directive. But using the ng-change directive we call a function on the scope (doSearch()) every time a user types or removes something in that textfield. This function is defined inside the controller and is responsible for performing a search on our endpoint with the param the user has been typing in the textfield (the search variable). As the search is being performed, the results populate the template automatically.
  3. Lastly, for the the bonus part, we define the open() method which takes a node id as argument and requests the node from our endpoint. Pressing the button, this callback function opens the dialog that uses a template defined inside of the tag with the id of loadedNodeTemplate and passes to it the current scope of the controller. And if we turn to the template file, we see that the dialog template simply outputs the title and the body of the node.

Conclusion

You can see for yourself the amount of code we wrote to accomplish this neat functionality. Most of it is actually boilerplate. A very fast node query block that delivers results asynchronously with all of its benefits. And if you know Angular.js, you can imagine the possibility of enhancing the Drupal experience further.

Now, are you interested to learn more about the love between Angular.js and Drupal? Would you have done anything differently? Let us know in the comments below!

Dec 15 2014
Dec 15

Today most of the websites have search functionality. With the help of Apache Solr the time spent on waiting for a search result can be radically reduced. In this article we are going to set up a basic searching infrastructure on a *nix-based system.

This tutorial is going to show an easy and fast way to set up a Solr search infrastructure powered by Jetty on an Ubuntu 14 or a Debian 7 or a Red Hat Enterprise Linux 7 server. Jetty is a pure Java-based webserver and Java servlet container shipped with Solr, so you do not need to install any third party servlet containers.

Apache Solr needs Java Runtime Environment 1.7 or higher. Check your system:

java -version

If you don't have Java, you can install it from package,

  1. # RHEL
    
  2. sudo yum install java-1.7.0-openjdk
    
  3. # Debian/Ubuntu
    
  4. sudo add-apt-repository ppa:webupd8team/java
    
  5. sudo apt-get update
    
  6. sudo apt-get install oracle-java7-set-default
    

...restart the terminal or reload your configuration,

source ~/.profile or source ~/.bashrc

...and give Java a try:

echo $JAVA_HOME

If you get an empty message, just insert the following lines into the ~/.profile or ~/.bashrc:

  1. # RHEL
    
  2. JAVA_HOME=/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.71-2.5.3.1.el7_0.x86_64/jre
    
  3. PATH=$PATH:$JAVA_HOME/bin
    
  4. export JAVA_HOME
    
  5. export PATH
    
  6. # Debian/Ubuntu
    
  7. JAVA_HOME=/usr/lib/jvm/java-7-oracle
    
  8. PATH=$PATH:$JAVA_HOME/bin
    
  9. export JAVA_HOME
    
  10. export PATH
    

If you can not find Java's home folder, try locating it with

readlink -f "$( which java )"

...and on Debian/Ubuntu install the daemon package for the Solr service:

  1. # Just on Debian/Ubuntu
    
  2. sudo apt-get install daemon
    

After these prerequisites are installed, it's time to install and configure Apache Solr. The archives can be found here with all versions of Solrs: http://archive.apache.org/dist/lucene/solr/. I recommend using 4.3.1 or a higher version on a Drupal 7 site. First download and decompress Solr:

  1. wget https://archive.apache.org/dist/lucene/solr/4.3.1/solr-4.3.1.tgz
    
  2. tar -xzf solr-4.3.1.tgz -C /home/drupal/solr431
    

In case you are as lazy as I am, you will also need a service which can be easily managed like any other (httpd, mysql, memcached etc.) on your system.

On RHEL7, download the Jetty service startup script,
  1. sudo curl -o /etc/init.d/solr http://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk/jetty-distribution/src/main/resources/bin/jetty.sh
    
  2. sudo chmod 0755 /etc/init.d/solr
    

...change references from Jetty configuration to Solr,

sudo perl -pi -e 's/\/default\/jetty/\/sysconfig\/solr/g' /etc/init.d/solr

...set up variables by inserting the lines below (if you previously did not set JAVA_HOME, uncomment the first line in your configuration),

  1. sudo vi /etc/sysconfig/solr
    
  2. # JAVA_HOME=/usr/lib/jvm/java-1.7.0-openjdk-1.7.0.71-2.5.3.1.el7_0.x86_64/jre/bin
    
  3. JAVA_OPTIONS="-Djetty.port=8984 -Dsolr.solr.home=/home/drupal/solr431/example/multicore $JAVA_OPTIONS" 
    
  4. JETTY_HOME=/home/drupal/solr431/example
    
  5. JETTY_USER=drupal
    
  6. JETTY_LOGS=/var/log/solr
    

...and eventually enable the Solr service:

sudo chkconfig solr on

On Debian/Ubuntu, get the Jetty service startup script,
  1. sudo curl -o /etc/init.d/solr http://cheppers.com/sites/default/files/attachments/solr.sh
    
  2. sudo chmod 0755 /etc/init.d/solr
    

...edit the below part of our script that has just been downloaded (set up the install folder, the port and Solr home):

  1. start () {
    
  2.     echo -n "Starting solr..."
    
  3.  
    
  4. # Start the daemon
    
  5.     daemon --chdir='/home/drupal/solr431/example' --command "/usr/bin/java -Djetty.port=8984 -Dsolr.solr.home=multicore -jar start.jar" --respawn --output=/var/log/solr/solr.log --name=solr --verbose
    
  6.  
    
  7.  
    
  8.     RETVAL=$?
    

...and enable the Solr service (if we reboot our system, it will start automatically):

sudo update-rc.d solr defaults

Now with your last command you are going to start the service that you can easily manage with the well-known commands, and after from the admin page: http://yoursite.com:8984/solr

sudo service solr start

Because we want to use the Solr with Drupal, copy the Solr configuration files from Search API Solr Search module into your core:

  1. cp -Rf /your/drupal/site/folder/sites/all/modules/contrib/search_api_solr/solr-conf/4.x/* /home/drupal/solr431/solr/example/multicore/core0/conf/
    
  2. sudo service solr restart
    

And that's all folks!

If you need any other Solr cores (for example you have several staging sites which needs different cores), you can copy an existing core into a new folder next to it, configure your Solr to load the core when Solr (re)starts and set up the new one on the admin page:

  1. cp -R /home/drupal/solr431/example/multicore/core0/ /home/drupal/solr431/example/multicore/othercore/
    
  2. vi /home/drupal/solr431/example/multicore/solr.xml
    

Add a new line at the bottom:

core name="othercore" instanceDir="othercore" />

And add a new core:

If you need different Solr services on your server (one for your development sites, one for your production sites etc.), just copy the Solr and the init script, set up a new home folder and a new port in the config and start it as well:

  1. cp -R /home/drupal/solr431/ /home/drupal/solr431_live/
    
  2. sudo cp /etc/init.d/solr /etc/init.d/solr_live
    
  3. # Edit the config file (on Ubuntu/Debian the init script, on RHEL the also copied /etc/sysconfig/solr_live configuration file)
    
  4. sudo update-rc.d solr_live defaults
    
  5. sudo service solr_live start
    

Sources:

In our next blog post one of my colleagues will show you how you can set up Solr to search in uploaded files as well, so don't forget to check back later!

Dec 15 2014
Dec 15

Many people know that addressfield hasn’t been the easiest module to maintain. There are over 200 countries in the world, each with its own addressing requirements. Addressfield attempted to provide a sane default for all of them, along with a plugin architecture for handling per-country customizations. But with so many countries, the list of needed improvements became never-ending, and the customizations themselves started gathering in only one plugin (address.inc), which quickly became impossible to maintain.

A radical change was needed, so after a lot of research we introduced a new plan for Drupal 8, along with a brand new PHP library we can depend on from addressfield 8.x-2.x. The new plan resolves around two powerful ideas:

  • The introduction of address formats, which hold information on how a country’s address and its form need to be rendered and validated.
  • The use of Google’s addressing dataset, freely available and built for Chrome and Android, with address formats for 200 countries.

The introduced solutions were obviously superior to anything we had before that, but Drupal 8 is still far from production, and we needed improvements on our Drupal 7 sites today, so we decided to try and backport as many concepts as we could into the 7.x-1.x codebase. The result of that is addressfield 7.x-1.0-rc1:

  • Created the concept of an address format.
    An address format is an array which contains the per-country list of used fields, list of required fields, and the field labels.
    Imported address formats for 200 countries, derived from Google’s dataset.
  • Moved administrative areas out of address.inc, into their own include file (addressfield.administrative_areas.inc), added an alter hook.
  • Added administrative areas for 20 countries:
    Argentina, Chile, China, Colombia, Egypt, Hong Kong, India, Indonesia, Ireland, Jamaica, Japan, Malaysia, Mexico, Peru, Russia, South Korea, Spain, Turkey, United Arab Emirates, Venezuela.
  • The field can now be optional.
  • Added plugins for hiding the street address and/or the postal code.

For the first time we know precisely which fields we need, and which labels they should have. We also know which fields are required, instead of just requiring them all, which is a relief to citizens of 130 countries with optional postal codes, for example. Our contributors from all over the world (Russia, China, Australia, others) have already confirmed the big improvements. The best part is, these 50 commits did not result in a single backwards compatibility break.

What’s left? These improvements are still exclusive to the Drupal 8 version:

  1. A “format” string for each address format, showing the exact layout of the fields. The D7 version already has most of this data, derived from Google’s dataset, but it is not as precise.
  2. A more complete list of administrative areas (some still haven’t been imported).
  3. Postal code validation
  4. Predefined localities for Brazil and Chile, localities and dependent localities for China/Taiwan/South Korea.
  5. Switching between major-to-minor and minor-to-major field orderings for China/Japan/South Korea.

The good news is that 1-3 are also backportable, so the community could make it happen.

Please update to 7.x-1.0-rc1 and help us find any remaining issues, so that we can tag 1.0 in the near future.

Dec 15 2014
Dec 15

In the first part of this series, “Scalable & Sustainable Media Management for Drupal Websites”, I talked about media management solutions for Drupal. Specifically, I am interested in managing large amounts of files in a reusable manner. The solution I like best at the moment is Scald.

Just so we don't get confused with some phrasing, Scald stores all media items as custom entities called "atoms"; Scald "contexts" are very similar to view modes.

Setting up and configuring Scald for Drupal takes work and planning. However, if you've ever used the node reference or entity reference modules, you'll catch on very quickly.

Scald comes with a number of sub-modules. To get it working you will first need to install:

  • Scald core – the basic engine to drive the rest

  • Atom reference – a field type to reference (display) media on your page (like entity reference module)

  • DnD Library – so you can drag files from the media library and drop them on your page

  • MEE – if you want users to be able to define metadata on media embedded in textareas (such as copyright information)

These modules will get Scald working, but you'll need to add in some providers (other sub-modules) to define how you get your media assets to your page. Here's what I like:

  • Scald Audio – allows you to upload audio files

  • Scald SoundCloud – allows a SoundCloud URL to be an audio asset

  • Scald File – allows files to be media assets

  • Scald Image – allows image files to be media assets

  • Scald YouTube – allows a YouTube URL to be a video asset

  • Scald Vimeo – allows a Vimeo URL to be a video asset

There is a full list of "Scald providers" here, so you can extend it to include Facebook, Instagram, Pinterest, Twitter, Flickr, Vine, DailyMotion, and more ... up to and including Drupal Commerce products as atoms!

With this many modules installed (I know, it's a lot), you are ready to use Scald. This is what you will need to do:

  1. Add an “Atom reference” field to a content type

  2. Select what atom types can be referenced by this field, for example video, audio, image

  3. Set the representation of the atom on the edit page (this is how the atom will look when you are creating or editing pages with atoms displayed)

  4. Create a node and add an atom to it, like so:

    1. Click on one of the icons in the sidebar slider for the type of atom you want, for example image

      1. Use the file select UI to choose what image you want to upload

      2. Give the image a title (you will be able to use this when searching for it in future)

      3. Add an author or authors (you will be able to find images by author later on)

      4. Add some tags (as above, you will be able to filter images by tag)

      5. Click on the sidebar slider's menu button and a list of all your atoms will show up

      6. Drag and drop the atom you want onto the “dropbox”

    2. Or, you can search for an find an atom that has already been uploaded by clicking on the little circle button on the top of the sidebar

      1. This will bring up the exposed filters which you can use to filter your atoms

      2. As it is views powered, you can configure this any way you want

      3. Drag and drop the atom you want onto the “dropbox” 

Scald media library for Drupal

This will give you basic functionality, but the media being displayed is probably not as you want it just yet – more than likely it will only be displaying the title of the file. To change this you will need to go to your “Manage Display” settings and select a different display format; set it to editor representation or preview representation, then go back and refresh your page, and things should look better.

These “representations” that I mentioned are called “contexts” in Scald. You can think of them as view modes for other entities. So, it's quite similar to using entity reference fields, in that you reference the entity of the edit page, and then set what view mode (or context in this case) you want to use to display it.

If you go to the Scald configuration page (admin/structure/scald) you can see how each atom type is going to get displayed for each view mode that it is used on, but also for each context selected. This makes it a bit like a view mode inside a view mode, which makes it very configurable and customisable. And, you can create your own custom contexts here (I suggest you do). Creating your own contexts allows you to have different display types for different atom types on different view modes (you might have meta data, authors, publishers, etc showing on backend views, the atom with a thumbnail preview on teasers, and the atom with a large image preset on full nodes).

Scald drupal media management configuration page

Once you are that far, you can then create features of all of our Scald items to store them in configuration and export them to your test and production servers. You can also, then, reuse them on other sites.

So, that's a basic run through how to install, set up, and configure Scald to manage large amounts of media in Drupal websites. If you need some help to get up to speed with all of this, get in contact by clicking the button below.

Get me up to speed

Dec 15 2014
Dec 15

In the spirit of the computer video game Doom and its skill levels, we’ll review a few ways you can improve  your Drupal speed performance     and optimize for better results and server response time. These tips that we’ll cover may be at times specific to Drupal 6 versions, although     you can always learn the best practices from these examples and apply them on your own code base.

Doom

Doom skill levels: (easiest first)

1. I’m too young to die

2. Hey, not too rough

3. Hurt me plenty

4. Ultra-violence

5. Nightmare!

  This post is rated “I’m too young too die” difficulty level.

Drupal 6 shipped with all tables being MyISAM, and then Drupal 7 changed all that and shipped with all of its tables using the InnoDB database engine. Each one with its own strengths and weaknesses but it’s quite clear that InnoDB will probably perform better for your Drupal site (though it has quite a bit of fine tuning configuration to be tweaked on my.cnf).

Some modules, whether on Drupal 6, or those on Drupal 7 that simply upgraded but didn’t quite review all of their code, might ship with queries like SELECT COUNT() which if you have migrated your tables to InnoDB (or simply using Drupal 7) then this will hinder on database performance. That’s mainly because InnoDB and MyISAM work differently, and where-as this proved as quite a fast responding query being executed on a MyISAM database which uses the main index to store this information, for InnoDB the situation is different and will result in doing a full table scan for the count. Obviously, on an InnoDB configuration running such queries on large tables will result in very poor performance

drupal_perf-5

Note to ponder upon – what about the Views module which uses similar type of COUNT() queries to create the pagination for its views?

Series Navigation
Dec 14 2014
Dec 14

Start: 

2014-12-17 (All day) America/New_York

Organizers: 

The monthly security release window for Drupal 6 and Drupal 7 core will take place on Wednesday, December 17.

This does not mean that a Drupal core security release will necessarily take place on that date for either the Drupal 6 or Drupal 7 branches, only that you should prepare to look out for one (and be ready to update your Drupal sites in the event that the Drupal security team decides to make a release).

There will be no bug fix release on this date; the next window for a Drupal core bug fix release is Wednesday, January 7.

For more information on Drupal core release windows, see the documentation on release timing and security releases, and the discussion that led to this policy being implemented.

tvn
Dec 14 2014
Dec 14


As part of our mission to reinvent Drupal.org, we’ve been digging deep to understand who uses the website and how. At DrupalCon Austin, we began the process of discovering the personas of users who visit Drupal.org: to do so, we interviewed numerous Drupal.org users and asked questions about how frequently they use Drupal.org, how they use the website, their frustrations with Drupal.org, the things they enjoy about the site, and how we can make it easier for people to learn, use, and connect on Drupal.org.

Once we had that data, we set about looking for patterns and common themes. We built categories where we grouped people's similar experiences and frustrations together, and at the end of the process we had come up with five distinct personas that can apply to everyone who visits Drupal.org. These personas detail our users’ familiarity with Drupal software and Drupal community, how they use Drupal.org, how they contribute (or don’t), and more.

The five personas that we drew up are based on proficiency in Drupal and the Drupal ecosystem. They are:

  • Newcomer: This person has heard of Drupal, but has never built a Drupal site and doesn’t know where to start.
  • Learner: This person knows a bit about Drupal and the general Drupal ecosystem. He or she may have built a Drupal website, but likely has used only a few contrib modules and hasn’t made any customizations.
  • Skilled: This person understands and is fluent in Drupal-specific terminology, can build a Drupal website themselves using contributed modules, themes or distributions, or with the help of Drupal service providers. She or he has spent a decent amount of time working with Drupal, and is lightly engaged with the community, often not directly, via some sort of liaison.
  • Expert: This person has a deep understanding of Drupal and the Drupal ecosystem, knows how to build advanced websites with Drupal. Expert typically has been working with Drupal for at least a couple of years, is actively engaged with the community online and via local/national events, and actively contributes back in a variety of ways.
  • Master: This person has pervasive knowledge of Drupal and the Drupal ecosystem. He or she knows how to build Drupal websites of great complexity, is deeply engaged in the Drupal community, knows and has access to other Masters. Usually this person has been using Drupal and been around the Drupal community for a long time.

Proficiency-based personas are a new facet through which we can look at our community. It’s important to note that these personas are NOT only about developers. All kinds of roles can be on different levels of this ladder — UX designers, project managers, and business owners can be Experts and Masters, just like developers and themers. Simultaneously, people can have different backgrounds and be experts in other areas, but when it comes to fluency in Drupal and Drupal ecosystem, they would be represented as Newcomers, or Learners, or any of the other personas.

How will we use personas?

User personas will guide feature prioritization and feature development for Drupal.org, as we improve the site to make it easier for our users to progress from Newcomers to Masters. There are a variety of different ways we can go about it, but since our resources are limited, we will focus on just a few critical areas that will have the biggest impact on the overall user experience. So, to start our work, we’ll be focused on removing barriers and helping our users move more easily from Learners to Skilled. We found that our users have great success moving from Newcomer to Learner today, whereas moving from Learner to Skilled is much more difficult, since so much of the project is focused on doing things “the Drupal way” and learning the processes. Our secondary focus will be on moving users from Skilled to Expert.

Growing our pool of Skilled users is crucial, because by doing so we grow the number of people who own and/or build websites using Drupal, thus grow Drupal adoption. On the path from Skilled to Expert is when our users begin to give back by contributing patches, writing documentation, building and sharing modules and themes, helping others in the issue queues, and bringing in their friends. By growing the number of Skilled and Expert users on Drupal.org, we’ll directly grow our community. It’s a win-win.

By growing Drupal adoption and growing our community, we directly support our mission and goals as an organization (you can read more about those in our 2015 Leadership plan and budget), and that’s why improving Drupal.org is one of our organizational imperatives in the coming year. The 2015 Drupal.org roadmap outlines the numerous ways we’re planning to do it.

As we use personas in our work, you may hear us refer to our “Primary” (Learner and Skilled), “Secondary” (Expert), and “Tertiary” (Master and Newcomer) personas — these distinctions correspond to the order of conversions we look to make easier, not to the users’ importance. Every Drupal.org user is important to us!

As we modify Drupal.org, we’ll be using the personas to help us make the experience for the whole community better. After all, that’s what these personas are — a representation of the entire Drupal community. To help bring our personas to life, we talked to five different community members, each representing one user persona. Over the next few days we’ll share the stories of each person’s unique Drupal journey so that we can see how they got to where they are now. We’d like to say a big thank you to each of our volunteers for sharing their personal stories — as always, they’ve reminded us how fantastic our community really is.

At the end of the series, we’ll close it all off with interviews with several prominent community members who will share their views on how personas can be used outside of Drupal.org development.

We enjoyed working on the user research project and are excited to share user personas with the Drupal community. As a reminder, you can view and download the full report. Take them, use them, go out and make great things!

Dec 14 2014
Dec 14
Posted: December 14, 2014 under Drupal

I recently had the opportunity to see Nate Haug deliver a presentation about his Backdrop CMS project and it's upcoming 1.0.0 release (Jan. 15). It had been a while since I had taken a look at Backdrop and I came away quite impressed with both its progress and direction.

Many of you reading this will be familiar with Backdrop, but for those of you who haven't heard of the project, it is the first fork of the Drupal project, and the source of a great deal of controversy and angst in the Drupal community.

Backdrop has been perceived as a threat by many Drupalists, but I think as we step through the features and approaches of the two projects, those fears will be at least somewhat allayed. My own take is that the two systems seem complementary instead of competitive.

As a bit of background for the origin of Backdrop CMS, Nate told the story of his reaction to the massive changes in Drupal 8. He realized that his own business, Webform.com, was going to have major issues with the upgrade path.

It was going to take a huge effort to upgrade his site - we're talking many, many months - to simply replicate the work he had already done in Drupal 7. He didn't want to throw away the huge investment he had already made in his business and start over. His solution to the problem was forking Drupal to create Backdrop CMS.

And then...all hell broke loose.

Feature Comparison

I'll set the controversy behind Backdrop aside and get straight into a comparison of the features. Keep in mind, however, I'm using the term "features" here a bit loosely. That's because I also want to talk about how Backdrop is managed as well as other differences between the two projects. This list is not exhaustive. It just has some of the things that seem to me the most significant or interesting.

Target Market

I know many will squirm uncomfortably when I say this, but the target market for Drupal 8 is large enterprises. By contrast, the target for Backdrop is small to medium size businesses and non-profits - really the original market of the Drupal project. As we go through this list, you'll see how this targeting plays out in some of the decisions the two projects have made.

Configuration Management

This has been widely touted as the killer feature of Drupal 8. If you've dreamed of having all the cool configuration management features in D8 available for Drupal 7, then Backdrop may be tempting because that is essentially what it offers. Instead of using YAML files to store configuration data, however, Backdrop uses JSON. Otherwise, it's pretty much the same.

Theming

Another one of the major additions to Drupal 8 is the Twig template engine. This is a big plus for many front-end folks and it's something that is not available in Backdrop at this time - and I'm not sure I would look for it in the near future. Backdrop currently uses the Drupal 7 PHPTemplate theme engine.

Responsive Images

As a front-end developer, I have a particular interest in this one. Drupal 8 includes the Responsive Image module, which is essentially a reworking of the Picture module in D7.

At this writing, Backdrop doesn't have a responsive image solution. I asked Nate about this and he's not a fan of the Picture module approach (he favors using srcset, something that may possibly be added in versions 1.1 or 1.2 of Backdrop), so if that is something you require, it will need to be added as either a custom or contributed module.

Contributed Modules

Speaking of contrib, most of you reading this will be familiar with Drupal's massive collection of contributed modules. The contributed modules for Backdrop CMS will be hosted on GitHub and managed similar to how the jQuery project organizes its plugin registry. I don't think there have been any ports as of yet (all the energy is going to the 1.0.0 release), so this is pending.

Some of you may have heard that Drupal 7 modules will be compatible with Backdrop. This isn't true, primarily due to modules needing to be rewritten to support configuration management. Porting a Drupal 7 module should be fairly straightforward, however. Instead of storing config in the variables table, it needs to be in JSON files. Here's a video that will help get you started.

As a quick aside, having Backdrop (and eventually the contrib modules) hosted on GitHub seems like it will be a more familiar and friendly environment for potential project contributors.

Project Organization

The "do-ocracy" that is the Drupal project has been much discussed lately. Nate has organized the Backdrop CMS project along the same lines as the Project Management Committee of the Apache project. That was very wise in my opinion. It bodes well for the project.

WYSIWYG

Another really nice thing in Drupal 8 is the inclusion of a default WYSIWYG editor. Love them or hate them, virtually every client wants one, so now with D8 you won't have to add one yourself for every project. As of version 1.0.0, Backdrop doesn't have this functionality, but look for it in version 1.1 or 1.2.

I remember Nate saying something about it being ironic that Backdrop was launching both without Twig or a WYSIWYG since he and Jen Lampton (a major Backdrop contributor) had been instrumental in bringing those to Drupal 8.

I suppose I should mention that Backdrop minor versions - from 1.0 to 1.1, for example - will occur regularly at an interval of about three or four months. So for the features mentioned that may be in version 1.1 or 1.2, it means they can be expected in either late spring or late summer.

Panels and Views

How about Panels and Views in core? Yeah, I like it! And that's what you get with Backdrop. Drupal 8 provides Views in core, but not Panels. It may be a while before Panels is ready for D8, but it may also be a while before D8 is ready, so I guess that's not a problem.

System Requirements and Backwards Compatibility

It may seem odd to group these two, but this is one point where the intended audiences (enterprise vs small organizations) are put into stark contrast. For example, Backdrop is intentionally friendly to cheap hosting. Drupal 8, by contrast, is almost certainly going to use more server resources than Drupal 7, potentially causing issues for those on shared hosting plans. 

For large organizations, the cost of hosting is not a big deal, but for some small organizations, it can be. So a solution architected to work well with limited resources may be attractive and also serves to highlight the different approaches between the two projects.

With backwards compatibility, we see the same philosophical divergence. Drupal has never focused much on backwards compatibility, making it a pain in the ass (and often expensive) to upgrade across major versions. The benefit of that approach is that Drupal has been able to innovate without being constrained by past decisions.

Backdrop, however, places a lot of value on carefully managing change so that existing sites can be upgraded affordably. I would recommend looking at Backdrop's philosophy, because it's there where you really find the motivations for the project and how it differs (and will differ more in the future) from the Drupal project. From system requirements, to upgrade path, to reaching out to hear voices not found in the issue queue, Backdrop CMS is consistently friendly to the needs of the little guy.

Wrap Up

Again, this isn't a comprehensive list of all the features or differences between the two systems. There is an issue on GitHub that might be of some help in learning more as well as this Drupal 8 feature list.

To me, these two projects don't compete with one another. Sure, some enterprises may use Backdrop and many small organizations may use Drupal 8. But really, the changes in Drupal 8 are a move toward the enterprise and the talk around Drupal 8 has reinforced that message. Having an alternative for small organizations on a budget and with a need to preserve software investments isn't a bad thing.

You may politely leave any comments below.

About the Author

John Hannah

I’m John Hannah, a frontend developer at Lullabot. When I'm not building websites, I travel as much as possible and enjoy hanging out with my wonderful family. My favorite place to spend my coffee breaks is Twitter, so please feel free to connect with me there.

Dec 14 2014
Dec 14
function mymodule_training_class_node_form_submit($form, &$form_state) {

  if ($form_state['input']['field_class_is_finished']['und'] == 1) {
    $nid = $form_state['values']['nid'];  
                
    $query = db_select('og_membership', 'ogm')
      ->condition('ogm.gid', $nid, '=')
      ->fields('ogm', array('etid'));                           
    $result = $query->execute();
                
    foreach ($result as $record) {
      $uid = $record->etid;
      _mymodule_training_class_assign_alumni_role($uid);
    }   
  }
}

function _mymodule_training_class_assign_alumni_role($uid){

  $rid = db_select("users_roles", "ur")
    ->fields("ur", array("rid"))
    ->condition('ur.uid', $uid, '=')
    ->execute()
    ->fetchField();
  
  if (empty($rid)) {  
    db_insert('users_roles')
    ->fields(array(
      'uid' => $uid,
      'rid' => ALUMNI,
    ))
    ->execute();
  }     
}
mysql> describe field_data_field_class_is_finished
    -> ;
+-------------------------------+------------------+------+-----+---------+-------+
| Field                         | Type             | Null | Key | Default | Extra |
+-------------------------------+------------------+------+-----+---------+-------+
| entity_type                   | varchar(128)     | NO   | PRI |         |       |
| bundle                        | varchar(128)     | NO   | MUL |         |       |
| deleted                       | tinyint(4)       | NO   | PRI | 0       |       |
| entity_id                     | int(10) unsigned | NO   | PRI | NULL    |       |
| revision_id                   | int(10) unsigned | YES  | MUL | NULL    |       |
| language                      | varchar(32)      | NO   | PRI |         |       |
| delta                         | int(10) unsigned | NO   | PRI | NULL    |       |
| field_class_is_finished_value | int(11)          | YES  | MUL | NULL    |       |
+-------------------------------+------------------+------+-----+---------+-------+
8 rows in set (0.00 sec)

mysql> mysql> describe og_membership;
+-------------+------------------+------+-----+---------+----------------+
| Field       | Type             | Null | Key | Default | Extra          |
+-------------+------------------+------+-----+---------+----------------+
| id          | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| type        | varchar(255)     | NO   |     |         |                |
| etid        | int(10) unsigned | NO   | MUL | 0       |                |
| entity_type | varchar(32)      | NO   |     |         |                |
| gid         | int(11)          | NO   | MUL | NULL    |                |
| group_type  | varchar(32)      | NO   | MUL |         |                |
| state       | varchar(255)     | YES  |     |         |                |
| created     | int(11)          | NO   |     | 0       |                |
| field_name  | varchar(255)     | NO   |     |         |                |
| language    | varchar(12)      | NO   |     |         |                |
+-------------+------------------+------+-----+---------+----------------+
10 rows in set (0.00 sec)


mysql> select * from og_membership where gid=1304;
+------+----------------------------+-------+-------------+------+------------+-------+------------+--------------+----------+
| id   | type                       | etid  | entity_type | gid  | group_type | state | created    | field_name   | language |
+------+----------------------------+-------+-------------+------+------------+-------+------------+--------------+----------+
| 8275 | og_membership_type_default |     1 | user        | 1304 | node       | 1     | 1402485115 | og_user_node | en       |
| 8276 | og_membership_type_default | 10106 | user        | 1304 | node       | 1     | 1402485280 | og_user_node | en       |
| 8277 | og_membership_type_default | 10113 | user        | 1304 | node       | 1     | 1402485286 | og_user_node | en       |
| 8278 | og_membership_type_default | 10114 | user        | 1304 | node       | 1     | 1402485292 | og_user_node | en       |
+------+----------------------------+-------+-------------+------+------------+-------+------------+--------------+----------+
4 rows in set (0.00 sec)

mysql> describe users;
+------------------+------------------+------+-----+---------+-------+
| Field            | Type             | Null | Key | Default | Extra |
+------------------+------------------+------+-----+---------+-------+
| uid              | int(10) unsigned | NO   | PRI | 0       |       |
| name             | varchar(60)      | NO   | UNI |         |       |
| pass             | varchar(128)     | NO   |     |         |       |
| mail             | varchar(254)     | YES  | MUL |         |       |
| theme            | varchar(255)     | NO   |     |         |       |
| signature        | varchar(255)     | NO   |     |         |       |
| signature_format | varchar(255)     | YES  |     | NULL    |       |
| created          | int(11)          | NO   | MUL | 0       |       |
| access           | int(11)          | NO   | MUL | 0       |       |
| login            | int(11)          | NO   |     | 0       |       |
| status           | tinyint(4)       | NO   |     | 0       |       |
| timezone         | varchar(32)      | YES  |     | NULL    |       |
| language         | varchar(12)      | NO   |     |         |       |
| picture          | int(11)          | NO   | MUL | 0       |       |
| init             | varchar(254)     | YES  |     |         |       |
| data             | longblob         | YES  |     | NULL    |       |
+------------------+------------------+------+-----+---------+-------+
16 rows in set (0.01 sec)

mysql> select * from users where uid=10106;
+-------+----------------------+---------------------------------------------------------+------------------------------------------------+-------+-----------+------------------+------------+------------+-------+--------+------------------+----------+---------+------------------------------------------------+------+
| uid   | name                 | pass                                                    | mail                                           | theme | signature | signature_format | created    | access     | login | status | timezone         | language | picture | init                                           | data |
+-------+----------------------+---------------------------------------------------------+------------------------------------------------+-------+-----------+------------------+------------+------------+-------+--------+------------------+----------+---------+------------------------------------------------+------+
| 10106 | user_authenticated_1 | $S$DBGDqh770IDr09aztKD8Ey8aNGxwx8iiCaYo/rGCcBpa5XzNKnDF | identity+user_authenticated_1@paulbooker.co.uk |       |           | full_html        | 1401792983 | 1401794533 |     0 |      1 | America/New_York |          |       0 | identity+user_authenticated_1@paulbooker.co.uk | NULL |
+-------+----------------------+---------------------------------------------------------+------------------------------------------------+-------+-----------+------------------+------------+------------+-------+--------+------------------+----------+---------+------------------------------------------------+------+
1 row in set (0.00 sec)

mysql> select * from users where uid=10113;
+-------+----------------------+---------------------------------------------------------+------------------------------------------------+-------+-----------+------------------+------------+--------+-------+--------+------------------+----------+---------+------------------------------------------------+------+
| uid   | name                 | pass                                                    | mail                                           | theme | signature | signature_format | created    | access | login | status | timezone         | language | picture | init                                           | data |
+-------+----------------------+---------------------------------------------------------+------------------------------------------------+-------+-----------+------------------+------------+--------+-------+--------+------------------+----------+---------+------------------------------------------------+------+
| 10113 | user_authenticated_2 | $S$DeG84e0QP/H2h2rGv6cw93krL3CDoQ6CZOzhiSQCZa4OpZOAeP21 | identity+user_authenticated_2@paulbooker.co.uk |       |           | full_html        | 1402485227 |      0 |     0 |      1 | America/New_York |          |       0 | identity+user_authenticated_2@paulbooker.co.uk | NULL |
+-------+----------------------+---------------------------------------------------------+------------------------------------------------+-------+-----------+------------------+------------+--------+-------+--------+------------------+----------+---------+------------------------------------------------+------+
1 row in set (0.00 sec)

mysql> select * from users where uid=10114;
+-------+----------------------+---------------------------------------------------------+------------------------------------------------+-------+-----------+------------------+------------+--------+-------+--------+------------------+----------+---------+------------------------------------------------+------+
| uid   | name                 | pass                                                    | mail                                           | theme | signature | signature_format | created    | access | login | status | timezone         | language | picture | init                                           | data |
+-------+----------------------+---------------------------------------------------------+------------------------------------------------+-------+-----------+------------------+------------+--------+-------+--------+------------------+----------+---------+------------------------------------------------+------+
| 10114 | user_authenticated_3 | $S$D4xWR53hWUcyoZmIuZOLv7K8oasOsPCmqWaQGT.kpMQiX9k7XpfD | identity+user_authenticated_3@paulbooker.co.uk |       |           | full_html        | 1402485256 |      0 |     0 |      1 | America/New_York |          |       0 | identity+user_authenticated_3@paulbooker.co.uk | NULL |
+-------+----------------------+---------------------------------------------------------+------------------------------------------------+-------+-----------+------------------+------------+--------+-------+--------+------------------+----------+---------+------------------------------------------------+------+
1 row in set (0.00 sec)



mysql> select rid from users_roles where uid=10106;
+-----+
| rid |
+-----+
|   7 |
+-----+
1 row in set (0.00 sec)
Dec 14 2014
Dec 14

The Drupal 7 Sweaver module makes it easy to change the style of your Drupal theme without having to write any CSS code or dig through any template files. The Sweaver module provides a simple to use designer toolbar that sits at the bottom of the page and allows you to instantly change the look of your Drupal theme.

The interface itself is not perfect, there are a few bugs with the toolbar, however it's still incredibly useful. You can change fonts, borders, spacing, backgrounds and much more. Plus you can even add your own CSS properties to extend the module to do exactly what you need it too. You can also easily save and load different saved styles making it easy to change the entire look of your site at a moments notice.

If you are already an experienced Drupal themer, this module is probably not for you. However if you are just getting started with Drupal or don't know much about CSS, subthemes, or theme template files, this module might save you a lot of time.

Here are the basic steps for using this module:

  1. Install and enable the Drupal 7 Sweaver module
  2. Use the style tab of the Sweaver design toolbar at the bottom of the page to change your theme style without CSS. You will need to click the element on the page that you want to style. This will turn on the style options for you to change. You can configure the font, borders, padding, margin, background, width/height, and much more. You can also upload images using the images tab on the Sweaver toolbar.
  3. Once your style is the way you like it, you can click the Save tab to save your design. You can also choose to publish it to your Drupal site. This will allow all users to see your design (Note: you may need to clear the cache for anonymous users to see your design because of caching).
  4. You can save and load multiple designs using the Drupal Sweaver module

So check out the module and let me know what you think. I would always recommend going the Theme route if you know what you are doing, however this is still an option for those who want to stay as far away from code as possible.

Happy designing... no... happy Sweavering!!!

Dec 13 2014
Dec 13

Niels de Feyter November 27, 2014

This article contains a detailed instruction on how to setup the Entity Translation module for Drupal 7 websites.

Goal of this tutorial is to set up a multilingual website that can be navigated in multiple languages by visitors and to enable the content to be easily manageable by editors / cms administrators.

To get multilingual right, it’s critical that you configure your content-types and fields with care and precision and upfront, because if content is already in your database it is almost impossible to change these configurations.

Entity Translation is part of Drupal 8 core and its approach is to translate fields instead of full nodes/entities. Entity Translation module page on drupal.org .

Table of contents

Example: a news article

Field configurations

Data model

Now what is in the database?

Most important things

Configuration entity translation

Setup entity translation globals

Settings per bundle (Node)

Step 1 node: settings “edit content-type"

Step 2 node: settings within “entity translation"

Settings per bundle (Taxonomy)

Step 1 taxonomy: settings of “edit vocabulary"

Step 2 taxonomy: settings within “entity translation"

Setup 'title module'

Title setup Node

Title setup Taxonomy

Settings per field

Creating translated content

Create content in the default site-language first

Create content translations

Result

Recap of most important things

The example we are going to work with is an easy-to-understand multilingual content-type: a news article. This content-type is part of the Drupal 7 ‘standard’ install profile.

Field configurations

Our news article contains the following fields:

Field

Field Type

Translated

Cardinality

Title

Text

Yes

1

Body

Text

Yes

1

Image

Image

No

1

Tags

Entity Reference -> Taxonomy Term

multi

table: field node 'news article’

The ‘tags vocabulary’ has the following configuration:

Field

Field Type

Translated

Cardinality

Name

Text

Yes

1

Description

Text

Yes

1

table: fields taxonomy 'tags'

Data model

The data model looks like this:

schema: data model translated content

Now what is in the database?

When looking with Devel, what is actually stored in the database, the node (article) is like this:

Property

Value

Node ID

1

Language

en

Field

Language

Value

Title

en

My title

nl

Mijn titel

Body

en

My text

nl

Mijn tekst

Image

und

1233

Tags

und

123,124,125

table: saved data node ‘news article'

For a ‘term’ you would see this:

Property

Value

Term ID

123

Language

en

Field

Language

Value

Name

en

My term name

nl

Mijn term naam

Description

en

My text

nl

Mijn tekst

table: saved data taxonomy 'tags'

Most important things

  • Properties are not translated. An example property is the ID of an entity (nid, tid).
  • The language property of an entity must have a value and defaults to the website language (in this case 'en').
  • The “language” ‘und’ stands for ‘undefined’: it is not a language.
  • Translated fields have a value for each language (in this case ‘en’ and ‘nl’).
  • You must use the ‘title’ module to translate title properties of nodes and terms.
  • Reference fields (entity reference, term reference) refer to the ID of another entity. The reference field itself is thereby not translated.

To configure entity translation you go through a few steps:

  1. Setup entity translation globals
  2. Settings per bundle (node: article, taxonomy: tags)
  3. Setup Title module
  4. Settings per field

Setup entity translation globals

go to /admin/config/regional/entity_translation

ON = Enable language fallback

ON = Display shared labels

OFF = Enable translation workflow permissions

Open “translatable entity types” fieldset and click the entity types that must be translated.

ON = Node

ON = Taxonomy Term

OFF = File

OFF = User

screendump: global settings Entity Translation

Settings per bundle (Node)

Step 1 node: settings “edit content-type"

Go to /admin/structure/types/manage/

Open the tab “Publishing options”

Below “multilingual support” choose the next settings:

ON = “Enabled, with field translation”

OFF = “Hide content translation links”

Save the page and repeat for each content-type (= bundle of node).

screendump: bundle settings for a node

Step 2 node: settings within “entity translation"

Go to /admin/config/regional/entity_translation

Open vertical tab "Node"

Apply same settings for each bundle (content-type)

Default language = Default language

ON = Hide language selector

ON = Exclude Language neutral from the available languages

OFF = Prevent language from being changed once the entity has been created

OFF = Hide shared elements on translation forms

screendump: entity translation settings node

Settings per bundle (Taxonomy)

Step 1 taxonomy: settings of “edit vocabulary"

Go to /admin/structure/taxonomy//edit

Below “multilingual option” apply the next settings:

Translation mode: "no multilingual options for terms. Only the vocabulary will be translatable"

Note: the translations are applied at field level. 'No multilingual options' is the right setting!

Save the page.

screendump: bundle settings taxonomy term

Step 2 taxonomy: settings within “entity translation"

Go to /admin/config/regional/entity_translation

Open the vertical tab "Taxonomy term"

Apply same settings for each bundle (content-type)

NB: these settings are exactly the same as "Node".

Default language = Default language

ON = Hide language selector

ON = Exclude Language neutral from the available languages

OFF = Prevent language from being changed once the entity has been created

OFF = Hide shared elements on translation forms

screendump: entity translation settings taxonomy term

Setup 'title module'

By default, Drupal uses properties for the title of a node and the name and description of a taxonomy term. Because properties are not translatable, we use the module 'title'. This replaces properties by real fields.

Beware that in other modules (Views and Search API) you should use these new fields (‘title_field’, ‘name_field’, ‘description_field’), instead of the usual 'title'.

Title setup Node

Go to the tab "Manage Fields"

At Title click "replace". The title field will be created.

Title setup Taxonomy

Go to the tab "Manage Fields"

At Name and Description click "replace". The field name and description will be created.

screendump: Title module has not been applied yet.

screendump: Title module has been applied

At this point the entity-bundles are configured to make advance of entity_translation.

Settings per field

Now it’s time to apply the per-field settings. It does not matter whether you're dealing with a node or taxonomy term.

If a field is used in multiple bundles its translation settings will be the same for all its instances. So if you've already made the ‘title’ a translatable field, this is the case everywhere.

  1. Go to "Manage fields" in the relevant bundle.
  2. Under "Operations" click "edit".

You are now on the "Edit" tab of the field.

At the top are "label", "required field" and "help text".

screendump: Edit tab "Body" field in content-type "Page".

  1. Scroll all the way down to "Field Translation".
  2. Click "Enable translation" and then "Confirm".

screendump: Untranslated field

screendump: Translated field

Repeat this process for all your fields and you're done with configuration.

Creating translated content

This is the step its all about: creating translated content.

First we will create a piece of content in the default site-language, next we’ll translate this piece.

Create content in the default site-language first

In this example, the default language is ‘English’ and we create content of content-type ‘Page’. The steps for taxonomy terms and other content bundles are the same.

New content is always stored in the default language (we forced that in global entity-translation settings).

  1. Go to /node/add/page.
  2. Write your content
  3. Save

screendump: Writing content

When you visit the Devel-tab immediately after saving new content, you should see something like below:

screendump: Devel tab immediately after creating content.

A couple of things stand out:

The fields 'body' and 'title' have a value in the language 'en'. These are our translations so far.

The property 'language' has the value 'en'. This is the default-language and is necessary to force the translate-tab to appear.

The property 'translatable' has a value "0" (false). This is correct, because we translate fields and not the entire node.

Create content translations

The new node has a new ‘tab’ next to ‘view’, ‘edit’ and ‘devel’. When you click on it you have the possibility to add and/or modify translations.

screendump: Translate tab immediately after creating content.

  1. Click 'add' for the relevant language.
  2. Write your translation
  3. Save content
  4. Repeat for all languages.

For each translation that is added a sub-tab appears for that language

screendump: Adding the Dutch translation

When visiting the Devel-tab after translation you will see that the fields will have a translated value. Exactly how we want it.

screendump: Devel-tab after translating content.

The content is now translated and can be viewed in different languages.

The language-selection takes place (which language should be visible) and is dependent how the Local module is configured. Configuring Local is not included in this manual.

Recap of most important things

  • Properties are not translated. An example property is the ID of an entity (nid, tid).
  • The language property of an entity must have a value and defaults to the website language (in this case 'en').
  • The “language” ‘und’ stands for ‘undefined’: it is not a language.
  • Translated fields have a value for each language (in this case ‘en’ and ‘nl’).
  • You must use the ‘title’ module to translate title properties of nodes and terms.
  • Reference fields (entity reference, term reference) refer to the ID of another entity. The reference field itself is thereby not translated.
Dec 13 2014
Dec 13

As you end up building more and more websites that target mobile devices (e.g. iPhone, iPad, Android, Windows), you need to supply an ever increasing amount of favicons. This process can be complex if done by hand, luckily there is an easy way to introduce these into your Drupal site.

What you will need

Before we start you will need a high quality icon to begin with, the icon should be:

  • 260x260px (i.e. square)
  • a PNG with transparency as needed
  • recognizable when shrunk right done to your browser favicon (so don’t use your entire logo complete with words).

Generating the favicons

This is where the really handy realfavicongenerator.net website comes into play. I have used many other websites that offer similar functionality, but this seems to be the best, and is dead simple to use.

You will need to upload the 260x260px PNG file, and also select a hex color for the Windows 8 tile, but this should be straight forward.

I also opt for the option “I will place favicon files (favicon.ico, apple-touch-icon.png, etc.) at the root of my web site.” as this seems the most sensible place for them anyway.

When you complete the process, you will be able to download a zip file containing a whole bunch of icons and XML files, this is fine, extract them to your docroot for Drupal.

Adding the favicons to Drupal

You now will need to edit your html.tpl.php inside your theme, and add the code that the generator provides. The code should resemble something like this:

 1 rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-57x57.png">
 2 rel="apple-touch-icon" sizes="114x114" href="/apple-touch-icon-114x114.png">
 3 rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-72x72.png">
 4 rel="apple-touch-icon" sizes="144x144" href="/apple-touch-icon-144x144.png">
 5 rel="apple-touch-icon" sizes="60x60" href="/apple-touch-icon-60x60.png">
 6 rel="apple-touch-icon" sizes="120x120" href="/apple-touch-icon-120x120.png">
 7 rel="apple-touch-icon" sizes="76x76" href="/apple-touch-icon-76x76.png">
 8 rel="apple-touch-icon" sizes="152x152" href="/apple-touch-icon-152x152.png">
 9 rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon-180x180.png">
10 rel="icon" type="image/png" href="/favicon-192x192.png" sizes="192x192">
11 rel="icon" type="image/png" href="/favicon-160x160.png" sizes="160x160">
12 rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96">
13 rel="icon" type="image/png" href="/favicon-16x16.png" sizes="16x16">
14 rel="icon" type="image/png" href="/favicon-32x32.png" sizes="32x32">
15 name="msapplication-TileColor" content="#b91d47">
16 name="msapplication-TileImage" content="/mstile-144x144.png">

You will notice though that Drupal likes to place it’s default favicon into the section of the page, we need to remove this in order for it not to mess up the above code you inserted.

rel="shortcut icon" href="http://[YOURSITE]/misc/favicon.ico" type="image/vnd.microsoft.icon" />

The following code below can be inserted into your template.php file for your theme to remove the default favicon from Drupal:

 1  2 /**
 3  * Remove the unneeded favicon from the head section.
 4  */
 5 function YOURTHEME_html_head_alter(&$head_elements) {
 6   foreach ($head_elements as $key => $element) {
 7     if (!empty($element['#attributes'])) {
 8       if (array_key_exists('href', $element['#attributes'])) {
 9         if (strpos($element['#attributes']['href'], 'misc/favicon.ico') > 0) {
10           unset($head_elements[$key]);
11         }
12       }
13     }
14   }
15 }
16 ?>

There you have it all done.

Google recently announced that from Chrome 39 onwards on Android Lollipop (5.0+), a new meta tag will be supported

name="theme-color" content="#b91d47" />

This is what your site’s title bar now looks like (instead of boring and grey).

The theme-color meta tag in use on www.maoritelevision.com

This meta tag can be added to your html.tpl.php file as above.

Let me know if this has helped you, and also if you have any other tips and tricks when it comes to favicons on your mobile devices.

Please enable JavaScript to view the comments powered by Disqus.

Dec 12 2014
Dec 12

Have you ever accidentally triggered emails to real users while working in a development environment? Or how about accidentally pushed data to a "live" third party service from a development environment?

There are tools (such as the Reroute Email module) you can use to prevent these mistakes, but the trick is remembering to enable them after importing the latest production database! It would take more than one hand to count the number of times I've only just remembered to "neuter" my local environment before taking an action that could have affected live users or data. Scary!

I don't like that kind of anxiety, so I wrote a small custom module that provides some peace of mind. I called it the "_environment" module, and now you may be thinking, "Wait, isn't there already an Environment contrib module on drupal.org?" Why yes, there is. It's a great tool for automatically managing modules (and taking any arbitrary action) on a per environment basis. However, it doesn't provide the security and peace of mind I needed to ensure a given module is enabled in all development environments, as it still requires the developer to run the "env-switch" drush command. Yes, this could be automated to happen after a call to sql-sync, but this isn't good enough, since I often use different methods for importing production data. Same goes for santizing user emails and other production data: Sanitization can be configured to happen automatically after sql-sync but cannot be guaranteed when the dump comes from other sources.

So far I've left out one major benefit: Not only does it protect me from making blunders with production data and emails, but it prevents everyone on the team from making them. As lead on a given project, this is invaluable peace of mind! I now have confidence that a newly-onramped team member can't accidentally affect production systems while getting familiar with the site.

A final note on the choice of implementation. I specifically needed my code to be included a module that ran on production. Otherwise, we're back to square one with needing to remember to enable a module after importing the database dump. I want it to happen automatically. So, as you'll see in the source code below, the module runs on production but does no work.

I used a hook_init() implementation to provide the desired safety net. Yes, it's a performance hit (runs on all but cached pages), but does very little work in the production environment. In addition to the code below, one step is needed: Set a $conf[] variable in the production site's settings.php as a flag for environment detection.

Here's an example of configuring Reroute Email to always protect development environments. Note that this example is slightly facetious, since Reroute Email can be enabled on production and configured to do work or not based on variables in settings.php $conf[].

A reassuring note that I am in a protected environment. If this message is missing, watch out!

No rerouting message; I must be in a production environment. Beware!


/**
 * Implements hook_init().
 */
function mymodule_init() {
  // We try REALLY HARD to determine if we're in a production environment.
  //
  // Check various flag names in case someone got cheeky and changed the value.
  // This is the $conf[] variable set in production settings.php.
  switch (variable_get('mymodule_environment')) {
    case 'prod':
    case 'production':
    case 'live':
      return;
  }
  // In case someone deleted the $conf[] variable, check sites directory name
  // for a production-ish value. Again, check various names in case the sites
  // directory name changes in the future.
  switch (conf_path()) {
    case 'sites/prod':
    case 'sites/production':
    case 'sites/live':
      return;
  }
  // We are in a development environment! Please proceed.
  //
  // Is email rerouting enabled?
  if (!module_exists('reroute_email')) {
    // Enable email rerouting.
    if (!module_enable(array('reroute_email'))) {
      // We couldn't enable the module. Darn! Don't let them proceed.
      throw new Exception('The Reroute Email module could not be enabled. This'
        . ' module is a required dependency for all development environments.');
    }
    // Flush caches to make rerouting take effect after module enable.
    drupal_flush_all_caches();
  }
  // Make sure the Reroute Email address is defined. Because when it is left
  // empty, emails go to their original destinations.
  if (empty(variable_get('reroute_email_address'))) {
    variable_set('reroute_email_address', 'admin@localhost');
  }
  // Remind the user about email rerouting.
  $message = t('All emails currently routed to @email. 
Click here to change.', array(
    '@email' => variable_get('reroute_email_address'),
    '@link'  => url('admin/config/development/reroute_email'),
  ));
  drupal_set_message($message, $type = 'status', $repeat = FALSE);
}

Additional Resources

Learning How To Install Drush on Non-Admin Rights Server | Mediacurrent Blog Post
A Better Access Denied Page with Panels | Mediacurrent Blog Post 

Dec 12 2014
Dec 12

Here at Skvare, we strive to make Drupal and CiviCRM work as one to accomplish goals in a way that is simple and intuitive. Continuing our work in Drupal/CiviCRM integrations, we’ve cooked something new up for you all. We would now like to take this opportunity to introduce Views in CiviCRM Dashlets.

What is Views in CiviCRM Dashlets?

Views in CiviCRM Dashlets is a Drupal module that allows one to create a dashlet containing a Drupal View. That is right, in addition to CiviCRM reports you can use the power of Drupal Views to create a customizable experience. This opens grand new opportunities to use our imagination and drive to strengthen the bond between Drupal and CiviCRM. A majority of the functionality of Views is currently at your fingertips, with further enhancements on the horizon.

How did we get to this point?

We had the idea of rendering a View in a dashlet, but that’s all it was. An idea. We researched extensively trying to find out how exactly a dashlet works. Curiously, I created a forum post where I received a small piece of knowledge and took our first big step in creating this module. After that, it was a matter of development of ideas. Thank you totten for the quick and helpful response.

Basic Instructions

It is very simple to create a dashlet. All you have to do is:

  1. Open/create your view and add a new “CiviCRM Dashlet” display
  2. Configure the display to your liking and save
  3. Visit www.yourdomain.com/civicrm and click “Configure Dashboard”
  4. Add your created dashlet to a column and click “Done”

And now your brand new dashlet is on your dashboard!

Why Views in CiviCRM Dashlets?

  • Simplicity -- Using CiviCRM reports to create dashlets is a great feature of CiviCRM, but new reports require PHP and SQL coding skills. Some organizations have staff with these skills, but many do not. Many more people do have the ability to use site-building techniques to create Views through its powerful UI.
  • Customize - Customize your view to render your CiviCRM or Drupal data just how YOU want it. Quickly add sorts, filters, fields, relationships, no results behaviors, and rewrite field output functionality.
  • Style - Make it pretty! Use View’s built in style features to wrap fields in html elements and add classes to fields for css styling.
  • Content -- More than just data, place content on the dashboard. Display a node, add links to documentation, or list your latest Drupal Commerce orders. If it can be Viewed, it can be on the Dashboard.
  • Combine - Use in combination with the CiviCRM Entity and Entity Reference modules to create rich data structure displays combining Drupal content with CiviCRM data. Also, there are tons of great modules that expand Views functionality and most will work with Views in CiviCRM Dashlets.
  • Reuse - Use the same view for a Drupal page and CiviCRM dashlet.
  • Much more that we have not thought of!

"What can I do to help?"

If you would like to join the journey of Views in CiviCRM Dashlets then go ahead and create an issue. This issue could be a bug report or even a feature request. Views in CiviCRM Dashlets is a work in progress, but there is ALOT you can do with it already. We would be very grateful for your feedback. This is a team effort, and our community is the number one team.

For more information, visit the project page at https://www.drupal.org/sandbox/brandonferrell/2389543

Pages