Newsfeeds

Warlord Games Overstock Clearance Sale Happening Now

Tabletop Gaming News - 24 August 2017 - 7:00am
Man, nobody likes having to buy things at full price. I mean, we do it most of the time, but if you can get it for cheaper than usual, why not do so? Well, Warlord Games is looking to move out some overstocked items from their warehouse, so they’re having a big sale. Now’s your […]
Categories: Game Theory & Design

Evolving Web: Migrating Aliases and Redirects to Drupal 8

Planet Drupal - 24 August 2017 - 7:00am

When content URLs change during migrations, it is always a good idea to do something to handle the old URLs to prevent them from suddenly starting to throw 404s which are bad for SEO. In this article, we'll discuss how to migrate URL aliases provided by the path module (part of D8 core) and URL redirects provided by the redirect module.

The Problem

Say we have two CSV files (given to us by the client):

The project requirement is to:

  • Migrate the contents of article.csv as article nodes.
  • Migrate the contents of category.csv as terms of a category terms.
  • Make the articles accessible at the path blog/{{ category-slug }}/{{ article-slug }}.
  • Make blog/{{ slug }}.php redirect to article/{{ article-slug }}.

Here, the term slug refers to a unique URL-friendly and SEO-friendly string.

Before We Start Migrate Node and Category Data

This part consists of two simple migrations:

The article data migration depends on the category data migration to associate each node to a specific category like:

# Migration processes process: ... field_category: plugin: 'migration_lookup' source: 'category' migration: 'example_category_data' no_stub: true ...

So, if we execute this migration, we will have all categories created as category terms and 50 squeaky new nodes belonging to those categories. Here's how it should look if we run the migrations using drush:

$ drush migrate-import example_article_data,example_category_data Processed 5 items (5 created, 0 updated, 0 failed, 0 ignored) - done with 'example_category_data' Processed 50 items (50 created, 0 updated, 0 failed, 0 ignored) - done with 'example_article_data'

Additionally, we will be able to access a list of articles in each category at the URL blog/{{ category-slug }}. This is because of the path parameter we set in the category data migration. The path parameter is processed by the path module to create URL aliases during certain migrations. We can also use the path parameter while creating nodes to generate URL aliases for those nodes. However, in this example, we will generate the URL aliases in a stand-alone migration.

Generate URL Aliases with Migrations

The next task will be to make the articles available at URLs like /blog/{{ category-slug }}/{{ article-slug }}. We use the example_article_alias migration to generate these additional URL aliases. Important sections of the migration are discussed below.

Source source: plugin: 'csv' path: 'article.csv' ... constants: slash: '/' source_prefix: '/node/' alias_prefix: '/blog/' und: 'und'

We use the article.csv file as our source data to iterate over articles. Also, we use source/constants to define certain data which we want to use in the migration, but we do not have in the CSV document.

Destination destination: plugin: 'url_alias'

Since we want to create URL aliases, we need to use the destination plugin url_alias provided by the path module. Reading documentation or taking a quick look at the plugin source at Drupal\path\Plugin\migrate\destination\UrlAlias::fields(), we can figure out the fields and configuration supported by this plugin.

Process ... temp_nid: plugin: 'migration_lookup' source: 'slug' migration: 'example_article_data' ... temp_category_slug: # First, retrieve the ID of the taxonomy term created during the "category_data" migration. - plugin: 'migration_lookup' source: 'category' migration: 'example_category_data' # Use a custom callback to get the category name. - plugin: 'callback' callable: '_migrate_example_paths_load_taxonomy_term_name' # Prepare a url-friendly version for the category. - plugin: 'machine_name'

Since we need to point the URL aliases to the nodes we created during the article data migration, we use use the migration_lookup plugin (formerly migration) to read the ID of the relevant node created during the article data migration. We store the node id in temp_nid. I added the prefix temp_ to the property name because we just need it temporarily for calculating another property and not for using it directly.

Similarly, we need to prepare a slug for the category to which the node belongs. We will use this slug to generate the alias property.

source: plugin: 'concat' source: - 'constants/source_prefix' - '@temp_nid'

Next, we generate the source, which is the path to which the alias will point. We do that by simply concatenating '/nid/' and '@temp_nid' using the concat plugin.

