Skip to main content

Blog

Punctuating Lists

If an article has multiple authors (using a user reference field or a content reference field), Drupal core displays each on their own line.

Like this ugly, rigid presentation:

Agaric initiatives - just what do they do?.

Initiatives

Agaric-led and Agaric-supported projects and causes.

We're proud to have worked with designer Todd Linkner to produce a bold and unique web site worthy of the world-renowned architectural firm Studio Daniel Libeskind.

Master planner for the Ground Zero memorial and architect of numerous acclaimed museums, offices, and residences around the world, Daniel Libeskind needed to present his and his studio's amazing work with commensurate impact online.

The Studio Daniel Libeskind project was one of our most ambitious to date. The architect partners and their one-woman public relations powerhouse, Amanda Ice, are fantastic to work with, as is the project's driving force and designer, Todd Linkner. We worked through many challenges, remaining flexible to the business needs and the design needs (developed in parallel with the work on base functionality)— and missed a September 11, 2011 launch date despite putting all hands on deck for as the scope outpaced the resources available. We continued, and completed the site successfully for beautiful presentation across browsers, iPad, and smart phones.

Agaric architected, built and themed the redesigned site. In addition to bringing the bold design to life and further making the site work for mobile devices (iOS, Android, and even BlackBerry), Agaric vastly improved the content creation workflow and press inquiry handling capabilities of the site, as well as search and filtering. We added generating stylish PDF versions of project pages, custom cropping and ordering of images and kept hardware requirements low and user perceived performance high by adding Varnish HTTP caching to the server.

Claudina Sarahe and Benjamin Melançon presented on the challenges and successes of this project at Pacific Northwest Drupal Summit.

We're thrilled to have had the opportunity to be such a large part in giving Studio Daniel Libeskind an online home worthy of the inspiring places they create in the physical world.

Cross-posted from opensource.com.

Install and configure the tools

Since it is good practice to use Composer to manage a Drupal site's dependencies, use it to install the tools for BDD tests: Behat, Mink, and the Behat Drupal Extension. The Behat Drupal Extension lists Behat and Mink among its dependencies, so you can get all of the tools by installing the Behat Drupal Extension package:

composer require drupal/drupal-extension --dev

Mink allows you to write tests in a human-readable format. For example:

Given I am registered user,
When I visit the homepage,
Then I should see a personalized news feed

Because these tests are supposed to emulate user interaction, you can assume they will be executed within a web browser. That is where Mink comes into play. There are various browser emulators, such as Goutte and Selenium, and they all behave differently and have very different APIs. Mink allows you to write a test once and execute it in different browser emulators. In layman's terms, Mink allows you to control a browser programmatically to emulate a user's action.

Now that you have the tools installed, you should have a behat command available. If you run it:

./vendor/bin/behat

you should get an error, like:

FeatureContext context class not found and can not be used

Start by initializing Behat:

./vendor/bin/behat --init

This will create two folders and one file, which we will revisit later; for now, running behat without the extra parameters should not yield an error. Instead, you should see an output similar to this:

No scenarios
No steps
0m0.00s (7.70Mb)

Now you are ready to write your first test, for example, to verify that website visitors can leave a message using the site-wide contact form.

Writing test scenarios

By default, Behat will look for files in the features folder that's created when the project is initialized. The file inside that folder should have the .feature extension. Let's tests the site-wide contact form. Create a file contact-form.feature in the features folder with the following content:

Feature: Contact form
  In order to send a message to the site administrators
  As a visitor
  I should be able to use the site-wide contact form

  Scenario: A visitor can use the site-wide contact form
    Given I am at "contact/feedback"
    When I fill in "name" with "John Doe"
    And I fill in "mail" with "john@doe.com"
    And I fill in "subject" with "Hello world"
    And I fill in "message" with "Lorem Ipsum"
    And I press "Send message"
    Then I should see the text "Your message has been sent."

Behat tests are written in Gherkin, a human-readable format that follows the Context–Action–Outcome pattern. It consists of several special keywords that, when parsed, will execute commands to emulate a user's interaction with the website.

