What I'd like to do is only display the friends of a user in the autocomplete field. I'm using the 'Flag Friend' module and the query I'd get from views when I display a user his friends is the following:

SELECT DISTINCT(users.uid) AS uid,
users.name AS users_name,
flag_friend.uid AS flag_friend_uid
FROM users users
LEFT JOIN flag_friend flag_friend ON users.uid = flag_friend.uid
WHERE (users.status <> 0)
AND ((users.uid IN (SELECT f.friend_uid FROM flag_friend f WHERE f.uid = 1)) OR (users.uid IN (SELECT f.uid FROM flag_friend f WHERE f.friend_uid = 1)))
GROUP BY uid

What I came up with was the following, but that obviously didn't work.


function fp_privatemsg_privatemsg_sql_autocomplete_alter(&$fragments, $search, $names) {
	global $user
		// Extend the query that searches for usernames

		  // $fragments is explained in the api documentation in detail

		  // The query is already set up, it's searching for usernames which start with $search and are not $names (may be empty)
		  // the main table is {user} a

		  // for example, add a join on a table where the user connections are stored
		  // and specify that only users connected with the current user should be loaded
		 $fragments['left_join'] = 'LEFT JOIN {flag_friend} f ON (f_friend.uid = u.uid AND (f.uid = %d OR f.friend_uid = %d ))';
		  $fragments['query_args']['join'][] = $user->uid;
		}

// This blocks messages between users, who are not related, it uses universal_relation_api so in theory should work with User Relationships and FriendsList modules, tested with User Relationships.

function mymodule_privatemsg_block_message($author, $recipients) {
  $blocked = array();
  foreach ($recipients as $recipient) {
    if (!module_invoke_all('socnet_is_related', $author->uid, $recipient->uid)) {
      $blocked[] = array(
        'uid' => $recipient->uid,
        'message' => t('!name is not a friend of yours.', array('!name' => $recipient->name))
      );
    }
  }
  return $blocked;
}

Any suggestions?

Many thanks,
Donald

Comments

berdir’s picture

Status: Active » Postponed (maintainer needs more info)

Can't look into the details right now, so just a few tipx:

- There is no 'left_join', only 'inner_join'. Yes, I know, it is ugly, it should just be join :)
- I think you want to use INNER JOIN anyway. LEFT JOIN still includes rows that can't be joined
- When you have two placeholders, you also need to add your query argument twice

Try that and if it still doesn't work, post your updated code.

Donaldd’s picture

Hi Berdir.

Thanks for the quick reply! I tried the following.


function fp_privatemsg_privatemsg_sql_autocomplete_alter(&$fragments, $search, $names) {
	global $user
		// Extend the query that searches for usernames

		  // $fragments is explained in the api documentation in detail

		  // The query is already set up, it's searching for usernames which start with $search and are not $names (may be empty)
		  // the main table is {user} a

		  // for example, add a join on a table where the user connections are stored
		  // and specify that only users connected with the current user should be loaded
		 $fragments['inner_join'] = 'INNER JOIN {flag_friend} f ON (f_friend.uid = u.uid AND (f.uid = %d OR f.friend_uid = %d ))';
		  $fragments['query_args']['join'][] = $user->uid;
		  $fragments['query_args']['join'][] = $user->uid;
		}

// This blocks messages between users, who are not related, it uses universal_relation_api so in theory should work with User Relationships and FriendsList modules, tested with User Relationships.

function mymodule_privatemsg_block_message($author, $recipients) {
  $blocked = array();
  foreach ($recipients as $recipient) {
    if (!module_invoke_all('socnet_is_related', $author->uid, $recipient->uid)) {
      $blocked[] = array(
        'uid' => $recipient->uid,
        'message' => t('!name is not a friend of yours.', array('!name' => $recipient->name))
      );
    }
  }
  return $blocked;
}

But that didn't work and I got the following error:
Parse error: syntax error, unexpected T_VARIABLE, expecting ',' or ';' in /Users/donalddonckers/Sites/stuffbase1/sites/all/modules/pm_flagfriend/pm_flagfriend.module on line 14

Many thanks,
Donald

berdir’s picture

You are missing a ; after the global $user line.

$fragments['inner_join'] = 'INNER JOIN {flag_friend} f ON (f_friend.uid = u.uid AND (f.uid = %d OR f.friend_uid = %d ))';

I think this will not work correctly. You always join on the uid column but then check the if uid OR friend_uid is you. However, only the second check makes any sense because the first will never by true except for the current user. Oh, and you are mixing f_friend and f...

Instead, try this. Either join on uid and check friend_uid OR join on friend_uid and check uid (untested!)

$fragments['inner_join'] = 'INNER JOIN {flag_friend} f ON ((f.friend_uid =u.uid AND f.uid = %d) OR (f.uid = u.uid AND f.friend_uid = %d ))';

For even quicker replies, you're welcome to join #drupal and ping me there. I'm always trying to help especially those that try something themself and read the documentation :)

Donaldd’s picture

Thanks again Berdir! :)

I tried what you said but I'm still getting an error, but this time it's on the next line.

Parse error: syntax error, unexpected T_VARIABLE in /Users/donalddonckers/Sites/stuffbase1/sites/all/modules/pm_flagfriend/pm_flagfriend.module on line 15

What I came up with was this:


