Planet Drupal

Subscribe to Planet Drupal feed
Drupal.org - aggregated feeds in category Planet Drupal
Updated: 5 hours 18 min ago

Deeson: Getting started with Drupal 8 and Composer

7 February 2017 - 3:00pm

At Deeson we are constantly looking for ways to improve the way we work, iterating on past projects to incorporate new techniques and best-practices.

We starting playing with Composer recently, as a tool for speeding up Drupal module updates. There were a few quirks, but it generally worked.

Then we discovered the Drupal Composer project, which makes it much simpler to manage Drupal core as well as modules.

This is great! We can use Composer to install and update Drupal core, modules and themes, and all in a consistent manner; anyone can run Composer and end up with the exact same set of code.

So now we can start excluding some of the off-the-shelf code from our Git repository (contrib modules, themes, and libraries.) This slims down our repositories and speeds up development for the whole team.

Combined with our approach to managing settings we’re really starting to limit the amount of custom stuff in the docroot now.

Having recently completed a site using this approach I started thinking: “Why do we even need the docroot in Git?”

So we got rid of it! One of the many benefits of working in self-managing teams!

We now have a very flat repository structure where the entire docroot is compiled during deployments. The project repository contains a CMI config directory, settings.php, modules and themes directories, and the all-important composer.json which manages everything that isn’t project-specific custom code.

Internally we use Bitbucket pipelines to manage building and deploying our projects.

Every commit triggers a pipelines build. The docroot is built, tests are run, and if all goes well, it gets pushed to the hosting platform.

We have put together a small Composer script which simply symlinks the modules, themes and settings into the docroot when running composer install. The rest of the build is vanilla composer.

Our composer.json is based on the version provided by Drupal Composer, but with a change to the post-install-cmd and post-update-cmd hooks.

"scripts": { "drupal-scaffold": "DrupalComposer\\DrupalScaffold\\Plugin::scaffold", "pre-install-cmd": [ "DrupalProject\\composer\\ScriptHandler::checkComposerVersion" ], "pre-update-cmd": [ "DrupalProject\\composer\\ScriptHandler::checkComposerVersion" ], "post-install-cmd": [ "@drupal-scaffold", "DrupalProject\\composer\\DeesonScriptHandler::createRequiredFiles" ], "post-update-cmd": [ "@drupal-scaffold", "DrupalProject\\composer\\DeesonScriptHandler::createRequiredFiles" ] }

Here we have replaced the script handler plugin with our own customised version, which creates the symlinks mentioned above during composer install and composer update. We also run the Drupal Scaffold plugin on every Composer install or Composer update, to ensure that all of the extra Drupal files like index.php and update.php exist in the docroot.

Taking the Drupal docroot out of our project repositories has required a shift in the way we think about developing Drupal projects, but ultimately we believe it has streamlined and simplified our development workflows.

We have turned this approach into a Drupal 8 Quick Start template, which can be used to very quickly get up and running with Drupal 8 using a composer-based workflow. The project is available on Github. PRs welcome!

Categories: Drupal

Agaric Collective: Help kick the door for new contributors to Drupal back open (and get credit for it)

7 February 2017 - 1:15pm

After years of giving a terrible initial experience to people who want to share their first project on Drupal.org, the Project Applications Process Revamp is a Drupal Association key priority for the first part of 2017.

A plan for incentivizing code review of every project, not just new ones, after the project applications revamp is open for suggestions and feedback.

Which makes it excellent timing that right now you can get credit on your Drupal.org profile and that of your organization, boosting marketplace ranking, for reviewing the year-old backlog of project applications requesting review. The focus is on security review for these project applications, but if you want to give a thorough review and then give your thoughts on how project reviews (for any project that opts in to this quality marker) should be performed and rewarded going forward, now's the time and here's the pressing need.

Categories: Drupal

InternetDevels: Drupal 8 SEO Checklist module: your reliable website optimization adviser

7 February 2017 - 8:18am

To do: improve your Drupal website’s SEO. This sounds like a pretty big task when it’s written out like that on your list! ;) Big tasks are easier to cope with when they are divided into clear, smaller steps. Great news! You can have a smart adviser, named SEO Checklist module, who can give you this list of steps and prompt you how to fulfill them.

Read more
Categories: Drupal

Flocon de toile | Freelance Drupal: Create an action for custom mass updates with Drupal 8

7 February 2017 - 7:00am

Drupal 8 makes it possible to carry out certain mass actions on the site's contents, such as publishing or unpublishing massively contents, positioning them at the top of lists, etc. It may be useful to provide to certain user profiles some customized actions related to the specificities of their site, such as highlighting certain taxonomy terms, changing the value of a specific field, and thus avoiding heavy and tedious update operations to users on each of the content to be modified.

Categories: Drupal

Drupal Association blog: Drupal in Europe - Community Survey

7 February 2017 - 6:17am

TL;DR If you are a European community member, please take our community survey about Drupal in Europe.

After 6+ years working at the Drupal Association and knowing so many members around the world, it’s easy for me to think I know what is going on with the Project. But, it is a big world and each region, country, and local market has unique, evolving needs.

To avoid assuming the best way to help the community, I am asking for your input. I'm gathering insight one region at a time. I’ll share the feedback with staff and the Drupal Association Board to refine how we serve the community.

I’m starting first with our European community. This is so it's well timed with our DrupalCon Europe planning. In fact, the Drupal Association Board meets on 23 and 24 February where we will strategize how we can best support the European community. We’ll use your input to drive that discussion.

I’m collecting input in a few ways. Recently, I held roundtable discussions with various community organizers. Now I’m opening up the discussion to all who Drupal in Europe. Please tell me how the Drupal Association can best support Drupal by taking this community survey before February 16th.

Thanks for sharing your thoughts and needs. I look forward to hearing from you.

Categories: Drupal

2bits: How to configure Varnish Cache for Drupal with SSL Termination Using Pound or Nginx

7 February 2017 - 4:49am

Secure Socket Layer (SSL) is the protocol that allows web sites to serve traffic in HTTPS. This provides end to end encryption between the two end points (the browser and the web server). The benefits of using HTTPS is that traffic between the two end points cannot be deciphered by anyone snooping on the connection. This reduces the odds of exposing sensitive information such as passwords, or getting the web site hacked by malicious parties. Google has also indicated that sites serving content exclusively in HTTPS will get a small bump in Page Rank.

