Can I add content to a Group from that content's regular node creation form? In other words, when I create, say, an Article at /node/add/article, is it possible to be presented with select box of Groups I can add this Article to?

The current workflow of navigating to /group/[group_id}/node/create/article, or clicking "Create Article" from within the Group page (/group/{group_id] is fine when a user very specifically wants to add an Article to just this Group. However, creating an Article (ie /node/add/article) directly should present options to choose which Group(s) this Article becomes a part of.

I couldn't find this mentioned in the docs nor the issue queue, apologies if I missed it!

Comments

illepic created an issue. See original summary.

kristiaanvandeneynde’s picture

Status: Active » Closed (works as designed)

This functionality was in D7 but has been removed in D8 in favor of the wizard.

The reason is that all content-to-group relationships are now fieldable and we therefore need to ensure the user is presented with the fields that were configured on said relationship. A simple auto-complete to select a group would therefore not work.

maxilein’s picture

Well since this seems to be a dead end by design: How do I create a node to a group using Restful JSON?
Or how do I assign a node to a group using Restful services?

mvogel’s picture

@maxilein

I needed this possibility too. So I created a Entity Reference View which display all groups where the current user belongs. After that I created a entity reference field e.g. for article and wrote a custom submit callback for the node_article_form and node_article_edit_form which creates or removes GroupContent relations depending on the selection in the entity reference field.

flocondetoile’s picture

Hi @mvogel

It would be nice if you could share your custom submit callback for reference ?

mvogel’s picture

I copied my code and hope it is useful, if you have questions just ask but please excuse my english.
And I can't guarantee that the code is without errors. It works for me and my site.

The Entity Reference Field explained in #4 is called 'field_node_add_group' in this example. I tried to write it abstract and commented some lines.

function my_module_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  // e.g 'node_article_form'
  if ($your_desired_node_add_form_id)) {
    // Unset - none - option (it's optional)
    unset($form['field_node_add_group']['widget']['#options']['_none']);

    // Add your custom callback function
    foreach (array_keys($form['actions']) as $action) {
      if ($action != 'preview' && isset($form['actions'][$action]['#type']) && $form['actions'][$action]['#type'] === 'submit') {
        $form['actions'][$action]['#submit'][] = '_my_module_node_add_custom_callback_group';
      }
    }
  }

  // e.g. 'node_article_edit_form'
  // you need for the edit form a different callback because maybe you have to delete or create GroupContent
  if ($your_desired_node_edit_form_id)) {
    // Unset - none - option (it's optional)
    unset($form['field_node_add_group']['widget']['#options']['_none']);

    // Add your custom callback function
    foreach (array_keys($form['actions']) as $action) {
      if ($action != 'preview' && isset($form['actions'][$action]['#type']) && $form['actions'][$action]['#type'] === 'submit') {
        $form['actions'][$action]['#submit'][] = '_my_module_node_edit_custom_callback_group';
      }
    }
  }
}


function _my_module_node_add_custom_callback_group($form, FormStateInterface $form_state) {
  $nid = $form_state->getValue('nid');
  $node = Node::load($nid);
  $gids = $form_state->getValue('field_node_add_group');
  foreach ($gids as $gid) {
    // Skip -none- option
    if ($gid['target_id'] == '_none') {
      continue;
    }
    $group = Group::load($gid['target_id']);
    /** @var \Drupal\group\Plugin\GroupContentEnablerInterface $plugin */
    $plugin = $group->getGroupType()->getContentPlugin('group_node:'.$node->bundle());
    $group_content = GroupContent::create([
      'type' => $plugin->getContentTypeConfigId(),
      'gid' => $group->id(),
      'entity_id' => $node->id(),
    ]);
    $group_content->save();
  }

}

