Skip to main content

Blog

See Permissions' Machine Names (and much more) with Xray Module for Drupal 7

With Drupal 7's third and final release candidate unleashed on us all this morning, it is long past time to help the #D7CX movement with a seasonal offering of our own.

+1 to Ending comment-to-subscribe on Drupal.org

As starving authors we at Agaric don't have a lot of cash to burn right now, but we've thrown $25 in the project to make it possible to subscribe to drupal.org issues without commenting. (On top of whatever we donated when this request for funding went out a year and a half ago).

Drupal Work Collectives

Agaric proposes the creation of a new kind of workplace, essentially a Drupal commune, but really more like an open source free software idea & brainstorming commune, kind of along the same lines as an artist's or writer's colony.

We're Writing a Book!

Yes it's true, for the past few months we've been hard at work with a lot of other co-authors on The Definitive Guide to Drupal 7.

Agaric Backs Community Coworking Center in NYC

Thinking it would be a great place to work a day or two while in New York City for clients or DrupalCamps, Agaric dropped a few dollars in the Kickstarter fund for New Work City: Community Coworking Center for Independents in NY.

Agaric Sponsors Modulecraft for the Building of Drupal Shared business, Development, and Training Tools

For community shared business, development, and training tools, Agaric throws a little sponsorship at modulecraft.

Agaric Provides Very Minor Assist in Readying Insert Module for Drupal 7

Benjamin Melançon of Agaric helped with a patch for the Drupal 7 version of Insert module.

A round red capped mushroom with white spots.

Agaric?

What the word agaric means and why Agaric took it for our cooperative's name.

Designed to Life

Functionality designed to your life is the Agaric Design signature. Utilizing open source, free software from around the world, Agaric Design websites are impeccably crafted with a modern, sophisticated and understated spirit.

The Story on Agaric

I've always had a passion for good design and healthy coding, even back in the days of owning a web site cart in downtown Natick. Back then, my business partner and I made all natural HTML roll-up web sites and, as an incentive for customers to wait in line, we baked Drupal into different flavored designs.

Social Simple buttons

This covers 90% of use cases, but what if we need to add a button for a new network?

Creating a Custom Social Simple Button

The Social Simple module already supports custom buttons, we just need to let the module know that we want to add one.

What we need to do is:

  • Create a class that implements SocialNetworkInterface.
  • Register this class in our services file.
  • Add the tag social_simple_network to our service.

For our example we are going to create a basic Mail button. We start by creating a custom module. Inside our module let's create a Mail php file inside of the src/SocialNetwork folder:

 

mkdir -p src/SocialNetwork cd src/SocialNetwork touch Mail.php

 

The next step is to create a class and implement the SocialNetworkInterface which interface has the following methods:

  • getShareLink: This is the most important method. It must return a rendered array which later Drupal will use to create the button.
  • getLabel: Here we will need to provide the name of our button. In our case Mail.
  • getId: The ID of the button. We can choose any ID here, we just need to make sure that it is unique. Let's use mail for our example.
  • getLinkAttributes: These attributes are going to be passed to the link. We can add custom parameters to the link in this part.

Our class looks like this:


namespace Drupal\social_simple\SocialNetwork; use Drupal\Core\Entity\EntityInterface; 

use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url; 

/** * The Mail button. */
class Mail implements SocialNetworkInterface { 

use StringTranslationTrait; 

/** * The social network base share link. */
const MAIL = 'mailto:'; 

/** * {@inheritdoc} */
public function getId() {
  return 'mail';
} 

/** * {@inheritdoc} */
public function getLabel() {
  return $this->t('Mail');
} 

/** * {@inheritdoc} */
public function getShareLink($share_url, $title = '', EntityInterface $entity = NULL, array $additional_options = []) {
  $options = [
    'query' => [
      'body' => $share_url,
      'subject' => $title,
    ],
    'absolute' => TRUE,
    'external' => TRUE,
  ]; 

  if ($additional_options) {
    foreach ($additional_options as $id => $value) {
      $options['query'][$id] = $value;
    }
  }
  $url = Url::fromUri(self::MAIL, $options);
  $link = [
    'url' => $url,
    'title' => ['#markup' => '' . $this->getLabel() . ''],
    'attributes' => $this->getLinkAttributes($this->getLabel()),
  ]; return $link;
} 

/** * {@inheritdoc} */
public function getLinkAttributes($network_name) {
  $attributes = [ 'title' => $network_name, ];
  return $attributes;
  }
}

The next step is to let the social network know about our new button and we do this by adding this class as a service in our module.services.yml. If you are not familiar with this file, you can read the structure of a service file documentation..

Basically we need to add something like this:


