Drupal News

Annertech: 8 Benefits to Implementing a Technical SEO Strategy

Main Drupal Feed - Mon, 04/26/2021 - 08:04

Implementing a technical SEO strategy means you can automate much of what it takes to make sure your content performs at its best.

Annertech: 8 Benefits to Implementing a Technical SEO Strategy

Main Drupal Feed - Mon, 04/26/2021 - 08:04

Implementing a technical SEO strategy means you can automate much of what it takes to make sure your content performs at its best.

Annertech: 8 Benefits to Implementing a Technical SEO Strategy

Main Drupal Feed - Mon, 04/26/2021 - 08:04

Implementing a technical SEO strategy means you can automate much of what it takes to make sure your content performs at its best.

hussainweb.me: Remote Drupal Development

Main Drupal Feed - Mon, 04/26/2021 - 03:39
Over the years, I have significantly lesser time for development and an increasing need to move around. After dealing with back-pain due to the weight of my Dell laptop while travelling for conferences, I bought a 15" MacBook Pro. More recently, with the issues with Docker performance on Mac, I have been thinking of getting a Linux box. I was further motivated when I bought an iPad last year. Now, with my old MacBook Pro failing because of the keyboard and hard disk, I have a new MBP with the M1 chip and just 8 GB RAM. I am more and more interested in making remote development work efficiently for me.

hussainweb.me: Remote Drupal Development

Main Drupal Feed - Mon, 04/26/2021 - 03:39
Over the years, I have significantly lesser time for development and an increasing need to move around. After dealing with back-pain due to the weight of my Dell laptop while travelling for conferences, I bought a 15" MacBook Pro. More recently, with the issues with Docker performance on Mac, I have been thinking of getting a Linux box. I was further motivated when I bought an iPad last year. Now, with my old MacBook Pro failing because of the keyboard and hard disk, I have a new MBP with the M1 chip and just 8 GB RAM. I am more and more interested in making remote development work efficiently for me.

hussainweb.me: Altering forms in Drupal: a workable mixed approach

Main Drupal Feed - Sun, 04/25/2021 - 03:17
I thought I was done with the series of posts on object-oriented programming after my last one. Of course, there is a lot we can write about object-oriented programming and Drupal, but that post covers everything noteworthy from the example. There is a way which is old-school but works as it should and another which looks modern but comes with problems. Is there a middle-ground? Tim Plunkett responded on Twitter saying there is.

hussainweb.me: Altering forms in Drupal: a workable mixed approach

Main Drupal Feed - Sun, 04/25/2021 - 03:17
I thought I was done with the series of posts on object-oriented programming after my last one. Of course, there is a lot we can write about object-oriented programming and Drupal, but that post covers everything noteworthy from the example. There is a way which is old-school but works as it should and another which looks modern but comes with problems. Is there a middle-ground? Tim Plunkett responded on Twitter saying there is.

hussainweb.me: Altering forms in Drupal: a workable mixed approach

Main Drupal Feed - Sun, 04/25/2021 - 03:17
I thought I was done with the series of posts on object-oriented programming after my last one. Of course, there is a lot we can write about object-oriented programming and Drupal, but that post covers everything noteworthy from the example. There is a way which is old-school but works as it should and another which looks modern but comes with problems. Is there a middle-ground? Tim Plunkett responded on Twitter saying there is.

hussainweb.me: Altering forms in Drupal: Comparing hooks and object-oriented approaches

Main Drupal Feed - Sat, 04/24/2021 - 03:55
I previously wrote about how the object-oriented style of programming can be seen as a solution to all programming problems. There is a saying: if all you have is a hammer, everything looks like a nail. It is not a stretch to say that object-oriented programming is the hammer in this adage. That post was quite abstract and today I want to share a more specific example of what I mean. More specifically, I'll talk about how using "objects" to alter forms without thinking it through can cause harm.

hussainweb.me: Altering forms in Drupal: Comparing hooks and object-oriented approaches

Main Drupal Feed - Sat, 04/24/2021 - 03:55
I previously wrote about how the object-oriented style of programming can be seen as a solution to all programming problems. There is a saying: if all you have is a hammer, everything looks like a nail. It is not a stretch to say that object-oriented programming is the hammer in this adage. That post was quite abstract and today I want to share a more specific example of what I mean. More specifically, I'll talk about how using "objects" to alter forms without thinking it through can cause harm.

hussainweb.me: Altering forms in Drupal: Comparing hooks and object-oriented approaches

Main Drupal Feed - Sat, 04/24/2021 - 03:55
I previously wrote about how the object-oriented style of programming can be seen as a solution to all programming problems. There is a saying: if all you have is a hammer, everything looks like a nail. It is not a stretch to say that object-oriented programming is the hammer in this adage. That post was quite abstract and today I want to share a more specific example of what I mean. More specifically, I'll talk about how using "objects" to alter forms without thinking it through can cause harm.