Historically, SSL certificate issuers have served a secondary purpose: identity verification. This is when the issuing authority vouches that a host or a domain is indeed owned by the entity that requests the SSL certificate for it. This is traditionally done by submitting paper work including government issued documentation, incorporation certificates, ...etc.

Historically, SSL certificates were costly. However, with the introduction of the Let's Encrypt initiative, functional SSL certificates are now free, and anyone who wants to use them can do so, minus the identity verification part, at least for now.

Implementing HTTPS with Drupal can be straightforward with low traffic web sites. The SSL certificate is installed in the web server, and that is about it. With larger web sites that handle a lot of traffic, a caching layer is almost always present. This caching layer is often Varnish. Varnish does not handle SSL traffic, and just passes all HTTPS traffic straight to Drupal, which means a lot of CPU and I/O load.

This article will explain how to avoid this drawback, and how to have it all: caching in Varnish, plus serving all the site using HTTPS.

The idea is quite simple in principle: terminate SSL before Varnish, which will never know that the content is encrypted upstream. Then pass the traffic from the encryptor/decryptor to Varnish on port 81. From there, Varnish will pass it to Apache on port 8080.

We assume you are deploying all this on Ubuntu 16.04 LTS, which uses Varnish 4.0, although the same can be applied to Ubuntu 14.04 LTS with Varnish 3.0.

Note that we use either one of two possible SSL termination daemons: Pound and Nginx. Each is better in certain cases, but for the large part, they are interchangeable.

One secondary purpose for this article is documenting how to create SSL bundles for intermediate certificate authorities, and to generate a combined certificate / private key. We document this because of the sparse online information on this very topic.

Install Pound aptitude install pound Preparing the SSL certificates for Pound

Pound does not allow the private key to be in a separate file or directory from the certificate itself. It has to be included with the main certificate, and with intermediate certificate authorities (if there are any).

We create a directory for the certificates:

mkdir /etc/pound/certs

cd /etc/pound/certs

We then create a bundle for the intermediate certificate authority. For example, if we are using using NameCheap for domain registration, they use COMODO for certificates, and we need to do the following. The order is important.

cat COMODORSADomainValidationSecureServerCA.crt \
  COMODORSAAddTrustCA.crt \
  AddTrustExternalCARoot.crt >> bundle.crt

Then, as we said earlier, we need to create a host certificate that includes the private key.

cat example_com.key example_com.crt > host.pem

And we make sure the host certificate (which contains the private key as well) and the bundle, are readable only to root.

chmod 600 bundle.crt host.pem Configure Pound

We then edit /etc/pound/pound.cfg

# We have to increase this from the default 128, since it is not enough
# for medium sized sites, where lots of connections are coming in
Threads 3000

# Listener for unencrypted HTTP traffic
ListenHTTP
  Address 0.0.0.0
  Port    80
 
  # If you have other hosts add them here
  Service
    HeadRequire "Host: admin.example.com"
    Backend
      Address 127.0.0.1
      Port 81
    End
  End
 
  # Redirect http to https
  Service
    HeadRequire "Host: example.com"
    Redirect "https://example.com/"
  End
 
  # Redirect from www to domain, also https
  Service
    HeadRequire "Host: www.example.com"
    Redirect "https://example.com/"
  End
End

# Listener for encrypted HTTP traffic
ListenHTTPS
  Address 0.0.0.0
  Port    443
  # Add headers that Varnish will pass to Drupal, and Drupal will use to switch to HTTPS
  HeadRemove      "X-Forwarded-Proto"
  AddHeader       "X-Forwarded-Proto: https"
 
  # The SSL certificate, and the bundle containing intermediate certificates
  Cert      "/etc/pound/certs/host.pem"
  CAList    "/etc/pound/certs/bundle.crt"
 
  # Send all requests to Varnish
  Service
    HeadRequire "Host: example.com"
    Backend
      Address 127.0.0.1
      Port 81
    End
  End
 
  # Redirect www to the domain
  Service
    HeadRequire "Host: www.example.com.*"
    Redirect "https://example.com/"
  End
End

Depending on the amount of concurrent traffic that your site gets, you may need to increase the number of open files for Pound. To do this, edit the file /etc/default/pound, and add the following lines:

# Increase the number of open files, so pound does not log errors like:
# "HTTP Acces: Too many open files"
ulimit -n 20000

Do not forget to change the 'startup' line from 0 to 1, otherwise pound will not start.

Configure SSL Termination for Drupal using Nginx

You may want to use Nginx instead of the simpler Pound in certain cases. For example, if you want to handle redirects from the plain HTTP URLs to the corresponding SSL HTTPS URls. Pound cannot do that. It redirects to the home page of the site instead.

Also, if you want to process your site's traffic using analysis tools, for example Awstats, you need to capture those logs. Although Pound can output logs in Apache combined format, it also outputs errors to the same log, at least on Ubuntu 16.04, and that makes these logs unusable by analysis tools.

First install Nginx:

aptitude install nginx

Create a new virtual host under /etc/nginx/sites-available/example.com, with this in it:

# Redirect www to no-www, port 80
server {
  server_name www.example.com;

  # Replace this line with: 'access_log off' if logging ties up the disk
  access_log /var/log/nginx/access-example.log;
 
  # Permanent redirect
  return 301 https://example.com$request_uri;
}

# Redirect www to no-www, SSL port 443
server {
  listen 80 default_server;
  listen [::]:80 default_server ipv6only=on;

  server_name example.com;

  # Replace this line with: 'access_log off' if logging ties up the disk
  access_log /var/log/nginx/access-example.log;
 
  # Permanent redirect
  return 301 https://$host$request_uri;
}