services: 
  social_simple.mail: 
    class: Drupal\custom_module\SocialNetwork\Mail 
    tags: - { name: social_simple_network, priority: 0 }

Next, rebuild the cache. Now when we visit the social simple configuration we will see our new button there, ready to be used.

Social Simple Configuration page

The only thing that we need to pay extra attention to is that the Social Simple module will just search the services with the tag social_simple_network otherwise our class will not be found.

If you want to see how the whole thing is working, you can check this patch that I made as a part of a project: https://www.drupal.org/project/social_simple/issues/2899517. As a bonus, I made an initial integration with the Forward module.

You – our clients, colleagues, and crazed adoring fans – are the reason we do what we do. We would love to hear from you.

The Views module provides a flexible method for Drupal site builders to present data. On a recent project we needed to filter a view's result set in a way we could not achieve by means of the module's UI. How do you programmatically alter a view's result set before rendering? Let's see how to do it using the hooks provided by the module.

The need surfaced while working on the web site for MIT's Global Studies and Languages department, which uses Views to pull in data from a remote service and display it. The Views module provides a flexible method for Drupal site builders to present data. Most of the time you can configure your presentation needs through the UI using Views and Views-related contributed modules. Notwithstanding, sometimes you need to implement a specific requirement which is not available out of the box. Luckily, Views provides hooks to alter its behavior and results. Let’s see how to filter Views results before they are rendered.

Assume we have a website which aggregates book information from different sources. We store the book name, author, year of publication, and ISBN (International Standard Book Number). ISBNs are unique numerical book identifiers which can be 10 or 13 characters long. The last digit in either version is a verification number and the 13 character version has a 3-character prefix. The other numbers are the same. A book can have both versions. For example:

ISBN10:    1849511160
ISBN13: 9781849511162

In our example website, we only use one ISBN. If both versions are available, the 10-character version is discarded. We do this to prevent duplicate book entries which differ only in ISBN as shown in the following picture:

Screenshot of Views result

To remove the duplicate entries, follow this simple two step process:

  1. Find the correct Views hook to implement.
  2. Add the logic to remove unwanted results.

After reviewing the list of Views hooks, hook_views_pre_render is the one we are going to use to filter results before they are rendered. Now, let’s create a custom module to add the required logic. I have named my module views_alter_results so the hook implementation would look like this:

/**
 * Implements hook_views_pre_render().
 */
function views_alter_results_views_pre_render(&$view) {
  // Custom code.
}

The ampersand in the function parameter indicates that the View object is passed by reference. Any change we make to the object will be kept. The View object has a results property. Using the devel module, we can use dsm($view->results) to have a quick look at the results.

Screenshot of array result.

Each element in the array is a node that will be displayed in the final output. If we expand one of them, we can see more information about the node. Let’s drill down into one of the results until we get to the ISBN.

Screenshot of expanded array result.

The output will vary depending on your configuration. In this example, we have created a Book content type and added an ISBN field. Before adding the logic to filter the unwanted results, we need to make sure that this logic will only be applied for the specific view and display we are targeting. By default, hook_views_pre_render will be executed for every view and display unless otherwise instructed. We can apply this restriction as follows:

/**
 * Implements hook_views_pre_render().
 */
function views_alter_results_views_pre_render(&$view) {
  if ($view->name == 'books'
    && $view->current_display == 'page_book_list') {
    // Custom code.
  }
}

Next, the logic to filter results.

/**
 * Implements hook_views_pre_render().
 */
function views_alter_results_views_pre_render(&$view) {
  if ($view->name == 'books'
    && $view->current_display == 'page_book_list') {
    $isbn10_books = array();
    $isbn13_books = array();
    $remove_books = array();

    foreach ($view->result as $index => $value) {
      $isbn = $value->field_field_isbn[0]['raw']['value'];
      if (strlen($isbn) === 10) {
        // [184951116]0.
        $isbn10_books[$index] = substr($isbn, 0, 9);
      }
      elseif (strlen($isbn) === 13) {
        // 978[184951116]2.
        $isbn13_books[$index] = substr($isbn, 3, 9);
      }
    }

    // Find books that have both ISBN10 and ISBN13 entries.
    $remove_books = array_intersect($isbn10_books, $isbn13_books);

    // Remove repeated books.
    foreach ($remove_books as $index => $value) {
      unset($view->result[$index]);
    }
  }
}

To filter the results we use unset on $view->result. After this process, the result property of the view object will look like this:

Screenshot of array after unset action.

And our view will display without duplicates book entries as seen here:

Screenshot of view after being overriden by code.

Before wrapping up, I’d like to share two modules that might help you achieve similar results: Views Merge Rows and Views Distinct. Every use case is different, if neither of these modules gets you where you want to be, you can leverage hook_views_pre_render to implement your custom requirements.

