OK, here's a fun one. There may or may not be an existing solution here, or it may need a new custom module. I am looking for advise on either one.

I have a node type, A. I have a group node type, G. There are many nodes of type G. I also have a Taxonomy vocabulary V, with over 100 terms in it. Nodes of type A can/must have one or more terms in vocab V.

Now, here's where it gets complicated. When assigning terms from vocab V to nodes of type A, we want them restricted to just those terms associated with the group of type G that the node belongs to. That is, we want to create a node and assign terms to it, where those terms are just those relevant to the node's group.

That seems straightforward, right? Terms can be associated to groups quite easily the same way as nodes in Drupal 7, which is awesome. However, there's no good way that I know of to do that sort of restriction in the UI.

If the node add form is built using Context Admin, then we could have the group provided as a context and then, with a custom Context Admin plugin, lock down the form to just that group and then custom filter the allowed values for the taxonomy reference with a custom widget that is Context-aware. That assumes, however: 1) Using context admin; 2) a node can be placed into only one group; 3) I can write a very specific widget; 4) that widget can be made Context-aware. That's a lot of assumptions that I don't think I can make.

If we're just using a normal node-add form, or we need to allow nodes of type A to be in multiple groups (as I do in this case), then I'm left with needing to make a new widget for taxonomy reference that is multi-value and somehow extracts client-side the current value of the group selector field and then filters itself client-side... AND then does the same filtering server-side. That also means loading up all terms in the vocabulary client-side so that we can show and hide them as the user changes the group selector, which runs into all kinds of fun performance problems. (At 100 terms, it's just a UI fail. At 1000 terms, it's a memory limit fail.)

So far, I do not have any good options. Are we going about this wrong? Is there something existing that will do the above? Is there a less crappy than I described new module to be written here?

I have at least 2 use cases for this, so input welcome. :-)

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

bassthiam’s picture

Hi there,
i have been facing a similar issue while working on a project for which we have to hide some tax_term based on user's groups.
I adapted the code i wrote, instead of a filter on user's membership, the following code is checking if the term to display matches the current node's group audience:
bass_og is my custom module's name


function bass_og_field_info_alter(&$info) {
  //overriding tax_term_reference's options list callback
  $info['taxonomy_term_reference']['settings'] = array(
      'options_list_callback' => '_bass_tax_allowed_values',
    );
}

function _bass_tax_allowed_values($field) {
  $options = array();
  foreach ($field['settings']['allowed_values'] as $tree) {
    if ($vocabulary = taxonomy_vocabulary_machine_name_load($tree['vocabulary'])) {
      //keep the default callback if the vocabulary is different 
      if ($vocabulary->machine_name != 'V') {
        $options = taxonomy_allowed_values($field) ;
      }
      else {
        //building custom option list
        //only terms with a group matching the viewing node will be displayed
        if ($terms = taxonomy_get_tree($vocabulary->vid, $tree['parent'])) {
          foreach ($terms as $term) {
            $terms_groups = _bass_og_term_groups($term->tid); 
            if(empty($terms_groups)) {
            $options[$term->tid] = str_repeat('-', $term->depth) . $term->name;
            } else {
              $matches = array_intersect(_bass_og_node_groups(), $terms_groups);
              if (!empty($matches)) {
                $options[$term->tid] = str_repeat('-', $term->depth) . $term->name;
              }
            }
          }
        }      
      }
    }
  }
  return $options;
}


function _bass_og_node_groups() {
    $node_groups = array();
    global $node;
    $nid = $node->nid;
    if ($node->type = 'A') {
      //fetch node GID's
      $query = db_select('og_membership', 'o')
        ->fields('o', array('gid'))
        ->condition('o.etid', $nid)
        ->condition('o.entity_type', 'node');
      $result = $query->execute();
      foreach ($result as $group) {
        $node_groups[] = $group->gid;
      }
    }
        
  return $node_groups;
}

function _bass_og_term_groups($term_id) {

  $term_groups = array();
  $query = db_select('og_membership', 'o')
      ->fields('o', array('gid'))
      ->condition('o.etid', $term_id)
      ->condition('o.entity_type', 'taxonomy_term');
      $result = $query->execute();
      $terms_groups = array();
      foreach ($result as $group) {
        $term_groups[] = $group->gid;
      }
  return $term_groups;
}