server {
  listen 443 ssl default_server;
  listen [::]:443 ssl default_server ipv6only=on;

  server_name example.com;

  # We capture the log, so we can feed it to analysis tools, e.g. Awstats
  # This will be more comprehensive than what Apache captures, since Varnish
  # will end up removing a lot of the traffic from Apache
  #
  # Replace this line with: 'access_log off' if logging ties up the disk
  access_log /var/log/nginx/access-example.log;

  ssl on;

  # Must contain the a bundle if it is a chained certificate. Order is important.
  # cat example.com.crt bundle.crt > example.com.chained.crt 
  ssl_certificate      /etc/ssl/certs/example.com.chained.crt;
  ssl_certificate_key  /etc/ssl/private/example.com.key;

  # Test certificate
  #ssl_certificate     /etc/ssl/certs/ssl-cert-snakeoil.pem;
  #ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;

  # Restrict to secure protocols, depending on whether you have visitors
  # from older browsers
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

  # Restrict ciphers to known secure ones
  ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256;

  ssl_prefer_server_ciphers on;
  ssl_ecdh_curve secp384r1;
  ssl_stapling on;
  ssl_stapling_verify on;

  add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
  add_header X-Frame-Options DENY;
  add_header X-Content-Type-Options nosniff;

  location / {
    proxy_pass                         http://127.0.0.1:81;
    proxy_read_timeout                 90;
    proxy_connect_timeout              90;
    proxy_redirect                     off;

    proxy_set_header Host              $host;
    proxy_set_header X-Real-IP         $remote_addr;
    proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header X-Forwarded-Port  443;
   
    proxy_buffers                      8 24k;
    proxy_buffer_size                  2k;
  }
}

Then link this to an entry in the sites-enabled directory

cd /etc/nginx/sites-enabled

ln -s /etc/nginx/sites-available/example.com

Then we add some performance tuning parameters, by creating a new file: /etc/nginx/conf.d/tuning. These will make sure that we handle higher traffic than the default configuration allows:

 
worker_processes       auto;

worker_rlimit_nofile   20000;

events {
  use epoll;
  worker_connections 19000;
  multi_accept       on;
}

http {
  sendfile           on;
  tcp_nopush         on;
  tcp_nodelay        on;
  keepalive_timeout  65;
  keepalive_requests 10000;
   
  client_body_buffer_size 128k;   
}

We now have either Pound or Nginx in place, handling port 443 with SSL certifcates, and forwarding the plain text traffic to Varnish.

Change Varnish configuration to use an alternative port

First, we need to make Varnish work on port 81.

On 16.04 LTS, we edit the file: /lib/systemd/system/varnish.service. If you are using Ubuntu 14.04 LTS, then the changes should go into /etc/default/varnish instead.

Change the 'ExecStart' line for the following:

Port that Varnish will listen on (-a :81)
Varnish VCL Configuration file name (/etc/varnish/main.vcl)
Size of the cache (-s malloc,1536m)

You can also change the type of Varnish cache storage, e.g. to be on disk if it is too big to fit in memory (-s file,/var/cache/varnish/varnish_file.bin,200GB,8K). Make sure to create the directory and assign it the correct owner and permissions.

We use a different configuration file name so as to not overwrite the default one, and make updates easier (no questions asks during update to resolve differences).

In order to inform systemd that we changed a daemon startup unit, we need to issue the following command:

systemctl daemon-reload Add Varnish configuration for SSL

We add the following section to the Varnish VCL configuration file. This will pass a header to Drupal for SSL, so Drupal will enforce HTTPS for that request.

# Routine used to determine the cache key if storing/retrieving a cached page.
sub vcl_hash {

  # This section is for Pound
  hash_data(req.url);

  if (req.http.host) {
    hash_data(req.http.host);
  }
  else {
    hash_data(server.ip);
  }

  # Use special internal SSL hash for https content
  # X-Forwarded-Proto is set to https by Pound
  if (req.http.X-Forwarded-Proto ~ "https") {
    hash_data(req.http.X-Forwarded-Proto);
  }
}

Change Apache's Configuration

If you had SSL enabled in Apache, you have to disable it so that only Pound (or Nginx) are listening on port 443. If you do not do this, Pound and Nginx will refuse to start with an error: Address already in use.

First disable the Apache SSL module.

a2dismod ssl

We also need to make Apache listen on port 8080, which Varnish will use to forward traffic to.

 
Listen 8080

And finally, your VirtualHost directives should listen on port 8080, as follows. It is also best if you restrict the listening on the localhost interface, so outside connections cannot be made to the plain text virtual hosts.

<VirtualHost 127.0.0.1:8080>
...
</VirtualHost>

The rest of Apache's configuration is detailed in an earlier article on Apache MPM Worker threaded server, with PHP-FPM.

Configure Drupal for Varnish and SSL Termination

We are not done yet. In order for Drupal to know that it should only use SSL for this page request, and not allow connections from plain HTTP, we have to add the following to settings.php:

// Force HTTPS, since we are using SSL exclusively
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
  if ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
    $_SERVER['HTTPS'] = 'on';
  }
}

If you have not already done so, you also have to enable page cache, and set the external cache age for cached pages. This is just a starting point, assuming Drupal 7.x, and you need to modify these accordingly depending on your specific setup.

// Enable page caching
$conf['cache'] = 1;
// Enable block cache
$conf['block_cache'] = 1;
// Make sure that Memcache does not cache pages
$conf['cache_lifetime'] = 0;
// Enable external page caching via HTTP headers (e.g. in Varnish)
// Adjust the value for the maximum time to allow pages to stay in Varnish
$conf['page_cache_maximum_age'] = 86400;
// Page caching without bootstraping the database, nor invoking hooks
$conf['page_cache_without_database'] = TRUE;
// Nor do we invoke hooks for cached pages
$conf['page_cache_invoke_hooks'] = FALSE;

// Memcache layer
$conf['cache_backends'][]    = './sites/all/modules/contrib/memcache/memcache.inc';
$conf['cache_default_class'] = 'MemCacheDrupal';
$conf['memcache_servers']    = array('127.0.0.1:11211' => 'default');
$conf['memcache_key_prefix'] = 'live';

And that is it. Now restart all the daemons:

service pound restart
service nginx restart # If you use nginx instead of pound
service varnish restart
service apache2 restart

Check that all daemons have indeed restarted, and that there are no errors in the logs. Then test for proper SSL recognition in the browser, and for correct redirects.

For The Extreme Minimalist: Eliminating Various Layers

