Problem/Motivation
To enable #34496: [meta] Add Flag module to allow users to subscribe/unsubscribe without posting a comment to be deployed on drupal.org, we need some flag integration in tracker2. We need to make sure that anything you "follow" also appears in your tracker page, and that if you unfollow, it disappears from your tracker page.
Proposed resolution
There are two parts to this:
- Automatically add/remove the appropriate records to tracker2's tables whenever a user flags/unflags a node to be followed. This can be done via hook_flag().
- Automatically flag any content that's being added to tracker2's tables (e.g. new nodes and comments) so that by default, if you create a node or comment on it, you're automatically following that node.
So, we're adding a new admin setting in tracker2 if flag.module is enabled that allows site admins to specify a flag that should be used for this functionality. If that setting is defined, we watch in our hook_flag() implementation to see if a user adds that flag to a node, and if so, we insert the appropriate records into the tracker2 tables as if that user had posted a comment. If the user unflags a given node we remove it from tracker2's tables (even if they actually authored or commented on the node) so that the node is no longer listed in their tracker page. If a user posts a comment or creates a node, we check this setting, and if the flag is configured to be compatible with the node in question (flag lets you restrict flagging to specific node types) then we flag the node on behalf of the user. We're not worrying about flags in the case where a node is deleted since flag.module already cleans up flags in that case. We're also not dealing with flags when a comment is deleted, since it makes sense for the user to opt-out of following in that case (citation needed *grin*).
Original report by dww
As part of #34496: [meta] Add Flag module to allow users to subscribe/unsubscribe without posting a comment we want to add some flag integration to tracker2. In particular:
A) Add tabs at user/N/track/subscriptions and tracker/UID/subscriptions for all nodes that you've subscribed to.
B) We move the current user/N/track tab to user/N/track/posts and we debate later which one is the default tab at user/N/track. ;)
David says he has no time to work on this patch this week, but would be happy to review/commit said patch if someone else produces the code. Any volunteers?
Thanks,
-Derek
Comments
Comment #1
sirkitree CreditAttribution: sirkitree commentedMy first inclination is to just use views for this. Was this discussed and decided against?
Comment #2
dww@sirkitree: Please look at the project this issue is assigned to. drupal.org uses tracker2 instead of the core tracker not because of the UI, but because of a performance issue. Core's tracker relies on queries that would be melting the d.o DB servers. Hence, tracker2 denormalizes the date we need so we can run these queries in a performant way.
The issue queue views will remain views and will (probably) just use flag's views integration.
However, the "Your posts" page and dashboard widget are going to continue to be powered by tracker2 and so we still need to add flag support for tracker2 to make this work.
In fact, once this is done, thanks to #928110: Expose tracker2 tables and columns to views we actually could use views for this. ;) And, we might be able to leverage tracker2's tables to prevent the default issue views from generating DB-melting queries.
But, before any of that can be a reality, we need to look into adding records to {tracker2_user} whenever a user flags a node to subscribe to it, which is what this issue is for.
Cheers,
-Derek
Comment #3
sirkitree CreditAttribution: sirkitree commentedAwesome, thanks for the quick reply.
The first item being the user/N/track/subscriptions and tracker/UID/subscriptions callbacks, when we go to render the page, we need to know what flag we're operating upon. So - more questions:
1) has the name of this flag been determined?
2) is this flag already present and is it in the database or is it exported to a particular module?
3) if not, are we prepared to either a) include that flag here or b) have a new module that simply defines the subscription flag which tracker would then have a dependency upon
The other thing would be to generalize the flag integration and simply have a setting on the tracker2 config page to choose what flag we want to operate upon - which would require an variable_get on our page render.
Thanks for any guidance you can give here.
Comment #4
sirkitree CreditAttribution: sirkitree commentedOk, I went ahead without the answers to some questions here, but I think this is a good start. Patch simply adds a new form field to the tracker2 configuration which allows you to choose the flag you want to use, then based on that decision, spits out a new 'Subscriptions' column in any tracker page.
I'd like to discuss the merits of this, as it seems perfectly reasonable to me that I could subscribe to an issue from the 'recent posts' list, or really from any list of issues.
By default, flag is checking against the current user who is viewing it - so it always reflects 'my' subscription no matter what list I'm looking at. I don't really think this too confusing if I'm looking at another users tracker and I want to subscribe to something that person is involved in.
This would remove the need for any new menu callbacks I think.
Comment #5
sirkitree CreditAttribution: sirkitree commentedMarking needs review.
Comment #6
sirkitree CreditAttribution: sirkitree commentedHere are some screenshots of what's changed by this patch:
Tracker2 configuration screen:
Any listing provided by Tracker2:
Comment #7
dwwThanks for trying to move this forward. However, that's not the point of this issue at all. We don't need to let people subscribe to stuff that's appearing in their "Your posts" list. They're already subscribed. ;) We need things they've flagged to show up as if they had created the post or authored a comment. Hence, what I said in #2. This issue should exclusively be about adding records to {tracker2_user} when you flag a node that you want to subscribe to it. To that end, we probably do still need the tracker2 setting provided by your patch, so tracker2 knows which flag to do this operation on. But, that's the only UI change we want. And we want to ensure that the flag support is optional, not a new dependency for tracker2.
Thanks!
-Derek
Comment #8
philbar CreditAttribution: philbar commentedComment #9
sirkitree CreditAttribution: sirkitree commentedAh, ok. So then what we're really looking for here is a few things:
1. Flag when _tracker2_add() is called.
2. Unflag when _tracker2_remove() is called.
3. Implement hook_flag() so that when our flag is flagged or unflagged, _tracker2_add() or _tracker2_remove() is called.
4. Make sure this doesn't loop...
The following patch keeps the tracker2 settings configuration to determine what flag we're operating upon, removes the Subscriptions column from the listings that the last patch had added, and adds in an implementation of hook_flag in order to execute _tracker2_add() or _tracker2_remove() based on the 'flag' or 'unflag' event. I placed comments where we should execute the flag within _tracker2_add() and _tracker2_remove(). We should probably use the actions integration for this in the flag_actions.module - but I haven't implemented this just yet - work in progress...
Comment #10
sirkitree CreditAttribution: sirkitree commentedFleshing out the un/flagging within _tracker2_add() and _tracker2_remove() with a new wrapper function called _tracker2_flag()
This now works two ways:
1) You use the subscribe flag and the node shows up in your list.
2) You comment and the node shows up in your list, also changing the 'Subscribe' flag to 'Unsubscribe'.
One usability aspect to discuss is within the _tracker_remove() where a user keeps their subscription:
Ideally, even if I've commented on an issue and I hit that 'Unsubscribe' flag - I definitely want to not be subscribed any more.
We should either remove access to this flag based on the criteria we're using here and tell the user so, or make this criteria configurable. That might be another issue however. Please review.
Comment #11
sirkitree CreditAttribution: sirkitree commentedHrm, helps to attach the patch I suppose...
Comment #12
moshe weitzman CreditAttribution: moshe weitzman commentedCode looks perfectly reasonable and implements what dww describes. Since #928110: Expose tracker2 tables and columns to views is done, we are getting close :)
Comment #13
quotesBro CreditAttribution: quotesBro commentedPatch #11 works fine for me.
But I believe once we set Subscription Flag in Tracker2 settings we need to make all old nodes from {tracker2_user} flagged. So users will be able to subscribe and unsubscribe to any node.
Comment #14
ohnobinki CreditAttribution: ohnobinki commented+
Comment #15
quotesBro CreditAttribution: quotesBro commentedI was wrong in comment #13 - it seems to not gonna work. I think we need a separate feature that allows to unsubscribe from tracking nodes.
Whatever, patch works fine and the only thing we don't actually need in it is a call of dsm() function :)
Comment #16
BerdirThere are some nasty trailing whitespaces in the patch but I guess that can be fixed during the commit.
Edit: Oh, I haz something useful to say too!
As mentioned in #1007094: Port Tracker2 to Drupal 7, the improvements from tracker2 have been added to core, so I guess the D7 version of this patch needs to go into flag.module?
Comment #17
anarcat CreditAttribution: anarcat commentedthis looks like debugging code that should be removed before commit, but I don't think it's worth marking needs work just for that one line.
this otherwise looks fine and could be a simple and elegant solution to #34496: [meta] Add Flag module to allow users to subscribe/unsubscribe without posting a comment or is something else missing? It sure looks like all pieces are in place to finally kill +1 comments...
Powered by Dreditor.
Comment #18
Fabianx CreditAttribution: Fabianx commentedReviewed the patch:
* It works fine and flag integration can be really useful for tracker2 and is really optional.
The patch does in human words:
* "Whenever a subscription is added or deleted the corresponding flag is synchronized."
* "Whenever the flag is set to unset one a node the tracker subscription is added or removed."
Re-roll of patch with git against latest 6.x-1.x branch with debug code and trailing white space removed.
Supplied also an interdiff patch to show differences.
Now we just need this to be commited by dww or David Strauss.
Edit: Found a problem:
* The _tracker2_flag function is ignoring the $uid parameter and always flagging as the current user. While this may be valid today, it might not be valid when the code changes.
Marked as "needs work".
Best Wishes,
Fabian
Comment #19
Fabianx CreditAttribution: Fabianx commentedNew patch based on the review from #18 with $flag->flag using $account for $uid parameter.
Please review again.
Best Wishes,
Fabian
Comment #20
catchThere is some nitpicky stuff like comments not having full stops.
Also
This should use node_load() - there is a 99% chance of the node being loaded during the same request that this happens - flag itself uses node_load() (via $flag->fetch_content($content_id)), so it can be pulled from the static cache instead of the direct database query.
Comment #21
pillarsdotnet CreditAttribution: pillarsdotnet commentedComment #22
amateescu CreditAttribution: amateescu commentedFixed catch's comments from #20.
Comment #23
catchLooks good to me now.
Comment #24
sunouch.
I'm working on #397458: Revamp mailing logic to leverage flag module, and @amateescu just pointed me to this issue.
project_issue doesn't have a dependency on tracker2, so this "subscribe" flag cannot be re-used for issue subscriptions in terms of e-mail notifications... :(
Comment #25
sunOn a second though, the setting doesn't necessarily have to be "re-used". It's perfectly fine to have two separate configuration settings for PI and tracker2 on which flags to use -- the use-cases are actually different, too, so in a non-d.o scenario, someone could validly configure two different flags for issue views and e-mail subscriptions.
Comment #26
Leeteq CreditAttribution: Leeteq commentedWould these kind of features trigger the need for a contributed module version for D7?
Comment #27
Dane Powell CreditAttribution: Dane Powell commentedsub
Comment #28
mikey_p CreditAttribution: mikey_p commentedHas anyone reviewed this to verify that the recursive flagging doesn't go into a loop?
Also, I'm not sure this is fully getting the point of being able to flag content in order to make it show up in your profile. That's the only time that we should be acting here, and there doesn't seem to be any reason to flag content that the user is subscribed to unless they explicitly flagged it.
To that end, I think all we need to do is mark the user as subscribed when they flag a node, we don't ever need to flag a node for the user on their behalf.
Removing _tracker_2_flag() and both the calls to that, should do the trick.
Comment #29
mikey_p CreditAttribution: mikey_p commentedTalked with sun in IRC about this, and was informed that the logic is there to auto subscribe users who comment on a node. I'm going to test this out, but I'd rather see that type of logic in project_issue or even in drupalorg.
Comment #30
mikey_p CreditAttribution: mikey_p commentedBased on what I'm seeing at https://drupal.org/node/397464 we don't need the code that flags the node if the user subscribes. This is configurable with Actions.
Comment #31
sunmmm, not sure about @mikey_p's remarks...
AFAICS, @mikey_p meant "user commented on an issue" wherever he used the term "subscribe".
Sounds like something that's much more simple to verify in a unit test...
Right now, it looks like all patches for this initiative are based on the assumption that Trigger/Actions is not available, and thus, they are manually adding corresponding integration behavior to the respective modules.
I mean, in other words, this entire patch is kinda pointless (and actually, most of tracker2's node/comment module integration code) if you base the entire functionality on actions. Instead, you'd implement actions for tracker2, and configure them for the desired "when a comment is posted" and "when a node is created" triggers...
However, I don't have the impression that basing everything on Trigger/Actions really was the intention.
Lastly, also note that Trigger module will most likely be removed from D8 core...
Comment #32
hunmonk CreditAttribution: hunmonk commentedfreshening up this patch a bit:
Comment #33
hunmonk CreditAttribution: hunmonk commentedlittle bit cleaner implementation of flagging/unflagging, flag module already provides a higher level function where we can just pass the flag name.
Comment #34
dwwhunmonk and I just discovered a bug in this patch. The code that's trying to auto-flag content as it gets created (and therefore inserted into tracker2's denormalized tables) is trying to flag *all* content on the site. However, the flag configured for this feature might not be configured to support all node types. So, we should probably be instantiating the flag object, checking the node type configuration, and *then* actually flagging...
Comment #35
hunmonk CreditAttribution: hunmonk commentedok, looking better now:
the one weirdness left is that _tracker2_add() gets called twice, because when the flag is added in _tracker2_add(), it calls hook_flag(), which calls _tracker2_add() again. there's no infinite loop, because once a flag is added, the is_flagged check prevents a re-flagging attempt. still it's an unfortunate inefficiency. at this moment, i can't think of a clean way to work around this problem -- we could add a _tracker2_add_and_flag() wrapper function, which calls _tracker2_add(), then use _tracker2_add_and_flag() everywhere except tracker2_flag(), but this doesn't seem like a very robust abstraction to me.
Comment #36
dwwCool, nice work. However, -1 to leaving a post in tracker2 if you unflag it. In the use case on d.o the idea is that if you unfollow something, you don't want to keep getting pinged about it. If it lives in tracker2, you'll keep getting pinged.
Comment #37
Lars Toomre CreditAttribution: Lars Toomre commentedJust reading patches to gain better understanding...
I noted that the docblock for tracker2_get_follow_flag() has the wrong indentation. Also my understanding is that the first line of a docblock should start with an active verb (although that may only apply to D7+ code).
Comment #38
pillarsdotnet CreditAttribution: pillarsdotnet commentedSpecifically, it should start with a third-person singular present indicative transitive verb.
Comment #39
hunmonk CreditAttribution: hunmonk commentednot tested.
Comment #40
dwwSweet, much better. Another close review yielded the following:
A) Since we're now instantiating the flag object to check the configure node types and all that, it's faster to just call
$flag->flag(...)
instead offlag('flag', $flag_name, ...)
.B) Since we only want to allow node flags for this setting, when initializing the list of options, we should restrict to node flags:
Should be:
C) This seems slightly fragile:
I think we should ensure the array key here is 0 so we treat this choice as a FALSE in the rest of the code. That's what's happening now, but only because it happens to be the first thing we put in the $flag_options array.
D) This comment is a bit confusingly worded:
Comment #41
Lars Toomre CreditAttribution: Lars Toomre commentedNot sure if it is appropriate here or should be opened up in a follow-up issue... However, I thought I should raise it since there is a docblock for tracker2_flag(). According to Documenting hook implementations, the first line of a hook implementation should be "Implements hook_". At minimum, the new docblock should follow this pattern.
This is a change from the former "Implementation of hook_" pattern that was in effect until recently. The old standard is used elsewhere in these files and need to be changed at some point. How should the other docblocks be changed? Here or through a new issue?
Comment #42
sun@Lars Toomre: There are things that really shouldn't matter at all for major patches like this. At least in contrib, that is. ;)
Comment #43
Lars Toomre CreditAttribution: Lars Toomre commentedSorry if my comment #41 was inappropriate... I thought that the intent was to get the code that drupal.org relies upon very close to core standards to help ease maintainability. If variations from documentation standards are fine, so be it. I not trying to hinder the implementation of this important flagging functionality.
Comment #44
pillarsdotnet CreditAttribution: pillarsdotnet commented@Lars Toomre:
Some related discussion.
Comment #45
hunmonk CreditAttribution: hunmonk commentedattached addresses all of the points in #40
Comment #45.0
hunmonk CreditAttribution: hunmonk commentedUpdating issue summary to reflect the real plan as it now stands.
Comment #46
sunLatest patch looks OK to me.
Comment #47
dwwI found a few minor problems with it. Probably still RTBC, but I should apply this on the dev/staging sites and make sure it's still working and it wouldn't hurt to have another pair of eyes on these minor changes.
Comment #48
dwwHrm. Now that I've got this on the dev site and am seriously testing it, it's not behaving as we expect (at least in the unflag case). _tracker2_remove() is actually doing lots of complicated queries to figure out if we should actually delete the record from {tracker2_user} or not. It's enforcing that if you authored the node or still have a published comment that it remains in {tracker2_user}. That makes it impossible to unfollow something you participated in and actually get it out of "Your posts" as intended here.
So, I believe in our tracker2_flag() method, we actually just want to run the targeted query we care about:
Unflagging something can't be changing comment timestamps, so we don't need any of the additional complication or expense of _tracker2_remove().
Should be an easy fix, but I'm in the middle of 2 things. Stay tuned...
Comment #49
dwwOkay, this fixes tracker2_flag() as explained in #48. While I was at it, I simplified the code a bit -- we're already restricting the choices for tracker2_follow_flag to be node flags, so we don't need to test that on every flag operation. That also removes a level of nesting making the code a bit more legible. A few other minor tweaks in there for code style (using ' instead of ", newline after break;, etc).
This is tested and working fine on http://subscribe-drupal.redesign.devdrupal.org
It's now possible to unfollow something and it actually disappears from "Your posts" and "Your issues" as expected. ;)
Any final concerns or can I commit and push this upstream?
Comment #50
webchickI'd like to test this, but I'm not sure how, since it doesn't look like the data migration is done yet on the test site.
http://subscribe-drupal.redesign.devdrupal.org/node/1236280 is an example of an issue I've created/commented on, but the flag in the upper-right says "Follow" instead of "Unfollow" as I would expect.
However, the patch looks sane. The main thing I would think would break here is if there were assumptions made about all flags affecting the viability in the tracker2 tables, but there's an explicit configuration page for this. This is good, since we might at some point want to have a "Add this project to your favourites" flag, and we wouldn't want that to show up in the Tracker.
Comment #51
sunIt would be safer to use the same data type for the default/false value; i.e., an empty string ('').
Alternatively, use a type-agnostic comparison in code elsewhere to check and compare the flag name.
Comment #52
dwwGood point, sun. I fixed that, did more testing on http://subscribe-drupal.redesign.devdrupal.org and finally decided to just commit and push this upstream:
http://drupal.org/commitlog/commit/5492/08d93bd6afb69ef91c7b7492ed514bc8...
I think davidstrauss has long since abandoned this module -- I pinged him in IRC and after the pong when I asked him about this patch, he never responded. ;) Since this is blocking progress on a major d.o initiative and I already had David's prior blessings to have commit access, I'm just taking matters into my own hands here.
Thanks to everyone who contributed towards this patch!
Cheers,
-Derek
Comment #53.0
(not verified) CreditAttribution: commentedfixed typo and clarified the summary problem