The sentences that start with the keywords Given, When, and Then indicate the Context, Action, and Outcome, respectively. They are called Steps and they should be written from the perspective of the user performing the action. Behat will read them and execute the corresponding Step Definitions. (More on this later.)

This example instructs the browser to visit a page under the "contact/feedback" link, fill in some field values, press a button, and check whether a message is present on the page to verify that the action worked. Run the test; your output should look similar to this:

1 scenario (1 undefined)
7 steps (7 undefined)
0m0.01s (8.01Mb)

 >> default suite has undefined steps. Please choose the context to generate snippets:

  [0] None
  [1] FeatureContext
 >

Type 0 at the prompt to select the None option. This verifies that Behat found the test and tried to execute it, but it is complaining about undefined steps. These are the Step Definitions, PHP code that will execute the tasks required to fulfill the step. You can check which steps definitions are available by running:

./vendor/bin/behat -dl

Currently there are no step definitions, so you shouldn't see any output. You could write your own, but for now, you can use some provided by the Mink extension and the Behat Drupal Extension. Create a behat.yml file at the same level as the Features folder—not inside it—with the following contents:

default:
  suites:
    default:
      contexts:
       - FeatureContext
        - Drupal\DrupalExtension\Context\DrupalContext
        - Drupal\DrupalExtension\Context\MinkContext
        - Drupal\DrupalExtension\Context\MessageContext
        - Drupal\DrupalExtension\Context\DrushContext
  extensions:
    Behat\MinkExtension:
      goutte: ~

Steps definitions are provided through Contexts. When you initialized Behat, it created a FeatureContext without any step definitions. In the example above, we are updating the configuration file to include this empty context along with others provided by the Drupal Behat Extension. Running ./vendor/bin/behat -dl again produces a list of 120+ steps you can use; here is a trimmed version of the output:

default | Given I am an anonymous user
default | When I visit :path
default | When I click :link
default | Then I (should )see the text :text

Now you can perform lots of actions. Run the tests again with ./vendor/bin/behat. The test should fail with an error similar to:

  Scenario: A visitor can use the site-wide contact form        # features/contact-form.feature:8
        And I am at "contact/feedback"                          # Drupal\DrupalExtension\Context\MinkContext::assertAtPath()
        When I fill in "name" with "John Doe"                   # Drupal\DrupalExtension\Context\MinkContext::fillField()
        And I fill in "mail" with "john@doe.com"                # Drupal\DrupalExtension\Context\MinkContext::fillField()
        And I fill in "subject" with "Hello world"              # Drupal\DrupalExtension\Context\MinkContext::fillField()
        Form field with id|name|label|value|placeholder "subject" not found. (Behat\Mink\Exception\ElementNotFoundException)

        And I fill in "message" with "Lorem Ipsum"              # Drupal\DrupalExtension\Context\MinkContext::fillField()

        And I press "Send message"                              # Drupal\DrupalExtension\Context\MinkContext::pressButton()

        Then I should see the text "Your message has been sent." # Drupal\DrupalExtension\Context\MinkContext::assertTextVisible()

 --- Failed scenarios:

        features/contact-form.feature:8 1 scenario (1 failed) 7 steps (3 passed, 1 failed, 3 skipped) 0m0.10s (12.84Mb)

The output shows that the first three steps—visiting the contact page and filling in the name and subject fields—worked. But the test fails when the user tries to enter the subject, then it skips the rest of the steps. These steps require you to use the name attribute of the HTML tag that renders the form field.

When I created the test, I purposely used the proper values for the name and address fields so they would pass.  When in doubt, use your browser's developer tools to inspect the source code and find the proper values you should use. By doing this, I found I should use subject[0][value] for the subject and message[0][value] for the message. When I update my test to use those values and run it again, it should pass with flying colors and produce an output similar to:

 

1 scenario (1 passed)
7 steps (7 passed)
0m0.29s (12.88Mb)

Success! The test passes! In case you are wondering, I'm using the Goutte browser. It is a command line browser, and the driver to use it with Behat is installed as a dependency of the Behat Drupal Extension package.

Other things to note

