Skip to main content

Blog

Keeping Drupal Relevant to the Grassroots

Drupal continues to be the go to content management system we use to build websites. Recently much has been said about Drupal's growing adoption in the enterprise world and what it means for grassroots organizations.

We believe it's the grassroots that made Drupal what it is today and continue to advocate and contribute to the Drupal ecosystem to keep it that way. Drupal 8 adoption is still lukewarm  due mostly to the changes in the way developers can write the code and nothing to do with the robust and stable platform it has always been. To get those adoption rates up, Drupal must stay relevant to small-budget, cause-driven groups.

For that reason we are active participants in Drutopia, an initiative to get the power of Drupal 8  into the hands of grassroots movements. We do this by building a distribution and Software as a Service platform for those groups. In 2018, we helped the project get to a beta release, launch a demo site and we launched three sites using the distribution.

We also built a custom distribution for the National Institute for Children's Health Quality in partnership with the Drupal shop Chocolate Lily. In doing so, we not only helped them launch platforms for each of their learning communities, but also significantly advanced the way Drupal can share and manage configuration across sites. This ability to share website features is key for groups with small budgets wanting to share and benefit from one another's work.

To further the grassroots spirit of Drupal in general and Drutopia in particular, we also attended and facilitated sessions at the Nonprofit Summit at BADCamp, the Nonprofit Software Development Summit and Design4Drupal. We're happy to say that this Drupal sub-community is alive and well, doing inspiring work.

2019 Intention: Kick off the Drutopia Software as a Service plan with a public membership-drive.

Providing Helpful Analytics In Your Website

A challenge we repeatedly heard from our clients and others in nonprofits and the cooperative world was the lack of time and resources to make meaningful use of site analytics. One way to help people act in more data-driven ways is to pull out key metrics and put them in an easy to find place - the website itself.

For Teachers with Guts, an online teacher cohort, we built custom reports on member activity to help them learn what discussions are active and what resources are most downloaded.

For Portside, an independent news site for the left, we started displaying pageviews on articles and site traffic info on the main administrative page for moderators to assess the traction of their stories.

In fact, we found this so helpful we did the same for ourselves, now easily seeing whose blog posts are most popular and gaining bragging rights accordingly.

2019 Intention: Help even more groups make use of analytics by bringing them into their websites.

Making Online Donations Simple and Cost Effective

There's a dizzying array of online donation platforms groups can turn to, but many are expensive and don't integrate with one's website. So this year we launched the Give module, a free and open source project for Drupal websites to easily build online donation forms. All you need is a Stripe account to get up and running. The module also provides helpful reporting and automated thank you messages.

We have installed the Give module on three of our clients' sites and we are pleased with the results. One client in particular raised the most they ever have before!

2019 Intention: Move the Give module from Beta to a full release.

Training Up Ourselves and Others in Free Software

We are strong believers in free software and breaking down the tech expert/passive consumer dichotomy. Each of us has a creative drive and should be empowered to use and customize the tools in our lives as we see fit.

To that end, we lead trainings and sessions across the globe to empower people with free software. Micky hosted monthly security webinars and traveled to speak to cooperatives in Mexico City last fall. In Boston, Mauricio taught Drupal users how to customize the look of pages with TWIG templates. In Berkeley, Ben and Mauricio trained people on migrating their sites to Drupal 8, without using PHP code. David spoke at the Puebla, Mexico PHP User Group about Drupal. Chris presented at Libre Planet and Clayton helped prisoner support groups adopt NextCloud and Discourse for file sharing and internal communications.

Mauricio presenting on Drupal migrations at DrupalCon Nashville. Mauricio led a session on migrating to Drupal 8 at DrupalCon, Design4Drupal and BADCamp this year.

In addition to teaching, we also learned a thing or two. Clayton expanded his design skills from books like Cadence and Slang and Refactoring UI. Mauricio and Chris deepened their knowledge of React with courses like the Complete React tutorial. David installed and configured Apache Solr for the first time. Micky learned more about the cultural barriers to entry that need to be discussed and changed.

2019 Intention: Train as many people as we can in migrating to Drupal 8 through sessions at the Nonprofit Technology Conference, DrupalCon Seattle and custom trainings.

Making Our Software and Communities More Inclusive and Diverse

Free software is only as strong as the people involved. We're at our best when we strive to better ourselves and our communities by un-learning toxic behavior and making the work of diversity and inclusivity front and center.

We're jubilant that our coop membership is majority people of color, hailing from three countries representing multiple genders and sexual orientations. This passion for diversity and equity carries over into the way we build software. Clayton helped the City of Cambridge connect with their most marginalized community members by making their online events and programs directory more helpful.  Micky works with UjimaBoston on projects that directly affect the Boston community; they recently launched the first democratic investment fund that can be utilized by many small business owners.

And while we're excited about what we have accomplished, we have much room for improvement. In 2019 we intend to dig in further to make our own workplace and communities more diverse and inclusive.

2019 Intention: Increase our support and participation in the Drupal Diversity & Inclusion initiative.

Strengthening the Solidarity Economy

The global wealth gap is the widest it has ever been. We see that disparity playing out in corrupted politics, precarious living conditions and the blind pursuit of profit at the expense of our climate and planet. At the same time, we see alternative ways of producing and distributing wealth gaining traction.

The solidarity economy is an alternative way of doing business in which democratic ownership, stewardship of the earth and solidarity between people supplants mainstream economic values.

As a worker-owned cooperative we both model that solidarity economy internally and also support others in this work. Our client Portside regularly publishes news on the solidarity economy and we helped them expand their audience in a time when Facebook and other social media platforms are making that even harder. 

Micky speaking on a panel at the Workers Economy Encuentro. Micky spoke on the role of free software in the cooperative movement at the Workers Economy Encuentro.

We are active members of the US Federation of Worker-Owned Cooperatives and attended the federation's annual conference. From that conference we began deepening our relationships with other cooperatives, especially tech coops. In November, Micky went on a solidarity economy speaking tour of three cities in Mexico and hosted a workshop at the The 3rd Regional Workers Economy Encuentro in Mexico City. She then met with unions, cooperatives and other groups. Clayton networked with fellow coops at the Shared Ownership Summit in Boulder, Colorado. We also attended CommonBound, a conference to connect those of us building the solidarity economy. 

2019 Intention: Expand the coop movement by publishing a series of blog posts on the value of cooperatives, how to start one, how to convert an existing business to a coop and how to effectively run one.

2019: Intention as Resistance