jramby’s picture

Hi,

I'm facing the same problem, I just want to know what are the configurations needed aside installing this module.

thx in advance for your answer.

donquixote’s picture

Version: 7.x-1.x-dev » 7.x-2.x-dev

I have the same use case - but on og-7.x-2.x..
(I think 1.x is history, so we can safely switch this issue to the new branch)

#1
hook_field_info_alter() sounds like a good start!

In 7.x-2.x we typically work with Entityreference prepopulate, so we need to grab the group nid from the $_GET.

The og_group_ref is in itself an entity reference field, whereas what we want to restrict is a term reference field.

So, the more generic use case is having three (entity/term)reference fields with some kind of transitive relation requirement.
This could lead to an approach that is totally independent of organic groups, and which could be pretty useful across the board.

Or seen in another way, we have two interdependent reference fields, which could be combined into a drilldown..

Interesting stuff here :) maybe I can figure something out.

donquixote’s picture

donquixote’s picture

Ok, i tried this and it works. However, there are some caveats.

In my use case, I have "Group", "Group document" and "Group document category".

1. Taxonomy + EntityFieldQuery + og_query_og_membership_alter() will cause a PDOException, due to the nonexistent field taxonomy_term_data.vocabulary_machine_name.
The solution I found was to use nodes instead of taxonomy. So I made "Group document category" a node type.

2. OG widget for entityreference is not properly supported by reference_option_limit, it seems.
So we switch all the og_group_ref field widgets to "Select list". I can only hope this doesn't have nasty side effects.

3. Multiple-value og_group_ref can cause "An illegal choice has been detected".
So we might wanna switch this field to single-value.

donquixote’s picture

And another caveat:

4. Entityreference prepopulate does not work nicely with og_group_ref + "select list" single-value.
#1825256: Does not work for Organic groups og_group_ref field
So we need to apply a patch from there..

EDIT:
Easy solution: #1825256-39: Does not work for Organic groups og_group_ref field

amitaibu’s picture

Sorry, Seems I missed the issue when it was opened.

Aren't we talking about OG-vocab module here?

donquixote’s picture

Aren't we talking about OG-vocab module here?

OG Vocabulary does not scale.
If you have e.g. 100 groups, you don't want a separate taxonomy bundle for each of them.

Reference field option limit is the name of the game :)

amitaibu’s picture

Have you looked in og vocab 7.x? It's different from the 6 version.
If so please explain why it won't scale? (I might contradict it as I have sites with more than 100 groups :)

donquixote’s picture

@Amitaibu: Can you catch me on IRC #drupal-contribute? I don't want to spam this issue..

donquixote’s picture