As mentioned above, BDD tests should be written from the perspective of the user performing the action. Users don't think in terms of HTML name attributes. That is why writing tests using subject[0][value] and message[0][value] is both cryptic and not very user friendly. You can improve this by creating custom steps at features/bootstrap/FeatureContext.php, which was generated when Behat initialized.

Also, if you run the test several times, you will find that it starts failing. This is because Drupal, by default, imposes a limit of five submissions per hour. Each time you run the test, it's like a real user is performing the action. Once the limit is reached, you'll get an error on the Drupal interface. The test fails because the expected success message is missing.

This illustrates the importance of debugging your tests. There are some steps that can help with this, like Then print last drush output and Then I break. Better yet is using a real debugger, like Xdebug. You can also install other packages that provide more step definitions specifically for debugging purposes, like Behatch and Nuvole's extension,. For example, you can configure Behat to take a screenshot of the state of the browser when a test fails (if this capability is provided by the driver you're using).

Regarding drivers and browser emulators, Goutte doesn't support JavaScript. If a feature depends on JavaScript, you can test it by using the Selenium2Driver in combination with Geckodriver and Firefox. Every driver and browser has different features and capabilities. For example, the Goutte driver provides access to the response's HTTP status code, but the Selenium2Driver doesn't. (You can read more about drivers in Mink and Behat.) For Behat to pickup a javascript enabled driver/browser you need to annotate the scenario using the @javascript tag. Example:

Feature:
 (feature description)

  @javascript
  Scenario: An editor can select the author of a node from an autocomplete field
    (list of steps)

Another tag that is useful for Drupal sites is @api. This instructs the Behat Drupal Extension to use a driver that can perform operations specific to Drupal; for example, creating users and nodes for your tests. Although you could follow the registration process to create a user and assign roles, it is easier to simply use a step like Given I am logged in as a user with the "Authenticated user" role. For this to work, you need to specify whether you want to use the Drupal or Drush driver. Make sure to update your behat.yml file accordingly. For example, to use the Drupal driver:

default:
  extensions:
    Drupal\DrupalExtension:
      blackbox: ~
      api_driver: drupal
      drupal:
        drupal_root: ./relative/path/to/drupal

I hope this introduction to BDD testing in Drupal serves you well. If you have questions, feel free to add a comment below, send me an email at mauricio@agaric.com (or through the Agaric contact form) or a tweet at @dinarcon.

Find It's Features and Functionality

Find It is a one-stop-shop for community-members to find opportunities for community engagement

Search a curated directory of events

  • Filter results based on age, location, cost, activity, and schedule
  • Click on a map to select which areas are accessible to you

Create a user account

  • Set event reminders about interesting opportunities
  • Receive notifications so that your Find It experience stays smooth.

Select a language

  • Navigate the site in your language of choice
  • Contribute improvements to the translations so that Find It can meet the needs of the diverse communities it serves

 

Find It makes it easier for government and non-profit organizations to reach the people they work to serve.

Log in with a service provider account

  • Share information about your organization
  • Post events and services for the residents of their community

 

Find It makes it easier for an individual or small team to make sure that members of the community they serve have access to all of the services they need.

Post information about public spaces

  • Keep track of all of the available public spaces on a single platform
  • Let residents know what public spaces are available as resources to them

Coordinate with all of the city's service providers together on one platform

  • Add service providers to the platform so they can post information about what they offer
  • Check for gaps and redundancies in services offered throughout your area in order to coordinate a more comprehensive set of services

Blog Update: Keep this conversation alive!

In this post, we call out for "Birds of a feather" to join us at DrupalCon, which has come and gone. However, this conversation remains relevant to our political condition and relevant to our work! Our scientific and government entities must continue to increasingly acknowledge racism as a public health threat. We believe that harnessing the power of data within our own communities is a path to the change that we want to see. Please help us keep this post and the discussion it provokes alive and circulating!

Impedit veniam consectetur dolores id provident. Voluptas non voluptates rerum. Aut et laudantium nisi quia pariatur vero nemo.

Enim aperiam dolor numquam saepe perferendis fugit nam veniam. Impedit rerum repellendus voluptatem voluptatem fugit consequatur. Omnis illum quaerat vel voluptatem error praesentium.