Some of the Agaric team enjoying burritos together. Some of the global Agaric team enjoying a rare in-person get together at Micky's favorite burrito place.

2018 was challenging on many fronts and we are already experiencing more of the same in 2019. The tech industry we are part of reflects so much of the abuse and corruption playing out in the world at large. However, we love what we were able to accomplish last year, showing technology can be built and used differently to preserve user freedom.

We could only accomplish what we did with the support and partnership of our clients and colleagues. To each one we are truly grateful. We rang in the new year by launching our new site at midnight EST. We could not think of a better way to show that overall, our intention for 2019 is to connect with others more. So, if you are a group looking to improve your online tools, a free software enthusiast inspired by the initiatives we are part of, or just curious to learn more about anything you read on the site, drop us a line. Together in the face of a power that attacks and divides we can build a different power, among one another, that heals and unites.

Sign up to be notified when Agaric gives a migration training:

In the 31 days of Drupal migrations series, we explained different aspects of the syntax used by the Migrate API. In today’s article, we are going to dive deeper to understand how the API interprets our migration definition files. We will explain how to configure process plugins and set subfields and deltas for multi-value field migrations. We will also talk about process plugin chains, source constants, pseudofields, and the process pipeline. After reading this article, you will better comprehend existing migration definition files and improve your own. Let’s get started.

Understanding the syntax of Drupal migrations.

Field mappings: process plugin configuration

The Migrate API provides syntactic sugar to make migration definition files more readable. The field mappings under the process section are a good example of this. To demonstrate the syntax consider a multi-value Link field to store links to online profiles. The field machine name is field_online_profiles and it is configured to accept the URL and the link text. For brevity, only the `process` section will be shown, but it is assumed that the source includes the following columns: `source_drupal_profile`, `source_gitlab_profile`, and `source_github_profile`.


process:
  field_online_profiles: source_drupal_profile

In this case, we are directly assigning the value from source_drupal_profile in the source to the field_online_profiles in the destination entity. For now, we are ignoring the fact that the field accepts multiple values. We are setting the link text either, just the URL. Even in this example, the Migrate API is making some assumptions for us. Every field mapping requires at least one process plugin to be configured. If none is set, the get plugin is assumed. It copies a value from the source to the destination without making any changes. The previous snippet is equivalent to the next one:


process:
  field_online_profiles:
    plugin: get
    source: source_drupal_profile

The process plugin configuration options should be placed as direct children of the field that is being mapped. In the previous snippet, plugin and source are indented one level to the right under field_online_profiles. 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

Check out the article on using process plugins for data transformation for a working example.

Field mappings: setting sub-fields

Let's expand the example by setting the a value for the Link text in addition to the URL. To accomplish this, we will migrate data into subfields. Fields can store complex data and in many cases they have multiple components. For example, a rich text field has a subfield to store the text value and another for the text format. Address fields have 13 subfields available. Our example uses Link fields which have three subfields:

  • uri: The URI of the link.
  • title: The link text.
  • options: Serialized array of options for the link.

For now, only the uri and title subfields will be set. This also demonstrates that, depending on the field, it is not necessary to provide values for all the subfields. One more thing we will implement is to include the name of the online profile in the Link text. For example: “Drupal.org profile”.


process:
  field_online_profiles/uri: source_drupal_profile
  field_online_profiles/title:
    plugin: default_value
    default_value: 'Drupal.org profile'

If you want to set a value for a subfield, you use the field_name/subfield syntax. Then, each subfield can define its own mapping. Note that when setting the uri we are taking advantage of the get plugin considered the default to simplify the value assignment. In the case of title, the default_value process plugin is used to set a fixed value to comply with our example requirement.

When setting subfields, it is very important to understand what format is expected. You need to make sure the process plugins return data in the expected format or the migration will fail. In particular, you need to know if they return a scalar value or an array. In the case of scalar values, you need to verify if numbers or strings are expected. In the previous example, the uri subfield of the Link field expects a string containing the URL. On the other hand, File fields have a target_id subfield that expects an integer representing the File ID that is being referenced. Some process plugins might return an array or let you set subfields directly as part of the plugin configuration. For an example of the latter, have a look at the article on migrating images using the image_import plugin. image_import lets you set the alt, title, width, and height subfields for images directly in the plugin configuration. The following snippets shows a generalization for setting subfields:


process:
  destination_field/subfield_1:
    plugin: plugin_name
    config_1: value_1
    config_2: value_2
  destination_field/subfield_2:
    plugin: plugin_name
    config_1: value_1
    config_2: value_2

If a field can have multiple subfields, how can I know which ones are available? For easy reference, our next blog post will include a list of subfields for different types of fields. To find out by yourself, check out this article that covers available subfields. In summary, you need to locate the class that provides the FieldType plugin and inspect its schema method. The latter defines the database columns used by the field to store its data. Because of object oriented practices, sometimes you need to look at the parent class to know all the subfields that are available. When migrating into subfields, you are actually migrating into those particular database columns. Any restriction set by the database schema needs to be respected. Link fields are provided by the LinkItem class whose schema method defines the three subfields we listed before.

If a field can have multiple subfields, how does the Migrate API know which one to set when no one is manually specified? Every Drupal field has at least one subfield. If they have more, the field type itself specifies which one is the default. For easy reference, our next blog post will indicate the default subfield for different types of fields. To find out by yourself, check out this article that covers default subfields. In summary, you need to locate the class that provides the FieldType plugin and inspect its mainPropertyName method. Its return value will be the default subfield used by the Migrate API. Because of object oriented practices, sometimes you need to look at the parent class to find the method that defines the default subfield. Link fields are provided by the LinkItem class whose mainPropertyName returns uri. That is why in the first example there was no need to specify a subfield to set the value for the link URL.

Field mappings: setting deltas for multi-value fields

Once more, let’s expand the example by setting the populating multiple values for the same field. To accomplish this, we will specify field deltas. A delta is a numeric index starting at 0 and incrementing by 1 for each subsequent element in the multi-value field. Remember that our example assumes that the source has the following columns: source_drupal_profile, source_gitlab_profile, and source_github_profile. One way to migrate all of them into the multi-value link field is:


process:
  field_online_profiles/0/uri: source_drupal_profile
  field_online_profiles/0/title:
    plugin: default_value
    default_value: 'Drupal.org profile'
  field_online_profiles/1/uri: source_gitlab_profile
  field_online_profiles/1/title:
    plugin: default_value
    default_value: 'GitLab profile'
  field_online_profiles/2/uri: source_github_profile
  field_online_profiles/2/title:
    plugin: default_value
    default_value: 'GitHub profile'

