I'm in interested in creating a Drupal system where each user has their own private taxonomy/vocabulary for freetagging. Has anyone had any experience with creating services using Drupal, where each user gets their own gated CMS? This relates to my privacy bounty. In addition to a users having their own private areas of the Drupal system where only they will view their content, each user would have their own vocabulary for freetagging entries.

Comments

igorzh’s picture

agree.

rmanola’s picture

I would like to make use of a feature like that too. Have you started any project about it?

geek-merlin’s picture

is here

alex_b’s picture

Community tags is what you're looking for: http://drupal.org/project/community_tags

rmanola’s picture

I was looking forward this some weeks ago, I wanted to allow each user to have their own vocabulary where they could edit and add terms but only inside their vocabulary, since I couldn'd find a module for that I had to hack the "taxonomy module", more exaclty in the hook_menu(), I did:

I commented the following:

/*
    $items[] = array('path' => 'admin/content/taxonomy/edit/term',
      'title' => t('Edit term'),
      'callback' => 'taxonomy_admin_term_edit',
      'access' => user_access('administer taxonomy'),
      'type' => MENU_CALLBACK);
*/

And inserted at the end of the hook:

   if( arg(0) == 'admin' && arg(1) == 'content' && arg(2) == 'taxonomy' && arg(3) == 'edit' && arg(4) == 'term' && is_numeric(arg(5)) )
   { 
     $tid = arg(5);
     $items[] = array('path' => 'admin/content/taxonomy/edit/term/'.$tid,
      'title' => t('Edit term'),
      'callback' => 'taxonomy_admin_term_edit',
      'callback arguments' => array($tid),
      'access' => user_access('administer taxonomy') || _special_acess(),
      'type' => MENU_CALLBACK);
   }

The "_special_access()" function relates to:

function _special_acess($vid = NULL)
{
	global $user;
	
	if( !isset($vid) )
	{
		$tid = arg(5);
		$result = db_fetch_object(db_query('SELECT vid FROM {term_data} WHERE tid = %d', $tid));
		$vid = $result->vid;
	}
	
	$vocab = db_fetch_object(db_query('SELECT name FROM {vocabulary} WHERE vid = %d', $vid));
	$vocab_name = $vocab->name;
	
	if($vocab_name == $user->name.'_taxonomy')
		return TRUE;
	else
		return FALSE;
}

This way, the user named "foo", for exemple, can insert and edit terms in the vocabulary named: "foo_taxonomy" without having the "edit taxonomy" permission. This can be achieved in D6 without hacking the taxonomy module, though.

ethanre’s picture

Very good hack. Thanks.

It works as you described. I do have a couple questions, see below.

What's happening:
A user called Mike
Admin created a vocabulary called Mike_taxonomy with vocabulary id 25
Admin created a test term called test, this term has the id of 123
Mike logs in, he goes to SITENAME/admin/content/taxonomy/edit/term/123

Since this term 123 belongs to Mike's vocab Mike can edit/delete this term.

The 3 questions that come up that we need to address:
1-Where does Mike go to add another term to his vocabulary? or does the Admin have to create the tems?

2-Where does Mike go to view all his Vocabulary terms? in a custom view?

3-In the example above the vocabulary for Mike, the Admin created the Vocabulary (and set it up to work with Blog Entry, multi-select, etc..). If a site has many users like Mike, would the Admin have to create the vocabulary for each user?

Things I'm looking for are like:

SITENAME/admin/content/taxonomy/edit/term/123 - this is working

To-Do:
Give Mike access to:
SITENAME/admin/content/taxonomy/edit/vocabulary/25 - to edit vocabulary
SITENAME/admin/content/taxonomy/25 - view tax terms
SITENAME/admin/content/taxonomy/25/add/term - add term
SITENAME/category/miketaxonomy/testtest - options to view/edit/clone/export displayed term

If the above is done, the only step left is creating the initial vocabulary for each new user.
Any ideas? lets brainstorm here

ethanre’s picture

1)
To add a term with this URL: SITENAME/admin/content/taxonomy/25/add/term

I did these changes:

/* commented out to be replaced by code 'B' */
      /*$items[] = array('path' => 'admin/content/taxonomy/'. $vid .'/add/term',
        'title' => t('Add term'),
        'callback' => 'drupal_get_form',
        'callback arguments' => array('taxonomy_form_term', $vid),
        'access' => user_access('administer taxonomy'),
        'type' => MENU_LOCAL_TASK);
 */