function fp_privatemsg_privatemsg_sql_autocomplete_alter(&$fragments, $search, $names) {
	global $user;
		// Extend the query that searches for usernames

		  // $fragments is explained in the api documentation in detail

		  // The query is already set up, it's searching for usernames which start with $search and are not $names (may be empty)
		  // the main table is {user} a

		  // for example, add a join on a table where the user connections are stored
		  // and specify that only users connected with the current user should be loaded
		$fragments['inner_join'][] = 'INNER JOIN {flag_friend} f ON ((f.friend_uid = u.uid AND f.uid = %d) OR (f.uid = u.uid AND f.friend_uid = %d ))';
		  $fragments['query_args']['join'][] = $user->uid;
		  $fragments['query_args']['join'][] = $user->uid;
		}

// This blocks messages between users, who are not related, it uses universal_relation_api so in theory should work with User Relationships and FriendsList modules, tested with User Relationships.

function mymodule_privatemsg_block_message($author, $recipients) {
  $blocked = array();
  foreach ($recipients as $recipient) {
    if (!module_invoke_all('socnet_is_related', $author->uid, $recipient->uid)) {
      $blocked[] = array(
        'uid' => $recipient->uid,
        'message' => t('!name is not a friend of yours.', array('!name' => $recipient->name))
      );
    }
  }
  return $blocked;
}

Any suggestions?

berdir’s picture

Well, first, you're hooks have the wrong name. They need to start with the name of your module, I assume that's pm_flagfriend.

So instead of fp_privatemsg and mymodule, you need to use pm_flagfriend, for example "function pm_flagfriend_privatemsg_sql_autocomplete_alter...".

Also, I even copied your code in a file and let it parse by PHP, which worked fine. Are you sure that this is the same code as in your file?

Donaldd’s picture

Thanks Berdir!

Now I'm no longer getting an error, but the autocomplete field still shows all the users of the site and when you try to send a private message you get the message: he's not a friend of yours' even if he is. :s

berdir’s picture

I guess you have to start debugging then. Add a drupal_set_message('bla') call to the privatemsg_autocomplete_alter() and make sure it is called when you insert a name in the autocomplete to field (refresh page to check).

The message will be blocked if no module returns socnet_is_related information, does flagfriend actually implement these hooks? You might need some sql queries similiar to above then instead.

berdir’s picture

Status: Postponed (maintainer needs more info) » Fixed

Closing this since there was no feedback for a month, feel free to re-open this if you have any questions.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

kmajzlik’s picture

function pm_flagfriend_privatemsg_sql_autocomplete_alter(&$fragments, $search, $names) {
  global $user;

  $fragments['inner_join'][] = 'LEFT JOIN {flag_friend} f ON ((f.friend_uid = u.uid AND f.uid = %d) OR (f.uid = u.uid AND f.friend_uid = %d ))';
  $fragments['where'][] = 'u.uid != ' . $user->uid;
  $fragments['query_args']['join'][] = $user->uid;
  $fragments['query_args']['join'][] = $user->uid;
}

works well for me.

harrrrrrr’s picture

drupal 7:

function yourmodule_query_privatemsg_autocomplete_alter($query) {
  global $user;
  $query->innerJoin('flag_friend', 'ff', 'ff.uid = u.uid AND ff.friend_uid = :user2 OR ff.friend_uid = u.uid AND ff.uid = :user2', array(':user2' => $user->uid));
}
plessas’s picture

Based on the code segments provided in previous posts, an approach for D7 could be the following:

function pm_flagfriend_query_privatemsg_autocomplete_alter($query) {
  global $user;
  $query->innerJoin('flag_friend', 'ff', 'ff.uid = u.uid AND ff.friend_uid = :user2 OR ff.friend_uid = u.uid AND ff.uid = :user2', array(':user2' => $user->uid));
}

function pm_flagfriend_privatemsg_block_message($author, $recipients, $context = array()) {
  $blocked = array();
  foreach ($recipients as $recipient_id => $recipient) {
	$result = db_query('SELECT count(uid) FROM {flag_friend} WHERE uid = :authorid AND friend_uid = :recid OR uid = :recid AND friend_uid = :authorid', array(':authorid' => $author->uid, ':recid' => $recipient->uid));
	if ($result->fetchfield() == 0) {
      $blocked[] = array(
        'recipient' => $recipient_id,
        'message' => t('Not allowed to write private messages to non friends'),
      );
    }
  }
  return $blocked;
}

This approach has the following problem (not sure if it is a problem!): If user A starts a discussion with B and C, which are his friends, but B and C are not friends, B can't receive the messages from C during this conversation and vice versa.
So if one wants to allow sending messages to non friends in the context of a conversation that has already started the code could be:

function pm_flagfriend_query_privatemsg_autocomplete_alter($query) {
  global $user;
  $query->innerJoin('flag_friend', 'ff', 'ff.uid = u.uid AND ff.friend_uid = :user2 OR ff.friend_uid = u.uid AND ff.uid = :user2', array(':user2' => $user->uid));
}

function pm_flagfriend_privatemsg_block_message($author, $recipients, $context = array()) {
  $blocked = array();
  if(!isset($context['thread_id'])){
	  foreach ($recipients as $recipient_id => $recipient) {
		
		$result = db_query('SELECT count(uid) FROM {flag_friend} WHERE uid = :authorid AND friend_uid = :recid OR uid = :recid AND friend_uid = :authorid', array(':authorid' => $author->uid, ':recid' => $recipient->uid));
		
		if ( $result->fetchfield() == 0 ) {
		  $blocked[] = array(
			'recipient' => $recipient_id,
			'message' => t('Not allowed to write private messages to non friends'),
		  );
		}
	  }
  }
  return $blocked;
}