If you want to set a value for a subfield, you use the field_name/delta/subfield syntax. Then, every combination of delta and subfield can define its own mapping. Both delta and subfield are optional. If no delta is specified, 0 is assumed which corresponds to the first element of a (multi-value) field. If no subfield is specified, the default subfield is assumed as explained before. In the previous example, if there is no need to set the link text the configuration would become:


process:
  field_online_profiles/0: source_drupal_profile
  field_online_profiles/1: source_gitlab_profile
  field_online_profiles/2: source_github_profile

In this example, we wanted to highlight syntax variations that can be used with the Migrate API. Nevertheless, this way of migrating multi-value fields is not very flexible. You are required to know in advance how many deltas you want to migrate. Depending on your particular configurations, you can write complex process pipelines that take into account an unknown number of deltas. Sometimes, writing a custom migration process plugin is easier and/or the only option to accomplish a task. Even if you can write a migration with existing process plugins, that might not be the best solution. When writing migrations, strive for them to be easy to read, understand, and maintain. For reference, the generic configuration for mapping fields with deltas and subfields is:


process:
  destination_field/0/subfield_1:
    plugin: plugin_name
    config_1: value_1
    config_2: value_2
  destination_field/0/subfield_2:
    plugin: plugin_name
    config_1: value_1
    config_2: value_2
  destination_field/1/subfield_1:
    plugin: plugin_name
    config_1: value_1
    config_2: value_2
  destination_field/1/subfield_2:
    plugin: plugin_name
    config_1: value_1
    config_2: value_2

Process plugin chains

So far, for every field_name/delta/subfield combination we only have used one process plugin. The Migrate API does not impose any restrictions to the number of transformations that the source data can undergo before being assigned to a destination property or field. You can have as many as needed. Chaining of process plugins 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. We have covered this topic in greater detail in the article on using process plugins for data transformation. For now, let’s consider an example chain of two process plugins:


process:
  title:
    - plugin: concat
      source:
        - source_first_name
        - source_last_name
      delimiter: ' '
    - plugin: callback
      callable: strtoupper

In this example, we are using the concat plugin to glue together the source_first_name and source_last_name. A space is placed in between as specified by the delimiter configuration. The result of this is later passed to the callback plugin which executes the strtoupper PHP function on the concatenated value effectively making the string uppercase. Because there are no more process plugins in the chain, the string transformed to uppercase is assigned to the title destination property. If source_first_name is ‘Mauricio’ and source_last_name is ‘Dinarte’, then title would be set to ‘MAURICIO DINARTE’. Refer to the article mentioned before for other things to consider when manipulating strings. The configuration of process plugin chains can be generalized as follows:


process:
  destination_field:
    - plugin: plugin_name
      source: source_column_name
      config_1: value_1
      config_2: value_2
    - plugin: plugin_name
      config_1: value_1
      config_2: value_2
    - plugin: plugin_name
      config_1: value_1
      config_2: value_2

It is very important to note that only the first process plugin in the chain should set a source configuration. Remember that the output of the previous process plugin is the input for the next one. Setting the source configuration in subsequent process plugins is unnecessary and can actually make the chain produce unexpected results or fail altogether.

Source constants, pseudofields, and the process pipeline

We have covered source constants, pseudo-fields, and the process pipeline in the article on using data placeholders in the migration process. This time, we are only going to give an overview to explain their syntax. Constants are arbitrary values that can be used later in the process pipeline. They are set as direct children of  the source section. Let’s consider this example:


source:
  constant:
    DRUPAL_LINK_TITLE: 'Drupal.org profile'
    GITLAB_LINK_TITLE: 'GitLab profile'
    GITHUB_LINK_TITLE: 'GitHub profile'
process:
  field_online_profiles/0/uri: source_drupal_profile
  field_online_profiles/0/title: constant/DRUPAL_LINK_TITLE
  field_online_profiles/1/uri: source_gitlab_profile
  field_online_profiles/1/title: constant/GITLAB_LINK_TITLE
  field_online_profiles/2/uri: source_github_profile
  field_online_profiles/2/title: constant/GITHUB_LINK_TITLE

To define source constants, you write a constants key and set its value to an array of name-value pairs. When you need to refer to them in the process section, you use constant/NAME and they behave like any other column present in the source. Although not required, it is customary to name constants in uppercase. This makes it easier to distinguish them from regular source columns. Notice how their use makes assigning the link titles simpler. Instead of using the default_value plugin, we read the value directly from the source constants.

Pseudofields also store arbitrary values for use later, but they are defined in the process section. Their names can be arbitrary as long as they do not conflict with a property name or field name in the destination. The value can be set to a verbatim copy from the source (a column or a constant) or they can use process plugins for data transformations. For the next example, consider that there is no need for the link text to be different among online profiles. Additionally, there is another Link field that can only store one value. This new field is used to store the URL to the primary profile. The example can be rewritten as follows:


source:
  constant:
    LINK_TITLE: 'Online profile'
process:
  pseudo_link_text:
    - plugin: get
      source: constant/LINK_TITLE
    - plugin: callback
      callable: strtoupper
  field_online_profiles/0/uri: source_drupal_profile
  field_online_profiles/0/title: '@pseudo_link_text'
  field_online_profiles/1/uri: source_gitlab_profile
  field_online_profiles/1/title: '@pseudo_link_text'
  field_online_profiles/2/uri: source_github_profile
  field_online_profiles/2/title: '@pseudo_link_text'
  field_primary_profile: '@field_online_profiles/0'