Monday night before the "Super Tuesday" primary, I'm searching for "does the bernie sanders app help you offer rides to polls to people" and finding no answer. (It does not.)

All I found was Lyft offering ride codes to for a handful of non-partisan non-profits to distribute. If Lyft can realize that simple physical access to vote is a barrier that affects different groups of people unequally and cite the facts about youth not voting, it surely came up on the Bernie Sanders Slack.

Yet in this highly online-connected campaign, some of the basic steps to winning (asking everyone: Do you have a plan to vote? Do you need help getting to the polls?) didn't make it into the official app, nor in any public side efforts.

There are a huge number of thoughtful, dedicated people working on the Bernie Sanders campaign (and in other political campaigns), but as in every movement I've witnessed I'm convinced that not all the best ideas are bubbling up.  This is especially true for communities like Drupal where even the idea of shared goals, let alone the mechanism for identifying and realizing them, can seem to disappear when you look for them directly.

Even when a goal is simple (get this one person elected president) the tactics are likely to need to be varied and complex.

This is vastly more true when we're talking about a movement. Even in a presidential campaign like for Sanders, the the goals behind the goal—health care, living wages, lots more jobs for everyone because we're putting people to work reversing global warming—are many, multifaceted, and cannot possibly be achieved only through electing someone, even to an office like the United State's imperial presidency.

After getting over my personal hangup of asking people for something without having at least the barest offer of help (a ride to go vote), I did start texting a few people to encourage them to vote. But as I texted my brother in New York, I'm still bummed we're organizing in the context of political campaigns, instead of having huge movements that, as an afterthought, choose our politicians.

I'm not making (or necessarily opposing) the argument that electoral organizing distracts from more important grassroots organizing.

I have gotten involved with a local solidarity network which focuses on direct action to help people with immediate problems— frequently a dozen people helping just one person or a few people at a time win livable spaces from landlords (or get security deposits back), or get stolen wages from an employer.

This sort of deep organizing—really only medium deep, but it's using available resources to nearly their maximum capacity—does not have the breadth of the typical mayoral campaign.

We need breadth as well as depth. There are many problems that can't be solved on a case by case basis. Although the type of organizing local solidarity networks engage in builds the capacity to take on bigger problems, it doesn't necessarily scale fast enough, or have clear mechanisms to translate built power and solidarity in one area to others.

The question of translating power built in one sphere to another is even more pressing for the election campaigns.

It's no secret, as Frank Chapman of the National Alliance Against Racist and Political Repression reminded people in Minneapolis when he visited from Chicago, that you build political power by going door to door and finding supporters.

What would our political movements be able to do if we didn't have to redo all the grunt work every time?

Or if people weren't canvassed only by campaigns (electoral or otherwise), but asked about their needs?

There are enough people who give a damn.

We could build immensely powerful movements from the ground up, if we had a way to agree how shared resources of movements—including communication channels—would be controlled.

To be a movement for, among other things, democracy, we need to be democratic ourselves. The DSA is probably farthest along in reach and democratic mechanisms, and so a natural place to join.

We need better technology to coordinate to achieve justice, liberty, and better lives for all. I don't mean merely a better canvassing app.

We need approaches and tools that let us share power. Then we can truly build power together.