// This function is a bit more complex because you have to do some mathematical set operations
function _my_module_node_edit_custom_callback_group($form, FormStateInterface $form_state) {

  $nid = $form_state->getValue('nid');
  $node = Node::load($nid);

  // Index-Array for wanted groups ( gid => gid )
  $gids = $form_state->getValue('field_node_add_group');
  $gids_wanted = [];
  foreach ($gids as $gid) {
    $gids_wanted[$gid['target_id']] = $gid['target_id'];
  }

  // Index-Array for existing groups for this node gid => gid
  $gids_existing = [];

  // Index-Array for gnodes for easier deletion gid => GroupContent
  $gnodes_existing = [];

  /** @var \Drupal\group\Entity\Storage\GroupContentStorageInterface $storage */
  $storage = \Drupal::entityTypeManager()->getStorage('group_content');
  // Loads all groups with a relation to the node
  $activGroupListEntity = $storage->loadByEntity($node);
  foreach ($activGroupListEntity as $groupContent) {
    // fill Index-Array with existing groups gid => gid
    $gids_existing[$groupContent->getGroup()->id()] = $groupContent->getGroup()->id();

    // fill Index-Array for existing gnodes
    $gnodes_existing[$groupContent->getGroup()->id()] = $groupContent;
  }

  // Union for existing and wanted groups
  $gids_union = $gids_existing + $gids_wanted;

  // Index-Array gnodes to create 
  // = (Union for existing and wanted) minus existing
  $gids_create = array_diff($gids_union, $gids_existing);

  // Index-Array gnodes to delete
  // = (Union for existing and wanted) minus wanted
  $gids_delete = array_diff($gids_union, $gids_wanted);

  foreach ($gids_create as $gid) {
    // Skip -none- option
    if ($gid == '_none') {
      continue;
    }
    $group = Group::load($gid);
    /** @var \Drupal\group\Plugin\GroupContentEnablerInterface $plugin */
    $plugin = $group->getGroupType()->getContentPlugin('group_node:'.$node->bundle());
    $group_content = GroupContent::create([
      'type' => $plugin->getContentTypeConfigId(),
      'gid' => $group->id(),
      'entity_id' => $node->id(),
    ]);
    $group_content->save();
  }

  foreach ($gids_delete as $gid) {
    // Skip -none- option
    if ($gid == '_none') {
      continue;
    }
    $gnodes_existing[$gid]->delete();
  }

}
oriol_e9g’s picture

@mvogel Your code needs the use staments to work or inject as a service, and complete the code.

I paste some code but only use if someone needs a dirty, dirty and fast solution. This works:

use Drupal\Core\Form\FormStateInterface;
use Drupal\node\Entity\Node;
use Drupal\group\Entity\Group;
use Drupal\group\Entity\GroupContent;

/**
 * Get content types and field to act.
 */
function _node_field_group_settings() {
  $ctypes = [
    'article',
    'actes',
    'post',
    'page',
    'book',
  ];
  $add = $edit = array();
  foreach ($ctypes as $ctype) {
    $add[] = 'node_' . $ctype . '_form';
    $edit[] = 'node_' . $ctype . '_edit_form';
  }
  return [
    'form_id_add' => $add,
    'form_id_edit' => $edit,
    'group_field' => 'field_node_group',
  ];
}


function node_field_group_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  $settings = _node_field_group_settings();
  // Add node.
  if (in_array($form_id, $settings['form_id_add'])) {
    foreach (array_keys($form['actions']) as $action) {
      if ($action != 'preview' && isset($form['actions'][$action]['#type']) && $form['actions'][$action]['#type'] === 'submit') {
        $form['actions'][$action]['#submit'][] = '_node_field_group_add_custom_callback_group';
      }
    }
  }

  // you need for the edit form a different callback because maybe you have to delete or create GroupContent
  if (in_array($form_id, $settings['form_id_edit'])) {
    foreach (array_keys($form['actions']) as $action) {
      if ($action != 'preview' && isset($form['actions'][$action]['#type']) && $form['actions'][$action]['#type'] === 'submit') {
        $form['actions'][$action]['#submit'][] = '_node_field_group_edit_custom_callback_group';
      }
    }
  }
}