Aten Design Group: Cascading permissions for complex content relationships in Drupal 8

Main Drupal Feed - Fri, 04/23/2021 - 20:37
Cascading permissions for complex content relationships in Drupal 8 Jordan Graham Fri, 04/23/2021 - 14:37 Drupal

Aten loves libraries. We’ve built a range of software solutions for libraries all over the country, including the John D. Rockefeller Jr. Library in Williamsburg, VA, the Denver Public Library in our own Denver county, the Richland Library in South Carolina, Nashville Public Library in Tennessee and the Reaching Across Illinois Library System (“RAILS”). It’s remarkable just how many commonalities libraries share when it comes to the features, tools, and considerations their websites need to better serve their users.

Some of those similarities are a no-contest justification for building common, configurable library solutions — solutions like Intercept: a reimagined, generalized approach to library event management, room and equipment reservation, and customer tracking. We co-developed Intercept for Drupal with Richland Library, reused it with L2 / RAILS and actively maintain it in the hopes that it goes on serving libraries for years to come. But for all of the similarities we see between libraries and their digital needs, there are some key differences, too.

Complex permissions needs

Library Directory and Learning Calendar (“L2”) — a project of RAILS — had unique permissions needs. Their staff needed custom view, edit, and delete permissions to website content managed at different organizational levels of the library system. Those organizational levels, structured hierarchically from Illinois State Library, to Regional Library System, through Catalog Consortium, Agency and finally Location, needed associated cascading permissions — i.e., permissions granted at a higher organizational level should cascade to associated content in lower organizational levels. An administrator for an Illinois State Library piece of content, for example, should be able to exercise their administrative permissions on Regional, Consortium, Agency, and Location content associated with that State Library content. An administrator for an Agency would have administrative permissions for that Agency’s Locations.

Granular role based Drupal permissions wouldn’t cut it. That’s because with standard Drupal permissions, each user role can be assigned view, edit, and delete permissions to each content type (say any Regional Library System Page), but we needed to assign those permissions to the appropriate instances of a content type — like the specific Regional Library System Page that belongs to the west-central region, for example.

There are plenty of contributed modules that start down the right path towards (for lack of a better term) cascading permissions by content affiliation, but they wouldn’t have gotten us all the way there. Both the Group and Permissions by term modules, for example, can be incredibly useful in similar situations. In this case, given the features and functionality contributed modules would introduce that we don’t need, plus the level of modification necessary to achieve our goals, we decided on a lightweight, custom solution.

Role and affiliation based custom permissions for L2

Permissions for L2 staff are established using a custom affiliation entity, which stores data about the role a particular user has in relation to a specific piece of content. The custom affiliation entity references a user, a role (a taxonomy term like Admin, Manager, or Staff, for example), and a specific piece of content (a node). A variety of other fields are established in the same affiliation entity in order to store additional metadata about the relationship like contact information, job title, job description, or other details.

Affiliation, Role / Access Group, and User fields establish a permission relationship. Metadata fields (blurred here) can provide some arbitrary data about the relationship.

The custom affiliation entities are organized into their own bundles, one for each of the hierarchically structured organizational levels previously described: Illinois State Library, Regional Library System, Catalog Consortium, Agency, and finally Location. This way each individual type of affiliation can contain the metadata fields appropriate for its specific organizational level. Finally, there is an arbitrary Group affiliation which affiliates a user with a piece of content without granting the cascading permissions that accompany standard affiliations.

Each organizational level is represented by its own custom affiliation entity type.

The content items (read nodes) whose permissions we’re controlling are organized along the same organizational levels: Illinois State Library, Regional Library System, etc. Each of these content types uses a unique entity reference field to establish a parent / child relationship along the organizational levels. Locations are associated with Agencies, Catalog Consortia, Regional Library Systems or directly with the Illinois State Library; Agencies are associated with other Agencies and / or Regional Library Systems; Catalog Consortia are associated with Regional Library Systems; and Regional Library Systems with the single parent Illinois State Library. It’s a complicated web!

Permissions granted on content at any one organizational level cascade to associated content in the lower levels.

Once the content for L2 was developed and properly structured with the appropriate parent / child relationships, granting a user specific view, edit, and delete permissions for a particular region of the structured content tree was simple. Simply create an affiliation that assigns the user a role in relation to content at a specific level of the organization, and voila! — the user gains access to that content and all of its children at each of the lower levels.

The permission grid: Tying it all together