alias: plugin: 'concat' source: - 'constants/alias_prefix' - '@temp_category_slug' - 'constants/slash' - 'slug'

And finally, we generate the entire alias by concatenating '/article/', '@temp_category_slug', a '/' and the article's '@slug'. After running this migration like drush migrate-import example_article_alias, all the nodes should be accessible at /article/{{ category-slug }}/{{ article-slug }}.

Generate URL Redirects with Migrations

For the last requirement, we need to generate redirects, which takes us to the redirect module. So, we create another migration named example_article_redirect to generate redirects from /blog/{{ slug }}.php to the relevant nodes. Now, let's discuss some important lines of this migration.

Source constants: # The source path is not supposed to start with a "/". source_prefix: 'blog/' source_suffix: '.php' redirect_prefix: 'internal:/node/' uid_admin: 1 status_code: 301

We use source/constants to define certain data which we want to use in the migration, but we do not have in the CSV document.

Destination destination: plugin: 'entity:redirect'

In Drupal 8, every redirect rule is an entity. Hence, we use the entity plugin for the destination.

Process redirect_source: plugin: 'concat' source: - 'constants/source_prefix' - 'slug' - 'constants/source_suffix'

First, we determine the path to be redirected. This will be the path as in the old website, example, blog/{{ slug }}.php without a / in the front.

redirect_redirect: plugin: 'concat' source: - 'constants/redirect_prefix' - '@temp_nid'

Just like we did for generating aliases, we read node IDs from the article data migration and use them to generate URIs to which the user should be redirected when they visit one of the /blog/{{ slug }}.php paths. These destination URIs should be in the form internal:/node/{{ nid }}. The redirect module will intelligently use these URIs to determine the URL alias for those paths and redirect the user to the path /article/{{ slug }} instead of sending them to /node/{{ nid }}. This way, the redirects will not break even if we change the URL alias for a particular node after running the migrations.

# We want to generate 301 permanent redirects as opposed to 302 temporary redirects. status_code: 'constants/status_code'

We also specify a status_code and set it to 301. This will create 301 permanent redirects as opposed to 302 temporary redirects. Having done so and having run this third migration as well, we are all set!

Migration dependencies migration_dependencies: required: - 'example_article_data'

Since the migration of aliases and the migration of redirects both require access to the ID of the node which was generated during the article data migration, we need to add the above lines to define a migration_dependency. It will ensure that the example_article_data migration is executed before the alias and the redirect migrations. So if we run all the migrations of this example, we should see them executing in the correct order like:

$ drush mi --tag=example_article Processed 5 items (5 created, 0 updated, 0 failed, 0 ignored) - done with 'example_category_data' Processed 50 items (50 created, 0 updated, 0 failed, 0 ignored) - done with 'example_article_data' Processed 50 items (50 created, 0 updated, 0 failed, 0 ignored) - done with 'example_article_alias' Processed 50 items (50 created, 0 updated, 0 failed, 0 ignored) - done with 'example_article_redirect'Next steps + more awesome articles by Evolving Web
Categories: Drupal

Acquia Developer Center Blog: Decoupled Drupal: POWDR’s Front End Architecture Build

Planet Drupal - 24 August 2017 - 6:46am

In this article we’ll discuss the three main areas that needed to be addressed during the build of POWDR’s front end architecture: Routing & Syncing with the API, Component Driven Content, and the Build Process & Tools.

Tags: acquia drupal planet
Categories: Drupal

Revolutionary approach brings 3D sound into the living room

Virtual Reality - Science Daily - 24 August 2017 - 6:36am
Computer vision and sound experts have demonstrated ‘Media Device Orchestration’ – an innovative home audio concept which enables users to enjoy immersive audio experiences by using all available devices in a typical living room.
Categories: Virtual Reality

Chromatic: Announcing our Drupal Coding Standards Series on Drupalize.me!

Planet Drupal - 24 August 2017 - 6:30am

The folks at Drupalize.me provide the best Drupal training materials on the web, so we were more than happy to oblige them when they asked if they could release our Coding Standards guide as a free series on their platform.

Categories: Drupal

