Hey guys,

some users have expressed suggestion that it would be great if FriendList and Privatemsg modules had some kind of integration. I also think that would be awesome. However, from what I understand, Friendlist can't integrate with Privatemsg independently. Actually, all Friendlist can do is - provide relationship data and privatemsg should do appropriate filtering (only allow messaging friends?). Unless I am misunderstanding how things work in Privatemsg.

Anyways,

if you guys are up to it, whatever support you will need from FriendList module (additional API methods etc.) - please feel free to bug us and we will gladly add those.

Cheers

Irakli

CommentFileSizeAuthor
#27 pm_friendlist_integration.zip1.25 KBzanhsieh
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

irakli’s picture

Here's the original issue thread that started the discussion: http://drupal.org/node/351253

irakli’s picture

Berdir’s picture

I started to document our API at http://drupal.org/node/369399, but it's still under development and the documentation isn't complete yet.

As soon as the api patches are commited, we can start with the integration, that could either be part of your module or be in an own submodule, for example privatemsg_friendlist.

Possible things:
- Limit the autocomplete function to display only friends (the query needs to be rewritten to use our own query builder to allow that)
- Block recipients which are not friends
- Only display send message links if users are not blocked/friends

Feel free to add what features you could think of, I don't know the friendlist module yet.

NaheemSays’s picture

I think most of this functionality belongs IN friendslist rather than privatemsg or a third module. All except for:

- Limit the autocomplete function to display only friends (the query needs to be rewritten to use our own query builder to allow that)

The autocomplete may need to be rewritten to allow control of usernames suggested- maybe linked to the block user messages hook? (I think that this may not be easily possible due to performance issues/overhead?)#

Actually, query builder should allow this without calling the hook.

- Block recipients which are not friends

This could be added via the current blocking hook to the friendslist - block message if the author and the recipient are not friends.

- Only display send message links if users are not blocked/friends

I assume you mean the link on the profile? That should be able to be updated to invoke the blocking hook to see if messages would be allowed to be sent.

Berdir’s picture

The autocomplete may need to be rewritten to allow control of usernames suggested- maybe linked to the block user messages hook? (I think that this may not be easily possible due to performance issues/overhead?)#

Yes, it's definitly better to use the query builder for that.. imagine a site with 100k users and you have two friends. Checking each single user would take endless :) I will rewrite that query...

This could be added via the current blocking hook to the friendslist - block message if the author and the recipient are not friends.

Yes.

I assume you mean the link on the profile? That should be able to be updated to invoke the blocking hook to see if messages would be allowed to be sent.
That's already part of my api patch.

Berdir’s picture

Status: Active » Postponed (maintainer needs more info)

Feel free to use http://drupal.org/node/373044 as a start for integrating your module with Privatemsg. If there are any question, just ask and I'll try to improve the example.

andypost’s picture

Suppose http://drupal.org/project/drupal_universal_relation_api covers all needs with integration for FL & UR

Berdir’s picture

No, actually not, not for the autocomplete feature...

What our hook currently allows is to extend/change the query that loads the usernames.. the universal_relation_api can't do that, as the schema for each friendlist module is different.

The solution has, as always, pro's and con's..

+ Each module can add options to disable/enable and control how the autocomplete works
+ We don't need any special checks in privatemsg (are there relation modules, are there any friends at all, ..). Currently, privatemsg doesn't care if anyone does extend the query or not, it just works

- Each module needs to integrate itself
- It will probably break or do unexpected things if two different friendlist modules are installed, but why would you do that... :)

But yes, each module that supports the universal_relation_api can simply call the hook in the block_message function.

Berdir’s picture

Status: Postponed (maintainer needs more info) » Fixed

Feel free to reopen this issue or ping me in icq if there are more questions about this. I'll close this issue as further steps need to be done in other modules.

Status: Fixed » Closed (fixed)

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

mansspams’s picture

Status: Closed (fixed) » Active

I dont get this... you said "not for the autocomplete feature...universal_relation_api can't do that, as the schema for each friendlist module is different"

universal_relation_api is supposed to give same results from all friends/relationships modules, here is hook...

// return a list of uids of established relationships of $uid, optionally filter by relationship name
function hook_socnet_get_related_users($uid, $relation_name = NULL, $relation_style='all') {};

Why can't this be source for autocomplete?

mansspams’s picture

Here is the code that actually blocks messages to users who are not in relationships. It uses hook from universal_relation_api so should work both with user relationships and friendlist.