function _node_field_group_add_custom_callback_group($form, FormStateInterface $form_state) {
  $settings = _node_field_group_settings();
  $nid = $form_state->getValue('nid');
  $node = Node::load($nid);
  $gids = $form_state->getValue($settings['group_field']);
  foreach ($gids as $gid) {
    // Skip -none- option
    if ($gid['target_id'] == '_none') {
      continue;
    }
    $group = Group::load($gid['target_id']);
    /** @var \Drupal\group\Plugin\GroupContentEnablerInterface $plugin */
    $plugin = $group->getGroupType()->getContentPlugin('group_node:'.$node->bundle());
    $group_content = GroupContent::create([
      'type' => $plugin->getContentTypeConfigId(),
      'gid' => $group->id(),
      'entity_id' => $node->id(),
    ]);
    $group_content->save();
  }

}

// This function is a bit more complex because you have to do some mathematical set operations
function _node_field_group_edit_custom_callback_group($form, FormStateInterface $form_state) {
  $settings = _node_field_group_settings();
  $nid = $form_state->getValue('nid');
  $node = Node::load($nid);

  // Index-Array for wanted groups ( gid => gid )
  $gids = $form_state->getValue($settings['group_field']);
  $gids_wanted = [];
  foreach ($gids as $gid) {
    $gids_wanted[$gid['target_id']] = $gid['target_id'];
  }

  // Index-Array for existing groups for this node gid => gid
  $gids_existing = [];

  // Index-Array for gnodes for easier deletion gid => GroupContent
  $gnodes_existing = [];

  /** @var \Drupal\group\Entity\Storage\GroupContentStorageInterface $storage */
  $storage = \Drupal::entityTypeManager()->getStorage('group_content');
  // Loads all groups with a relation to the node
  $activGroupListEntity = $storage->loadByEntity($node);
  foreach ($activGroupListEntity as $groupContent) {
    // fill Index-Array with existing groups gid => gid
    $gids_existing[$groupContent->getGroup()->id()] = $groupContent->getGroup()->id();

    // fill Index-Array for existing gnodes
    $gnodes_existing[$groupContent->getGroup()->id()] = $groupContent;
  }

  // Union for existing and wanted groups
  $gids_union = $gids_existing + $gids_wanted;

  // Index-Array gnodes to create
  // = (Union for existing and wanted) minus existing
  $gids_create = array_diff($gids_union, $gids_existing);

  // Index-Array gnodes to delete
  // = (Union for existing and wanted) minus wanted
  $gids_delete = array_diff($gids_union, $gids_wanted);

  foreach ($gids_create as $gid) {
    // Skip -none- option
    if ($gid == '_none') {
      continue;
    }
    $group = Group::load($gid);
    /** @var \Drupal\group\Plugin\GroupContentEnablerInterface $plugin */
    $plugin = $group->getGroupType()->getContentPlugin('group_node:'.$node->bundle());
    $group_content = GroupContent::create([
      'type' => $plugin->getContentTypeConfigId(),
      'gid' => $group->id(),
      'entity_id' => $node->id(),
    ]);
    $group_content->save();
  }

  foreach ($gids_delete as $gid) {
    // Skip -none- option
    if ($gid == '_none') {
      continue;
    }
    $gnodes_existing[$gid]->delete();
  }

}

igonzalez’s picture

#7 Thanks for the code, but it generates the following errors:

When create content:
Fatal error: Call to a member function getGroupType() on a non-object in custom.module on line 63

When edit content:
Fatal error: Call to a member function getGroupType() on a non-object in custom.module on line 123

I can't solve the problem. Any ideas?

oriol_e9g’s picture

@igonzalez you need to enable group node module, not only group.

igonzalez’s picture

#9 I have enabled group node module. It's configured and working properly.
I'm building an intranet. It's not practical to assign content from the group.
Excuse my English.

tomsaw’s picture

I want the exact same behavior.

Great approach from mvogel at #4 and #7 cause it fits nice with other necessities like 'quick-edit' and 'entity reference' to make the whole feature delicious.

Im a newbie in coding PHP but gonna try it out. How about a module with this?

mvogel’s picture