Add:

 /**
*code 'B' 
*This allows access to this URL
*SITENAME/admin/content/taxonomy/vid#/add/term 
*to add term
*with vocab called UserName_taxonomy
*/

   if( arg(0) == 'admin' && arg(1) == 'content' && arg(2) == 'taxonomy' && is_numeric(arg(3)) && arg(4) == 'add' && arg(5) == 'term' )

   {
     $vid = arg(3);
     $items[] = array('path' => 'admin/content/taxonomy/'. $vid .'/add/term',
      'title' => t('Add term'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array('taxonomy_form_term', $vid),
      /*'callback arguments' => array($tid),*/
      'access' => user_access('administer taxonomy') || _special_acess_b(),
      'type' => MENU_LOCAL_TASK);
   }
/* end 'B' inserted*/

Notice the call to _special_acess_b, let's add special access b code:

/*code 'SpecialAccessB' */
function _special_acess_b($vid = NULL)
{
    global $user;
   
   $vid = arg(3);

   
    $vocab = db_fetch_object(db_query('SELECT name FROM {vocabulary} WHERE vid = %d', $vid));
    $vocab_name = $vocab->name;
   
    if($vocab_name == $user->name.'_taxonomy')
        return TRUE;
    else
        return FALSE;
}
/*end added special access b*/

2)
To view terms with a URL like: SITENAME/admin/content/taxonomy/25 - view tax terms

Modify one line i.e. this: "'access' => user_access('administer taxonomy')" by adding the special access:

if (arg(0) == 'admin' && arg(1) == 'content' && arg(2) == 'taxonomy' && is_numeric(arg(3))) {
      $vid = arg(3);
      $items[] = array('path' => 'admin/content/taxonomy/'. $vid,
        'title' => t('List terms'),
        'callback' => 'taxonomy_overview_terms',
        'callback arguments' => array($vid),
        'access' => user_access('administer taxonomy') || _special_acess_b(), /* the only Modification*/
        'type' => MENU_CALLBACK);

Now Mike can do the following:

Add terms to vocab - example URL: SITENAME/admin/content/taxonomy/25/add/term
View terms in vocb - example URL: SITENAME/admin/content/taxonomy/25
Edit term (see my previous comment) - URL: SITENAME/admin/content/taxonomy/edit/term/123

Two thing left, I'll work on this later,
1) Allow Mike to see admin options when viewing a term with a URL like: SITENAME/taxonomy/term/123
2) Automatically create a vocabulary for a new user upon registering, or have them press a button to create the vocab.

If you have ideas that can help pls share.

ethanre’s picture

I ran across an issue that's a show stopper.
If I had a 100 users like Mike, this means 100 vocabularies, each of these will have 'Blog Entry' selected as content type, this means that when a user opens a 'Blog Entry' form they will have all these 100 categories show up on that 'blog entry' form. Unless we create a new content type to go with the new category for each user. oh my.

electricmonk’s picture

Your solution is to add another table that binds vid to uid instead of relying on the username.

I'm currently working on a similar module that allows users to have their own terms within a common vocabulary (to be called private_taxonomy). I'll publish it when it's done :)

ethanre’s picture

Great! I'll be looking forward to your module.

electricmonk’s picture

allows settings a vocabulary as "private". Vocabularies with this setting have per-user terms, each user can only view / alter their own terms. I'm not sure that this is exactly what you need, and I still can't upload the code (it's pre-alpha), but the general idea is using hook_db_rewrite_sql to add a join and a where clause on an external table which holds the term and user ids. I guess that implementing this for vocabulary ids instead should be too difficult.

electricmonk’s picture

dodorama’s picture

I found and tested this workaround.
Since we can't have private free tagging vocabularies I just modified the autocomplete field on the node edit form. Instead of fetching results from all the terms of a given vocabulary the autocomplete field just fetch terms from a given vocabulary AND assigned to nodes authored by the current user
I did it by altering the 'taxonomy/autocomplete' menu defined on taxonomy module. It now calls a custom function that fetches the desired results.
All you have to do is create a custom module and insert this code snippet


// We alter the behaviour of the menu that controls the autocomplete features by calling our custom function
function yourmodule_menu_alter(&$callbacks) {
  $callbacks["taxonomy/autocomplete"]['page callback'] = 'yourmodule_autocomplete';
}

// This function is a modified version of the taxonomy_autocomplete function founded on taxonomy.module. We just change the query to fetch only the desired terms
function yourmodule_autocomplete($vid, $string = '') {
  global $user;
  // The user enters a comma-separated list of tags. We only autocomplete the last tag.
  $array = drupal_explode_tags($string);

  // Fetch last tag
  $last_string = trim(array_pop($array));
  $matches = array();
  if ($last_string != '') {
    $result = db_query_range(db_rewrite_sql("SELECT t.tid, t.name FROM {term_data} t INNER JOIN {term_node} r ON r.tid = t.tid INNER JOIN {node} n ON n.vid = r.vid WHERE t.vid = %d AND n.uid = %d AND LOWER(t.name) LIKE LOWER('%%%s%%')", 't', 'tid'), $vid, $user->uid, $last_string, 0, 10);

    $prefix = count($array) ? implode(', ', $array) .', ' : '';

    while ($tag = db_fetch_object($result)) {
      $n = $tag->name;
      // Commas and quotes in terms are special cases, so encode 'em.
      if (strpos($tag->name, ',') !== FALSE || strpos($tag->name, '"') !== FALSE) {
        $n = '"'. str_replace('"', '""', $tag->name) .'"';
      }
      $matches[$prefix . $n] = check_plain($tag->name);
    }
  }

graceman9’s picture

Thanks!! Good idea!

that0n3guy’s picture

My module for basically using the override autocomplete method above for D7:

http://drupal.org/sandbox/that0n3guy/1379404