Ukraine Day Giveaway From Corvus Belli Happening Now

Tabletop Gaming News - 24 August 2017 - 6:00am
As is tradition for Corvus Belli, during a country’s National Day, they have themselves a giveaway. Today it’s Ukraine’s turn. Want to get yourself a free unit? Check out their Facebook post and put your name in the hat. From the announcement: Happy Ukraine Day! We draw 1 RIOT GRRLS! To participate, you just need […]
Categories: Game Theory & Design

Social Auth Gitlab

New Drupal Modules - 24 August 2017 - 3:09am
Categories: Drupal

Social Auth Uber

New Drupal Modules - 24 August 2017 - 3:02am
Categories: Drupal

Social Auth Twitch

New Drupal Modules - 24 August 2017 - 3:01am
Categories: Drupal

Social Auth Slack

New Drupal Modules - 24 August 2017 - 3:01am
Categories: Drupal

Social Auth Paypal

New Drupal Modules - 24 August 2017 - 3:01am
Categories: Drupal

Social Auth Microsoft

New Drupal Modules - 24 August 2017 - 3:01am
Categories: Drupal

Social Auth Mailru

New Drupal Modules - 24 August 2017 - 3:00am
Categories: Drupal

Social Auth Instagram

New Drupal Modules - 24 August 2017 - 3:00am
Categories: Drupal

Agiledrop.com Blog: AGILEDROP: Web Accessibility in Drupal 8 – part 2

Planet Drupal - 24 August 2017 - 2:48am
This blog post is the second part of the session Web accessibility in Drupal 8 from our Development director Bostjan Kovac. We will look at the most common mistakes developers make, Drupal Contrib modules and other tools that will help you out when it comes to web accessibility. If you have missed the first part, you can read it here. Most common mistakes developers make Simple markup: And then there is a classy theme: The Ignorant theme (Bostjan called it that way) with no HTML elements, no roles for each element, nothing the screen readers should help themselves with. That means that… READ MORE
Categories: Drupal

Amazee Labs: Extending GraphQL: Part 2 - Types and Interfaces

Planet Drupal - 24 August 2017 - 1:27am
Extending GraphQL: Part 2 - Types and Interfaces

After successfully creating a field with arguments and context, we are going to have a look at types and interfaces in GraphQL and how they help to build complex, yet self-documenting and type safe schemas.

Philipp Melab Thu, 08/24/2017 - 10:27

The last blog post in this series culminated in the epic achievement of adding a "page title" field to every URL object in our schema. Now we can request the page title for every internal URL. But menus and link fields can also store external addresses.

Wouldn't it be cool if we can request their page title's just the same way?

Overriding a field

Let's try it and ask questions later:

query { route(path: "http://www.drupal.org") { pageTitle } }

Unfortunately, this doesn't work out. The route field checks if the provided path is a Drupal route and if the user has access to it, and will return null if either of the two doesn't apply. So, the first thing we will do is extend the route field so it also can handle external URLs.

Note: At the time of writing there is a pending pull request that adds exactly this enhancement. If you are reading this in a couple of weeks from now (my now, not yours - unless you own a DeLorean), there's a chance that this already works for you. But since this is a nice example of overriding a field, we stick with it. If you don't just want to read but really play through this tutorial, make sure you work based on the 8.x-3.0-alpha3 version of the GraphQL module.

We create a new field called ExampleRoute in our graphql_example module. If you are not yet proud owner of one, please refer to the last blog post. This new field simply extends the existing Route field and even copies its annotation.

With one difference: We add a new property called weight which we set to "1". It's quite simple. When the schema builder assembles all field plugins for a given type and stumbles upon two with the same name, the higher weight takes precedence. That's how we tell GraphQL to use our custom implementation of a field.

The resolveValues method checks if the path is an external Url. In this case, it just constructs a Url object, else it will pass down to the parent implementation.

The result is still not satisfying. The route field now returns an Url object, but our page title field can only retrieve internal page titles.

So let's modify the PageTitle field. First, we check if the current value is a routed URL. In this case, we still leave it to the title resolver. Otherwise, we fire up Drupal's http_client (aka Guzzle), fetch the content behind the address, load it into an XML document, search for the title element and yield its contents. I am aware that this is not the most performant solution, but I'm trying to keep these examples short and concise.

It worked. Our query for an external page title yields the correct result.

{ "data": { "route": { "pageTitle": "Drupal - Open Source CMS | Drupal.org" } } }

The result is correct, but it doesn't feel right. Internal and external URLs are fundamentally different. The page title might make sense on both, but the similarities end there. External URLs won't route to an entity or provide any other information specific to Drupal. These fields won't break and will just return NULL instead, but that doesn't seem very elegant.

Diff: Page title of external URLs

Interfaces and Types

We have already met the Url type, and we know that it connects a certain value with a list of fields that can be executed on it. A GraphQL interface is in some ways similar to interfaces in an object oriented language. It gives a group of types with shared fields a common name.
Right now we've got the Url type provided by the GraphQL module, representing internal URLs (not 100% true, but for the sake of simplicity we leave it there). And we have our external URL which is emitted by the same route field, but operates differently. So what we need to do now:

  1. Create a GraphQL interface called GenericUrl
  2. Change the route field to return this interface instead.
  3. Attach our pageTitle field to this interface.
  4. Add a ExternalUrl GraphQL type that implements this interface.
Creating the interface

GraphQL interfaces live in their own plugin namespace Plugin\GraphQL\Interfaces where the schema builder will pick them up.

The plugin annotation for interfaces is quite simple. In most cases, it consists of the plugin id and a name to be used within the schema. The base class for interfaces contains an abstract method: resolveType. This method will receive a runtime value and has to select the appropriate GraphQL type for it. In our case, it checks if the URL is external or not and uses the schema manager service to return an instance of either Url or ExternalUrl.

Using the interface

This won't have any effect as long as we don't use this interface type somewhere. So we change the pageTitle field to attach it to the GenericUrl instead of Url and adapt our override of the route field to return a GenericUrl.

Creating the new type

The new type we need is rather simple. It's an empty class, extending TypePluginBase. The most important part is the annotation that defines a list of interfaces. Just the GenericUrl interface in our case.

GraphQL type source

Diff: Generic Url interfaces

Now our query still works. But there is a new problem. Internal URLs don't work anymore but emit an error message instead:

Type "Url" does not implement "GenericUrl"

We need to adapt the of the Url type, which is defined in another module. Sounds like a job for the hero we don't deserve, but we need right now. You can't say Drupal without screaming hook_alter from the top of your lungs!

Altering plugins

There's an alter hook for each plugin type in GraphQL. So, all we need is to implement hook_graphql_types_alter and add the GenericUrl interface to the Url types interface list.
Note that the types are indexed by their plugin-ID.

Diff: Altering existing plugins

Great! Now we are able to fetch page titles from both internal and external urls.

query { admin:route(path: "/admin") { pageTitle } drupal:route(path: "http://www.drupal.org") { pageTitle } }

Will return:

{ "data": { "admin": { "pageTitle": "Administration" }, "drupal": { "pageTitle": "Drupal - Open Source CMS | Drupal.org" } } }

But you will notice that we lost all the other fields attached to the Url type. Thats because they are not attached to the GenericUrl type, but to the Url type. And that makes sense, since you can't request for example an entity or the current user context for an external path.

Query composition and fragment selection

And this brings us to the most important and powerful aspect of interfaces and types. We are able to apply different query fragments and fetch different information based on the result type.

Assume the following scenario: Our Article type has a Links field that can contain links to either other articles or external URLs, as well as a Description field. Additionally, we extended our ExternalUrl type with an additional meta field that pulls meta tags out of the XML tree (Bonus objective: implement that yourself). Now we could do this:

query { route(path: "/node/1") { ... on Url { nodeContext { ... on NodeArticle { fieldLinks { url { pageTitle ...InternalLink ...ExternalLink } } } } } } } fragment InternalLink on Url { nodeContext { ... on NodeArticle { description:fieldDescription } } } fragment ExternalLink on ExternalUrl { description:meta(property: "og:description") }

The first part simply routes to the article with id 1 and fetches it's Links field, which will emit a list of URLs that might be internal or external. There we first pull the common page title and then include two fragments that apply on either type of URL and invoke different fields based on that information. So elegant!