The above solution stack works trouble free, and has been tested with several sites. However, there is room for eliminating different layers. For example, instead of having Apache as the backend web server, this can be replaced with Nginx itself, listening on both port 443 (SSL), and 8080 (backend), with Varnish in between. In fact, it is possible to even remove Varnish altogether, and use Ngnix FastCGI Cache instead of it. So Nginx listens on port 443, decrypts the connection, and passes the request to its own cache, which decides what is served from cache versus what gets passed through to Nginx itself on port 8080, which hands it over to PHP and Drupal.

Don't let the words 'spaghetti' and 'incest' take over your mind! Eventually, all the oddities will be ironed out, and this will be a viable solution. There are certain things that are much better known in Apache for now in regards to Drupal, like URL rewriting for clean URLs. There are also other things that are handled in .htaccess for Apache that needs to gain wider usage within the community before an Nginx only solution becomes the norm for web server plus cache plus SSL.

Apache MPM Worker Multithreaded with PHP-FPM is a very low overhead, high performance solution, and we will continue to use it until the Nginx only thing matures into a wider used solution, and has wider use and support within the Drupal community to remain viable for the near future.

Tags: Contents: 
Categories: Drupal

Vardot: Install Apache Solr 5.x for a Drupal Website on Ubuntu

7 February 2017 - 4:24am
How to Read time: 2 minutes

To install Apache Solr and run it with a Drupal website on an Ubuntu server. Follow these steps:

Install Java

You need to make sure you have Java installed.
To check run this command:

java -version

If it's not installed, simply install from packages:

sudo apt-get -y install openjdk-7-jdk jetty

 

Install Latest Apache Solr 5.x

Download the latest zip file release 5.x.x from http://mirror.cc.columbia.edu/pub/software/apache/lucene/solr/

Unzip the downloading: 

unzip solr-5-x-x.zip;

More the unzipped folder to where is should be:

sudo mv solr-5-x-x /usr/local/solr cd /usr/local/solr

 

Configuring Apache Solr for your Drupal site

Make a copy of the Solr core to be used for Drupal sites

cp -r example/ drupal/

Create cores for your sites. Every core is an instance of a Drupal site:

cd drupal/multicore sudo cp -r core0 MYSITENAME

Edit the solr.xml to add your new core:

sudo vim solr.xml

and add a new core similar to:

<core name="My Site Name" instanceDir="MYSITENAME" />

 

Replace the default Solr configuration with the Drupal  Solr configuration provided from the Search API Solr Search module.

sudo rm -r MYSITENAME/conf sudo cp -r /PATH/TO/DRUPAL/sites/all/modules/contrib/search_api_solr/solr-conf/5.x /usr/local/solr/drupal/multicore/conf/MYSITENAME/conf

 

Start Solr

cd .. java -Dsolr.solr.home=multicore -jar start.jar

 

Create Solr user

sudo useradd -d /usr/local/solr -M -s /usr/sbin/nologin solr sudo chown -R solr:root /usr/local/solr

 

Add Solr as a Service

Download the init.d script to start Apache Solr. Thanks to 2bits.

wget http://2bits.com/sites/2bits.com/files/solr-init.d.sh_.txt sudo mv solr-init.d.sh_.txt /etc/init.d/solr

Edit the file to suit your environment and change the SOLR_HOME=/opt/solr/example environment variable (line 18) to /usr/local/solr/drupal

sudo vim /etc/init.d/solr

Replace line 18 to be:

SOLR_HOME=/usr/local/solr/drupal

 

Make the service to work properly:

sudo chmod a+rx /etc/init.d/solr sudo update-rc.d solr defaults

 

Access Solr

Your Solr URL will be: http://localhost:8983/solr/#

If you want to setup authentication for Solr, please follow: http://2bits.com/articles/configuring-apache-solr-4x-drupal-password-aut...

Tags:  Drupal Solr Setup Guide Drupal Planet Title:  Install Apache Solr 5.x for a Drupal Website on Ubuntu
Categories: Drupal

Drupal Modules: The One Percent: Drupal Modules: The One Percent — Contact Emails (video tutorial)

6 February 2017 - 1:39pm
Drupal Modules: The One Percent — Contact Emails (video tutorial) NonProfit Mon, 02/06/2017 - 15:39 Episode 18

Here is where we bring awareness to Drupal modules running on less than 1% of reporting sites. Today we'll investigate Contact Emails, a module which allows a single contact form submission to be sent to multiple users each of which contains unique subject and body fields.

Categories: Drupal

Drupal @ Penn State: Web components, Polymer and decoupling design in Drupal

6 February 2017 - 12:54pm

UPDATE: You should also read this great post from Mediacurrent: Benefits of decoupled architecture

Categories: Drupal

Acquia Developer Center Blog: Contribution Stories: Distilling Acquia experience into products and open source tools.

6 February 2017 - 9:20am

How Acquia distills tens of thousands of hours of client work into products and open source tools for Drupal. Drupal gets better when companies, organizations, and individuals build or fix something they need and then share it with the rest of us. Our community becomes better, stronger, and smarter when others take it upon themselves to make a positive difference contributing their knowledge, time, and energy to Drupal. Acquia is proud to play a part, alongside thousands of others, in making tomorrow’s Drupal better than today’s.

Tags: acquia drupal planet
Categories: Drupal

ThinkShout: The 2017 ThinkShout Front-End Stack

6 February 2017 - 5:00am

Front-end development is full of challenges - changing design trends, browser idiosyncrasies, client demands, and ever-evolving web standards to name a few. Over the last few years though, a new challenge has emerged. Which development stack should you choose?

Once upon a time, front end development didn’t really have a “dev stack.” You had markup in files, maybe output from a CMS, a stylesheet, and some jQuery if you felt like getting fancy. Now though, the array of options can be paralysing. Pre-processors, post-processors, task runners, and package managers have made many aspects of development faster, but which ones are best? Where do you start?

Here at ThinkShout, under the watchful eye of Eric Paxton, our Senior Front End Engineer, we’ve been trying out the various options whenever we take on a new project, to see how well it fits in our theming process. We’re pretty busy, so this covers a lot of ground quickly. We’ve been careful to fully document the tools used in the past so that we don’t bedevil the maintenance folks. (We are often the maintenance folks).

The last few builds have seen our dev stack settle down to a flexible tool set that is easy to setup and maintain, while providing us with excellent modern theming tools. Let’s dive in!

Getting Started: Languages, Handlers, and Package Management