Berdir (or anyone else), can you take a look at code and tell me if it is half way decent (particulary $blocked[] part), because i dont know php yet and did this code with trial/error and copy/paste methods. And if it is, maybe you can use it to update handbook page (http://drupal.org/node/373044). Ill try to make module as answer to my own request here (http://drupal.org/node/458046) so if you want to test it, download from there.


function user_relationship_pmsg_options_privatemsg_block_message($author, $recipient) {
	global $user;
  
  foreach($recipient as $key => $val) {
    $reci_uid = $key;
	
  $friends = (array) module_invoke_all('socnet_get_related_users', $uid);
  $friends = array_unique($friends);
  
	if (in_array($reci_uid, $friends)) {
		drupal_set_message(t('Success sent to ID ' . $reci_uid));
	}
	else {
		drupal_set_message(t('Failed to send to ID ' . $reci_uid));
		$blocked[] = array(
		  'uid' => $reci_uid,
		);
		return $blocked;
	}
	
}
}

Berdir’s picture

Version: 6.x-1.0-rc2 » 6.x-1.x-dev

No, you are testing something that is pretty strange, but not if $author is a friend of $recipient.

Note that the code below is untested and is against the -dev version.

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

If that works, feel free to update the howto.

mansspams’s picture

all right, in my first attempt i did it all wrong, but it worked, now your code is much better and i did 2 changes

1) typo in word "array"
2) added return $blocked; because it didn't work without it

and it works beautifully :) Thanks! Ill add this to handbook.

<?php
function user_relationship_pmsg_options_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;
    }
  }
}
?>

Can you make up simple example for autocomplete field? Does it HAVE to be DB query? Ill try it now, but im not coder :)

Berdir’s picture

Oh, yes I forgot the return $blocked. However, you need to move it to the end of the function. Currently, it would only block a single recipient, because it returns the array after the first non-match.

I'll try to come up with a query example later, but I don't know the db schema of the friendlist (or whatever module you use). See also http://blog.worldempire.ch/de/api/group/sql/1

mansspams’s picture

Fixed return $blocked; position in my code and handbook.