@igonzalez
have you installed the content plugins here /admin/group/types/manage/{group_type}/content ?

@tomsaw
good luck :P, I hope they will work on the related issue from maxilein and finish it someday

tomsaw’s picture

Applied #7 and works fine.

Through this approach, group tastes much more like other structuring approaches of Drupal, f.e. Taxonomies

  • Ability to assign group(s) in content-form
  • Quickedit assigned groups via content-view
  • Filter content directly by assigned groupswithin views. No need for additional relations.
  • Redundant group-information within entities allows faster queries

---
I bet many others would enjoy the consistency, too! +1 for a module!

However, here's a disadvantage of the approach:

Access - Problems

The solution runs in redundancy-problems. With groups solution of using group_content entities there is no access trouble like this:

When editing an entity with a groups-reference-field, a user must be allowed to access all assigned groups. Otherwise, inaccessible assigned groups will be erased when saving. They have not been loaded into the form because groups doesn't deliver the inaccessible group-entity.

Simple entity_relations solved the problem like this: If a user edits an entity that holds entity-references he has no access to, the specific selection-field shows "- Restricted Access -" instead of the inaccessible entity. When saving, all referenced entities are kept.

mvogel’s picture

nice to hear, the project open social tried something similar.
But I think our approach is almost better because of your mentioned points above ;)

dan_metille’s picture

+1 to have this function declined into a module too

ssserkkk’s picture

Hello. I'm beginner at Drupal. How can I use #7 codes in drupal 8?

mvogel’s picture

@ssserkkk

you should follow #4 with the code from #7.
Paste #7 in your custom module (module name in #7 is node_field_group) in the *.module file

Otherwise, I would recommend you to follow this issue https://www.drupal.org/project/group/issues/2813405 which is maybe a more sophisticated approach

ssserkkk’s picture

Thanks for your answer. I created and activated the module. I created a field for my content type and I added a field a way that Source -> Other -> Content -> Group. I can select a grup in the content add page. But group privileges are not valid for users of diffirent groups.

somebodysysop’s picture

Thank you @mvogel and @oriol_e9g for the code referenced in #4 and #7. I wanted to follow up for those who, like me, may be a little confused by "I created a entity reference field ".

Here is a video on the subject: https://www.google.com/search?q=drupal+8+add+entity+refernece+field&oq=d...

Essentially, you need to create a field in your content type: Structure->Content types->Manage fields->add fields

It should be a Reference->Content field. I named my field: Node Group (machnine name "field_node_grouip") to match the code in #7.

In field settings, the type of item to reference will be "Group". Save.

In manage form display, set the widget type to "Select list". Save and that should be it.

I just wanted to add this because these were the questions I had when I tried to implement this code. I copied it into my module, replaced "node_field_group" with my module name, and it just works!

scotwith1t’s picture

For others who find themselves here, take a look at #2813405: Add a field to view and edit content's groups

brad.bulger’s picture

"This functionality was in D7 but has been removed in D8 in favor of the wizard."

What is the wizard?

This seems like the most basic functionality. I just installed Group to try it out and after creating a group it was the very next logical step. But there is no menu item, no tab, no UI component that I can find anywhere, to add content to a group. Nothing in anything that could loosely be called documentation that describes how to do it. Are we supposed to write custom code to be able to do this? Do we add some kind of field somewhere?

How do I add a node to a group? Or how do I find out why that concept is mistaken, if it is?

mvogel’s picture

@brad.bugler

First, you need to install the content plugins you want to use. Go to admin/group/types/manage/[yourGroupTypeName]/content and install the plugins you need.

After that, you can add a node to a group out of the box through group/[groupId]/nodes page. If you want to add a group directly from a node creation and edit form you may try this module https://www.drupal.org/project/entitygroupfield

debasisnaskar’s picture

Component: Group Node (gnode) » Code

#4 & #7 good job done! But it’s not correctly working for the translation. When we translate a node and assign the group from the node, its getting added in the soecific Group but multiple times! I think it needs refactored to make it fully compatible with Group v2 as well as translation.