A psedofield named pseudo_link_text has been created. It has its own process pipeline to provide the link text that will be used for all online profiles. When you want to use the pseudo, you have to enclose it in quotes (') and prepend an at sign (@) to the name. The pseudo_ prefix in the name is not required. In this case it is used to make it easier to distinguish among pseudofields and regular property or field names.

The previous snippets is also a good example of how the migrate process pipeline works. When setting field_primary_profile, we are reusing a value stored in another field: the first delta of field_online_profiles. There are many things to note here:

  • The migrate process pipeline lets you reuse anything that has been defined previously in the file. It can be source constants, pseudo fields, or regular destination properties and fields. The only requirement is that whatever you want to use needs to be previously defined in the migration definition file.
  • Source columns are accessed directly by name. Source constants are accessed using the constant/NAME syntax.
  • Any element defined in the process section can be reused later in the process pipeline by enclosing its name in quotes (') and prepending an at sign (@). This applies to pseudofields and regular destination properties and fields.

When reusing an element in the process pipeline, its whole structure becomes available. In the previous example, we set field_primary_profile to '@field_online_profiles/0'. This means that all subfields in the first delta of the field_online_profiles field will be assigned to field_primary_profile. Effectively this means both the uri and title properties will be set. Be mindful that when you reuse a field, all its delta and subfields are copied along unless specifically restricted. For example, if you only want to reuse the uri of the first delta you would use '@field_online_profiles/0/uri'. In none of these scenarios, indicating that you want to reuse something guarantees that it will be stored in the new element assignment. For example, the field_primary_profile field only accepts one value. Even if we used '@field_online_profiles' to reuse all the deltas of the multi-value field, only the first one will be stored per the field's (cardinality) definition.

The Migrate API is pretty flexible and you can write very complex process pipelines. The examples we have presented today have been exaggerated to demonstrate many syntax variations. Again, when writing migrations, strive for process pipelines that are easy to read, understand, and maintain.

What did you learn in today's article? Did you know that it is possible to specify deltas and subfields in field mappings? Were you aware that process plugins can be chained for multiple data transformations? How have you used source constants and psuedofield before? Please share your answers in the comments. Also, we would be grateful if you shared this article with your friends and colleagues.

Agaric's newsletter will contain news updates and links to blog posts and articles on a variety of topics:

  • Agaric's Free Software Platforms and the communities using them
  • Free Software tools that we use and development projects that we support
  • Agaric's regular online community events
  • Strategies and ideas for democratizing technology
  • Strategies for protecting your data and privacy
  • The variety of services that we offer
  • News regarding cooperatives, organizations, and movements in our solidarity network

Sign Up

We value learning new things and helping one another, so every Thursday at 3pm Eastern Time we take an hour to share things we are working on. We have deep conversations on the ways we can work together We use screen-sharing to show projects we are involved in and sometimes we doodle on the white board as we talk. 

Show and Tell Collaborative Doodle

Everyone is welcome to join the chat or just listen. The informal atmosphere supports us getting to know each other better and form stronger relationships. Anyone can suggest a topic for discussion or give a presentation or ask for input and help on a project. We use a poll to determine if we want to record the session.   Agaric hosts these show and tells publicly because we realize some of us work alone or in organizations that do not encourage skill-sharing, or may just be interested in broadening their knowledge or sharing some code.  So we invite you—our partners, students, colleagues, friends—to take part in watching or giving short presentations.

Direct link to the Show and Tell chatroom

Get on the Show and Tell Mailing List   to receive invitations each week with the upcoming topics. 

Do you want to host a Show and Tell discussion or presentation? Here is the email template to send  a notice to the list at: showandtell@lists.mayfirst.org  If you are already signed up on the email list, you should be able to send a notice to the group! Feel free to choose an alternative time if Thursdays at 3PM ET does not work well for you - experiment!

We shall see you soon!

 

When continuing development of a web site, big changes occur every so often. One such change that may occur, frequently as a result of another change, is a bulk update of URLs. When this is necessary, you can greatly improve the response time experienced by your users—as they are redirected from the old path to the new path—by using a handy directive offered in Apache's mod_rewrite called RewriteMap.

At Agaric we regularly turn to Drupal for it's power and flexibility, so one might question why we didn't leverage Drupal's support for handling redirects. When we see an opportunity for our software/system to respond "as early as it can", it is worth investigating how that is handled. Apache handles redirects itself, making it entirely unnecessary to hand-off to PHP, never mind Drupal bootstrapping and retrieving up a redirect record in a database, just to tell a browser (or a search engine) to look somewhere else.

There were two conditions that existed making use of RewriteMap a great candidate. For one, there will be no changes to the list of redirects once they are set: these are for historical purposes only (the old URLs are no longer exposed anywhere else on the site). Also, because we could make the full set of hundreds of redirects via a single RewriteRule—thanks to the substitution capability afforded by RewriteMap—this solution offered a fitting and concise solution.

So, what did we do, and how did we do it?

We started with an existing set of URLs that followed the pattern: http://example.com/user-info/ID[/tab-name]. Subsequently we implemented a module on the site that produced aliases for our user page URLs. The new patten to the page was then (given exceptions for multiple J Smiths, etc via the suffix): http://example.com/user-info/firstname-lastname[-suffix#][/tab-name]. The mapping of ID to firstname-lastname[-suffix#] was readily available within Drupal, so we used an update_hook to write out the existing mappings to a file (in the Drupal public files folder, since we know that's writable by Drupal) . This file (which I called 'staffmapping.txt') is what we used for a simple text-based rewrite map. Sample output of the update hook looked like this:

# User ID to Name mapping:
1 admin-admin
2 john-smith
3 john-smith-2
4 jane-smith

The format of this file is pretty straight-forward: comments can be started on any line with a #, and the mapping lines themselves are composed of {lookupValue}{whitespace}{replacementValue}.

To actually consume this mapping somewhere in our rules, we must let Apache know about the mapping file itself. This is done with a RewriteMap directive, which can be placed in the Server config or else inside a VirtualHost directive. The format of the RewriteMap looks like this: RewriteMap MapName MapType:MapSource. In our case, the file is a simple text file mapping, so the MapType is 'txt'. The resulting string added to our VirtualHost section is then: RewriteMap staffremap txt:/path/to/staffmapping.txt This directive makes this rewrite mapping file available under the name "staffremap" in our RewriteRules. There are other MapTypes, including ones that uses random selection for the replacement values from a text file, using a hash map rather than a text file, using an internal function, or even using an external program or script to generate replacement values.

Now it's time to actually change incoming URLs using this mapping file, providing the 301 redirect we need. The rewrite rule we used, looks like this:

RewriteRule ^user-detail/([0-9]+)(.*) /user-detail/${staffremap:$1}$2 [R=301,L]

The initial argument to the rewrite rule identifies what incoming URLs this rule applies to. This is the string: "^user-detail/([0-9]+)(.*)". This particular rule looks for URLs starting with (signified by the special character ^) the string "user-detail/", then followed by one or more numbers: ([0-9]+), and finally, anything else that might appear at the end of the string: "(.*)". There's a particular feature of regex being used here as well: each of search terms in parenthesis are captured (or tagged) by the regex processor which then provides some references that can be used in the replacement string portion. These are available with $<captured position>—so, the first value captured by parenthesis is available in "$1"—this would be the user ID, and the second in "$2"—which for this expression would be anything else appearing after the user ID.

Following the whitespace is our new target URL expression: "/user-detail/${staffremap:$1}$2". We're keeping the beginning of the URL the same, and then following the expression syntax "${rewritemap:lookupvalue}", which in our case is: "${staffremap:$1}" we find the new user-name URL. This section could be read as: take the value from the rewrite map called "staffremap", where the lookup value is $1 (the first tagged expression in the search: the numeric value) and return the substitution value from that map in place of this expression. So, if we were attempting to visit the old URL /user-detail/1/about, the staffremap provides the value "admin-admin" from our table. The final portion of the replacement URL (which is just $2) copies everything else that was passed on the URL through to the redirected URL. So, for example, /user-detail/1/about includes the /about portion of the URL in the ultimate redirect URL: /user-detail/admin-admin/about

The final section of the sample RewriteRule is for applying additional flags. In this case, we are specifying the response status of 301, and the L indicates to mod_rewrite that this is the last rule it should process.
That's basically it! We've gone from an old URL pattern, to a new one with a redirect mapping file, and only two directives. For an added performance perk, especially if your list of lookup and replacement values is rather lengthy, you can easily change your text table file (type txt) with a HashMap (type dbm) that Apache's mod_rewrite also understands using a quick command and directive adjustment. Following our example, we'll first run:

$> httxt2dbm -i staffrepam.txt -o staffremap.map

Now that we have a hashmap file, we can adjust our RewriteMap directive accordingly, changing the type to map, and of course updating the file name, which becomes:

RewriteMap staffremap dbm:/path/to/staffremap.map

RewriteMap substitutions provide a straight-forward, and high-performance method for pretty extensive enhancement of RewriteRules. If you are not familiar with RewriteRules generally, at some point you should consider reviewing the Apache documentation on mod_rewrite—it's worthwhile knowledge to have.

The entity_generate process plugin receives a value and checks if there is an entity with that name and if the term exists then uses it and if it does not then creates it (which is precisely what I need).

So, here is a snippet of the article migration YAML file using the entity_generate plugin:

id: blog migration_group: Drupal label: Blog source: plugin: d7_node node_type: blog destination: plugin: entity:node process: status: status created: created field_tags: plugin: sub_process source: field_tags process: target_id: - plugin: entity_generate source: name value_key: name bundle_key: vid bundle: tags entity_type: taxonomy_term ignore_case: true …

In our field_tags field we are using the Drupal 7  field_tags Values We are going to read the entities and pass that value into the entity_generate plugin to create the entities. In this example, there is a problem.  The d7_node migrate plugin (included in the migrate module) provides the taxonomy term IDs and this will make the entity_generate plugin create some taxonomy terms using the IDs as the term names, and this is not what we want.

So what I need to do is to get from somewhere the terms' names, not their ids. To do that I need to add an extra `source property`.

First, we need to create a new custom module and in there create a source plugin which extends the Node process plugin, something like this (let's say that our custom module’s name is my_migration):

Create the file:

my_migration/src/Plugin/migrate/source/MyNode.php

 

And the Content of MyNode file should have this code:

 

namespace Drupal\my_migration\Plugin\migrate\source; use Drupal\migrate\Row; use Drupal\node\Plugin\migrate\source\d7\Node; /** * Adds a source property with the taxonomy term names. * * @MigrateSource( * id = “my_node", * source_module = "node" * ) */ class MyNode extends Node { public function prepareRow(Row $row) { $nid = $row->getSourceProperty('nid'); // Get the taxonomy tags names. $tags = $this->getFieldValues('node', 'field_tags', $nid); $names = []; foreach ($tags as $tag) { $tids[] = $tag['tid']; } if (!$tids) { $names = []; } else { $query = $this->select('taxonomy_term_data', 't'); $query->condition('tid', $tids, 'IN'); $query->addField('t', 'name'); $result = $query->execute()->fetchCol(); $names[] = ['name' => $result['name']]; foreach ($result as $term_name) { $names[] = ['name' => $term_name]; } } $row->setSourceProperty('field_tags_names', $names); return parent::prepareRow($row); } }

The most important part of this code is:

$nid = $row->getSourceProperty('nid'); // Get the taxonomy tags names. $tags = $this->getFieldValues('node', 'field_tags', $nid); $names = []; foreach ($tags as $tag) { $tid = $tag['tid']; $query = $this->select('taxonomy_term_data', 't'); $query->condition('tid', $tid); $query->addField('t', 'name'); $result = $query->execute()->fetchAssoc(); $names[] = ['name' => $result['name']]; } $row->setSourceProperty('field_tags_names', $names);

 

It does the following things:

  • It reads the nid of the article
  • Gets all the terms' IDs of the field_tags field
  • For each ID, it gets the name of the term and puts it in the `names` array
  • Finally, it sets this value inside the $row using the `setsourceProperty` method.

 

Now our rows will have a property called fields_tags_names with the terms' names, and we can pass this data to the entity_generate plugin.

We need to make a few adjustments in our initial migration file. First and most important, update the  source plugin to use our new source plugin:

source: plugin: my_node …

And update the source in the `field_tags` field to use the new  `field_tags_names` source property.

… field_tags: plugin: sub_process source: field_tags_names ….

 

The final migration file looks like this:

 

id: blog migration_group: Drupal label: Blog source: plugin: my_node node_type: blog destination: plugin: entity:node process: status: status created: created field_tags: plugin: sub_process source: field_tags_names process: target_id: - plugin: entity_generate source: name value_key: name bundle_key: vid bundle: tags entity_type: taxonomy_term ignore_case: true …

And that’s it; if we run the migration, it will create on the fly the terms that do not exist and use them if they do exist.

A teacher standing in front of a blackboard.

Empieza con Drupal

Nos encanta presentar a los principiantes los conceptos básicos de Drupal. Ofrecemos seminarios introductorios cortos para grupos pequeños según demanda para la construcción del sitio, la creación de plantillas y el desarrollo de extensiones. Cada seminario incorpora, cuando corresponde, la arquitectura de la información, las pruebas de experiencia del usuario y las técnicas de liderazgo y colaboración necesarias para que cualquier proyecto que no sea solo sea un éxito.

Desarrolla tus Habilidades

Enseñamos equipos de desarrollo y programadores en solitario en nuestro lugar de trabajo o en el suyo, con el programa adaptado a los problemas que está tratando de resolver. Hemos proporcionado tal capacidad en programación Drupal, temática y plantillas, migración de datos y Backbone.js, pero siempre estamos mejorando nuestras habilidades tecnológicas, así que háganos saber lo que está buscando y es posible que estemos justo delante de Estás en la curva de aprendizaje y listo para tirar una cuerda - y listo para tirar una cuerda.

Inscribirse en un Curso

We offer courses with a defined curriculum on building, theming and developing a site. This is great for intermediate trainings of groups. We also offer decision-maker seminars for leaders who need a better understanding of the technology underlying their projects.

¿Por qué Agaric?

Aprenda Drupal 8 de expertos profesionales, con énfasis en los profesionales: Tenemos la experiencia práctica en el desarrollo de sitios web para impartir las habilidades necesarias para realizar el trabajo y hacerlo bien.

Como desarrolladores, Agaric es conocido por asumir las tareas difíciles cuando colaboramos en equipos más grandes. Estamos encantados de capacitarlo, porque siempre hay algo difícil que querrá contratarnos para hacer. En serio, desde nuestros primeros proyectos hace diez años, nuestro objetivo ha sido hacer que nuestros clientes nos necesiten lo menos posible, para poner todo el poder posible en las manos de nuestros clientes. Es por eso que comenzamos a utilizar los sistemas de administración de contenido en primer lugar, y es una tradición que continuamos desarrollando con Software Libre, escribiendo documentación y brindando capacitación.

Agaric participa activamente en la comunidad Drupal en general y se ha presentado en varios Campamentos Drupal, así como en la organización de Jornadas Mundiales de Capacitación en Nicaragua desde 2013.

 

Solicitar un Entrenamiento

The International Summit of Cooperatives convened in Quebec in 2016. The general message of the conference was that cooperatives are everywhere and one only needs to raise awareness for this idea to spread. That seems to be happening as evidenced by the attendance at this conference - 3000 people from 117+ countries according to the International Co-operative Alliance (ICA.coop) one of the sponsors of #ISCOOP2016.

I have been to many cooperative conferences and events, but this one was very different. From the facility to the attendees, the event had an air of style and conform that went beyond attire. I was swimming in a sea of 70-80% middle-aged men in black suits as far as the eye could see. There were also a few groups I saw that were wearing indigenous dress from India, Nepal, Chile, and Congo. Quebec is a mixture of modern and old culture. There were women in authentic Breton garb serving food in the restaurant we visited for lunch, in Old Quebec City, but they were not represented at the coop summit.
 

International Summit of Cooperatives attendees outside the venue.

The largest sponsors of the Summit were the Canadian Government, Canada Economic Development, ICA International Co-operative Alliance and DesJardins. You can see a full listing of all the cooperative sponsors for the event. The attendees were mostly members of Agriculture and Financial coops, both small and large. When I say large, I am talking thousands of members. The point was brought up and highlighted that the International Co-operative Alliance represents close to one billion individual members. Statistics are calculated using the Alliance's formula based on active subscriptions. The ICA maintains the internationally recognised definition of a co-operative in the Statement on the Co-operative Identity and they represent 272 co-operative federations and organizations in 94 countries as of January 2014. The National Cooperative Bank released its annual report in 2015, listing the nation’s top 100 revenue-earning cooperative businesses. These 100 businesses posted revenue of approximately $243.2 billion.

A dominant presence by DesJardins, with over 6 million members, and the Boston Consulting Group (BCG). In my opinion, the BCG message was depressing and the same old Capitalistic message cloaked in a message of "growth" suggesting that "you must grow in order to prosper!" Luckily some cooperative panelists responded with how irresponsible it is to grow for growth sake. A member of DesJardins told me that he personally thinks the coop has gotten too big and we had a great talk about how empathy training should be available to larger entities. Marc Thomas, a DesJardins member, talked about helping people and how the DesJardins cooperative has made some positive changes for him and his community. They are much more willing to lend to smaller cooperatives and they have a host of connections to nurture a small business in start up phases. They play a role similar to a credit union on a much larger scale and they also have deep ties in the community and a network of cooperatives to connect new ideas to funding opportunities.

According to Howard Brodsky there are 50 cooperatives larger than Facebook. Brodsky is Co-Founder, Chairman, and Chief Executive Officer of CCA Global Partner. He is responsible for creating a cooperative retail powerhouse in the marketplace. 

Broadsky on the big screen.

His message was most similar to all the coop conferences I had attended, yet he was much more vocal about how expansive the coop movement is - we are large and we are everywhere. In my opinion, Howard gets it and understands how empathy and caring figure into the movement. He touched on how this simply will not work if we are not honest and caring in our work. He spoke about how important "Stories" are and how they create bonds. The International Co-op Alliance (ica.coop) has built a wonderful way to share our narratives in this digital era - http://stories.coop

Trebor Scholz, a professor at the New School in NYC, and Nathan Schneider, a professor at University of Colorado Boulder, were each on a panel. Nathan's panel was on Multi Sector Activity and he talked about Platform Cooperativism as a way to bring cooperative communities together and how important it is to own the utilities and services we depend on. Trebor on the next panel framed platform coop as a movement and points to the recently published book "Ours to Hack and to Own" as the handbook to get involved in the movement. Copies of book are available from OR Books.

Coop panel

Both panels were lively and got a good response of people talking amongst each other after they ended. Attendees I talked to during the conference were diverse. People from Kenya and the Congo seemed to be the only ones shocked at the implications when I told them about free software. They had never heard of it. People from India that I met either said they knew about it or that they used it in their work. Other people from afar seemed bored and made excuses to not hear about it.

Translation for the speakers and panelists was stellar. No time lag at all and the team was professional and consistent. This did not carry over into the main event participants and attendees. The language barriers seemed to keep people from mingling outside of their party of friends. They sat in groups at the meals and at the sessions. The lunch/dinner seating was round table, with place settings ala extra forks etc. very convenient for conversations. Meals were also used as a venue for a sort of Keynote delivery that happened on several giant screens while the appetizers were served.

The event was full of Pros and positive energy, there were only a few minor Cons:
1. A lot of people in the crowd were unaware of free software, and almost no one used encryption.
2. Most panels were all male - even ones discussing diversity.
3. Some financial coops place most emphasis on growing, as if growth is the only measure of success and value.

 

Robert Reich gave the keynote on the importance of coops.

Robert Reich got a standing ovation for his keynote with a message that coops are an important part of the business landscape. He spent most of his talk telling an anecdotal touchy feely story with the point that we all need to get along. He seems to be a progressive at times, but he still operates on a lesser of two evils mentality in a two party system - I ask, why won't he support a third party if he is in agreement with most of their platforms?

I spoke to many people throughout the three day event about what Agaric is and what we do. I also talked about Free Software and how it impacts cooperatives and their goals. Some were not aware of free software and the vital role it will play in the future success of cooperatives maintaining autonomy and privacy. I also spoke about Platform Cooperativism and Drutopia as a platform concept. Anyone seeking more information can sign up at the Drutopia.org website to be invited to discussions and have a voice in the process of building a platform cooperative from the ground up.

So, things are looking bright for cooperatives in the future. The cooperative branding and marketing needs building, and the network needs to keep expanding and cross-pollinating. The tireless work and dedication of small groups like the ICA is what makes this all happen. Coops do not need to be large, they need to be nimble and they need to be flexible. With apps like BuyCott it will be much easier to purchase responsibly, buy from cooperatives and support ethical companies. I just got the app and am happily surprised to find out how many dedicated cooperative people there are in the world shopping responsibly already! We can each do our part to make the network stronger and to bring the cooperative movements closer together. How would you bring something cooperative to your community? Even a small local event at your neighborhood coffee shop or a blog post or a Tweet can do a lot to raise the level of awareness of how strong we are together!

The results are in:
Typically an awesome event will end and as time passes, there will be little to no follow-up or tangible results that are published. You wonder if that great project you heard about is flourishing or forgotten. You can see the results of the workshops in Quebec in 2016 and rejoice in the knowledge that we are on our way to autonomy.

Our friend Chuck Bordman has written an excellent blog covering this conference here: Coopmatters.com

![Old timey crowd rushing into an event.](/sites/default/files/inline-images/thepeople_0.gif)

Today, we are going to talk about how to manage migrations as configuration entities. This functionality is provided by the Migrate Plus module. First, we will explain the difference between managing migrations as code or configuration. Then, we will show how to convert existing migrations. Finally, we will talk about some important options to include in migration configuration entities. Let’s get started.

Example of migration defined as configuration entity.

Drupal migrations: code or configuration?

So far, we have been managing migrations as code. This is functionality provided out of the box. You write the migration definition file in YAML format. Then, you place it in the migrations directory of your module. If you need to update the migration, you make the modifications to the files and then rebuild caches. More details on the workflow for migrations managed in code can be found in this article.

Migrate Plus offers an alternative to this approach. It allows you to manage migrations as configuration entities. You still use YAML files to write the migration definition files, but their location and workflow is different. They need to be placed in a config/install directory. If you need to update the migration,  you make the modifications to the files and then sync the configuration again. More details on this workflow can be found in this article.

There is one thing worth emphasizing. When managing migrations as code you need access to the file system to update and deploy the changes to the file. This is usually done by developers.  When managing migrations as configuration, you can make updates via the user interface as long as you have permissions to sync the site’s configuration. This is usually done by site administrators. You might still have to modify files depending on how you manage your configuration. But the point is that file system access to update migrations is optional. Although not recommended, you can write, modify, and execute the migrations entirely via the user interface.

Transitioning to configuration entities

To demonstrate how to transition from code to configuration entities, we are going to convert the JSON migration example. You can get the full code example at https://github.com/dinarcon/ud_migrations The module to enable is UD config JSON source migration whose machine name is udm_config_json_source. It comes with four migrations: udm_config_json_source_paragraph, udm_config_json_source_image, udm_config_json_source_node_local, and udm_config_json_source_node_remote.

The transition to configuration entities is a two step process. First, move the migration definition files from the migrations folder to a config/install folder. Second, rename the files so that they follow this pattern: migrate_plus.migration.[migration_id].yml. For example: migrate_plus.migration.udm_config_json_source_node_local.yml. And that’s it! Files placed in that directory following that pattern will be synced into Drupal’s active configuration when the module is installed for the first time (only). Note that changes to the files require a new synchronization operation for changes to take effect. Changing the files and rebuilding caches does not update the configuration as it was the case with migrations managed in code.

If you have the Migrate Plus module enabled, it will detect the migrations and you will be able to execute them. You can continue using the Drush commands provided the Migrate Run module. Alternatively, you can install the Migrate Tools module which provides Drush commands for running both types of migrations: code and configuration. Migrate Tools also offers a user interface for executing migrations. This user interface is only for migrations defined as configuration though. It is available at /admin/structure/migrate. For now, you can run the migrations using the following Drush command: drush migrate:import udm_config_json_source_node_local --execute-dependencies.

Note: For executing migrations in the command line, choose between Migrate Run or Migrate Tools. You pick one or the other, but not both as the commands provided by the two modules have the same name. Another thing to note is that the example uses Drush 9. There were major refactorings between versions 8 and 9 which included changes to the name of the commands.

UUIDs for migration configuration entities

When managing migrations as configuration, you can set extra options. Some are exposed by Migrate Plus while others come from Drupal’s configuration management system. Let’s see some examples.

The most important new option is defining a UUID for the migration definition file. This is optional, but adding one will greatly simplify the workflow to update migrations. The UUID is used to keep track of every piece of configuration in the system. When you add new configuration, Drupal will read the UUID value if provided and update that particular piece of configuration. Otherwise, it will create a UUID on the fly, attach it to the configuration definition, and then import it. That is why you want to set a UUID value manually. If changes need to be made, you want to update the same configuration, not create a new one. If no UUID was originally set, you can get the automatically created value by exporting the migration definition. The workflow for this is a bit complicated and error prone so always include a UUID with your migrations. This following snippet shows an example UUID:

uuid: b744190e-3a48-45c7-97a4-093099ba0547
id: udm_config_json_source_node_local
label: 'UD migrations configuration example'

The UUID a string of 32 hexadecimal digits displayed in 5 groups. Each is separated by hyphens following this pattern: 8-4-4-4-12. In Drupal, two or more pieces of configuration cannot share the same value. Drupal will check the UUID and the type of configuration in sync operations. In this case the type is signaled by the migrate_plus.migration. prefix in the name of the migration definition file.

When using configuration entities, a single migration is identified by two different options. The uuid is used by the Drupal’s configuration system and the id is used by the Migrate API. Always make sure that this combination is kept the same when updating the files and syncing the configuration. Otherwise you might get hard to debug errors. Also, make sure you are importing the proper configuration type. The latter should not be something to worry about unless you utilize the user interface to export or import single configuration items.

If you do not have a UUID in advance for your migration, you can try one of these commands to generate it:

# Use Drupal's UUID service.
$ drush php:eval "echo \Drupal::service('uuid')->generate(). PHP_EOL;"

# Use a Drush command provided by the Devel module, if enabled.
$ drush devel:uuid

# Use a tool provided by your operating system, if available.
$ uuidgen

Alternatively, you can search online for UUID v4 generators. There are many available.

Technical note: Drupal uses UUID v4 (RFC 4122 section 4.4) values which are generated by the `uuid` service. There is a separate class for validation purposes. Drupal might override the UUID service to use the most efficient generation method available. This could be using a PECL extension or a COM implementation for Windows.

Automatically deleting migration configuration entities

By default, configuration remains in the system even if the module that added it gets uninstalled. This can cause problems if your migration depends on custom migration plugins provided by your module. It is possible to enforce that migration entities get removed when your custom module is uninstalled. To do this, you leverage the dependencies option provided by Drupal’s configuration management system. The following snippet shows how to do it:

uuid: b744190e-3a48-45c7-97a4-093099ba0547
id: udm_config_json_source_node_local
label: 'UD migrations configuration example'
dependencies:
  enforced:
    module:
      - ud_migrations_config_json_source

You add the machine name of your module to dependencies > enforced > module array. This adds an enforced dependency on your own module. The effect is that the migration will be removed from Drupal’s active configuration when your custom module is uninstalled. Note that the top level dependencies array can have others keys in addition to enforced. For example: config and module. Learning more about them is left as an exercise for the curious reader.

It is important not to confuse the dependencies and migration_dependencies options. The former is provided by Drupal’s configuration management system and was just explained. The latter is provided by the Migrate API and is used to declare migrations that need be imported in advance. Read this article to know more about this feature. The following snippet shows an example:

uuid: b744190e-3a48-45c7-97a4-093099ba0547
id: udm_config_json_source_node_local
label: 'UD migrations configuration example'
dependencies:
  enforced:
    module:
      - ud_migrations_config_json_source
migration_dependencies:
  required:
    - udm_config_json_source_image
    - udm_config_json_source_paragraph
  optional: []

What did you learn in today’s blog post? Did you know that you can manage migrations in two ways: code or configuration? Did you know that file name and location as well as workflows need to be adjusted depending on which approach you follow? Share your answers in the comments. Also, I would be grateful if you shared this blog post with others.

Next: Workflows and benefits of managing Drupal migrations as configuration entities

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 it is the migration series or other topics.

Manifestante con signo en el fondo que dice "Lucha contra el racismo. El sexismo. Toda opresión". Atribución: Johnny Silvercloud CC Share Igual

Portside

Amplificando diversas voces a la izquierda.

Hi friends and collaborators, join us today at 3pm ET (or any subsequent Thursday at 3) as we kick off a series of research, planning, discussion, and building sessions for Visions Unite.

As our primary pro bono project, Agaric is working on Visions Unite, "where people seeking to make the world more whole can share ideas and information and gather the commitment and resources to build power to be the change we need", which a dozen projects have tried to do—what makes this different is sharing power via democratic mass communication.

Here are some initial user stories for Visions Unite.

Help plan and build the interface and underlying technology! (Drupal friends, we have been leaning against Drupal but might do it for the MVP— would love to hear your thoughts for or against.)

Connection info will always be up-to-date at agaric.coop/show (for these sessions we are taking over most of our Show & Tell hour, which is weekly on Thursdays 3pm Eastern).

There is an important correction to be made to the top-selling Drupal book, the Definitive Guide to Drupal 7.

The slogan printed on the cover is incorrect. The book does not contain "Everything you need to know about Drupal"— rather, a central goal of the book is to show how to keep learning and growing within the Drupal community. Everything to know about Drupal could never fit in one book or set of books.

Admittedly, that point is probably made in 20 pages. The other thousand-plus pages are a solid effort to give a whole lot of information to help everyone (at every level) succeed with Drupal:

The Definitive Guide to Drupal 7 accelerates people along the Drupal learning curve by covering all aspects of building web sites with Drupal: architecture and configuration; module development; front end development; running projects sustainably; and contributing to Drupal's code, documentation, and community.

Which brings us to the actual slogan for the book: Configuration, Code, and Community. This broad sweep of what it takes to do Drupal right comes in these pages from the perspective (and blood, sweat, and tears) of thirty-four contributing authors, including top Drupal contributors. The talent and effort in this book is humbling, and inspiring.

The book site, DefinitiveDrupal.org, is lagging behind what the community of readers needs for updates and discussion, but i could not put aside telling people about the book any longer just because that site (or Agaric's, for that matter) has been neglected. You, your friends, and the cousin looking for work should know you can buy this book for cheaper than i can photocopy it for you. And the publisher, Apress, did a great job with the layout and production, despite the slogan slip-up.

Note: E-book versions will come; it seems we have to wait on Amazon and Apple, but we will also pursue the ePub format. Sign up to learn about new formats and new material (so very rare updates only when there's something definitely worth knowing about).

Thank you for your kind attention!

benjamin melançon

Agaric is not hiring! As a worker-owned-and-run collective, we don't have bosses or employees. We do need to add to our team to keep up with our opportunities, and if we have to we'll make an exception and hire someone with strong administrative skills, willingness and ability to lead projects, and knowledge or willingness to learn finances. Front end development skills a bonus. We believe in cross-training! Especially if you can see becoming a Principal at a go-great-and-do-good cooperative technology shop,

TL;DR: For PHP Hexadecimals, Decimals and Octals are all Integers, so they must be declared as @param integer

While I was working on a patch I had to write the docblock of a function which received a hexadecimal number and I wasn't sure what I was supposed to put in the @type param.

I went to Drupal's API documentation and comments standards page to see which is the best type for this param and I found the following:

Data types can be primitive types (int, string, etc.), complex PHP built-in types (array, object, resource), or PHP classes.

Alright, a hexadecimal number is not a complex PHP built-in type nor a PHP Class so it must be a primitive type, so I went to the PHP documentation page to see which primitives PHP has and I found the following:

  • boolean
  • integer
  • float (floating-point number, aka double)
  • String

So there wasn't a specific reference for a Hexadecimal number...

The solution:

In the end Pieter Frenssen helped me (Thanks!) with this, and he showed me that in PHP, it doesn't matter what the base number is and it can be an octal, hexadecimal or a decimal, for PHP they all are integers (which makes sense but I wanted to be sure) and he shared this small snippet where we can see that PHP sees the numbers as integers and the base doesn't matter:

$ php -a
Interactive shell

php > var_dump(gettype(0x0f));
string(7) "integer"

php > var_dump(0x08 === 8);
bool(true)

So if you are writing the documentation of a function in which one of its params is a hexadecimal number you must declare it as Integer.

Flock of birds flying through the sky.