diff --git a/config/schema/og.schema.yml b/config/schema/og.schema.yml index 4455539b5..0c848b90f 100644 --- a/config/schema/og.schema.yml +++ b/config/schema/og.schema.yml @@ -116,3 +116,17 @@ og.og_role.*: sequence: type: string label: 'Permission' + +field.widget.settings.og_complex: + type: mapping + label: 'OG Group Audience field widget' + mapping: + match_operator: + type: string + label: 'Autocomplete matching' + size: + type: integer + label: 'Size of textfield' + placeholder: + type: label + label: 'Placeholder' diff --git a/src/Og.php b/src/Og.php index 6a7edb8f3..e31da01ab 100644 --- a/src/Og.php +++ b/src/Og.php @@ -9,6 +9,7 @@ use Drupal\Component\Render\FormattableMarkup; use Drupal\Component\Utility\NestedArray; +use Drupal\Core\Entity\Display\EntityFormDisplayInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\FieldableEntityInterface; use Drupal\Core\Field\FieldDefinitionInterface; @@ -59,7 +60,7 @@ public static function createField($plugin_id, $entity_type, $bundle, array $set // Get the field definition and add the entity info to it. By doing so // we validate the the field can be attached to the entity. For example, - // the OG accesss module's field can be attached only to node entities, so + // the OG access module's field can be attached only to node entities, so // any other entity will throw an exception. /** @var \Drupal\og\OgFieldBase $og_field */ $og_field = static::getFieldBaseDefinition($plugin_id) @@ -72,7 +73,6 @@ public static function createField($plugin_id, $entity_type, $bundle, array $set FieldStorageConfig::create($field_storage_config)->save(); } - if (!$field_definition = FieldConfig::loadByName($entity_type, $bundle, $field_name)) { $field_config = NestedArray::mergeDeep($og_field->getFieldConfigBaseDefinition(), $settings['field_config']); @@ -83,6 +83,34 @@ public static function createField($plugin_id, $entity_type, $bundle, array $set static::invalidateCache(); } + // Make the field visible in the default form display. + /** @var EntityFormDisplayInterface $form_display */ + $form_display = \Drupal::entityTypeManager()->getStorage('entity_form_display')->load("$entity_type.$bundle.default"); + + // If not found, create a fresh form display object. This is by design, + // configuration entries are only created when an entity form display is + // explicitly configured and saved. + // @see entity_get_form_display() + if (!$form_display) { + $form_display = \Drupal::entityTypeManager()->getStorage('entity_form_display')->create([ + 'targetEntityType' => $entity_type, + 'bundle' => $bundle, + 'mode' => 'default', + 'status' => TRUE, + ]); + } + + $widget = $form_display->getComponent($plugin_id); + $widget['type'] = 'og_complex'; + $widget['settings'] = [ + 'match_operator' => 'CONTAINS', + 'size' => 60, + 'placeholder' => '', + ]; + + $form_display->setComponent($plugin_id, $widget); + $form_display->save(); + return $field_definition; } diff --git a/src/Plugin/Field/FieldType/OgStandardReferenceItem.php b/src/Plugin/Field/FieldType/OgStandardReferenceItem.php index 78549aafe..84c4ec7fb 100644 --- a/src/Plugin/Field/FieldType/OgStandardReferenceItem.php +++ b/src/Plugin/Field/FieldType/OgStandardReferenceItem.php @@ -23,7 +23,7 @@ * default_widget = "og_complex", * default_formatter = "og_complex", * list_class = "\Drupal\Core\Field\EntityReferenceFieldItemList", - * constraints = {"ValidReference" = {}, "ValidOgMembershipReference" = {}} + * constraints = {"ValidOgMembershipReference" = {}} * ) */ class OgStandardReferenceItem extends EntityReferenceItem { diff --git a/src/Plugin/Field/FieldWidget/OgComplex.php b/src/Plugin/Field/FieldWidget/OgComplex.php index 5d612452b..8364b68b4 100644 --- a/src/Plugin/Field/FieldWidget/OgComplex.php +++ b/src/Plugin/Field/FieldWidget/OgComplex.php @@ -307,10 +307,13 @@ public function otherGroupsSingle($delta, EntityInterface $entity = NULL, $weigh * {@inheritdoc} */ public function massageFormValues(array $values, array $form, FormStateInterface $form_state) { - $parent_values = $values; + // Remove empty values. The form fields may be empty. + $values = array_filter($values, function ($item) { + return !empty($item['target_id']); + }); // Get the groups from the other groups widget. - foreach ($form['other_groups'] as $key => $value) { + foreach ($form[$this->fieldDefinition->getName()]['other_groups'] as $key => $value) { if (!is_int($key)) { continue; } @@ -319,18 +322,16 @@ public function massageFormValues(array $values, array $form, FormStateInterface // be captured in it's own group, with the key 'id'. preg_match("|.+\((?[\w.]+)\)|", $value['target_id']['#value'], $matches); - if (empty($matches['id'])) { - continue; + if (!empty($matches['id'])) { + $values[] = [ + 'target_id' => $matches['id'], + '_weight' => $value['_weight']['#value'], + '_original_delta' => $value['_weight']['#delta'], + ]; } - - $parent_values[] = [ - 'target_id' => $matches['id'], - '_weight' => $value['_weight']['#value'], - '_original_delta' => $value['_weight']['#delta'], - ]; } - return $parent_values; + return $values; } /** diff --git a/tests/src/Functional/OgComplexWidgetTest.php b/tests/src/Functional/OgComplexWidgetTest.php new file mode 100644 index 000000000..51daa99ae --- /dev/null +++ b/tests/src/Functional/OgComplexWidgetTest.php @@ -0,0 +1,98 @@ +createContentType(['type' => 'group']); + Og::groupManager()->addGroup('node', 'group'); + + // Add a group audience field to the "post" node type, turning it into a + // group content type. + $this->createContentType(['type' => 'post']); + Og::createField(OgGroupAudienceHelper::DEFAULT_FIELD, 'node', 'post'); + } + + /** + * Tests the "Other Groups" field. + */ + function testOtherGroups() { + $admin_user = $this->drupalCreateUser(['administer group', 'access content', 'create post content']); + $group_owner = $this->drupalCreateUser(['access content', 'create post content']); + + // Create a group content type owned by the group owner. + $settings = [ + 'type' => 'group', + 'uid' => $group_owner->id(), + ]; + $group = $this->createNode($settings); + + // Log in as administrator. + $this->drupalLogin($admin_user); + + // Create a new post in the group by using the "Other Groups" field in the + // UI. + $edit = [ + 'title[0][value]' => "Group owner's post.", + 'other_groups[0][target_id]' => "group ({$group->id()})", + ]; + $this->drupalGet('node/add/post'); + $this->submitForm($edit, 'Save'); + $this->assertSession()->statusCodeEquals(200); + + // Retrieve the post that was created from the database. + /** @var QueryInterface $query */ + $query = $this->container->get('entity.query')->get('node'); + $result = $query + ->condition('type', 'post') + ->range(0, 1) + ->sort('nid', 'DESC') + ->execute(); + $post_nid = reset($result); + + /** @var NodeInterface $post */ + $post = Node::load($post_nid); + + // Check that the post references the group correctly. + /** @var OgMembershipReferenceItemList $reference_list */ + $reference_list = $post->get(OgGroupAudienceHelper::DEFAULT_FIELD); + $this->assertEquals(1, $reference_list->count(), 'There is 1 reference after adding a group to the "Other Groups" field.'); + $this->assertEquals($group->id(), $reference_list->first()->getValue()['target_id'], 'The "Other Groups" field references the correct group.'); + } + +}