One last element ties our whole permissions system together: a robust permissions map that associates view, edit, and delete permissions per custom defined Role / Access Group in relation to various entities. Unlike the unwieldy Drupal permissions grid that assigns roles to broadly defined permissions with the help of about a million radio buttons, our definitions can be static (think code, not GUI) and only have to deal with view, edit, and delete permissions for entities or entity fields. Each Role / Access Group has its view, edit and delete permissions defined per bundle or per specific bundle / field combination, resulting in very cleancut — and extremely granular — permission control.

[ { "entity": "node", // Entity type we're granting access to "field": "", // Field for this entity, left blank we're defining with entity level access "affiliation bundle": "location", // Affiliation entity that controls access, in this case the location affiliation type "target bundle": "location", // Entity bundle we're granting access to, in this case a location node "role_access_group": "location_manager", // Custom defined Role / Access Group that grants this permission "edit": 1, // Edit permission "view": "", // View permission "delete": "" // Delete permission } ]

Our “permissions grid” is made up of about 500 similar declarations in a single JSON file, which range through a variety of Roles / Access Groups, a couple of entity types, and tons of bundle / field combinations for some of the more complex field level permissions.

Individual permissions grants are then handled through hook_ENTITY_TYPE_access() and hook_entity_field_access(), which use a a custom Service to load all of the requesting user’s affiliation entities, determine their role in relation to the content (node) in question, then find that role’s particular permissions using our custom JSON permissions map. Here’s an example for node access.

/** * Determines if the operation is allowed for a node. * * @param object $relationship * The relationship object. * @param string $operation * The operation being attempted. * @param Drupal\node\Entity\Node $node * The node on which the operation is being attempted. * * @return bool * True if the operation is allowed. */ public function nodePermission(object $relationship, $operation, Node $node) { // Create an array of data from l2_access_permissions.json, each element // of which is an array with elements matching $relationship object // properties. $module_path = drupal_get_path('module', 'l2_access'); $matrix = json_decode(file_get_contents($module_path . '/l2_access_permissions.json'), TRUE);   // Create an array matching the structure of $matrix elements to see if // it matches any $matrix elements (which would mean that there might be // a permission that allows this $operation.) $relationship_array = [ 'entity' => 'node', 'field' => '', 'affiliation bundle' => $relationship->affiliation_bundle, 'target bundle' => $relationship->target_bundle, 'role_access_group' => $relationship->role_access_group, ];   // Set the $relationship_array's 'edit' and 'view' elements based on the // $operation's value. $operation = ($operation == 'update') ? 'edit' : $operation; $operations = ['view', 'edit', 'delete']; foreach ($operations as $op) { $relationship_array[$op] = ($op == $operation) ? 1 : ""; }   // Handy here that array_search() can test whether an array is an element // in another array. Here: is $relationship_array an element of $matrix? $match = array_search($relationship_array, $matrix); if (!$match) { return FALSE; }   // Found a match. Does it allow access? switch ($operation) { case 'edit': if ($matrix[$match]['edit'] == 1) { return TRUE; } break;   case 'update': if ($matrix[$match]['edit'] == 1) { return TRUE; } break;   case 'view': if ($matrix[$match]['view'] == 1) { return TRUE; } break; }   // If we're here, this $relationship doesn't provide $operation access. return FALSE; }

The end result is powerful and flexible. Our JSON permissions map tells us which Roles / Access Groups have which permissions per entity or entity / field combination. The custom affiliation entities grant users a specific Role / Access Group in relation to a specific piece of content, and that content’s entity references to other entities allow the permissions to cascade to entities in lower organizational levels.

That may sound like a lot, but it’s surprisingly simple. The solution boils down to a handful of entity access hook implementations that use a custom service to lookup the current user’s permissions by affiliation via a JSON permissions map. The total footprint sits at around 1000 lines of code — not counting the permissions map itself — and flexibly manages thousands of users’ permissions across thousands of complex, hierarchical content relationships down to the field level. Pretty neat.

Jordan Graham

Aten Design Group: Cascading permissions for complex content relationships in Drupal 8

Main Drupal Feed - Fri, 04/23/2021 - 20:37
Cascading permissions for complex content relationships in Drupal 8 Jordan Graham Fri, 04/23/2021 - 14:37 Drupal

Aten loves libraries. We’ve built a range of software solutions for libraries all over the country, including the John D. Rockefeller Jr. Library in Williamsburg, VA, the Denver Public Library in our own Denver county, the Richland Library in South Carolina, Nashville Public Library in Tennessee and the Reaching Across Illinois Library System (“RAILS”). It’s remarkable just how many commonalities libraries share when it comes to the features, tools, and considerations their websites need to better serve their users.

Some of those similarities are a no-contest justification for building common, configurable library solutions — solutions like Intercept: a reimagined, generalized approach to library event management, room and equipment reservation, and customer tracking. We co-developed Intercept for Drupal with Richland Library, reused it with L2 / RAILS and actively maintain it in the hopes that it goes on serving libraries for years to come. But for all of the similarities we see between libraries and their digital needs, there are some key differences, too.

Complex permissions needs

Library Directory and Learning Calendar (“L2”) — a project of RAILS — had unique permissions needs. Their staff needed custom view, edit, and delete permissions to website content managed at different organizational levels of the library system. Those organizational levels, structured hierarchically from Illinois State Library, to Regional Library System, through Catalog Consortium, Agency and finally Location, needed associated cascading permissions — i.e., permissions granted at a higher organizational level should cascade to associated content in lower organizational levels. An administrator for an Illinois State Library piece of content, for example, should be able to exercise their administrative permissions on Regional, Consortium, Agency, and Location content associated with that State Library content. An administrator for an Agency would have administrative permissions for that Agency’s Locations.

Granular role based Drupal permissions wouldn’t cut it. That’s because with standard Drupal permissions, each user role can be assigned view, edit, and delete permissions to each content type (say any Regional Library System Page), but we needed to assign those permissions to the appropriate instances of a content type — like the specific Regional Library System Page that belongs to the west-central region, for example.

There are plenty of contributed modules that start down the right path towards (for lack of a better term) cascading permissions by content affiliation, but they wouldn’t have gotten us all the way there. Both the Group and Permissions by term modules, for example, can be incredibly useful in similar situations. In this case, given the features and functionality contributed modules would introduce that we don’t need, plus the level of modification necessary to achieve our goals, we decided on a lightweight, custom solution.

Role and affiliation based custom permissions for L2

Permissions for L2 staff are established using a custom affiliation entity, which stores data about the role a particular user has in relation to a specific piece of content. The custom affiliation entity references a user, a role (a taxonomy term like Admin, Manager, or Staff, for example), and a specific piece of content (a node). A variety of other fields are established in the same affiliation entity in order to store additional metadata about the relationship like contact information, job title, job description, or other details.

Affiliation, Role / Access Group, and User fields establish a permission relationship. Metadata fields (blurred here) can provide some arbitrary data about the relationship.

The custom affiliation entities are organized into their own bundles, one for each of the hierarchically structured organizational levels previously described: Illinois State Library, Regional Library System, Catalog Consortium, Agency, and finally Location. This way each individual type of affiliation can contain the metadata fields appropriate for its specific organizational level. Finally, there is an arbitrary Group affiliation which affiliates a user with a piece of content without granting the cascading permissions that accompany standard affiliations.

Each organizational level is represented by its own custom affiliation entity type.

The content items (read nodes) whose permissions we’re controlling are organized along the same organizational levels: Illinois State Library, Regional Library System, etc. Each of these content types uses a unique entity reference field to establish a parent / child relationship along the organizational levels. Locations are associated with Agencies, Catalog Consortia, Regional Library Systems or directly with the Illinois State Library; Agencies are associated with other Agencies and / or Regional Library Systems; Catalog Consortia are associated with Regional Library Systems; and Regional Library Systems with the single parent Illinois State Library. It’s a complicated web!

Permissions granted on content at any one organizational level cascade to associated content in the lower levels.

Once the content for L2 was developed and properly structured with the appropriate parent / child relationships, granting a user specific view, edit, and delete permissions for a particular region of the structured content tree was simple. Simply create an affiliation that assigns the user a role in relation to content at a specific level of the organization, and voila! — the user gains access to that content and all of its children at each of the lower levels.

The permission grid: Tying it all together

One last element ties our whole permissions system together: a robust permissions map that associates view, edit, and delete permissions per custom defined Role / Access Group in relation to various entities. Unlike the unwieldy Drupal permissions grid that assigns roles to broadly defined permissions with the help of about a million radio buttons, our definitions can be static (think code, not GUI) and only have to deal with view, edit, and delete permissions for entities or entity fields. Each Role / Access Group has its view, edit and delete permissions defined per bundle or per specific bundle / field combination, resulting in very cleancut — and extremely granular — permission control.

[ { "entity": "node", // Entity type we're granting access to "field": "", // Field for this entity, left blank we're defining with entity level access "affiliation bundle": "location", // Affiliation entity that controls access, in this case the location affiliation type "target bundle": "location", // Entity bundle we're granting access to, in this case a location node "role_access_group": "location_manager", // Custom defined Role / Access Group that grants this permission "edit": 1, // Edit permission "view": "", // View permission "delete": "" // Delete permission } ]

Our “permissions grid” is made up of about 500 similar declarations in a single JSON file, which range through a variety of Roles / Access Groups, a couple of entity types, and tons of bundle / field combinations for some of the more complex field level permissions.

Individual permissions grants are then handled through hook_ENTITY_TYPE_access() and hook_entity_field_access(), which use a a custom Service to load all of the requesting user’s affiliation entities, determine their role in relation to the content (node) in question, then find that role’s particular permissions using our custom JSON permissions map. Here’s an example for node access.

/** * Determines if the operation is allowed for a node. * * @param object $relationship * The relationship object. * @param string $operation * The operation being attempted. * @param Drupal\node\Entity\Node $node * The node on which the operation is being attempted. * * @return bool * True if the operation is allowed. */ public function nodePermission(object $relationship, $operation, Node $node) { // Create an array of data from l2_access_permissions.json, each element // of which is an array with elements matching $relationship object // properties. $module_path = drupal_get_path('module', 'l2_access'); $matrix = json_decode(file_get_contents($module_path . '/l2_access_permissions.json'), TRUE);   // Create an array matching the structure of $matrix elements to see if // it matches any $matrix elements (which would mean that there might be // a permission that allows this $operation.) $relationship_array = [ 'entity' => 'node', 'field' => '', 'affiliation bundle' => $relationship->affiliation_bundle, 'target bundle' => $relationship->target_bundle, 'role_access_group' => $relationship->role_access_group, ];   // Set the $relationship_array's 'edit' and 'view' elements based on the // $operation's value. $operation = ($operation == 'update') ? 'edit' : $operation; $operations = ['view', 'edit', 'delete']; foreach ($operations as $op) { $relationship_array[$op] = ($op == $operation) ? 1 : ""; }   // Handy here that array_search() can test whether an array is an element // in another array. Here: is $relationship_array an element of $matrix? $match = array_search($relationship_array, $matrix); if (!$match) { return FALSE; }   // Found a match. Does it allow access? switch ($operation) { case 'edit': if ($matrix[$match]['edit'] == 1) { return TRUE; } break;   case 'update': if ($matrix[$match]['edit'] == 1) { return TRUE; } break;   case 'view': if ($matrix[$match]['view'] == 1) { return TRUE; } break; }   // If we're here, this $relationship doesn't provide $operation access. return FALSE; }

The end result is powerful and flexible. Our JSON permissions map tells us which Roles / Access Groups have which permissions per entity or entity / field combination. The custom affiliation entities grant users a specific Role / Access Group in relation to a specific piece of content, and that content’s entity references to other entities allow the permissions to cascade to entities in lower organizational levels.

That may sound like a lot, but it’s surprisingly simple. The solution boils down to a handful of entity access hook implementations that use a custom service to lookup the current user’s permissions by affiliation via a JSON permissions map. The total footprint sits at around 1000 lines of code — not counting the permissions map itself — and flexibly manages thousands of users’ permissions across thousands of complex, hierarchical content relationships down to the field level. Pretty neat.

Jordan Graham

Aten Design Group: Cascading permissions for complex content relationships in Drupal 8

Main Drupal Feed - Fri, 04/23/2021 - 20:37
Cascading permissions for complex content relationships in Drupal 8 Jordan Graham Fri, 04/23/2021 - 14:37 Drupal

Aten loves libraries. We’ve built a range of software solutions for libraries all over the country, including the John D. Rockefeller Jr. Library in Williamsburg, VA, the Denver Public Library in our own Denver county, the Richland Library in South Carolina, Nashville Public Library in Tennessee and the Reaching Across Illinois Library System (“RAILS”). It’s remarkable just how many commonalities libraries share when it comes to the features, tools, and considerations their websites need to better serve their users.

Some of those similarities are a no-contest justification for building common, configurable library solutions — solutions like Intercept: a reimagined, generalized approach to library event management, room and equipment reservation, and customer tracking. We co-developed Intercept for Drupal with Richland Library, reused it with L2 / RAILS and actively maintain it in the hopes that it goes on serving libraries for years to come. But for all of the similarities we see between libraries and their digital needs, there are some key differences, too.

Complex permissions needs

Library Directory and Learning Calendar (“L2”) — a project of RAILS — had unique permissions needs. Their staff needed custom view, edit, and delete permissions to website content managed at different organizational levels of the library system. Those organizational levels, structured hierarchically from Illinois State Library, to Regional Library System, through Catalog Consortium, Agency and finally Location, needed associated cascading permissions — i.e., permissions granted at a higher organizational level should cascade to associated content in lower organizational levels. An administrator for an Illinois State Library piece of content, for example, should be able to exercise their administrative permissions on Regional, Consortium, Agency, and Location content associated with that State Library content. An administrator for an Agency would have administrative permissions for that Agency’s Locations.

Granular role based Drupal permissions wouldn’t cut it. That’s because with standard Drupal permissions, each user role can be assigned view, edit, and delete permissions to each content type (say any Regional Library System Page), but we needed to assign those permissions to the appropriate instances of a content type — like the specific Regional Library System Page that belongs to the west-central region, for example.

There are plenty of contributed modules that start down the right path towards (for lack of a better term) cascading permissions by content affiliation, but they wouldn’t have gotten us all the way there. Both the Group and Permissions by term modules, for example, can be incredibly useful in similar situations. In this case, given the features and functionality contributed modules would introduce that we don’t need, plus the level of modification necessary to achieve our goals, we decided on a lightweight, custom solution.

Role and affiliation based custom permissions for L2

Permissions for L2 staff are established using a custom affiliation entity, which stores data about the role a particular user has in relation to a specific piece of content. The custom affiliation entity references a user, a role (a taxonomy term like Admin, Manager, or Staff, for example), and a specific piece of content (a node). A variety of other fields are established in the same affiliation entity in order to store additional metadata about the relationship like contact information, job title, job description, or other details.

Affiliation, Role / Access Group, and User fields establish a permission relationship. Metadata fields (blurred here) can provide some arbitrary data about the relationship.

The custom affiliation entities are organized into their own bundles, one for each of the hierarchically structured organizational levels previously described: Illinois State Library, Regional Library System, Catalog Consortium, Agency, and finally Location. This way each individual type of affiliation can contain the metadata fields appropriate for its specific organizational level. Finally, there is an arbitrary Group affiliation which affiliates a user with a piece of content without granting the cascading permissions that accompany standard affiliations.

Each organizational level is represented by its own custom affiliation entity type.

The content items (read nodes) whose permissions we’re controlling are organized along the same organizational levels: Illinois State Library, Regional Library System, etc. Each of these content types uses a unique entity reference field to establish a parent / child relationship along the organizational levels. Locations are associated with Agencies, Catalog Consortia, Regional Library Systems or directly with the Illinois State Library; Agencies are associated with other Agencies and / or Regional Library Systems; Catalog Consortia are associated with Regional Library Systems; and Regional Library Systems with the single parent Illinois State Library. It’s a complicated web!

Permissions granted on content at any one organizational level cascade to associated content in the lower levels.

Once the content for L2 was developed and properly structured with the appropriate parent / child relationships, granting a user specific view, edit, and delete permissions for a particular region of the structured content tree was simple. Simply create an affiliation that assigns the user a role in relation to content at a specific level of the organization, and voila! — the user gains access to that content and all of its children at each of the lower levels.

The permission grid: Tying it all together

One last element ties our whole permissions system together: a robust permissions map that associates view, edit, and delete permissions per custom defined Role / Access Group in relation to various entities. Unlike the unwieldy Drupal permissions grid that assigns roles to broadly defined permissions with the help of about a million radio buttons, our definitions can be static (think code, not GUI) and only have to deal with view, edit, and delete permissions for entities or entity fields. Each Role / Access Group has its view, edit and delete permissions defined per bundle or per specific bundle / field combination, resulting in very cleancut — and extremely granular — permission control.

[ { "entity": "node", // Entity type we're granting access to "field": "", // Field for this entity, left blank we're defining with entity level access "affiliation bundle": "location", // Affiliation entity that controls access, in this case the location affiliation type "target bundle": "location", // Entity bundle we're granting access to, in this case a location node "role_access_group": "location_manager", // Custom defined Role / Access Group that grants this permission "edit": 1, // Edit permission "view": "", // View permission "delete": "" // Delete permission } ]

Our “permissions grid” is made up of about 500 similar declarations in a single JSON file, which range through a variety of Roles / Access Groups, a couple of entity types, and tons of bundle / field combinations for some of the more complex field level permissions.

Individual permissions grants are then handled through hook_ENTITY_TYPE_access() and hook_entity_field_access(), which use a a custom Service to load all of the requesting user’s affiliation entities, determine their role in relation to the content (node) in question, then find that role’s particular permissions using our custom JSON permissions map. Here’s an example for node access.

/** * Determines if the operation is allowed for a node. * * @param object $relationship * The relationship object. * @param string $operation * The operation being attempted. * @param Drupal\node\Entity\Node $node * The node on which the operation is being attempted. * * @return bool * True if the operation is allowed. */ public function nodePermission(object $relationship, $operation, Node $node) { // Create an array of data from l2_access_permissions.json, each element // of which is an array with elements matching $relationship object // properties. $module_path = drupal_get_path('module', 'l2_access'); $matrix = json_decode(file_get_contents($module_path . '/l2_access_permissions.json'), TRUE);   // Create an array matching the structure of $matrix elements to see if // it matches any $matrix elements (which would mean that there might be // a permission that allows this $operation.) $relationship_array = [ 'entity' => 'node', 'field' => '', 'affiliation bundle' => $relationship->affiliation_bundle, 'target bundle' => $relationship->target_bundle, 'role_access_group' => $relationship->role_access_group, ];   // Set the $relationship_array's 'edit' and 'view' elements based on the // $operation's value. $operation = ($operation == 'update') ? 'edit' : $operation; $operations = ['view', 'edit', 'delete']; foreach ($operations as $op) { $relationship_array[$op] = ($op == $operation) ? 1 : ""; }   // Handy here that array_search() can test whether an array is an element // in another array. Here: is $relationship_array an element of $matrix? $match = array_search($relationship_array, $matrix); if (!$match) { return FALSE; }   // Found a match. Does it allow access? switch ($operation) { case 'edit': if ($matrix[$match]['edit'] == 1) { return TRUE; } break;   case 'update': if ($matrix[$match]['edit'] == 1) { return TRUE; } break;   case 'view': if ($matrix[$match]['view'] == 1) { return TRUE; } break; }   // If we're here, this $relationship doesn't provide $operation access. return FALSE; }

The end result is powerful and flexible. Our JSON permissions map tells us which Roles / Access Groups have which permissions per entity or entity / field combination. The custom affiliation entities grant users a specific Role / Access Group in relation to a specific piece of content, and that content’s entity references to other entities allow the permissions to cascade to entities in lower organizational levels.

That may sound like a lot, but it’s surprisingly simple. The solution boils down to a handful of entity access hook implementations that use a custom service to lookup the current user’s permissions by affiliation via a JSON permissions map. The total footprint sits at around 1000 lines of code — not counting the permissions map itself — and flexibly manages thousands of users’ permissions across thousands of complex, hierarchical content relationships down to the field level. Pretty neat.

Jordan Graham

Drupal Core News: Drupal 9.2.0-alpha1 will be released the week of May 3rd

Main Drupal Feed - Fri, 04/23/2021 - 19:30

In preparation for the minor release, Drupal 9.2.x will enter the alpha phase the week of May 3rd 2021. Core developers should plan to complete changes that are only allowed in minor releases prior to the alpha release. The 9.2.0-alpha1 deadline for most core patches is April 30. (More information on alpha and beta releases.)

  • Developers and site owners can begin testing the alpha after its release.

  • The 9.3.x branch of core will be created, and future feature and API additions will be targeted against that branch instead of 9.2.x. All outstanding issues filed against 9.2.x will be automatically migrated to 9.3.x.

  • Once 9.3.x is branched, alpha experimental modules will be removed from the 9.2.x codebase (so their development will continue in 9.3.x only).

  • All issues filed against 9.1.x will then be migrated to 9.2.x, and subsequent bug reports should be targeted against the 9.2.x branch.

  • During the alpha phase, core issues will be committed according to the following policy:

    1. Most issues that are allowed for patch releases will be committed to 9.2.x and 9.3.x.
    2. Most issues that are only allowed in minor releases will be committed to 9.3.x only. A few strategic issues may be backported to 9.2.x, but only at committer discretion after the issue is fixed in 9.3.x (so leave them set to 9.3.x unless you are a committer), and only up until the beta deadline.

Roughly two weeks after the alpha release, the first beta release will be created. All the restrictions of the alpha release apply to beta releases as well. The release of the first beta is a firm deadline for all feature and API additions. Even if an issue is pending in the Reviewed & Tested by the Community (RTBC) queue when the commit freeze for the beta begins, it will be committed to the next minor release only.

The release candidate phase will begin the week of May 31. See the summarized key dates in the release cycle, allowed changes during the Drupal 8 and Drupal 9 release cycles, and Drupal 8 and 9 backwards compatibility and internal API policy for more information.

The scheduled release date of Drupal 9.2.0 is June 16 2021.

Bugfix and security support of Drupal 9.1.x, 9.0.x, and 8.9.x

Security coverage for Drupal 8 and 9 is generally provided for the previous minor release as well as the newest minor release. However, Drupal 8.9.x is a Long-Term Support release where support is provided until November 2021. The following changes are upcoming:

Drupal 8.9.x Security releases will be provided until November 2021. Bugfix support is restricted to selected low-disruption major and critical bug-fixes. Drupal 9.1.x. Normal bugfix support ends on June 16, 2021. However, security releases are provided until the release of Drupal 9.3.0 on December 8, 2021. Drupal 9.0.x. Security releases are provided until the release of Drupal 9.2.0 on June 2, 2021.

Drupal Core News: Drupal 9.2.0-alpha1 will be released the week of May 3rd

Main Drupal Feed - Fri, 04/23/2021 - 19:30

In preparation for the minor release, Drupal 9.2.x will enter the alpha phase the week of May 3rd 2021. Core developers should plan to complete changes that are only allowed in minor releases prior to the alpha release. The 9.2.0-alpha1 deadline for most core patches is April 30. (More information on alpha and beta releases.)

  • Developers and site owners can begin testing the alpha after its release.

  • The 9.3.x branch of core will be created, and future feature and API additions will be targeted against that branch instead of 9.2.x. All outstanding issues filed against 9.2.x will be automatically migrated to 9.3.x.

  • Once 9.3.x is branched, alpha experimental modules will be removed from the 9.2.x codebase (so their development will continue in 9.3.x only).

  • All issues filed against 9.1.x will then be migrated to 9.2.x, and subsequent bug reports should be targeted against the 9.2.x branch.

  • During the alpha phase, core issues will be committed according to the following policy:

    1. Most issues that are allowed for patch releases will be committed to 9.2.x and 9.3.x.
    2. Most issues that are only allowed in minor releases will be committed to 9.3.x only. A few strategic issues may be backported to 9.2.x, but only at committer discretion after the issue is fixed in 9.3.x (so leave them set to 9.3.x unless you are a committer), and only up until the beta deadline.

Roughly two weeks after the alpha release, the first beta release will be created. All the restrictions of the alpha release apply to beta releases as well. The release of the first beta is a firm deadline for all feature and API additions. Even if an issue is pending in the Reviewed & Tested by the Community (RTBC) queue when the commit freeze for the beta begins, it will be committed to the next minor release only.

The release candidate phase will begin the week of May 31. See the summarized key dates in the release cycle, allowed changes during the Drupal 8 and Drupal 9 release cycles, and Drupal 8 and 9 backwards compatibility and internal API policy for more information.

The scheduled release date of Drupal 9.2.0 is June 16 2021.

Bugfix and security support of Drupal 9.1.x, 9.0.x, and 8.9.x

Security coverage for Drupal 8 and 9 is generally provided for the previous minor release as well as the newest minor release. However, Drupal 8.9.x is a Long-Term Support release where support is provided until November 2021. The following changes are upcoming:

Drupal 8.9.x Security releases will be provided until November 2021. Bugfix support is restricted to selected low-disruption major and critical bug-fixes. Drupal 9.1.x. Normal bugfix support ends on June 16, 2021. However, security releases are provided until the release of Drupal 9.3.0 on December 8, 2021. Drupal 9.0.x. Security releases are provided until the release of Drupal 9.2.0 on June 2, 2021.

Drupal Core News: Drupal 9.2.0-alpha1 will be released the week of May 3rd

Main Drupal Feed - Fri, 04/23/2021 - 19:30

In preparation for the minor release, Drupal 9.2.x will enter the alpha phase the week of May 3rd 2021. Core developers should plan to complete changes that are only allowed in minor releases prior to the alpha release. The 9.2.0-alpha1 deadline for most core patches is April 30. (More information on alpha and beta releases.)

  • Developers and site owners can begin testing the alpha after its release.

  • The 9.3.x branch of core will be created, and future feature and API additions will be targeted against that branch instead of 9.2.x. All outstanding issues filed against 9.2.x will be automatically migrated to 9.3.x.

  • Once 9.3.x is branched, alpha experimental modules will be removed from the 9.2.x codebase (so their development will continue in 9.3.x only).

  • All issues filed against 9.1.x will then be migrated to 9.2.x, and subsequent bug reports should be targeted against the 9.2.x branch.

  • During the alpha phase, core issues will be committed according to the following policy:

    1. Most issues that are allowed for patch releases will be committed to 9.2.x and 9.3.x.
    2. Most issues that are only allowed in minor releases will be committed to 9.3.x only. A few strategic issues may be backported to 9.2.x, but only at committer discretion after the issue is fixed in 9.3.x (so leave them set to 9.3.x unless you are a committer), and only up until the beta deadline.

Roughly two weeks after the alpha release, the first beta release will be created. All the restrictions of the alpha release apply to beta releases as well. The release of the first beta is a firm deadline for all feature and API additions. Even if an issue is pending in the Reviewed & Tested by the Community (RTBC) queue when the commit freeze for the beta begins, it will be committed to the next minor release only.

The release candidate phase will begin the week of May 31. See the summarized key dates in the release cycle, allowed changes during the Drupal 8 and Drupal 9 release cycles, and Drupal 8 and 9 backwards compatibility and internal API policy for more information.

The scheduled release date of Drupal 9.2.0 is June 16 2021.

Bugfix and security support of Drupal 9.1.x, 9.0.x, and 8.9.x

Security coverage for Drupal 8 and 9 is generally provided for the previous minor release as well as the newest minor release. However, Drupal 8.9.x is a Long-Term Support release where support is provided until November 2021. The following changes are upcoming:

Drupal 8.9.x Security releases will be provided until November 2021. Bugfix support is restricted to selected low-disruption major and critical bug-fixes. Drupal 9.1.x. Normal bugfix support ends on June 16, 2021. However, security releases are provided until the release of Drupal 9.3.0 on December 8, 2021. Drupal 9.0.x. Security releases are provided until the release of Drupal 9.2.0 on June 2, 2021.

Pages