-
Hiding Country from an Address Field’s Output in Drupal 8
There are two field formatters available on an Address field in Drupal 8:
The Plain formatter uses a Twig template file but it would take some work to override with the proper markup. The Default formatter doesn’t use a Twig template file so you cannot simply override via Twig.
If your goal is to simply hide the country value from the output you can use hook_preprocess_field to alter the country value. As it turns out, unsetting the country field doesn’t help, but setting the value to an empty string does work.
Here’s an example (in a module; you can put in your .theme file if you’d prefer the setting to be at the theme level).
12345678/*** Implements hook_preprocess_HOOK().*/function mymodule_preprocess_field(&$vars) {if ($vars['field_name'] == 'field_person_address') {$vars['items'][0]['content']['country']['#value'] = '';}} -
Modifying Rows During a Drupal 8 CSV Migration
Migrate Source CSV is currently the source plugin of choice for doing a CSV-to-Drupal migration with the Migrate API in Drupal 8. In this post I will demonstrate how to manipulate the CSV data in realtime during the migrate:import operation. You can think of this as the equivalent to prepareRow() that you have seen elsewhere, like my blog post Extending the Migrate Plus JSON Parser in Drupal 8.
Please make sure you have a working migration before you begin; it’ll make things easier to troubleshoot if you know you had a good starting point.
-
404 Error Serving a /libraries page in Drupal 8
It took some time to figure out why I kept seeing a 404 error (page not found) on http://www.mysite.com/libraries. The issue seemed to only present itself on our Acquia environments. Ultimately I realized that there was a conflict due to having a “libraries” directory in the document root of the site (e.g., /docroot/libraries).
It was a quick fix after realizing the cause of the problem; I added this to my .htaccess file, above the other index.php rewrite rules.
12RewriteCond %{REQUEST_URI} ^/libraries/?$RewriteRule ^ index.php [L]The reason this wasn’t happening on my local machine is because I’m using nginx locally for this particular website; the .htaccess file isn’t used.
-
Joining Strings in a Drupal Views Field using Twig
I have two optional fields on a Drupal 8 content type: City and State. Both are rendered as simple text.
In a fields-based View I wanted to show the field output as “Portland” or “Portland, OR”, or “OR”.
First, I added the two fields, State then City (the order is important). Next I excluded the State field from display.
Finally, I opened the Rewrite Results pane of the City field and checked the Override the output of this field with custom text checkbox.
After several attempts at using Twig’s join, replace, spaceless, and more, I landed on this simple solution as the rewrite text:
1234567{% if field_member_city %}{{- field_member_city -}}{% if field_member_state %}, {% endif %}{% endif %}{% if field_member_state %}{{- field_member_state -}}{% endif %}Note the whitespace modifiers (hyphens). These are the key to getting Portland, OR instead of Portland , OR .
Also, we cannot just use {{field_member_city}}, {{field_member_state}} because we could end up with , OR if City is empty.
-
Using GROUP_CONCAT to Combine Rows in a Drupal Query
Recently I was working on a D7 to D8 migration. I was trying to import news items and their taxonomy terms (among many other things). To make things simple I wanted the query results to have (for each node) a single field that contained a comma-separated list of taxonomy terms which I could then explode during the row processing. Using GROUP_CONCAT we can achieve this! Let’s break it down:
The Drupal 7 site has the following structure (focusing on the important bits for this blog post):
123-- News Item (news_item) <content type>---- Categories (field_categories) <field collection>------ Audience (field_term_audience) <taxonomy reference; unlimited values>The migration relies on the d7_node migrate source plugin, which basically queries for nodes of a specific type. The query object looks like this (simplified for this blog post):
-
Adding Fields to Inline Entity Form Table
Inline Entity Form is a useful module for reference entities and being able to edit them in place.
Here’s what the edit form looks like out of the box for an unlimited value entity reference:
Often it’s helpful to provide additional information to your editors.
If you have a look at inline_entity_form.module you will find a function called theme_inline_entity_form_entity_table(). Within this you will see how this table is built, and how you can manipulate the form to add additional columns of information.
-
Basic HTTP Authentication in Drupal Site Using settings.php
Here’s a quick and painless way of preventing public access to a Drupal site using settings.php (or settings.local.php).
I’ve been using this for development and staging sites that I want to keep private.
If you want this to be available to all settings*.php files you should put this near the top of your settings.php file:
123456789101112131415161718192021222324/*** Locks the site via basic http auth.** D7: if (!drupal_is_cli())* D8: if (PHP_SAPI !== 'cli')** @param array $users* Array of users e.g., ['user1' => 'pass1', 'user2' => 'pass2'].** @see https://agileadam.com/2018/04/basic-http-auth-drupal-site-using-settings-php/*/function lock_with_basicauth($users) {if (PHP_SAPI !== 'cli') {$valid_users = $users;$valid_usernames = array_keys($valid_users);$user = (!empty($_SERVER['PHP_AUTH_USER'])) ? $_SERVER['PHP_AUTH_USER'] : '';$pass = (!empty($_SERVER['PHP_AUTH_PW'])) ? $_SERVER['PHP_AUTH_PW'] : '';if (!((in_array($user, $valid_usernames)) && ($pass == $valid_users[$user]))) {header('WWW-Authenticate: Basic realm="Private Site"');header('HTTP/1.0 401 Unauthorized');die('Not authorized.');}}}Then, you can leverage it wherever you’d like. For example, on an Acquia site I might add this to the bottom of settings.php:
12345678910if (!empty($_ENV['AH_SITE_ENVIRONMENT'])) {switch ($_ENV['AH_SITE_ENVIRONMENT']) {case 'dev':lock_with_basicauth(['agileadam' => 'mysecretdevpass']);break;case 'test':lock_with_basicauth(['agileadam' => 'mysecretstagepass']);break;}}For non-Acquia sites I’d call the function at the bottom of settings.local.php.
-
Valet+ Quickstart for Drupal Development
Here’s a README.md file that I’ve developed over time. It explains how I setup and use Valet+ for quick and powerful Drupal development.
Sorry for the formatting. I’ll get markdown support on my blog sometime…
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160# Initial Valet SetupFollow official install guide at https://github.com/weprovide/valet-plus# InstallationThe following procedure is what it took to get Valet+ installed correctly on my Macbook.I had issues with MySQL not starting.# Install Valetbrew updatebrew tap henkrehorst/phpbrew install valet-php@7.2brew install composerbrew link --overwrite composerbrew install php@7.1composer global require weprovide/valet-plusvim ~/.bash_profilevalet fixvalet install# Link an appcd ~/valet/mysite/docroot/valet link mysitevalet restart# Create a databasevalet db create mysite(COMMAND FAILED; MYSQL NOT RUNNING?)valet stop# Fix MySQLbrew uninstall mysqlbrew uninstall mysql@5.7brew cleanuprm -rf /usr/local/var/mysqlrm /usr/local/etc/my.cnfbrew install mysql@5.7brew link --force mysql@5.7brew services start mysql@5.7valet fixvalet install# Create a databasevalet startvalet db create mysite# SUCCESS!----Tips:- Make a folder to store your sites `mkdir ~/valet`- Do not `valet park`. See notes below.# Site Setup (serving from ~/valet/[sitename]/docroot)1. Create site directory via `mkdir ~/valet/mysite`2. Move into it via `cd mysite`3. Use rsync/git/composer/whatever to acquire your site files --e.g., `git clone git@bitbucket.org:mysite.git .`4. Move into docroot via `cd docroot`5. Tell valet to serve a site from this folder via `valet link mysite`6. Browse to `http://mysite.test` -- if you get a DB error, that's okay for now.7. Create a new database via `valet db create mysite`. If this fails, see top of this doc!8. Existing site? Import the data with `valet db import ~/db_snapshots/mysite.sql.gz mysite`9. Update the db credentials in `sites/default/settings.local.php` -- database=mysite,username=root, password=root10. Browse to `http://mysite.test` -- hopefully it _just works_----# Explanation of Serving Sites from Docroot/Web SubdirectoriesDon't `valet park` in this `~/valet` directory. You can do itsomewhere else if you need auto-folder-to-domain functionalitywhere you create a folder and it results in having`http://folder.test` available immediately.For this `~/valet` directory we have sites that have `/docroot`subdirectories containing the actual site files, so instead of `park`,use `valet link [sitename]` from the docroot directory. This willcreate symlinks in `~/.valet/Sites` which will be accessibleat `http://sitename.test`----# Other Tips- RTFM! Valet does some amazing things. Read the docs to learn about xdebug, loggingsharing, redis, mailhog, etc.- `valet links` to view current links- `valet unlink [sitename]` to remove a link- `valet use 5.6` to change php version (5.6 7.0 7.1 7.2)- Edit `~/.valet/config.json` to set domain suffix (`.test` is default)(`valet restart` after)- Edit `~/.valet/Drivers/` to setup new drivers (shouldn't need to)(`valet restart` after maybe?)- `valet share` to share your site over the internetHit CTRL-C to stop sharing- You may not have a `$_SERVER['DOCUMENT_ROOT']` value. Until I have time to lookinto this, I'm just setting that at the top of my Drupal `settings.local.php` file.```// Manually set because Valet+ doesn't, for some reason$_SERVER['DOCUMENT_ROOT'] = '/Users/adam/valet/mysite/docroot';```- Install and enable memcached with:`brew install php71-memcached && valet restart`- Configure php with ini files at, for example, `/usr/local/etc/php/7.1/php.ini`- To generate nginx file specific to site you can:- Generate config file by securing the site: `valet secure mysite`- Copy the nginx config code in the resulting nginx file at `~/.valet/Nginx/mysite.test`- Stop here if you want to keep SSL... otherwise:- Use `valet unsecure mysite` to remove SSL (if you don't want the site served over 443)- Re-create `~/.valet/Nginx/mysite.test` with the copied code- Rework config file to serve port 80 without any SSL certs attached- To fix `upstream sent too big header while reading response header from upstream` error:- Add these lines to your `~/.valet/Nginx/mysite.test`:- `fastcgi_buffers 16 16k;`- `fastcgi_buffer_size 32k;`- NOTE: Add these to the `location ~ \.php$ {` section if you have a full nginx conf file- To fix `504 Gateway Timeout` error:- Add these lines to your `~/.valet/Nginx/mysite.test`:- `fastcgi_buffers 16 16k;`- `fastcgi_buffer_size 32k;`- `fastcgi_read_timeout 180;`- NOTE: Add these to the `location ~ \.php$ {` section if you have a full nginx conf file----# LinksValet+ Docshttps://github.com/weprovide/valet-plusParking and Linkinghttps://laravel.com/docs/5.5/valet#serving-sites -
Drupal 8 User Photo Update Form
Recently I had to come up with a simple way for users to change their member profile photo without requiring them to visit the user edit screen. Here’s the result:
First, I added a new Image field called “Member Photo” to the user account fields (machine name field_user_picture). Here are the settings I used:
- Allowed extensions: png, gif, jpg, jpeg
- File directory: users/[date:custom:Y]-[date:custom:m]
- Max size: 10 MB
- Checked: Enable alt field
- Checked: Alt field required
-
Creating a Drupal 8 Route to a User Page with Dynamic User Object
It took me some time to figure out the right combination of properties to make this work.
My goal was to create a form that lives at /user/UID/photo (think /user/1/edit).
I wanted the user object to be passed into the form as an argument.
Here’s the mymodule.routing.yml file:
123456789101112mymodule.profile_photo:path: '/user/{user}/photo'defaults:_form: '\Drupal\mymodule\Form\ProfilePhoto'_title: 'ProfilePhoto'options:parameters:user:type: entity:userrequirements:user: \d+_entity_access: 'user.update'Here’s the src/Form/ProfilePhoto.php file: