All RPGs and Storygames by Tod Foley are now available at DrivethruRPG. Bring these games to your table!
Authenticated Entity Access is a Drupal access control module which provides access checks to entities at the bundle level. Via the admin interface, you can select which bundles should have the check added. Selecting a bundle will add a check box on the entity which will be used to determine if a user should be able to access it. If the checkbox is checked, access checks will provide a "granted" if a user is logged and "denied" if not.
There was a tendency, when I was running years ago, to run into players that would use the following logic, “you can do anything in an RPG, so let’s do [insert thing not well supported by the game or setting].” Because I have been running a lot more roleplaying games that have a focus on genre emulation, and because I’m much more likely to discuss campaign frameworks and to have session zeros to set expectations, I don’t see this as much now as I once did, but it still comes up on occasion.
Because this may be something that game moderators may still run into from time to time, I thought I would walk through some thoughts on why this may be a problem, and how to get a focused game back on track.Addressing the Issue Head-On
Whenever a player wants to go off on a vector that isn’t really what the game or setting supports, it is really important to actually have a discussion. One of the most important things to do in these situations is to make sure that you don’t try to fix this problem “in game.” Setting up the campaign to remove the player’s desired course of action without a discussion is just going to create an adversarial relationship and create frustration.
When a player wants to do something outside of the expected realm of the game, one of the first things you should do in a discussion is to frame your game as a “writer’s room.” Everyone at the table is a collaborator on a story, and you want the input of your players. The next thing you should discuss is what you think the game, setting, and campaign is good at doing well, and ask if the player agrees with that vision.
If the player disagrees, and thinks that the actions their player wants to participate in fits in the core competencies of the game, listen to their reasoning. It may be that the game is more flexible than you initially thought, or it may be that you misunderstand what the player really wants to get from their deviation.Drilling Down
If the player wants to go off on a tangent, ask them if they want a short- or long-term deviation. If it is a short-term deviation, it may be something easily adjudicated. The player may just want to roleplay a certain scene, or it may just be a quick roll to see if something can be accomplished or not.
One style of short-term deviation often seen in various television shows or movies is the change of pace scene that transitions into the traditional action of the game. This may end up being more invigorating, because it accomplishes the same end goal of the game, but forces you to begin in unfamiliar territory, and find a way to connect those activities to the expected narrative of the game.
Examples of this kind of short-term deviation include stories with all-powerful characters trying to teach the characters a lesson or impart a clue about a greater threat, where they get dropped into a very unfamiliar situation (think Q transporting the Enterprise across the galaxy to see the Borg, or Gabriel dropping Sam into a version of Groundhog Day to deal with Dean’s impending death). Other examples might be seeing the characters on vacation, where similar problems find them abroad that they usually deal with at home.Long Term Deviations
If the player wants to make a long-term deviation from the regular action of the game, it might be worth finding out if they want a break from their character, or just from what their character does. If they still enjoy playing their character, but want to do things that the current game system is not adept at handling, you may be able to drift the campaign to a new rules system.
Characters that want to hunt monsters, but want to do so in a more direct manner, might convert their characters from Call of Cthulhu to Monster of the Week. Characters that have enjoyed low level, over the top, highly lethal fantasy games for a while, but want more rules support for downtime and less lethality may want to keep the same characters but shift from Dungeon Crawl Classics to Dungeons and Dragons 5th Edition.
The important part about this kind of drift is that the whole group needs to be enthusiastic about playing the same characters and moving to a new game system, and the game moderator needs to either be enthusiastic about running the new system, or handing the GM reins off to a new player.
This kind of drift might happen more than once. If the party is okay with it, it may even be possible to drift just one character from the previous campaign to a new game. The Professional from Monster of the Week might end up bringing that character into a Night’s Black Agents game, as the monster hunting moves from open-ended to focused on a massive vampire conspiracy.
When performing this kind of drift, it’s important to make sure that everyone is okay with the concept that their game happens across different game systems. This can be similar to characters from one cast getting a spin-off series (like several characters moving from Buffy to Angel), or moving a single character from one show to another (think Frasier or Worf). The important thing, as always, is to discuss this as a group and to make sure everyone is enthusiastic about the change.New Campaigns it is very important to not attempt to use game rules to fix interpersonal issues, because that’s not what they are designed to do Share20Tweet4Reddit2Email
Sometimes, people don’t know what they want, and having an open discussion on why the fighter wants to start having more adventures where they sell goods from one nation to another in your D&D game might reveal that they really don’t want to play that character any more.
Sometimes people have invested a lot of time and emotion in a character, and even when they aren’t getting what they want out of the game anymore, they don’t want to abandon something they have invested so much into. In this case, it is important to discuss that it might be possible to end the campaign in a less permanent manner. Put it on hold, make sure you have a special, secure, safe place to store the PCs, and do something new.
If it makes the players feel better, plan the new campaign as a short sequence of 3-5 adventures, just to have a change of pace. Maybe you don’t want the campaign to end, but you really want to take the summer off of thinking about the troubles of this world you have been in for the last year or so.Checking In
When a player starts wanting to take actions that the campaign doesn’t support, or that the game you are playing isn’t good at, try having a conversation with the following steps:
- Discuss what you think the campaign is about, and what you think the game is good at
- Listen to what the player thinks the campaign is about, and what they think the game is good at
- If there is a disagreement, try to understand a broader point of view
If you agree on what the campaign and the game are about, determine if the player wants a short- or long-term deviation from what the game is about. If it is a short-term deviation, determine what you can do to satisfy this desire:
- Adjudicate quick scenes that introduce new elements into the narrative
- Start adventures in new ways that can eventually shift to the expected action of the game
If it is a long-term deviation, determine if the game system or the characters are what the player wants to drift from:
- Determine if the group wants to find a game system that can handle similar, but different, assumptions to convert the characters
- Determine if anyone wants to change characters when others are converted
- Determine if the GM duties will change
- Determine if the group wants to put the campaign on hold for a while to try something completely different
If the group is worried about putting a game they enjoy on hold:
- Make sure they know you can return to the original campaign
- Schedule a short interlude game with a definite endpoint to allow them to decide if they are ready to return to the old campaign
Finally, and potentially the most important step, is to make sure that the player is happy in the group. If they want to do things that the group isn’t doing, they may not be enjoying the gaming group in general. The important part of this discussion is to make sure the player realizes that if a game group isn’t for you, it isn’t the same as determining that you dislike the people in the group. Play cultures develop, and sometimes a gamer’s sensibilities do not fit with that group. It’s important that leaving a group is not portrayed as being synonymous with fighting with or making a judgement call about a group of people.Many Facets to the Same Solution
As with a lot of gaming problems, it’s important to have focus, open discussion, and to clearly define the purpose of discourse. It is important for adults to have reasonable conversations that remove blame or moral judgements from personal preferences. And it is very important to not attempt to use game rules to fix interpersonal issues, because that’s not what they are designed to do.
I've been putting off learning how to build sites in Drupal 8, and migrating my existing Drupal 7 sites over to Drupal 8. Why? Drupal 8 uses a lot of new tools. I want to learn how to set up a Drupal 8 site in the "right" (optimal) way so that I don't incur technical debt for myself later on. That means I have a lot of tools to learn. That takes time, which I don't have a lot of. So I've procrastinated.Blog Category: Drupal Planet
Creating a faceted search in Drupal implies some configuration steps. This can be overwhelming to people new to Drupal.
The MixItUp Views Drupal 8 module allows you to create a simplified version of a faceted search based on the taxonomies of the content type. It also provides a nice animation, that makes the user experience even better.
I catch up with Brendan Blaine, a developer for the Drupal Association, to find out what it takes to run events.drupal.org, why the conferences run so smoothly, and always remember to use a coaster.
Using cloud is about leveraging its agility among other benefits. For the Drupal-powered website, a right service provider can impact how well the website performs and can affect the business revenue.
1:- Enable Dark Mode Switch module in the Drupal admin.
1:- Place the Dark Mode Switch block in the region desired.
2:- Customize the markup using the template 'dark-mode-switch--block.html.twig'.
3:- Add the css selector '.darkmode' in your theme CSS.
This Module is supported by drupaloid
In the previous entry, we learned how to use process plugins to transform data between source and destination. Some Drupal fields have multiple components. For example, formatted text fields store the text to display and the text format to apply. Image fields store a reference to the file, alternative, and title text, width, and height. The migrate API refers to a field’s component as a subfield. Today we will learn how to migrate into them and know which subfields are available.Getting the example code
Today’s example will consist of migrating data into the `Body` and `Image` fields of the `Article` content type that are available out of the box. This assumes that Drupal was installed using the `standard` installation profile. As in previous examples, we will create a new module and write a migration definition file to perform the migration. The code snippets will be compact to focus on particular elements of the migration. The full code snippet is available at https://github.com/dinarcon/ud_migrations The module name is `UD Migration Subfields` and its machine name is `ud_migrations_subfields`. The `id` of the example migration is `udm_subfields`. Refer to this article for instructions on how to enable the module and run the migration.source: plugin: embedded_data data_rows: - unique_id: 1 name: 'Michele Metts' profile: 'freescholar on Drupal.org' photo_url: 'https://agaric.coop/sites/default/files/2018-12/micky-cropped.jpg' photo_description: 'Photo of Michele Metts' photo_width: '587' photo_height: '657'
Only one record is presented to keep snippet short, but more exist. In addition to having a unique identifier, each record includes a name, a short profile, and details about the image.Migrating formatted text
The `Body` field is of type `Text (formatted, long, with summary)`. This type of field has three components: the full text (value) to present, a summary text, and a text format. The Migrate API allows you to write to each component separately defining subfields targets. The next code snippets shows how to do it:process: field_text_with_summay/value: source_value field_text_with_summay/summary: source_summary field_text_with_summay/format: source_format
The syntax to migrate into subfields is the machine name of the field and the subfield name separated by a slash (/). Then, a colon (:), a space, and the value. You can set the value to a source column name for a verbatim copy or use any combination of process plugins. It is not required to migrate into all subfields. Each field determines what components are required, so it is possible that not all subfields are set. In this example, only the value and text format will be set.process: body/value: profile body/format: plugin: default_value default_value: restricted_html
The `value` subfield is set to the `profile` source column. As you can see in the first snippet, it contains HTML markup. An `a` tag to be precise. Because we want the tag to be rendered as a link, a text format that allows such tag needs to be specified. There is no information about text formats in the source, but Drupal comes with a couple we can choose from. In this case, we use the `Restricted HTML` text format. Note that the `default_value` plugin is used and set to `restricted_html`. When setting text formats, it is necessary to use its machine name. You can find them in the configuration page for each text format. For `Restricted HTML` that is /admin/config/content/formats/manage/restricted_html.
Note: Text formats are a whole different subject that even has security implications. To keep the discussion on topic, we will only give some recommendations. When you need to migrate HTML markup, you need to know which tags appear in your source, which ones you want to allow in Drupal, and select a text format that accepts what you have whitelisted and filter out any dangerous tags like `script`. As a general rule, you should avoid setting the `format` subfield to use the `Full HTML` text format.Migrating images
There are different approaches to migrating images. Today, we are going to use the Migrate Files module. It is important to note that Drupal treats images as files with extra properties and behavior. Any approach used to migrate files can be adapted to migrate images.process: field_image/target_id: plugin: file_import source: photo_url reuse: TRUE id_only: TRUE field_image/alt: photo_description field_image/title: photo_description field_image/width: photo_width field_image/height: photo_height
When migrating any field you have to use their machine in the mapping section. For the `Image` field, the machine name is `field_image`. Knowing that, you set each of its subfields:
- `target_id` stores an integer number which Drupal uses as a reference to the file.
- `alt` stores a string that represents the alternative text. Always set one for better accessibility.
- `title` stores a string that represents the title attribute.
- `width` stores an integer number which represents the width in pixels.
- `height` stores an integer number which represents the height in pixels.
For the `target_id`, the plugin `file_import` is used. This plugin requires a `source` configuration value with a URL to the file. In this case, the `photo_url` column from the source section is used. The `reuse` flag indicates that if a file with the same location and name exists, it should be used instead of downloading a new copy. When working on migrations, it is common to run them over and over until you get the expected results. Using the `reuse` flag will avoid creating multiple references or copies of the image file, depending on the plugin configuration. The `id_only` flag is set so that the plugin only returns that file identifier used by Drupal instead of an entity reference array. This is done because each subfield is being set manually. For the rest of the subfields (`alt`, `title`, `width`, and `height`) the value is a verbatim copy from the source.
Note: The Migrate Files module offers another plugin named `image_import`. That one allows you to set all the subfields as part of the plugin configuration. An example of its use will be shown in the next article. This example uses the `file_import` plugin to emphasize the configuration of the image subfields.Which subfields are available?
Some fields have many subfields. Address fields, for example, have 13 subfields. How can you know which ones are available? The answer is found in the class that provides the field type. Once you find the class, look for the `schema` method. The subfields are contained in the `columns` array of the value returned by the `schema` method. Let’s see some examples:
- The `Text (plain)` field is provided by the StringItem class.
- The `Number (integer)` field is provided by the IntegerItem class.
- The `Text (formatted, long, with summary)` is provided by the TextWithSummaryItem class.
- The `Image` field is provided by the ImageItem class.
The `schema` method defines the database columns used by the field to store its data. When migrating into subfields, you are actually migrating into those particular database columns. Any restriction set by the database schema needs to be respected. That is why you do not use units when migrating width and height for images. The database only expects an integer number representing the corresponding values in pixels. Because of object-oriented practices, sometimes you need to look at the parent class to know all the subfields that are available.
Another option is to connect to the database and check the table structures. For example, the `Image` field stores its data in the `node__field_image` table. Among others, this table has five columns named after the field’s machine name and the subfield:
Looking at the source code or the database schema is arguably not straightforward. This information is included for reference to those who want to explore the Migrate API in more detail. You can look for migrations examples to see what subfields are available. I might even provide a list in a future blog post. ;-)
Tip: You can use Drupal Console for code introspection and analysis of database table structure. Also, many plugins are defined by classes that end with the string `Item`. You can use your IDEs search feature to find the class using the name of the field as hint.Default subfields
Every Drupal field has at least one subfield. For example, `Text (plain)` and `Number (integer)` defines only the `value` subfield. The following code snippets are equivalent:process: field_string/value: source_value_string field_integer/value: source_value_integer process: field_string: source_value_string field_integer: source_value_integer
In examples from previous days, no subfield has been manually set, but Drupal knows what to do. As we have mentioned, the Migrate API offers syntactic sugar to write shorter migration definition files. This is another example. You can safely skip the default subfield and manually set the others as needed. For `File` and `Image` fields, the default subfield is `target_id`. How does the Migrate API know what subfield is the default? You need to check the code again.
The default subfield is determined by the return value of `mainPropertyName` method of the class providing the field type. Again, object oriented practices might require looking at the parent classes to find this method. In the case of the `Image` field, it is provided by ImageItem which extends FileItem which extends EntityReferenceItem. It is the latter that contains the `mainPropertyName` returning the string `target_id`.
What did you learn in today’s blog post? Were you aware of the concept of subfields? Did you ever wonder what are the possible destination targets (subfields) for each field type? Did you know that the Migrate API finds the default subfield for you? Please share your answers in the comments. Also, I would be grateful if you shared this blog post with your colleagues.
This blog post series, cross-posted at UnderstandDrupal.com as well as here on Agaric.coop, is made possible thanks to these generous sponsors. Contact Understand Drupal if your organization would like to support this documentation project, whether the migration series or other topics.
This module provides a feeds fetcher and a parser for importing content from Personify.
This week's roundup includes Epic's Fortnite World Cup, analyses of Nintendo's new Fire Emblem and Wolfenstein: Youngblood, as well as time loops in games, Candyland, Elsinore and lots more besides. ...
In the previous entry, we wrote our first Drupal migration. In that example, we copied verbatim values from the source to the destination. More often than not, the data needs to be transformed in some way or another to match the format expected by the destination or to meet business requirements. Today we will learn more about process plugins and how they work as part of the Drupal migration pipeline.Syntactic sugar
The Migrate API offers a lot of syntactic sugar to make it easier to write migration definition files. Field mappings in the process section are an example of this. Each of them requires a process plugin to be defined. If none is manually set, then the get plugin is assumed. The following two code snippets are equivalent in functionality.process: title: creative_title process: title: plugin: get source: creative_title
The get process plugin simply copies a value from the source to the destination without making any changes. Because this is a common operation, get is considered the default. There are many process plugins provided by Drupal core and contributed modules. Their configuration can be generalized as follows:process: destination_field: plugin: plugin_name config_1: value_1 config_2: value_2 config_3: value_3
The process plugin is configured within an extra level of indentation under the destination field. The plugin key is required and determines which plugin to use. Then, a list of configuration options follows. Refer to the documentation of each plugin to know what options are available. Some configuration options will be required while others will be optional. For example, the concat plugin requires a source, but the delimiter is optional. An example of its use appears later in this entry.Providing default values
Sometimes, the destination requires a property or field to be set, but that information is not present in the source. Imagine you are migrating nodes. As we have mentioned, it is recommended to write one migration file per content type. If you know in advance that for a particular migration you will always create nodes of type Basic page, then it would be redundant to have a column in the source with the same value for every row. The data might not be needed. Or it might not exist. In any case, the default_value plugin can be used to provide a value when the data is not available in the source.source: ... process: type: plugin: default_value default_value: page destination: plugin: 'entity:node'
The above example sets the type property for all nodes in this migration to page, which is the machine name of the Basic page content type. Do not confuse the name of the plugin with the name of its configuration property as they happen to be the same: default_value. Also note that because a (content) type is manually set in the process section, the default_bundle key in the destination section is no longer required. You can see the latter being used in the example of writing your Drupal migration blog post.Concatenating values
Consider the following migration request: you have a source listing people with first and last name in separate columns. Both are capitalized. The two values need to be put together (concatenated) and used as the title of nodes of type Basic page. The character casing needs to be changed so that only the first letter of each word is capitalized. If there is a need to display them in all caps, CSS can be used for presentation. For example: FELIX DELATTRE would be transformed to Felix Delattre.
Tip: Question business requirements when they might produce undesired results. For instance, if you were to implement this feature as requested DAMIEN MCKENNA would be transformed to Damien Mckenna. That is not the correct capitalization for the last name McKenna. If automatic transformation is not possible or feasible for all variations of the source data, take notes and perform manual updates after the initial migration. Evaluate as many use cases as possible and bring them to the client’s attention.
To implement this feature, let’s create a new module ud_migrations_process_intro, create a migrations folder, and write a migration definition file called udm_process_intro.yml inside it. Follow the instructions in this entry to find the proper location and folder structure or download the sample module from https://github.com/dinarcon/ud_migrations It is the one named UD Process Plugins Introduction and machine name udm_process_intro. For this example, we assume a Drupal installation using the standard installation profile which comes with the Basic Page content type. Let’s see how to handle the concatenation of first an last name.id: udm_process_intro label: 'UD Process Plugins Introduction' source: plugin: embedded_data data_rows: - unique_id: 1 first_name: 'FELIX' last_name: 'DELATTRE' - unique_id: 2 first_name: 'BENJAMIN' last_name: 'MELANÇON' - unique_id: 3 first_name: 'STEFAN' last_name: 'FREUDENBERG' ids: unique_id: type: integer process: type: plugin: default_value default_value: page title: plugin: concat source: - first_name - last_name delimiter: ' ' destination: plugin: 'entity:node'
The concat plugin can be used to glue together an arbitrary number of strings. Its source property contains an array of all the values that you want put together. The delimiter is an optional parameter that defines a string to add between the elements as they are concatenated. If not set, there will be no separation between the elements in the concatenated result. This plugin has an important limitation. You cannot use strings literals as part of what you want to concatenate. For example, joining the string Hello with the value of the first_name column. All the values to concatenate need to be columns in the source or fields already available in the process pipeline. We will talk about the latter in a future blog post.
To execute the above migration, you need to enable the ud_migrations_process_intro module. Assuming you have Migrate Run installed, open a terminal, switch directories to your Drupal docroot, and execute the following command: drush migrate:import udm_process_intro Refer to this entry if the migration fails. If it works, you will see three basic pages whose title contains the names of some of my Drupal mentors. #DrupalThanksChaining process plugins
Good progress so far, but the feature has not been fully implemented. You still need to change the capitalization so that only the first letter of each word in the resulting title is uppercase. Thankfully, the Migrate API allows chaining of process plugins. This works similarly to unix pipelines in that the output of one process plugin becomes the input of the next one in the chain. When the last plugin in the chain completes its transformation, the return value is assigned to the destination field. Let’s see this in action:id: udm_process_intro label: 'UD Process Plugins Introduction' source: ... process: type: ... title: - plugin: concat source: - first_name - last_name delimiter: ' ' - plugin: callback callable: mb_strtolower - plugin: callback callable: ucwords destination: ...
The callback process plugin pass a value to a PHP function and returns its result. The function to call is specified in the callable configuration option. Note that this plugin expects a source option containing a column from the source or value of the process pipeline. That value is sent as the first argument to the function. Because we are using the callback plugin as part of a chain, the source is assumed to be the last output of the previous plugin. Hence, there is no need to define a source. So, we concatenate the columns, make them all lowercase, and then capitalize each word.
Relying on direct PHP function calls should be a last resort. Better alternatives include writing your own process plugins which encapsulates your business logic separate of the migration definition. The callback plugin comes with its own limitation. For example, you cannot pass extra parameters to the callable function. It will receive the specified value as its first argument and nothing else. In the above example, we could combine the calls to mb_strtolower() and ucwords() into a single call to mb_convert_case($source, MB_CASE_TITLE) if passing extra parameters were allowed.
Tip: You should have a good understanding of your source and destination formats. In this example, one of the values to want to transform is MELANÇON. Because of the cedilla (ç) using strtolower() is not adequate in this case since it would leave that character uppercase (melanÇon). Multibyte string functions (mb_*) are required for proper transformation. ucwords() is not one of them and would present similar issues if the first letter of the words are special characters. Attention should be given to the character encoding of the tables in your destination database.
Technical note: mb_strtolower is a function provided by the mbstring PHP extension. It does not come enabled by default or you might not have it installed altogether. In those cases, the function would not be available when Drupal tries to call it. The following error is produced when trying to call a function that is not available: The "callable" must be a valid function or method. For Drupal and this particular function that error would never be triggered, even if the extension is missing. That is because Drupal core depends on some Symfony packages which in turn depend on the symfony/polyfill-mbstring package. The latter provides a polyfill) for mb_* functions that has been leveraged since version 8.6.x of Drupal.
What did you learn in today’s blog post? Did you know that syntactic sugar allows you to write shorter plugin definitions? Were you aware of process plugin chaining to perform multiple transformations over the same data? Had you considered character encoding on the source and destination when planning your migrations? Are you making your best effort to avoid the callback process plugin? Please share your answers in the comments. Also, I would be grateful if you shared this blog post with your colleagues.
This blog post series, cross-posted at UnderstandDrupal.com as well as here on Agaric.coop, is made possible thanks to these generous sponsors. Contact Understand Drupal if your organization would like to support this documentation project, whether the migration series or other topics.
Code examples for Drupal 8.
Covers major sub-systems of Drupal 8 feature with examples code.
How to use correctly in Drupal 8 projects.
Decoupled Days 2019 last month, the third edition of the conference, was fantastic. I had the privilege to speak and attend last year, as well. The conference has quickly risen to be one of my favorite conference of the year.
Not familiar with Decoupled Days? Spawned in the “Hallway Track” of DrupalCon between its founders, the conference originated as Decoupled Drupal Days in 2017. Last year saw the phasing out of the word “Drupal” as the conference became focused on decoupling in general, not just Drupal. That is one reason it has quickly become a favorite event. It is an engineering and design conference. The act of decoupling in a system requires specific system design and presents engineering challenges. The organizers identify it as:
The only conference on the future of CMS, headless CMS, and decoupled CMS.