Update #1 Tue, 06/02/2015

As indicated by Leon and efpapado this approach only works for views that present all results in a single page. That was the original use case. The altering presented here only affects the current page and the pager will not work as expected.

Knowledge is not power.  Power is coordination.

Scaling Community Conversations and Decisions

presentation @ FluConf 2025

The grocery store was open for a brief time, but it was never a cooperative. I know, because I joined as a member the day it first opened in 2017, on August 11. (I first e-mailed to ask about becoming a member ten months earlier, but that required meeting in person and this was the first time it was feasible.)

On 2018, June 12, after Wirth Cooperative Grocery had been closed for two months, I received my only formal communication as a member owner: an e-mail acknowledging that the store was closed, noting that the current grocery market is extremely competitive, and saying they had plans to re-open by the end of the month.

The e-mail did not ask for decisions. It did not ask for volunteers or help of any kind. While addressed to us as member owners, it did not afford us any opportunity to affect the future of the store. It did not provide any information on which we might try to act. Instead it told us to wait to be notified when an opening date was set. An opening date was never set.

Although I'm certain some staff and volunteers worked hard behind the scenes, from my perspective as a member owner the grocery store went down without a fight.

That's why it's so important for cooperatives to be true cooperatives. The seven cooperative principles aren't a "pick any three" sort of deal.

The first principle specifies that membership in a cooperative be open to all people willing to accept the responsibilities, but a person cannot accept responsibilities which aren't offered.

The second principle is democratic member control, but people cannot exercise direct control or hold representatives accountable without information and without a means to communicate with one another.

Likewise for the next three cooperative principles: the third, that members democratically control capital; the fourth, that external agreements preserve democratic control; and the fifth, that a cooperative educate, train, and inform members so they can contribute to it effectively. An organization with no mechanisms for discussion nor for democracy violates these principles, too.

Principles six and seven, cooperation among cooperatives and concern for community, are likely to be hollow without functioning internal democracy— and certainly cannot be realized if the business fails.

A cooperative can't exist only on good intentions.

When I hear this sentiment expressed by experienced cooperators—founders and developers of cooperatives—it usually means that there needs to be a solid business model for a cooperative, because a cooperative that isn't economically viable can't fulfill any of its goals.

A more fundamental meaning is that a business can't be a cooperative if it merely intends to be; it must act like a cooperative. Otherwise, it's just another business with some co-op lip service gloss— and given the greater success of cooperatives compared to other businesses it's less likely to be around as a business at all, if it does not live up to its cooperative principles.

I'm not trying to use technicalities to dodge counting the failed Wirth grocery store as a failure for "team cooperative". On the contrary, this is a wakeup call for everybody who supports cooperatives, one that must rouse us, because fully-functioning cooperatives are bigger than the cooperative movement. Cooperatives can prefigure true democracy. We need to show that economic democracy works in our voluntary organizations; we need to give people in a world which is already ruled unjustly, and threatening to spiral out of control, a promise and a practice for how whole communities can take collective control.

In my experience as a member owner, Wirth Cooperative Grocery was not a co-op beyond its name and some vague intentions. Now I know that my experience matched everyone else's, thanks to Cirien Saadeh's reporting, both last year and in the most recent issue of North News (an invaluable local non-profit enterprise).

What worries me, then, is that no one quoted in these articles called out this failure to meet the basic requirements of being a cooperative. Minneapolis and Minnesota have perhaps the highest rate of cooperative membership in the United States, with about half of all residents estimated to belong to at least one cooperative of one kind or another. If we, here, don't have the awareness and interest to note when a cooperative doesn't act like a cooperative, who will?

More important than calling out failures is providing pathways to success. There are many programs and organizations supporting cooperatives in Minnesota and beyond, but none put ensuring member control first.

The bias of my profession and my passion is to lead with an online tool: ensure member owners can communicate with one another. Although a technological fix can't solve political problems, people who are affected need a way to talk among themselves to come up with a solution.

Efforts like a local cooperative grocery are small enough that a Loomio group or any mailing list would mostly work for member owner discussion. A better software application would work for collaborative groups of any size: members would filter messages for quality without ceding control to any outside group. This self-moderation for groups of equals, including cooperatives, is a goal of Visions Unite.

Are you in a position to provide resources to cooperatives and other groups seeking to grow and be democratic? Are you starting or do you want to start a cooperative or group? Do you have other ideas on how to help new and established cooperatives live by the foundational cooperative principles? I would like to hear from you!

Tell us how you would like to use the online learning platform. The more details you can give, the easier it will be for us to help your mission!