Hi Ice,
Nice seeing you here :-D
I would love your module to integrate with FriendList (thanks for your help there BTW).
I looked at the code, and it does feel like your module might be better off with a bit of separation.
I think this is a possible path:
* Create an admin form where a relation module is set. I guess Friend, FriendList and User Relationships
* Create a layer around any calls about relations. I looked at the code. I think you'd need the following ones:
- facebook_status_relation_types. This will return the right list according to the module configured above
- facebook_status_get_status_query. This will basically create the right query according to the configured module. The query will then be used by facebook_status_get_ur_status (to be renamed :-D ) which from the line "while ($row = db_fetch_array($user_rel)) {" will be unchanged
Basically, it would be a matter of having 2 or 3 "layering functions" that would do the dirty work according to the installed module.
What do you think?
Merc.
Comments
Comment #1
mariusooms commentedI am shamelessly subscribing to this :)
Comment #2
mercmobily commentedHi,
Sorry, moving this. I am mobing this to the right module!
Merc.
Comment #3
icecreamyou commentedYes, I've been intending to refactor the facebook_status_get_ur_status function for awhile to be more generic. Unfortunately, the way it's written right now has a lot of code specific to UR... but really, it should be rewritten so that it just passes an array of UIDs to facebook_status_get_status. Then we also need a way to choose the relationship type... haven't really thought that one through yet, and it's complicated because one-way and two-way relationships require much different treatment.
But I doubt anyone would have more than one UR*/FL*/etc. module installed at once, so I don't think we need to create a new form for that.
Also, I probably won't get to this for awhile. If you have any inclination to work on it, code will be most welcome.
Comment #4
icecreamyou commentedHi.
I worked on this tonight some, and I'm nearly there, but won't be able to finish until next weekend.
Could you write me three functions?
The first function should return an array of relationship types. It should take the parameter
$advf = FALSEand include something like this:The second function should build an array of the UIDs matching the current user's friends, pass it to facebook_status_get_status(), and return the result of passing that to facebook_status_list_render(). (Don't bother testing with facebook_status_get_status - it won't work.) This function should take the parameters $rtid, $num_results, and $group. Your code will probably conclude something like this:
The last function is for advanced_forum integration. It should take the parameter
$accountwhich is the full user object for the author of the relevant comment. It should then return a true or false value based on whether or not the current user has the correct relationship type with $account. The chosen relationship type is stored invariable_get('facebook_status_advf', -2), and remember that a value of-1indicates "any relationship" and a value of-2indicates "ignore relationships" (just return TRUE). Also keep in mind the difference between mutual and one-way relationships.Thanks. If you need help on any of this I'll try to assist as much as I can.
I still have to figure out how facebook_status is going to decide which friend module to look for at all the various places it needs to know... but that's not terribly difficult. Oh, and I've been developing on 5.x instead of 6.x so I can actually test, so I need to port my changes too. :-P
Comment #5
mercmobily commentedHi,
1)
-------------------------------------
The first function should return an array of relationship types. It should take the parameter $advf = FALSE and include something like this:
--------------------------------------
Are you saying that the array could be something lile
By "relationship types", you mean "friends", "fan" etc. right?
2)
-------------------------------------------------------
The second function should build an array of the UIDs matching the current user's friends, pass it to facebook_status_get_status(), and return the result of passing that to facebook_status_list_render(). (Don't bother testing with facebook_status_get_status - it won't work.) This function should take the parameters $rtid, $num_results, and $group. Your code will probably conclude something like this:
--------------------------------------------------------
Is that alright if I only do the first bit, "The second function should build an array of the UIDs matching the current user's friends,"? But... what's a "friend"? Do you mean "a function that returns the users with the right relation types with the user", where the relation type is passed as a parameter?
3)
--------------------------------------------------
The last function is for advanced_forum integration. It should take the parameter $account which is the full user object for the author of the relevant comment. It should then return a true or false value based on whether or not the current user has the correct relationship type with $account. The chosen relationship type is stored in variable_get('facebook_status_advf', -2), and remember that a value of -1 indicates "any relationship" and a value of -2 indicates "ignore relationships" (just return TRUE). Also keep in mind the difference between mutual and one-way relationships.
--------------------------------------------------
Can you pass the relation type as a parameter?
Please let me know :-D
Merc.
Comment #6
Babalu commentedsubscribing
Comment #7
icecreamyou commented1) Yes, that's what I mean.
2) Yes, that's fine; yes, that's what I mean.
3) The relationship type doesn't need to be passed as a parameter because it's stored in a variable. But if it's easier for you to write it that way, that's fine; I can fix it afterwards.
Comment #8
mercmobily commentedHi,
All of this functionality is pretty much available out of the box from FriendList.
Make sure you get the latest snapshot, I only added friendlist_api_db_statuses_array() recently.
I haven't tested this code... but it should work, and it should be _dead_ easy for you to fix whatever problem since everything is documented like there was no tomorrow.
1)
2)
3)
I am curious to see what the equivalent functions for User Relationships will look like.
Bye!
Merc.
Comment #9
mercmobily commentedHi,
Wooops...
1) Should be:
Comment #10
icecreamyou commentedGood enough for me. I'll see what I can do.
Comment #11
mercmobily commentedHi,
OK thanks!
Merc.
Comment #12
mercmobily commentedHi,
One note.
Maybe this:
Should only show two way relationships -- that is, when friendships are established. What do you think?
Merc.
Comment #13
icecreamyou commentedI have it working one-way for UR types - one-way meaning if the relevant user is a "fan" of another user then the "fan" can see the relevant user's status, but not the other way around.
I think. I might have to check that actually. I'm going to change it to what I wrote above if it's not already that way though.
Comment #14
mercmobily commentedHi,
Get my code working. Once you've done that, I will give you the patch to have it working for one way relationships as well.
It's just an "OR" for the status, but it's better to do things one step at a time.
Merc.
Comment #15
nitram079 commentedI will definitely be subscribing too. Would be exceptional to get these modules working together.
Comment #16
mercmobily commentedHi,
Hang on, Icecream.
We are working on an API that will allow you to ask *any* friend module about relations.
Please just hang on a second... If you follow the "new" way, it won't matter if it's friendlists, friends, or whatever.
Merc.
Comment #17
icecreamyou commentedI doubt it will make a huge difference for my development because I need pretty specific things from the database... so I'll keep doing what I'm doing, and just let me know when the API comes out so I can take a look and implement it if possible.
Comment #18
mercmobily commentedHi,
The idea is to make sure that the API we create is actually totally OK with you. This is a very important point I made in the very discussion about how to write this API.
It will be a little different, but it *should* make it very possible for you to use it to do what you're trying to do -- knowing that your users will be able to deal with _any_ relationship module.
Bye,
Merc.
Comment #19
icecreamyou commentedFair enough. I'll implement the API when it gets released. Until then I'm not going to hold anyone else up... if I find the time to get a release out before you do. ;)
Comment #20
icecreamyou commentedJust a heads-up: I've restructured the API in the 5.x branch. It will be at least Wednesday before I port the changes to 6.x - more likely Friday or Saturday. At that point I'll finally add in the Friendlist integration, which may not happen until as late as Sunday.
Comment #21
mercmobily commentedHi,
Ok. If you could hold on _just_ a little longer... I am waiting to sort out some things with Alex.K and define a really good generic API, and it would be _fantastic_ if you were the first module developer to use it.
Thanks,
Merc.
Comment #22
icecreamyou commentedThat's fine. I'm pretty sure I'll be ready for integration next weekend, but there's no rush really to actually integrate until there's a simple and effective way to do so.
Comment #23
mercmobily commentedHi,
Icecream... we did it!
You can now actually do things without ever worrying what Relation API is used.
http://drupal.org/project/drupal_universal_relation_api
It's hook-based. So, you don't even have to install a module. Friendlist CVS supports it, although I haven't fully tested it yet.
PLEASE use it for your module... it would be the very first one to integrate properly... and it would be quite an achievement.
Bye,
Merc.
Comment #24
icecreamyou commentedOkay. I'll see what I can do... it may or may not be this weekend, I'm getting a lot more work than I expected.
Comment #25
icecreamyou commentedI've just ported changes from the 5.x branch so the next thing on my list (which I'll get to either tomorrow or next weekend) is integration with this new API. If the "coming soon" documentation was better by then it would probably help... ;)
Comment #26
mercmobily commentedHi,
OK, I'll do my best. However... what's there now should really be enough!
Please note that the Friendlist implementation of the layer is not fully tested yet. I am planning on finishing testing it and release FriendList 1.x as soon as I know that the API actually works.
Bye!
Merc.
Comment #27
icecreamyou commentedOkay, but I'm confused about why you'd use the $relation_name instead of $rtid. And I'll have to look at module_invoke_all() some more.
Comment #28
mercmobily commentedHi,
We use the name because this is a generic API, and each api might have different rtids for different relation types. The most universal, ID-free piece of information is the relation type name!
Bye,
Merc.
Comment #29
icecreamyou commentedI don't understand. I don't think there are any APIs that don't use an RTID, and if you're just doing a database call why should it matter what relationship type the RTID stands for? Anyway it's *way* easier to get the RTID from user input; otherwise every developer has to do an extra database call using the RTID to find out what the name of the relationship type is.
I used to use Buddylist, I now use User Relationships, and I've looked at FriendList some. To the best of my knowledge, they all use RTIDs the same way.
Comment #30
mercmobily commentedHi,
The problem is that you might have two different APIs with two different relation type tables and they might have totally different relation types, but with two different IDs.
This API is rtid-agnostic, and that's a good thing.
Sorry, why do you need to know the relationship id precisely?
Merc.
Comment #31
icecreamyou commentedI still don't understand why that's a problem. You're not going to be looking at more than one table at once. There is no use case where more than one relationship module is going to be installed at once. If there is, it doesn't make any sense to get relationships from all modules at once: even assuming that there's a relationship in both modules with the same name, there's no guarantee that they are intended for the same thing (and in fact if you have two modules installed they probably aren't intended to do the same thing).
The RTID is far easier to work with in terms of form elements and such, and there's no reason to worry about capitalization. Also it lets you do custom DB calls if you need to reference other tables; because you'll never find a string used with Joins.
Can you explain a case where it would be a bad thing to have the RTID in the API? Maybe that would clear it up for me.
Comment #32
mercmobily commentedHi,
This is the wrong assumption: "There is no use case where more than one relationship module is going to be installed at once". Flag, with no rtid and only one virtual relation type called "flag", could be used in conjunction with FriendList. I know someone who's doing this right now.
This "global" API is an abstraction API. It's there to hide the inner workings of the underlying module(s).
Modules which adhere with the API make sure that theree aren't two relation types with the same name.
The problem would be there if you decided to use the "flag" module for 1-way relations, and friendlist for 2- way relations. (Yes, flag will be one of the modules supporting this API). No, flag doesn't have rtid.
Second case: you want to use user relationships for 1-way relations (because it allows you to define them so that you need confirmation) and friendlist for 2-way ones. What if both of them have the same for the two different modules?
With this:
"The RTID is far easier to work with in terms of form elements and such " -- Not sure why...
"and there's no reason to worry about capitalization" -- ditto
"Also it lets you do custom DB calls if you need to reference other tables; because you'll never find a string used with Joins."
Can you give me an example scenario where you reference other tables? And... if you have indexes, what's wrong with joins on strings?
Merc.
Comment #33
icecreamyou commentedWhat if you had a flag called "Friend" to mark that, say, an image was of one of your friends; and a FriendList relationship type also called "Friend"? Are you telling me Flag and FriendList won't let that happen?
Same thing--how are UR and FL supposed to know if there's already a relationship type with the same name? Basically, my point is that you end up with the same issues whether you're using RTIDs or strings, they're just slightly different cases.
As far as using RTIDs with form elements, they make it easier to manipulate the order of elements in '#options' arrays. Not a big deal, really.
For DB calls, the reason it's an issue is because if you have a relationship type (say "friend") and you want to find out whether two users are friends (I know there's an API call for this, but it's an example) in the {relationships} table then you'd have to JOIN on the {relationship_types} table; but if you had the RTID you could skip the join and just query the {relationships} table. It's an efficiency concern, albeit a relatively minor one. It's why indexes are almost always numeric; the same reason why any kind of ID exists.
There's another rare but potentially critical issue with using strings: the string '0' is evaluated to FALSE, so you could end up with cases where modules need to check if a relationship type exists and the wrong result is returned.
Comment #34
mercmobily commentedHi,
------------------------------------------------------
What if you had a flag called "Friend" to mark that, say, an image was of one of your friends; and a FriendList relationship type also called "Friend"? Are you telling me Flag and FriendList won't let that happen?
-------------------------------------------------------
Correct.
(This is not true _right now_, but it will be soon).
-----------------------------
Same thing--how are UR and FL supposed to know if there's already a relationship type with the same name?
------------------------------
By using the generic API we have just designed.
------------------------------------------------
Basically, my point is that you end up with the same issues whether you're using RTIDs or strings, they're just slightly different cases.
------------------------------------------------
This is incorrect.
-------------------------------------------------
As far as using RTIDs with form elements, they make it easier to manipulate the order of elements in '#options' arrays. Not a big deal, really.
--------------------------------------------------
OK.
------------------------------------
For DB calls, the reason it's an issue is because if you have a relationship type (say "friend") and you want to find out whether two users are friends (I know there's an API call for this, but it's an example) in the {relationships} table then you'd have to JOIN on the {relationship_types} table; but if you had the RTID you could skip the join and just query the {relationships} table. It's an efficiency concern, albeit a relatively minor one. It's why indexes are almost always numeric; the same reason why any kind of ID exists.
--------------------------------------
This is a non-issue. If you want to know about relations, you use the API. if you are doing anything other than that, then you are doing something very wrong.
-----------------------------------------------------------------
There's another rare but potentially critical issue with using strings: the string '0' is evaluated to FALSE, so you could end up with cases where modules need to check if a relationship type exists and the wrong result is returned.
-----------------------------------------------------------------
Please have a look at how the API is used. For boolean results, all you have to do is count how many results you get. if count(...) > 0 then it's a "true" (because at least one of the invoked module returned a "yes", which makes it a yes).
We spent quite a bit of time designing this, and went through _all_ of this. I guess I will use this issue as an explanation on why the API works the way it works...!
Merc.
Comment #35
icecreamyou commentedI'll take your word for it, except for the 'string zero' issue. The reason this could be a problem is if you're evaluating things in a loop, so you don't know what's going to come up. Obviously there's no reason to check if a relationship exists if you've just grabbed it using the API. Still, not really worth worrying about I suppose; and really if it's ever a problem it's a problem in the modules themselves.
Anyway, it's going to take longer than expected to get this into facebook_status. I find myself having less and less time these days, and I have to test that the changes ported from 5.x don't break anything in 6.x.
Comment #36
mercmobily commentedHi,
----------------------------------
I'll take your word for it, except for the 'string zero' issue. The reason this could be a problem is if you're evaluating things in a loop, so you don't know what's going to come up.
-----------------------------------
I might have missed something here... I am interested in working out of there is actually a problem with the API.
Can you please explain it a little bit more, with a practical example in terms of code that might have a problem?
-----------------------------------
Anyway, it's going to take longer than expected to get this into facebook_status. I find myself having less and less time these days, and I have to test that the changes ported from 5.x don't break anything in 6.x.
------------------------------------
That's totally fine :-D
The important thing is that you know "how it's done". I would love to use this module as a possible use case for the API, since it would be the only one that uses it _fully_ at least at the beginning.
Thanks for listening!
Merc.
Comment #37
icecreamyou commentedEh... I'm going blank. I had a good example but I forgot what it is. I'll post back if I remember it.
Comment #38
mercmobily commentedHi,
OK :-D
Merc.
Comment #39
icecreamyou commentedOkay, here's one example I'm encountering ATM.
How am I supposed to assign a default relationship to a variable that I'll need to use with the API? If I can't assign a default relationship, none of the relationship stuff will work until a relationship is expressly assigned. That's a major problem for me right now, because my settings form isn't working. :-p
A *good* alternative for me would be if there was a way that I could pass the RTID to the API and get the relationship name returned. This would also save a *lot* of effort on my part rewriting a good part of the module.
Comment #40
mercmobily commentedHi,
Please explain this:
> How am I supposed to assign a default relationship to a variable that I'll need to use with the API?
$variable = 'relationship_name';
> If I can't assign a default relationship, none of the relationship stuff will work until a relationship is expressly assigned.
I don't understand this part at all.
> That's a major problem for me right now, because my settings form isn't working. :-p
I am not sure what you mean. What's stopping you from assigning a string to a settings variable?
merc.
Comment #41
icecreamyou commentedA string has to be arbitrary. For example I could assign the string 'Friend' but a lot of people aren't going to have a relationship type called 'Friend.' If I assign a blank or arbitrary string, I get no results until the variable is explicitly set. But if I'm using RTIDs instead, I can assign zero as the default because there's pretty much always going to be a relationship type of zero if there are relationship types at all.
As I'm thinking about this more, I suppose I could invoke the function that grabs relationship types and just use the first one that comes up, but that takes *way* more work each time I want to use variable_get().
Comment #42
mercmobily commentedHi,
"But if I'm using RTIDs instead, I can assign zero as the default because there's pretty much always going to be a relationship type of zero if there are relationship types at all."
I think this assumption is very dangerous. A person might (and possibly will) create a new relation type, then delete the two defualt ones, and bang, you no longer have RTID 0.
Merc.
Comment #43
icecreamyou commented*sigh*
Am I supposed to use 'name' or 'plural_name'? Both are stored in the database at least for UR. Which one gets returned and which one do I pass?
Comment #44
mercmobily commentedHi,
What gets stored in the database shouldn't matter to you.
The default API returns "Name", rather than "plural name". However, since your interaction with the underlying layer should be absolute 0, this shouldn't matter to you!
Merc
Comment #45
icecreamyou commentedIt becomes relevant when the name is displayed. I think there's another reason too that prompted me to ask but I'm not at my work PC right now so I can't check.
Comment #46
mercmobily commentedHi,
Ah. This could well be something that the generic API might lack. Although, the "display part of the deal is a _very_ tricky one. Friend modules go to great lengths to get messages displayed correctly in different situations. Words like "associate", "friend", and some others need very different grammatical structures.
Merc.
Comment #47
icecreamyou commentedI just checked - the reason I needed to know was that I have a block called "[plural-relationship-name]' recent status updates," i.e. "Friends' recent status updates."
So... maybe there should be a new function to grab the plural form, if it exists?
Comment #48
mercmobily commentedHi,
I looked into this. It's not super trivial to do this with the API. Using hooks is fine for booleans and for array-like results. But it's a bit tricky to do something like "convert this".
Can you please try and live without the plural form for now? (Rewording the block title should do the trick, I hope!)
Merc.
Comment #49
icecreamyou commentedI can't think of another name. For the sake of argument, let's say our relationship type is called 'Friends.' My original title would then be "Friends' Recent Status Updates." The only substitute I can think of that wouldn't require the plural would be to use a neutral word like "Relationship" which doesn't work out too well.
Comment #50
mercmobily commentedHi,
you can't do that, because in Drupal when you call a hook, you don't know how many modules will respond, and as a result an *array* will be returned -- one for each responding module.
So, while hooks are totally fine for boolean functions (that's why we count() the results) and for functions that return lists (see the API documentation for info about that), the generic API won't work to convert string A to string B.
What about "Recent status updates for relation 'XXX'" ?
Merc.
Comment #51
icecreamyou commentedYes, but module_invoke_all does an array_merge on the result, so you'll just get one array returned. It's a bad idea to have two relationship types with the same name, so modules invoking the hook can just use the first item in the array. The only reason I used an array in the hook I wrote above was to facilitate array_merging. (But you're right, the key would have to be numeric.)
That name is way too long and clunky, and would definitely cause a "WTF?" for me if I saw that on a site.
I'm postponing this because it's taking up more time than I have right now and I'm having trouble getting it all sorted out. Sorry.
Comment #52
mercmobily commentedHi,
No worries. The API is there when you need it.
One note: you realise that people can override a block's name, right? This means that they can set it to whatever they like.
Let me know if you have any other questions or doubts!
Merc.
Comment #53
icecreamyou commentedYes, although the goal of any module should be to 'just work' without configuration... ;)
...theoretically if the module is well-written any text should be possible to change...
Comment #54
andypostsubscribe
Comment #55
mercmobily commentedHi,
"Yes, although the goal of any module should be to 'just work' without configuration... ;)"
Which it would...!
"...theoretically if the module is well-written any text should be possible to change..."
In this case, there is a definite spot to change a block title. Having it in two spot would be, in my opinion, unnecessary duplication.
However, this is probably becoming philosophical...
Merc.
Comment #56
igorik commentedsubscribe
Comment #57
PGiro commentedsubscribe
Comment #58
cyberskier commentedsubscribe
Comment #59
icecreamyou commentedComment #60
mr.andrey commentedsubscribing - would love to see this happen, especially for with the Flag module.
Comment #61
icecreamyou commentedI don't know how quickly I'm capable of doing it, but I have some free time now finally, so I'm definitely planning to get to this soon.
Comment #62
icecreamyou commented*Sigh*
As much as I would love to do this, it's more complicated than I'm willing to tackle at the moment. I may or may not decide to work on this in the next two weeks or so; after that, there's not much chance I will have time to add this feature without help. Of course, if anyone can write a patch, that would be awesome, and I'll be greatly impressed that you understand the code well enough to do so because this stuff is by far the most complex logic in the module and my approach didn't make it any easier. :P Writing a patch would basically involve searching for every instance of 'ur' and 'rel' in the code and looking to see how the User Relationships implementation was done.
Comment #63
irakli commentedHey,
late to the discussion, have not gone through all the messages. Just wanted to mention that integration with this module is very high on the priorities of FriendList module and we will renew efforts very soon.
Thank you.
Comment #64
icecreamyou commentedSounds good. If someone can hack out a patch that works and looks good I'll commit it. I am planning to completely rebuild the module at some point though... I don't know how long it will be before I get around to that, but this will definitely be a priority when the time comes if it hasn't already been implemented.
Comment #65
igorik commentedIt would be great now when FacebookStatus is in version 1.0. when it will be have friendlist integration or to have some news about it.
I really like to use it but I am really missing friendlist integration.
Thanks
Igorik
http://www.somvprahe.sk
Comment #66
icecreamyou commentedFirst of all, if someone writes a patch for FriendList integration in FBS 1.x, I will probably commit it.
However, I'm going to be spending most of my effort rewriting a good part of the module for the 2.x branch. FriendList integration will be in the 2.x branch, but that will likely be at least a few months away; and no one can work on FL integration in FBS 2.x until the API is finalized.
I apologize for the delay, but it is unfortunately necessary to make the module as versatile as possible.
That said, it's not very hard to manually integrate FBS with FL. The API functions for both modules should be simple enough for almost any use case. If anyone needs help doing this, feel free to open a support issue and I will help as much as I can.
Comment #67
icecreamyou commentedThe 2.x branch is now practically complete, and is in CVS/dev. A few things remain before I will create an official release, and Views integration is one of those things. I believe that Views integration should take care of this issue as well (as discussed here) so I'm marking as duplicate.
I do need some help with Views integration though. We'll see how that works out.