Getting the group into the URL with Purl

Parent Feed: 

The corners of Drupal 8 that aren't there are quickly dwindling, but there are still some that need to get worked out. While upgrading our internal issue tracker, we hit a new one -- getting a group context set via a URL alias, and generally keeping posts within a group.

In Drupal 6 and 7, we made extensive use of Organic Groups, Spaces, and Purl to make it easy to go straight to a project by URL. For example, with a project site of bugs.freelock.com, to get to the group for the "acme" client, we might have a URL of https://bugs.freelock.com/acme. Purl is a module that can take a specific part of a Drupal URL -- a subdomain, a query parameter, or the first part of the path -- and convert that part into a group context. It then rewrites the URL internally to a normal Drupal path where the rest of the system can do whatever needs to be done, and return a result. Finally, it inserts the group context back into links that Drupal generates. The end result? When you're in https://bugs.freelock.com/acme, views can be filtered to show only content inside the group, entity references can be filtered by the group context, new content gets automatically added to the group, you basically stay in the group until you follow a link out of it.

With our new PM site, we decided to give the new Group module a try, with its attempt to provide a simpler architecture than the previous Organic Group module. As best we can tell, Og is useful if you want to have different roles and permissions within specific groups, whereas with Group individual groups can only vary by creating a new type of group.

Challenges getting Group and Purl working in D8

We managed to get Group up and running, and all our old content migrated easily enough. But... then we ran into issue#2717981: Group ID unavailable when editing the referenced entity, which is one of the 3 blockers for a full release of the module. And... no progress on this for months.

I then checked out Purl to see where it stood -- which turned out to have an 8.x version started but not functional, with some progress made in a gitub repository. There was also some good starting documentation for implementing plugins for Purl. Given our recent under-the-hood Drupal 8 experience, we dove into the code and started working through issues. It probably ended up taking 5 or 6 hours to get it all working, but along the way we ended up fixing 7 different issues in Purl, in addition to creating a new Group Purl module and posting it to Drupal.

So all in all, I think we've arrived at a pretty slick solution! Here's how to get it up and running on your Group site...

Installation

These steps are sure to improve, but for the time being, there are a few hurdles to get the working code.

Two things in particular - getting a version of Purl with all the current patches, and installing the Purl fields on the various types. Here's the gist:

  1. On a working Drupal 8 site, download group and group_purl as usual from Drupal.org
  2. Until there is an Alpha release of Purl on Drupal.org, check to see if the ActiveLamp github repo has incorporated the pull requests -- if not, you can get a copy from the Freelock github repo. In either case, use the "master" branch.
  3. Enable Group, Purl, and Group Purl modules.
  4. At the current writing, you may get a stack trace/fatal error. To fix, you might need to use Drush -- running "drush entity-updates" will install the Purl fields appropriately that are the cause of this error.

Now, on to configuration...

Configuring Purl for Group module

At the moment, there are several "hidden" configurations you'll need to check out to get all of this working, so there's several basic steps to get to a working solution. The order you do these in don't particularly matter, once you have a group type set up you just need to get these other elements in place to get a working result:

1. Install the Group Purl provider plugin.

Under Configuration -> Search and Metadata -> Persistant Urls, Add a Purl Provider.

Group Purl provider

Give it a name, select "Group Provider" for the provider, and "Group Prefix" for the method. Then save.

The Group Purl provider loads the matched group into a Drupal context, which may then be used by views, blocks, etc.

The Group Prefix method creates a list of all groups using their currently configured URL alias, and matches when that alias matches the first part of the visited path. It strips off that first part, and then creates a new subrequest that it passes into the Drupal kernel for the actual processing. It also provides a URL helper that re-injects it into the start of URLs generated by the Url service.

At the moment, this prefix method does not match the actual group -- it only rewrites children paths. This allows you to access the Group entity itself, without causing redirect loops or taking you to a different page.

2. Create a group type and configure content types to be part of the group

Under Groups -> Group Type, add and configure a group type. Then, after you have the basic group type set up, you need to configure which content types go into the group. After you've saved the group type, go to its "Content" tab, which you can reach using the "Set available content" operation:

Group operations menu

When planning out your site, I would recommend having certain content types set to be in groups, and other content types outside groups. That's what you can configure on the "Content" page for a group -- which content types may be part of the group.

What you actually configure here is a special many-to-many relationship between content and groups. This relationship is an entity type called "group content", and is the fundamental way everything gets associated with a group. Group module provides a built-in Group Content configuration for users -- on this tab, you create the relationship for each content type you want in the group. Click "Install" for a content type, and then configure the nature of the relationship:

Configure a group content type

... Unless you have more sophisticated needs, I recommend setting Group Cardinality to 1 -- content of this type may only appear in a single group -- and uncheck the "use 2-step wizard" option.

3. Set the "Purl context" option on each content type that goes into a group

Under Structure -> Content Types, edit each content type you've configured as "group content". On the main edit page, go to the Purl tab and check the box to "Keep context of the node":

Purl content type settings

... if this option is set, then when you visit a node inside a group path, Purl keeps the group path active. Using our previous example, a node might be at https://bugs.freelock.com/group/2/node/234 -- this option keeps the URL the same.

If you uncheck this box, Purl will redirect out of the group when landing on a node of this type. So if node/5 is a basic page, and basic pages have this box unchecked, users visiting https://bugs.freelock.com/group/2/node/5 will get redirected automatically to https://bugs.freelock.com/node/5, and lose the group context.

Note: The Group Prefix method is aware of URL aliases -- if you set a pathauto alias pattern for group entities, the path prefix will use the alias instead of /group/xxx.

4. Configure views to use Group ID from Purl as a default argument.

The next bit to get in place are views -- views blocks, views pages, and entityreference views. There are two crucial parts to making this work: setting up the relationship to the Group Content, and providing the Purl default argument.

Starting with a view of content, under the Advanced section, click Add next to the Relationships header, and add a relationship to "Group content for content". If this view only uses a single content type, select it under "Filter by plugin".

Next, add a Contextual Filter of "Parent Group".

Finally, under "When the filter value is not available", select "Provide Default Value", and select "Group ID from Purl":

Group ID from Purl default argument

In the validation section you can define what to do with this view if you're outside a group -- generally you would either hide it (to make blocks only visible inside a group, for example) or show all values.

If you make an entityreference view, this becomes a great way to limit the list of available reference fields to other items in the group.

5. Menu links

The last thing to address are links to get in and out of groups. Purl adds a context to each menu -- you can use this to define a distinct menu for a particular group. More scalable than that is relying on the URL rewriting.

When you specify a menu link inside a view, Purl will rewrite the URL for that link, pretty much always. If you want to be able to control a whether a particular menu item keeps you in the group or takes it out, you need to add the menu link directly.

Maintain or strip Purl context in menu link

So on custom menu links, if you "maintain context", the links will get rewritten to include the group path. If you "Strip PURL context", the links will not get rewritten, and will take you out of the group. Or you can make a link go to a specific group right from this dropdown.

That's it! Those are the building blocks you need to make groups workable in Drupal 8. Leave any feedback below, and if we can help with your Group-based site, drop us a line!

Author: 
Original Post: 

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web