I use User Relationships (http://drupal.org/project/user_relationships), but would be cool to make queries for both most popular modules, so also for Friendslist. But lets start with User Relationships as it has larger intall base.

As for db schema, I have no idea, here is query made by views that returns users relationships when i use it as user with UID 1. is this useful?

SELECT user_relationships.rid AS rid,
user_relationship_types_user_relationships.plural_name AS user_relationship_types_user_relationships_plural_name,
users_user_relationships.name AS users_user_relationships_name,
users_user_relationships.uid AS users_user_relationships_uid,
user_relationships.updated_at AS user_relationships_updated_at
FROM user_relationships user_relationships
LEFT JOIN users users_user_relationships ON user_relationships.requestee_id = users_user_relationships.uid
LEFT JOIN users users_user_relationships_1 ON user_relationships.requester_id = users_user_relationships_1.uid
LEFT JOIN user_relationship_types user_relationship_types_user_relationships ON user_relationships.rtid = user_relationship_types_user_relationships.rtid
WHERE (user_relationships.approved = '1') AND (user_relationships.requester_id = 1)
ORDER BY user_relationship_types_user_relationships_plural_name ASC

NaheemSays’s picture

Try messing around with this:

function mymodule_privatemsg_sql_autocomplete_alter(&$fragments, $search, $names) {
  global $user;
  // Filter the usernames so that only approved friends will be shown in the autocomplete.
  $fragments['inner_join'] = 'INNER JOIN {user_relationships} ur ON (ur.requestee_id = u.uid AND m.requester_id = %d)';
  $fragments['query_args'][] = $user->uid;
  $fragments['where'][] = 'ur.approved = 1';
}

You may need to extend it further though and also use the table user_relationship_types (I have not looked at the code to see what happens at the module - just at the schema)

mansspams’s picture

Iv got it to work and as usual I don't understand why, here is code that does the job, i just need to replace hard coded "requester_id = 1" in second line from bottom with dynamic something...

How can I make this better? Any suggestions? Why should I unset things?

<?php
function user_relationship_pmsg_options_privatemsg_sql_autocomplete_alter(&$fragments, $search, $names) {

  unset($fragments['select']);
  unset($fragments['where']);
  unset($fragments['order_by']);

  global $user;

  $fragments['primary_table'] = '{user_relationships}';
  $fragments['select'][] = 'users_user_relationships.name AS name';
  $fragments['where'][] = "name LIKE '%s'";
  $fragments['inner_join'][] = 'INNER JOIN users users_user_relationships ON user_relationships.requestee_id = users_user_relationships.uid';
  $fragments['query_args'][] = $user->uid;
  $fragments['where'][] = 'user_relationships.approved = 1';
  $fragments['where'][] = 'user_relationships.requester_id = 1';
  $fragments['order_by'][] = 'name ASC'; 
}
?>
mansspams’s picture

this technically works correctly. any suggestions on code quality/best practices/Privatemsg way of doing things?

<?php
function user_relationship_pmsg_options_privatemsg_sql_autocomplete_alter(&$fragments, $search, $names) {

  unset($fragments['select']);
  unset($fragments['where']);
  unset($fragments['order_by']);

  global $user;

  $fragments['primary_table'] = '{user_relationships}';
  //$fragments['select'][] = 'user.uid';
  $fragments['select'][] = 'users_user_relationships.name AS name';
  $fragments['where'][] = "name LIKE '%s'";
  $fragments['inner_join'][] = 'INNER JOIN users users_user_relationships ON user_relationships.requestee_id = users_user_relationships.uid';
  $fragments['query_args'][] = $user->uid;
  $fragments['where'][] = 'user_relationships.approved = 1';
  $fragments['where'][] = 'user_relationships.requester_id =' . $user->uid;
  $fragments['order_by'][] = 'name ASC'; 
}
?>
litwol’s picture

Why are you unsetting previous query fragments ?

NaheemSays’s picture

He is unsetting the whole query before creating it again. What needs to eb done is see what parts of that query are previously set and remove them from the above function, and then refactor the rest to use those parts (and the primary table is unlikely to be user_relationships in the original query).

litwol’s picture

Unsetting the whole fragment is too dangerous and not flexible at all. It prevents all modules that execute before this to not be able to interact with privatemsg. This needs to be refactored.

mansspams’s picture

I see, can anyone give me practical examples? Im just a begginner.

mercmobily’s picture

Hi,

I am the creator and comaintainer of FriendList.
First of all, thank you for your help.

Just my 2c: using the universal API is the way to go. I am talking to the FlagFriend developer, and he's willing to implement the Unviersal Friends Api for that too, which would be fantastic. There are no real issues if you have 2 friend modules installed with the universal API, believe it or not :D

I agree that your approach is good: use the universal API in your module, and then delegate the autocompletion to other modules. Nice one.

So, just wondering... what's the status on this one? Has the Universal API part been implemented already? Are we just missing the single module's integration with the autocompletion?

If you need anything, I am here...

Merc.

mansspams’s picture

Hi Merc,

I wrote this without any knowledge of php or mysql, just by trail and error. Here is result http://drupal.org/node/458046. This small module does 2 things - allows sending messages only between users with relationships and feeds pmsg autocomplete field list of users friends. First feature uses universal api, but second is now made for user relationships only, because, as i understand, autocomplete field needs data directly from DB and i could only come this far (user relationships only) so if you could

1) adapt code above so it returns related users from friendlist tables
2) make this code better (i hope you have skills)
3) make some code that detects which module is in use to know which autocomplete alter fuction to use (is this needed at all?)
3) add form alter or menu alter or whatever to have 2 checkboxes that lets turn on/off one or both of these features (is this needed at all?)

then we could call it a day.

P.S. thanks for not quitting drupal ;)

mercmobily’s picture

Hi,

Thank you so much for your work. Now I have to write a blog entry, "Why I am not quitting Drupal -- the users!" :D

Anyhow, Berdir can you give me your insight on this? Specifically, is there an easy way for your module to get the right autocompletion in a pluggable way, so that it's up to the modules to export it? Do you see anything majorly wrong with mansspam's code? I am not asking you to fix it! Just if you see anything that I should fix.

Yes, I am totally happy to make the query for FriendList AND for FlagFriend (which is a fantastic module as well, BTW).

Let me know,

Merc.

zanhsieh’s picture

I read mansspams' advice and made one for friendlist integration. My case is two users could privatemsg each other only on 'TW_BOTH' situation.

oadaeh’s picture

Issue summary: View changes
Status: Active » Closed (won't fix)

This issue is being closed because it is against a branch for a version of Drupal that is no longer supported.
If you feel that this issue is still valid, feel free to re-open and update it (and any possible patch) to work with the 7.x-1.x branch (bug fixes only) or the 7.x-2.x branch.
Thank you.