My main concern is not performance (can't say much about that).
I am concerned about maintenance, if there are too many bundles. E.g. each bundle having its own display settings, etc.

When I tried og vocab on D7 this is what I saw - one bundle per group. Am I mistaken?

amitaibu’s picture

When I tried og vocab on D7 this is what I saw - one bundle per group.

and (OG) Vocabulary can be attached to a single group, yes. The display settings are done on the OG-vocab entity (see the docs)

ggevalt’s picture

So what's the resolution, solution or suggestion in this?

If I understand the question it is how can one easily restrict the list of terms/tags/vocabs in the node create form to ONLY the terms/tags/vocabs that are associated with a group. The issues are these: When the node create form is called, the first information that is brought up is the user's group access -- if there is more than one choice, then it has to deliver all. HOWEVER, is there a way for the node content form to allow the user to choose the group and then be given ONLY those terms/tags/vocabs that are associated with that group?

I have not used the D7 terminology. I have been working in D6 and don't fully understand the termonology of references and entities, etc. Very confusing. Sorry. And I'm not a coder.

But I think the issue being raised here is an important one. As has been referenced, the "scalability" has to do with having a node form produce dozens or hundreds of terms/tags/vocabularies that are not relevant to the user. AND the really painful process of creating a new vocabulary/term OUTSIDE the node content form. So a user with the proper permissions has to go elsewhere to create a new vocabulary/tag/term and then has to thread his or her way to make sure it is associated with the content form.

Very ugly from a user perspective.

So is there an answer to the original question? Because I have looked at OG vocab and the solution linked here cannot realistically be done in a site where there are hundreds of groups and thousands of terms and vocabularies.

thanks so much,
g

bmunslow’s picture

Hi,

I came up with that exact same problem. I went even further, since I also needed to nest taxonomy terms and my admin users should only be able to see the terms that belong to the same OG when choosing relations (parent term).

This is how I went around it:

  1. I used the Entity Reference Prepopulate module so that the OG is already selected when the user edits the content
  2. I wrote a small module that alters the taxonomy_form_term and CONTENT_TYPE_node_form forms and filters the available taxonomy terms according to the OG context
  3. OG context is determined by either the OG id parameter in the URL, or the OG if already selected in the term/node

If there's some interest I can post this small module.

With that being said, when reading this thread I discovered the reference_option_limit module, which seems to do exactly what you want (although I haven't tried it).

Greetings.

t.payne’s picture

@bmunslow
I would love for you to post the module!~ This is exactly what I am trying to do as well.

Thanks

nasia123’s picture

I would also like to see this module...

bmunslow’s picture

FileSize
1.72 KB

Hi,

Please find attached the module I wrote to achieve this functionality.

For the module to work properly, you have to replace a couple words with your custom settings in the file og_taxonomy_filter.module.

  1. Find and replace YOUR_CUSTOM_VOCABULARY_MACHINE_NAME with the machine name of the vocabulary you are working with
  2. FInd and replace YOUR_CUSTOM_CONTENT_TYPE with the machine name of the content type you are working with.

Like I mentioned in the previous comment, you need to have enabled the entityreference_prepopulate module so that the OG context can be determined.

I hope this helps.

FAAREIA’s picture

Alright, here is my solution and my use case:
- Users who creates theirs groups can create nodes of an specific content type and can create taxonomy terms within an specify vocabulary

Steps:

  1. Create a group audience for your vocabulary
  2. Edit the created field and within "GROUPS AUDIENCE FIELD OPTIONS", set "Node", "Organic groups"
  3. Go to Views and create a "Term View"
  4. Set "Format" to "Entity Reference list"
  5. Set "Format options" to the field you need (Taxonomy name for example)
  6. Add a "Filter Criteria" "Vocabulary name" to filter terms
  7. Add a "Contextual Filter" "OG membership: Group ID". There is no context since we are creating a node, so tick "Provide default value" > "The OG groups of the logged in user". Leave the other two settings "Group type" and "Concatenator".
  8. Go to your content type and create an "Entity Reference" field
  9. In "Field options" set "Target type" to "Taxonomy Term"
  10. In "Entity selection" set "mode" and select your View. I leave arguments empty
  11. Create your node and check how taxonomies are filtered

Hope it helps someone :)
If your user can't create the term via admin-interface, perhaps rules can help to set the group audience. It's only a thought...

held69’s picture

Did anyone found a proper working solution other then the above.

I thought that the views solution came pretty close.
However didn't manage to get any groupcontext.
I tried to seperate different taxonomies with og permission however they only work on node edit and node view.
Not node add.

babruix’s picture

Category: Support request » Bug report
Status: Active » Needs review
FileSize
676 bytes

After debugging, the problem seems to be in 'og_query_og_membership_alter'.
This hook overrides the corrections that are made on the query by the taxonomy 'taxonomy_entity_query_alter' hook.
So the condition on 'vid', is reverted to a condition on 'vocabulary_machine_name'.

Attaching a patch that fixes taxonomy query with organic group condition.
To avoid condition on 'vid' being reverted to a condition on 'vocabulary_machine_name' simply set entity keys bundle to 'vid'.

Status: Needs review » Needs work

The last submitted patch, 20: og-field-query-1507608-20.patch, failed testing.

babruix’s picture

Status: Needs work » Needs review
FileSize
755 bytes

Better patch: only set back to 'vid' when bundle keys is 'vocabulary_machine_name'.

sarvab’s picture

Slight update to the above patch so it only alters bundle key with taxonomy_term queries. Maybe some other entity that just happens to use a vocabulary_machine_name (however unlikely) actually has that column on its table instead of vid.

Status: Needs review » Needs work

The last submitted patch, 23: og-field-query-1507608-23.patch, failed testing.