The finish line

We've reached the (preliminary) end of our streak of practical GraphQL blog posts. Next up will be a peek into the future of the GraphQL module with planned features and possible use cases. But if you are interested in more advanced topics like performance optimisation, caching or deeper integration with Drupal subsystems (fields, views, contexts ...) ping me @pmelab and I'll see what I can do.

Categories: Drupal

Superseeds: Greater than their Sum, Part Two

RPGNet - 24 August 2017 - 12:00am
More on gestalts.
Categories: Game Theory & Design

Title HTML

New Drupal Modules - 23 August 2017 - 11:03pm

This modules extend Title module to allow node titles to have HTML tags.

Title module brings title field to replace entity title property. We can configure that title field to have filtered text. However, as of certain roadblocks in Drupal core, we cannot get formatted title on different places in sites. This module fixes that roadblocks.

Categories: Drupal

Enzolutions: The Drupal Console Journey

Planet Drupal - 23 August 2017 - 5:00pm

Before the existence of the Drupal Console as a project, it all began with an idea to make Drupal 8 better. Every great invention/innovation begins with an idea, and the Drupal transition from 7 to 8 came with massive changes to the fundamental operating procedures of yesterday. Symfony components were making a splash into Drupal Core. Jesus and David, the initiators of Drupal Console project, came up with the idea of including the symfony console into the Drupal core. The same way that other symfony components were being included into the Drupal core.

Powering Though Frustration

As helpful as the Drupal Console project is nowadays, it wasn’t very widely accepted into the drupal community initially. In fact, it turned out to be a huge challenge to get anyone to listen to the idea. For Jesus and David, the primary objective to include the Symfony Console in Drupal was to have the option to have code generators, in the same way, the Symfony community does. Who wouldn’t want that? A way to automate the annoying redundancies that plague developers everywhere. So they decided to propose the idea to the Drupal core maintainers via the issue queue. That idea was however quickly dismissed.

After few attempts to request the inclusion and trying to collaborate into different drupal projects, it dawned on Jesus and David that inclusion and collaboration was not going to happen. They needed to regroup and find a better approach.

While at lunch at Drupalcamp Costa Rica, Jesus and David were casually discussing the frustrations they had encountered trying to bring innovation to Drupal and related projects, and Larry Garfield chimed in “someone needs to create a separate project that includes Symfony Console and code generation”. That sentence gave birth to the Drupal Console project as you know it today.

Building A Community

Jesus stacked up his calendar with almost every Drupal event in the U.S. The goal was to talk about the project in sessions at all Drupal community gatherings he could physically attend, or at minimum, present the idea at BOFs where sessions were not possible. The code sprints helped him interact with developers and users forming a critical source of feedback.

Along the way, he convinced me to join the project as a maintainer. I also embarked on his outreach campaign to help spread the word. Only, my campaign was global because it was important to reach non-english speakers because they often feel left out of major open source projects. Currently, the Drupal Console project is available, with some variations, in the 18 languages listed below.

  • English
  • Spanish
  • Catalán
  • French
  • Korean
  • Hindi
  • Hungarian
  • Indonesian
  • Japanese
  • Marathi
  • Punjabi
  • Brazilian Portuguese
  • Romanian
  • Russian
  • Tagalog
  • Vietnamese
  • Chinese Simplified
  • Chinese Traditional
One Million Downloads!

After four years of development in July 2017, we reached our first million downloads across different releases. This achievement is thanks to our more that 250 contributors across the globe.

This brought a great sense of validation for deciding to stick to our guns, do the right thing and most importantly... be globally inclusive.

Categories: Drupal

Larry Garfield: Best practices are contextual

Planet Drupal - 23 August 2017 - 3:51pm
Best practices are contextual

Articles, blog posts, and Twitter debates around "best practices" abound. You can't swing a dead cat without bumping into some article espousing the benefits of designing and building software a certain way. (Side note: What kind of sick person are you that you're swinging a dead cat around? Stop that! You're desecrating the dead!)

Larry 23 August 2017 - 6:51pm
Categories: Drupal

Pages

Subscribe to As If Productions aggregator