A positive spin on this extremely spun election: media coverage has meant a ton but advertising has not. And the national, corporate media (which, if for instance you haven't checked who owns your local newspaper, if you even have one, is nearly all of the news media) is the sworn enemy to economic fairness and equal political power. No one with resources should put a cent into our enemies pockets by buying ads, especially when it doesn't even work.

It's a perfect opportunity to build institutions that work for us, rather than pouring resources and energy into institutions that are getting us killed.

We can build a communication network through which we collectively decide what we want, and then figure out how to coordinate to get it— whether it's electing someone or holding politicians or businesses accountable with direct action or forming ourselves into a giant cooperative corporation to negotiate as workers and buyers more equally with the huge corporations we deal with on a day-to-day basis.

If you're in the position to connect us to campaigns, cooperatives, parties, or other organizations who see a need for communication tools controlled by all the people in an organization or movement, where the ideas and control of resources can build from below, please contact Agaric.

 

Somos una cooperativa que se especializa en construir herramientas y sitios web que respetan la libertad. También impartimos  capacitaciones and consultoria para que puedas alcanzar tus metas. Todo lo que hacemos es parte de nuestro proposito de ayudar a toda la gente a obtener la mayor cantidad de poder posible sobre sus propias vidas.

Desde nuestra fundación en 2006, nuestro método ha sido la de comprometernos profundamente con las necesidades de nuestros clientes y colaborar con las comunidades de software Libre  para crear soluciones solidas y sostenibles.

Al construir herramientas abiertas y libres de usar y adaptar protegemos la web abierta y expandimos el uso del software mientras cumplimos con las necesidades unicas de las personas.

Hemos construido desde plataformas digitales de publiciación hasta espacios digitales de colaboración y directorios comunitarios de recursos.  Nuestro mejor trabajo es cuando nos aseguramos de que el mundo puede ver el gran trabajo de una organización.

Siempre que es posible, contribuimos nuestro trabajo a la comunidad de software libre para empoderar a otros y que se beneficien de las soluciones que creamos. Mantenemos mas de 50 proyectos para que cualquiera pueda usar o contribuir.

Por favor lee mas acerca de nuestras habilidades y servicios.

También somos lideres en movimientos de desarrollo de tecnología ética para movimientos sociales. Nuestros miembros son parte del equipo de lideres de MayFirst/PeopleLink  y del comite de planeación del DrupalCon. Somos miebros de la United States Federation of Worker Cooperatives, de la  Free Software Foundation y de la Drupal Association. Finalmente, somos orgullosos miembros de la Industrial Workers of the World.

 

Have you ever been asked to log into a website while you are viewing a page? And after doing so you get redirected to some page other than the one you were reading? This is an obvious and rather common usability problem. When this happens people lose track of what they were doing and some might not even bother to go back. Let's find out how to solve this in Drupal 8.

In a recent project a client wisely requested exactly that: whenever a user logs into the site, redirect them to the page they were before clicking the login link. This seemed like a very common request so we looked for a contrib module that provided the functionality. Login Destination used to do it in Drupal 7. Sadly the Drupal 8 version of this module does not provide the functionality yet.

Other modules, and some combinations of them, were tested without success. Therefore, we built Login Return Page. It a very small module that just does one thing and it does it well: it appends destination=/current/page to all the links pointing to /user/login effectively redirecting users to the page they were viewing before login. The project is waiting to be approved before promoting it to full project.

Have you had a similar need? Are there other things you are requested to do after login? Please share them in the comments.

UPDATE: It seems to this is a regression from Drupal 7 and there is an issue that would fix it. Thanks to Wim Leers for letting me know about it.

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.

Syntax for process plugin definition and chaining

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. #DrupalThanks

Chaining 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.

Next: Migrating data into Drupal subfields

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

Creating an issue fork to contribute to a Drupal module, without getting confused.

(This cheat sheet is for myself. Fellow cheaters welcome.)

Check the branch that the issue is against— even if you filed the issue yourself, it should have defaulted to the branch people should be working on.

Go to the module page and scroll down, if you are lucky there will be a dev release toward the bottom, for example "Development version: 2.0.x-dev", which you can click into and copy the composer command.

(If there is not a dev release, you'll have to look at Development in the sidebar, click through "Source code" and copy from the Code dropdown either the URL under Clone with SSH or Clone with HTTPS, and git clone that into a modules.

Press the Create issue fork button and wait a minute.

Press the "Show commands" link, and copy-paste the commands in the Add & fetch this issue fork’s repository

And then copy-paste the commands from Check out this branch for the first time

Assign to yourself in "Issue metadata fieldset and save the issue form with any comment about how you plan to work on this that you hope somebody might provide feedback on or as a reminder to your later self.

Micky is a highly active and dedicated individual who is deeply involved in various movements and networks related to free software, cooperative business models, and community building. She is a worker/owner of Agaric, a member of numerous "free software" networks and movements, and actively uses tools like BigBlueButton, Drupal, and the GNU/Linux operating system to promote and introduce people to the world of free software.

Micky plays a crucial role in connecting different organizations and networks and she serves on the board of May First Movement Technology (MFMT), The US Solidarity Economy Network (SEN) and, SnowDrift.coop. As a member of The Tech Workers Peer Network a coalition of the US Federation of Worker Cooperatives (USFWC), she works to foster ongoing dialogues and collaborations in building a new economy network rooted in community-based, shared ownership. She also works with organizations like, Platform Cooperativism Consortium, The Free Software Foundation, The Center for Global Justice, The Greater Boston Chamber of Cooperatives, Restore the Fourth, and MassMesh, among others, to raise awareness about free software, cooperative business models, privacy protection, plus local and global opportunities to share knowledge. She has been a Keynote speaker, lecturer and panel member at conferences over the past 20 years.

As a member of the May First Movement Technology board, Micky actively collaborates with technical activists to provide people with the necessary information and tools to transition from being a local or small global network to becoming part of a global movement based on solidarity and cooperative principles. She strongly believes that the workers' economy requires free software tools to protect our freedoms, and she combines the principles of free software liberation and cooperative development in her presentations and talks.

Micky is also an active member of the Drupal community, an international group centered around a free software content management system. She has contributed to the Drupal community as a writer and has shared her experiences as a contributing author in the book "Ours to Hack and to Own," which is considered the handbook for the Platform Cooperativism Movement. The book was initiated by Trebor Scholz and Nathan Schneider at the New School in NYC and was listed as one of the top tech books of 2017 by Wired magazine.

As a public speaker, Micky passionately delivers the message of cooperative software development to various networks and movements. Her presentations cover a wide range of topics, including free software, cooperative tech development, personal digital privacy, worker-owned cooperatives, artificial intelligence, surveillance and capitalism, introduction to web technology, and the incorporation of the seven cooperative principles using Sociocracy, into work and living environments. Additionally, she provides software training for free tools such as BigBlueButton, Signal Instant Messenger, NextCloud, and encrypted email, and she can offer advice on alternatives to proprietary software.

Outside of her professional endeavors, Micky has a rich personal history. She was a resident of Weston, CT in the 50s, 60s, and 70s and currently resides in Boston, MA with her long-time partner John M. Crisman. Micky was a member of a few bands in Boston, MA during the 1970’s through the 90’s, such as The Phantoms. The band is featured in the book "Hit Girls" by Jen B. Larsen, a compendium of female-led punk bands in the USA during the late 70s and early 80s as well as an exhibit at Harvard's Loeb Music Library in 2024-2025.

Micky's dedication to her work and her commitment to promoting free software, cooperative business models, art, music and community building make her a valuable asset to the movements and networks she is involved in. She continues to make a positive impact in the world through her activism, public speaking engagements, and the creation of site like CommunityBridge, which prioritize privacy and provide a more secure environment for meaningful video chats between individuals who share a passion for education and activism.

 

Here is a trailer for a video documentary about Micky's High School days  - It is Rock and Roll History - The High School that ROCKED.

Request an interview, presentation or workshop with Micky

Presentations and Workshops:

*Updated list and references available upon request.  

2021

  • Live interview - Sweden CivicTech Lab - The Two Money Problems

2020

  • Hosted workshop: Surveillance Capitalism, Predictive Analysis and YOU at HOPE 2020  (Hackers on Planet Earth)
  • Live interview on Radio Statler at the HOPE 2020 conference
  • Libreplanet LIVE online 3/14/2020. We hosted a discussion at 4:20 ET on fsf.org - Libreplanet 2020
  • Surveillance Capitalism, Predictive Analysis and YOU - Lecture, Biblioteque, San Miguel de Allende
  • Internet Security - Workshops - Biblioteque, San Miguel de Allende
  • Boston College - presentation on AI and Predictive analysis
  • Colorado.edu online interview on Cooperative development and Agaric values

2019

2018

 

2017

  • Open.coop 2017 - London University, London, UK
    • Panel: Empowering digital collaboration: Introducing the open app ecosystem
    • Workshop: Designing interoperable apps for the open app ecosystem