At the bottom of a development stack are the languages used, the language handlers, and the package managers that allow you to include pre-built tools and libraries in your project. Some of these are interchangeable, but it solves a lot of problems if everyone uses the same fundamental tools.

In our case, we use Ruby and JavaScript as the base languages, and rbenv and Node as their handlers. By using Ruby and JavaScript, we get access to an extremely wide array of applications, tools, plugins, and more. Once these are installed (Using an OS package manager! In this case, Homebrew (since we all use Macs), we add package handling for these languages: Bundler and NPM respectively. This gives us the following base:

  • Ruby via rbenv, managing gems using Bundler
  • JavaScript via Node.js, managing packages using NPM

Now we can specify Ruby Gems and Node packages in a Ruby Make file (Rakefile), and a complex project setup is as simple as running rake install once from the theme directory, and starting the task watcher using rake serve. (To be more precise, we use the Rakefile to install the Ruby Gems as defined in the Gemfile, and the Node modules as specified in the package.json file).

The complete project setup for a new developer would be the following:

~: brew install rbenv ~: gem install bundler ~: brew install node ~: brew install npm; ~: cd ~/path/to/theme/directory ~: rake install ~: rake serve

After that, any new projects would only need the last three lines run.

The key to making this work is to have a Rakefile, a Gemfile and a package.json set up in our project’s theme so that rake install works properly. In our case we use the Rakefile to first run bundle install, which installs the appropriate gems and their dependencies:

Rakefile:

task :install do system 'bundle install' // this runs the Gemfile contents! system 'npm install -g browser-sync' end

Gemfile:

source 'http://rubygems.org' gem 'sass' gem 'sass-globbing'

This generates a Gemfile.lock listing all of the installed packages/versions.

The npm install lines in the Rakefile setup tools that we’ll discuss later. Our next layer in the stack are the SASS tools that Bundler installed.

SASS at ThinkShout (please pass the Bourbon)

In the middle of our stack is [SASS](http://sass-lang.com/. We use SASS in a fairly simple way at ThinkShout, installing it with [sass-globbing](https://rubygems.org/gems/sass-globbing. This allows us to set up directories that allow any files using the appropriate _filename.scss syntax to be included in the build. We also tend to keep the directory structure fairly minimal:

styles.scss:

@import 'lib/bourbon/bourbon'; @import 'lib/neat/neat'; @import 'lib/normalize/normalize'; @import 'global/*'; @import 'layout/*'; @import 'modules/*';

The first thing we include is the Bourbon mixin library. This includes coding shortcuts such as the pixels-to-rems syntax rem(24). This allows us to read a design’s pixel spacing and it converts them to the appropriate rem values. The Bourbon Docs are excellent and well-maintained as well. Never worry about browser prefixes or fallbacks again.

Next up is the Bourbon-related grid framework, [Neat](http://neat.bourbon.io/. A simple but powerful grid that uses semantic markup and easy-to-read terminology such as @include span-columns(9). No extra wrappers, no specific classes to add, and it’s extremely robust. We haven’t run into any cross-browser issues in over two years of using it, which says a lot, and since it’s only applied as you specify, it’s easy to break out of the grid if you need to.

Next up is [normalize.css](https://github.com/necolas/normalize.css, a modern update to the old CSS reset stylesheets. Not really much to add to that except it’s really well commented, so make sure you change it from normalize.css to _normalize.scss so that you don’t bloat your final site.css file.

The Global directory has the following:

_01.vars.scss _02.mixins.scss _03.extends.scss _04.base.scss

The _01, _02, etc. prefixes take advantage of the sass-globbing’s alphabetical file inclusion. All our site variables (colors, font weights, and so forth) are in vars, our custom mixins are next, then extends. Base has all of the base markup styles:

body { font-size: rem(16); font-style: normal; font-weight: $regular; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } h1, h2, h3, h4, h5, h6 { text-rendering: optimizeLegibility; // Fix the character spacing for headings } p, a, ul, ol, etc...

The layouts directory usually has a _layouts.scss file, which covers the basics of site layout. Since we use Drupal, we’ll often add a _regions.scss as well to specify layout for the various Drupal-generated content zones on a site. These files are where we use the Neat framework the most.

Finally, we have the modules directory - where most of the theming takes place. These are usually named by content type (_basic-pages.scss, _articles.scss, etc.), though there are often files such as _forms.scss and _homepage.scss as well. Sometimes we don’t even have to use our source maps to know where code is!

One of our good habits is to start with our mobile-first, responsive _01.template.scss file:

// Default / Mobile // Tablet (580px) @media all and (min-width: $tablet) { } // Large Tablet (768px) @media all and (min-width: $lg-tablet) { } // Desktop (1228px) $max-width: 1440px @media all and (min-width: $desktop) { }

When you want to add another theming module, you just make a copy of the template and your progressive breakpoints are included! (The $max-width: 1440px is there in a comment because it’s handy).

All of this gets handled by a task in our Rakefile, which sets a watcher for changes to any SASS file and compiles them into a single css/style.css:

desc 'Watch sass' task :sasswatch do system 'sass -r sass-globbing --watch sass/style.scss:css/style.css' end Pulling It All Together: Browsersync!

Finally, at the top of our stack, we have Browsersync. Eric Paxton, our Senior Front End Engineer, wrote an excellent overview of why we use this amazing tool, what it does, as well as how to install it in detail for Drupal 8.

In our stack it’s as simple as another task in that Rakefile:

desc 'Running Browsersync' task :browsersync do system 'browser-sync start --proxy "local.dev" --files "css/*.css" --no-inject-changes' end

And adding the following (generated by running browser-sync start) to the site’s <head> :

<!-- <script id="__bs_script__"> //<![CDATA[ document.write("<script async src='http://HOST:3000/browser-sync/browser-sync-client.2.12.3.js'><\/script>".replace("HOST", location.hostname)); //]]> </script> -->

This also sets a watcher on the CSS, and refreshes every browser you have open to localhost:3000 or the local network IP address it generates upon running rake serve.

The last part of the Rakefile implements the tasks we set up:

desc 'Serve' task :serve do threads = [] %w{sasswatch browsersync}.each do |task| threads << Thread.new(task) do |devtask| Rake::Task[devtask].invoke end end threads.each {|thread| thread.join} puts threads end

This has the magical effect of opening a new browser window to localhost:3000 when you run rake serve, and reloading it every time you save any of your SASS files. It also scrolls all open windows together, even when you open up things on your phone using the local network proxy, which it helpfully provides as output:

>>> Sass is watching for changes. Press Ctrl-C to stop. [BS] Proxying: http://site.dev [BS] Access URLs: ------------------------------------- Local: http://localhost:3000 External: http://172.16.5.235:3000 ------------------------------------- UI: http://localhost:3001 UI External: http://172.16.5.235:3001 ------------------------------------- [BS] Watching files... [BS] File changed: css/style.css write css/style.css write css/style.css.map

This is really the cherry on top of the dev stack - after using it for a little while, you’ll wonder how you ever got along reloading everything manually.

Stack Overview

In summary, here’s that front-end stack:

  • Ruby via rbenv, managing gems using Bundler
  • JavaScript via Node.js, managing packages using NPM
  • SASS with globbing, set up in a simple directory structure
  • Bourbon Mixin library
  • Neat Grid system
  • Normalize.css as _normalize.scss
  • A simple module template containing responsive breakpoints
  • Browsersync

None of this is carved in stone of course, and it gets slightly tweaked for every new build based on the project requirements, such as internationalization, the base CMS (Drupal, WordPress, or Jekyl in our case), and the desire to try out something new, which is fairly constant. After all, that’s how we got to the stack we have today!

Categories: Drupal

Janez Urevc: Possible solution for knowledge sharing in the Drupal 8 media domain

6 February 2017 - 1:48am
Possible solution for knowledge sharing in the Drupal 8 media domain slashrsm Mon, 06.02.2017 - 10:48

Drupal 8 has very good media handling support. Media team has (and still is) working hard to make Drupal the best CMS in the world when it comes to managing media. We have many modules in the contrib space that solve most of the common use cases in the domain. There is one problem though; there are many of them and some of them are quite complex and abstract. I've noticed that many times users struggle to completely understand what each module is responsible for, which features it comes with and specially how they all together fit into the bigger picture.

There are so-called feature modules (such as File entity browser, Media, Content browser, Media entity browser, ...) and distributions (such as NP8, Thunder, Lightning, ...) which ship with default configuration for the 80% use case. They are plug and play solutions, but it is also possible to use them as a base for learning and research of the ecosystem.

But unfortunately that's not enough. While some people learn the quickest by exploring existing solutions (myself included) that isn't the case for everyone. A lot of people need more guidance and those are the users that we're currently not supporting as much as we should. In order to drive further adoption of the media ecosystem and Drupal 8 itself we need to solve this knowledge sharing problem that I believe we have.

I was thinking about this problem a lot lately and I think I came up with an idea that could help us solve it.

It is a book.

A problem solving oriented book which would guide users through the ecosystem, explain individual parts and, most importantly, offer a bunch of recipes for the common problems. It wouldn't be one of those 800+ pages technical books (who has time to read that?!). Rather it would be a compact source of information which you can have on your desk and use it when you run into a problem. We all want to build websites and the purpose of this book would be to help you do that as fast and as efficiently as possible.

Book would produced by the people that designed and built the Drupal 8 media ecosystem, which would ensure highest levels of quality. It would be released under a Creative commons license with its sources publicly available on GitHub. Printed and compiled eBook versions would be sold through the standard channels.

Why copyleft?

I honestly believe into the free sharing of knowledge in our society. My opinion is that the only way to evolve our civilization is to freely share the knowledge that we have. There are also practical reasons besides the philosophical one. Making the book publicly available ensures that it will be likely updated as the ecosystem and Drupal itself evolve and change. This wouldn't necessarily be the case if the standard copyright license would be used.

Great idea! Why didn't you realize it already?

I am glad that you agree! :) Well... it is not that easy. Producing a real book is not that simple and it also comes with quite some expenses. There is the cost of the content production, proofreading, design, print, shipping, ... I've done a back of a napkin calculation and estimated that we'd need around 20.000€/$21.500/17.000GBP to do it.

My idea was to start a crowdfunding campaign to raise this amount. Backers would, depending on the perk level, get an eBook, print edition or both. Besides that they'd also get early access to the repository with the ability to provide feedback during the process of writing.

Now I need your feedback

I need to hear from you. Is this something that would benefit the community? Is there any better way to educate users about the ecosystem? Would you prefer an online video course instead of a book? In-person training? Something else? Would you be prepared to back the crowdfunding campaign? If yes, what amount would you be prepared to invest?

Please use the form below to share your thoughts. Thank you! I appreciate it.

Enjoyed this post? There is more! Join us at the next Drupal Media sprint at the Mountain camp in Davos! Playing with the Sculpin static site generator Results of the Drupal 8 media sprint
Categories: Drupal

fluffy.pro. Drupal Developer's blog: Ctools: custom access plugin

5 February 2017 - 9:12am
Last time we've learned how to create custom ctools content type plugin. In that post we've already created a module example_module where we defined the plugin. This time we will learn how to create custom ctools access plugin. This type of ctools plugins can be used as a selection rule for a panel variant or as a visibility rule for a panel pane. Please, read previous post before continue reading this. There is described how to create a module and integrate it with ctools.
Read more »
Categories: Drupal

fluffy.pro. Drupal Developer's blog: Ctools: custom content type plugin

4 February 2017 - 5:37am
Ctools content types are an alternative to standard Drupal blocks. They are more comfortable and powerfull than blocks. Ctools content type plugins also known as panel panes. In this post you will learn how to create a configurable ctools content pane plugin.
Read more »
Categories: Drupal

Freelock : Ways to measure your website’s effectiveness

3 February 2017 - 3:11pm

We've worked with many clients over the years, who all have very specific website development needs. While some clients may share common goals, each may approach those business goals in different ways. But, time and time again, we usually start by asking a client in what ways are they measuring their website's effectiveness. In this 4 part series, I'll discuss identifying purpose and overcoming obstacles, complaints of current site capabilities and establishing budget, metrics to success and selecting a vendor, then finally risk tolerance and disaster recovery planning.

Categories: Drupal

TheodorosPloumis blog: Presenting DREC (DRupal Eight Commands)

3 February 2017 - 8:47am

DREC is a collection of basic bash scripts that work together to accomplish common development tasks for Drupal 8.x projects. In simple words drec uses shell commands, unix utilities, drush, drupal console, git, docker and other software that all together can install, update, clone etc a Drupal project.

The main problem I wanted to solve with drec is to setup and update a local development environment from scratch for a Drupal distribution on LAMP. So, you should probably use drec locally or on a development environment and not on a production server. I created this for Linux but it could also play well with any unix* system (hope so).

Some of the characteristics of drec is that it can run commands in chain and that it uses a configuration file (.drecfile) where each project can keep useful details about it that drec can (re)use when running.

Here is a list of the current drec commands:

Command Description git-clone Clone a site from a git repo create-folder Create the project folder delete-folder Delete the project folder create-vhost Create a virtualhost for project's domain delete-vhost Create a virtualhost install-drupal Install Drupal using drush prepare-install Prepare a Drupal site for installation (public files, writable settings etc) create-docker Create a docker container delete-docker Delete a docker container create-drush Create a drush alias delete-drush Delete a drush alias composer-install Run composer install on the project folder composer-project Run composer create-project ... on the project folder create-db Create a mysql database download-db Download a mysql database delete-db Delete a mysql database askme Asks user to continue or exit init Create a *.drecfile settings file on the ~/.drec folder delete-project Delete all project files, database, container, drush alias etc projects Show all *.drecfile filenames inside ~/.drec folder debug Show variables on cli

and here are some usage examples:

// Clone a project and start a docker container with mapping folders drec init,git-clone,create-docker // Clone a project from a git repo, create a virtualhost and create a new configuration file drec init,git-clone,create-vhost // Create a new database, clone from git repo, prepare for Drupal installation and install Drupal drec init,create-db,git-clone,prepare-install,install-drupal // Just debug a configuration file and read its values drec debug /path/to/MYFILE.drecfile drec debug filename/of/.drecfile

 

Issues, suggestions and your opinion are always welcome.

 

View drec on Github

 

Categories: Drupal

TheodorosPloumis blog: Writing Documentation for Drupal 8.x teams

3 February 2017 - 7:47am

Last year I had the chance to collaborate with a Drupal company in Greece for a large scale Drupal 8.x project. One of these that you say Drupal was made for!

It was early 2016 when Drupal 8.x had just arrives. At that time thing were a bit confusing (although promising) with the new Drupal version. Composer adoption was still in "alpha", popular Drupal contributed modules where not ready for production, new concepts in core had to be learned, Phpstorm (or another advanced IDE at least) was required etc. In general things were unclear and the development team needed a guide in order to avoid the "search on Google" disease.

One of my responsibilities for this project was to document the "best practices" and HowTo​ work with the new Drupal version, with software such as git, Docker, composer and all the software needed. In plain English I had to create, maintenance and update the official documentation of the project.

After some discussion we had internally we decided to publish these docs using the ReadTheDocs.org platform under an open source license (Open Publication License). We did this not only to make the things easier for us or reuse the knowledge we got but also to get help from anyone else that wanted to do the same. The audience are people that already know Drupal 7.x and want to dive into Drupal 8.x. In fact, I was also learning new concepts and methodologies (but, yes, with Drupal we never stop learning new things, don't you agree?)

Copying the first lines of the docs to get the purpose of the docs:

A useful manual for Drupal developers, themers and site builders that want to start collaboration work with the new Drupal 8.x stuff.

The initial documentation created to help us in the maintenance and development of a complex Drupal 8.x distribution but became more generic and public available in the future.

Common best practices are in place and new methodologies of development are proposed on this manual.

Before starting I 'd like to describe a bit the project I am talking about.

It was a multilingual portal with multidomains, unified Dashboard per (sub)domain, content workflows and many content types and fields. More about the parameters can be found at dropdog.readthedocs.io/parameters. We decided to use Features module and Drush make files, Github and the Git Flow model for the git branches. We were also using Docker for local development environments as also as other Shell scripts to automate the processes.

The Docs are separated into 11 major sections which are:

  1. Learn
  2. Parameters
  3. Infrastructure
  4. Development
  5. Theming
  6. Tests
  7. Reviews - Entity
  8. Reviews - Views
  9. Scripts
  10. Development thoughts
  11. Terminology

 

The titles of each section are self describing. Unfortunately some sections are incomplete and contain only the headers  (Scripts, Tests and Terminology).

One interesting thing I would like to promote here are the naming conventions we decided to use while developing the website (entities & fields, views, features etc).  Using the pattern field_CONTENTTYPE_SHORTNAME and other similar conventions, for example, allowed us to make changes in bulk, search for issues more easy, avoid Features issues and conflicts with configuration and "standardize" the theming. Furthermore, in order to make some human reviews for Entities and Views we created lists of checkboxes ready to be copied on a Github issue when a new Entity or Views is created.

Another one is the rules we had when setting User Roles such as that "Every custom module should define its own permissions. Never reuse an existing permission!" I still believe strongly that this rule should be implemented with Drupal core modules also but this is another issue that will not discuss now...

For the Theming we followed a variation of the Atomic Design approach (see Patternlab) and created our own "Drupal specific" folder structure expecially for the scss files. Of course for scss we set up some well known rules and linting to avoid development conflicts. Another useful thing here is that we followed the desktop first design. That means that we first designed for desktop but with mobile in mind and not the opposite since the mobile visits were a few (and there were no obvious signs that this would change in the future).

I could say a lot for the whole experience but it's better to let you "explore" the docs and take what you find may be useful. Pull requests and suggestions are always welcome.

dropdog.readthedocs.io

 

Categories: Drupal

Convivio: Healthy Practices for Designing a Web API for Drupal 8

3 February 2017 - 3:14am

Modern digital services need to integrate and interact with each other to provide a seamless user experience and data integrity in every…

Categories: Drupal

Vardot: 12 most shared articles about Drupal in 2016

2 February 2017 - 2:14pm
News Read time: 6 minutes

January is always a time of conclusions. We evaluate a past year, remember its best moments, and hope that the new year will bring us even more positive moments. It’s also a time of different rankings. We at Vardot decided to contribute to the good vibe of the community by going through the best articles of the last year according to BuzzSumo and choosing those ones that had a better feedback from you. The blog post you read now lists, in ascending order, the 12 most shared articles about Drupal in 2016.

 

12. Happy birthday Drupal (924 shares)

On Drupal's 15th birthday, the community celebrated with an infographic that reminisces about the significant events and achievements in the past year. No open source project can survive and

even thrive for so long without a vibrant community. Scroll through the article and you will feel the urge to attend that next DrupalCon in some exotic location.

 

11. Drupal founder sets up Acquia Labs to research the future of the web (958 shares)

Celebration of the past 15 years aside, Dries Buytaert is never complacent with his brainchild. He is already thinking ahead and inventing the next-generation web user experience. To that end, Dries set up Acquia Labs to engage in R & D. If he has his way, the web of the future won't be browser based, and page views will be a thing of the past. The next generation of user interaction will be voice-controlled, visual in the style of VR (Virtual Reality) or AR (Augmented Reality), and adaptable to user situations.

 

10. Improving collaboration with forks (1.1k shares)

The success of the open-sourced Drupal project hinges on the collaboration of a large community of developers to enhance and customize the product. Drupal developers collaborate by writing and sharing code. When an upstream developer modifies code that has been previously forked, the changes often need to be propagated downstream in a controlled manner. This article is a brief introduction on pull requests created from a fork. You will find the link to a longer guide at the end of the original article.

 

9. Several critical remotely exploitable flaws found in Drupal modules, patch ASAP! (1.5k shares)

The Panama Papers refer to 11.5 million private financial documents which were leaked from a Panamanian law firm. These documents collectively implicate 72 current and former heads of state in fraudulent activities such as tax evasion. The leak was caused by vulnerabilities in Drupal and WordPress, two leading content management systems (CMS). Drupal site administrators did well for heeding the call of this article to patch the known vulnerabilities.

 

8. Drupal: 15 years old and still gaining momentum (1.7k shares)

This is a blog post written by Dries Buytaert on the day of the 15th anniversary of releasing Drupal 1.0. It gives us a glimpse, from the firsthand perspective, of the path the Drupal project has taken over the years. Dries even recounted some product decisions he made, both rightly AND wrongly, over that time period. He shared about his own professional development as a result of this journey, and his renewed commitment to building a better and safer web for all.

  

7. Panama Papers hack: Unpatched WordPress, Drupal bugs to blame? (1.8k shares)

The author of this article plays the role of investigative reporter on the Panama Papers leak. It discusses 2 possible entry points for the intruder (or intruders) into the Panamanian law firm's computer systems: a public WordPress website and a customer-only Drupal portal. Both installations were running outdated and unpatched versions of the respective CMS software at the time of the leak. The author advocates keeping your systems up-to-date in this article. He praises WordPress for automating security updates, and then chastises Drupal for lagging behind in this crucial process.

 

6. Is this how a hacker got the Panama Papers? (2.3k shares)

It is very difficult to pinpoint, without access to the system log files, how an intruder hacks into a web system. But, that does not stop the author of this article to put forth hypotheses in the Panama Papers leak, even identifying the plugin that may be the culprit. Site administrators should read this article for the long list of things that the Panamanian law firm did wrong, so that they can avoid making the same mistakes.

 

5. The security flaws at the heart of the Panama Papers (2.4k shares)

This is the Wired magazine's report on the Panama Papers leak. The article distinguishes itself from other similar reports because it conducts its own primary research. Specifically, it cites both named security experts and anonymous sources alike. It is obvious that this article does not target a technical audience. However, it compensates for the lack of technical depth by offering breadth in its research. It covers some lesser reported probable causes for the leak, including the possibility of it being an inside job.

 

4. From encrypted drives to Amazon's Cloud - the amazing flight of the Panama Papers (2.7k shares)

The Forbes article tells a great story, not on what can possibly cause the Panama Papers leak, but rather on what actually happens after the leak. After the hacker with a pseudonym of John Doe handed over the papers, the journalists had a huge problem in their hands: one with a volume of 11.5 million documents and 2.6 TB in size. The huge database had to be encrypted for confidentiality, and, at the same time, be accessible to a horde of journalists around the world. In addition, powerful text analysis tools were required to help make sense with that many documents. This article is a good read to find out how the journalists solved their big data problem with open-source solutions.

 

3. Five ways to speed up Drupal 8 sites (2.8k shares)

This article lists 5 ways to optimize the speed of a Drupal 8 website. Many of the tips are new and specific to Drupal 8 installations.

 

2. Panama Papers: Email hackable via WordPress, docs hackable via Drupal (3k shares)

WordFence is a leading web security company, and this article is their take on the Panama Papers leak. It illustrates how an intruder can potentially hack into the unpatched WordPress server hosting the Panama Papers, and from there, gain access to the corporate email server. It also summarizes how the outdated Drupal installation makes it easy for hackers to break into Drupal and steal documents.

More to this topic: 5 security modules that every Drupal website must have

 

1. How to configure your Drupal 8 ARM template deployment to Azure using existing MySQL Server (4k shares)

The most shared article about Drupal in 2016 explains the parameters required to configure Drupal 8 on the Microsoft Azure cloud computing platform.

 

The end of 2016 marks the first full year of Drupal 8.0 after its official release. With the buzz surrounding a major new release and some high-profile security flaws, the Drupal community was very active during the year. We are looking forward to more interesting blog posts in 2017. Don’t forget to share those ones you like most!

  Tags:  Drupal Planet drupal 8 Title:  12 most shared articles about Drupal in 2016
Categories: Drupal

Texas Creative: The Future is Encrypted

2 February 2017 - 1:49pm

Google is leading the charge to a more secure web. The tech giant is taking steps in the way it handles non-secure websites in both search rankings and for its over 1 billion Google Chrome users. In search results, non-secure websites will now take a back seat to those that are secured with trusted SSL certificates. Texas Creative has answered the call by retroactively adding free SSL encryption to all of our Drupal websites.

What is SSL?

SSL (Secure Sockets Layer) is a web technology that creates an encrypted connection between the server and the browser to secure the data while in transit. Sites equipped with SSL signify this with a lock icon in the address bar. Until recently, you’ve likely only encountered this on your bank’s website or any site that is collecting personal information like credit card numbers.

Read More
Categories: Drupal

Pages