I'm requesting assistance in implementing a plugin (or patch) that would give the option to globally defer matching until an Action is triggered from, say workflow.

Here's why:
I would like to use Subscriptions with the Workflow module, and Workflow requires that nodes be published throughout the work flow (series of state changes - e.g. Draft, Submit, Review, Post). On my site users may take weeks to format and build their nodes, so many edits will take place on the nodes (edit content, add remove terms from vocabularies), and other users may alter their subscriptions in between when a node is created and finally posted (made accessible to other users on the site).

So with subscriptions' current implementation of nodeapi for content and taxonomy (I think different for content-type sub.'s), it seems that when used with workflow keyword matches and queue population only occur at the point of initial node creation. This because Workflow needs to have content-types set to Publish upon create. (I've tested and observed this)

I've looked at subscriptions_content.module and your taxonomy module, and... well, I'm not sure where to start. Is the idea of adding an option in hook_menu to "defer matching to action-X" feasible? And then create/register the action so it's available to workflow?

I would love to contribute and help test etc, but I'm not yet familiar with drupal coding style and confident enough to jump into such a huge, complex module as this.

Some guidance and/or opinions would be great. Thanks. (PS, I marked this as a support request rather than a feature req. because I expect to help as much as I can)

Comments

salvis’s picture

Thanks for opening a new thread to continue the dicussion that outgrew #504698: Subscriptions Don't Send on Node Publish, only on Node Creation!

You have surely noticed that Subscriptions doesn't expose/use any actions; I must admit that I'm not familiar with them nor with Workflow, so you'll have to help me in that area.

Right now I can provide some explanations:

1. The {subscriptions_queue} table usually receives multiple records, one for each subscription that was triggered. The order of those entries is dependent on the usual order of the submodules (by weight if set, and alphabetical). The first entry is the one that will be processed by cron, and any additional entries are flushed to avoid duplicate emails.

I haven't really analyzed the implications of having multiple subscription trigger, beyond avoiding duplicate emails. Trying to gather multiple terms won't be easy, because !term_name is set in _subscriptions_content_node_mailvars() in subscriptions_content.module, and the duplicates are eliminated in subscriptions_cron() in subscriptions_mail.module. There would need to be a way to collect the information about additional {subscriptions_queue} entries before they are flushed, and to make this information available to the other modules that might be interested.

When you look at the records in {subscriptions_queue}, does this explain the behavior that you're seeing?

2. There's a hook_subscriptions_queue(), which is fired before any {subscriptions_queue} entries are created. If you implement this hook, you can decide on a case by case basis whether you want to suppress this event or not.

3. OTOH, there's the subscriptions_queue() function which you can call to insert an event.

4. Subscriptions has two types of events: 'create' and 'update'. However, when a node was unpublished before, and an update event makes it published, then the event is converted into a 'create' event. I'm surprised that you see different behavior depending on whether a node is created in published state or it's published later. Both should result in the same {subscriptions_queue} records and ultimately in the same notifications, at least for non-administrative users. (Users with administer nodes should get an additional create notification with !is_published==0 at 'create' time.)

It's quite likely that Subscriptions would benefit from implementing Drupal actions, and that this would provide much of what you need, but I'm not prepared to take this on in the near future.

mudd’s picture

Okay, I'm fast realizing that I don't have the right stuff to build a quality plugin, so I'm going to have to hack it temporarily for my needs (sorry!)

I only just got your note, so I'll tell you what track I was on up to this point, and come monday I'll consider more of what you just gave me.

I was trying to insert some code into your subscriptions_content.module to watch for a variable that I set with an action. I just added the action into this module (see below). The idea is rather than fire off the matching routine (which I'm fuzzy how it works) by looking for $op == 'insert' in function subscriptions_content_nodeapi(), instead look for the variable set by the action. But wasn't able to trace the $event var you set in case 'insert' (does it get used in subscriptions.module?), and you prob use $op elsewhere too, etc.

Also, by using drupal_set_message to echo comments I noticed that this function gets run like six times, and I'm confused about $node->subscriptions_notify = TRUE; in case 'prepare', and $node->subscriptions_currentstatus == '1', etc. I was hoping to make a patch as small as possible just to keep it simple.

More next week. Thanks a 10E6 salvis!

/**
* Implementation of hook_action_info().
*
* This registers an Action to invoke matching process.
*/
function subscriptions_action_info() {
  return array(
    'node_subscrp_match_action' => array(
      'description' => t('Run Subscriptions Matching'),
      'type' => 'node',
      'configurable' => FALSE,
      'hooks' => array(
        'nodeapi' => array('presave', 'delete', 'insert', 'update', 'view'),
      ),
    ),
  );
}

/**
 * Function for matching Action above.
 * 
 */
 
function node_subscrp_match_action(&$object, $context = array(), $op, $arg = 0) {
  global $match;
  $match=1;
}
salvis’s picture

Yes, Drupal calls hook_nodeapi() with various values of $op. See http://api.drupal.org/api/function/hook_nodeapi/6

$event is the input parameter to subscriptions_queue(), where all the work of matching of the existing subscriptions to the current insert/update event is done to generate the entries in the {subscriptions_queue} table.

$node->subscriptions_notify is set to TRUE so that users with admin permissions can suppress sending notifications when they create/edit a node. Search for subscriptions_notify only to find where it's used.

mudd’s picture

4. Subscriptions has two types of events: 'create' and 'update'. However, when a node was unpublished before, and an update event makes it published, then the event is converted into a 'create' event. I'm surprised that you see different behavior depending on whether a node is created in published state or it's published later. Both should result in the same {subscriptions_queue} records and ultimately in the same notifications, at least for non-administrative users. (Users with administer nodes should get an additional create notification with !is_published==0 at 'create' time.)

I'm not sure if you read me that I saw different results depending on whether I "created in published state or published later" -- I've only tested nodes created in a published state. I was referring to changing subscriptions between the create and accessible states, and that notification for content-type seems to reflect the latest subscriptions vs taxonomy which seems to reflect the subscriptions at create time.

I'm focusing on subscriptions_content_nodeapi() and trying adapt the 'insert' op case into a test for my Action variable. One issue I'm having is how to check for an existing record in {subscriptions_queue} just in case the action is triggered a second (and 3rd, 4th ...) time. Since a node is only created once, using $op == 'insert' makes sense, but by using a trigger/action I introduce the need to limit the events in the subscription queue so that I if un-post and then re-post (workflow states) I'd need to remove the queue records for that node and then rerun subscription. Does the load_args column simply translate to nid? If it does then could I check for an existing record for the same node + uid? Also, what's the best way to make this check? And is there a there a function for this task already?

salvis’s picture

Yes, I mis-read what you wrote because you make a distinction between created and accessible. AFAIK, nodes are accessible unless they're unpublished (or protected by node access). Apparently, Workflow has a way to make a node inaccessible, even though it's published.

Duplicate entries in {subscriptions_queue} are not a problem. They're removed with

      db_query("DELETE FROM {subscriptions_queue} WHERE load_function = '%s' AND load_args = '%s' AND uid = %d", $s['load_function'], $s['load_args'], $s['uid']);

in subscriptions_cron().

load_args is whatever load_func takes. So you should use the three columns above to look for a match.

salvis’s picture

What I wrote in #1 about how multi-term subscriptions are handled is superseded by roderik's analysis in #524332-4: DB colum type fixes (make PostgreSQL work). Due to a recent 'optimization', only one queue entry was generated for taxonomy subscriptions, even when multiple terms triggered, but I've reverted that change.

We might now attempt to assemble a !term_names variable in addition to the !term_name variable, which contains only one arbitrary term out of the set that matched, by retrieving the {subscriptions_queue} records. However, this is an additional SELECT for each notification, and I'm not sure it's worth the effort...

mudd’s picture

Thanks for pointing out the terms stuff. I upgraded to the 7/20 6.x dev version and see that it's entering a record in {subscriptions_queue} for each matched term, but then the query in #5 deletes the extras on cron (the last record is the one reported in the notification).

So does each line in {subscriptions_queue} correspond to an email notice to one user about one node? And I see that in my case load_function is "subscriptions_content_node_load" making load_args a nodeID. What are the other possibilities for load_func and would load_args be NOT an nid for some of those cases? I wonder if field 'value' can be a comma-separated list of matching terms(??), and if so then can they be translated to term names.

Getting back to my hack for workflow, I'm still unclear on what either sets the below variables, and what they effect (what do they make happen or not-happen?):

1. $node->subscriptions_currentstatus == '1'

2. $node->subscriptions_notify

3. is_new

So far my super simple hack is to blend the "case 'insert'" code with 'update' and checks if:
- $op = update
- $subscriptions_action == 1 (this is just a variable I set whe nthe action is fired - used to be "$match")

But to make it work I seem to need to change 'action' in the $event array to "insert" and set 'is_new' to TRUE or ($op == 'update' && $subscriptions_action == 1). I know this is a gross kludge due to my not following the complex logic, but I really need this feature on my site yesterday.

Also, perhaps to prevent future repeat notifications - a table to mark nodes previously processed?

And finally, since my changes are confined to hook_nodeapi(), maybe an admin form checkbox setting that asks whether to use in standard mode or with workflow actions.

salvis’s picture

So does each line in {subscriptions_queue} correspond to an email notice to one user about one node?

Close. It corresponds to an email notice to one user about one node, generated by one subscription (= one entry in the {subscriptions} table.

load_func could also be 'subscriptions_content_comment_load', and in that case $load_args is a cid (comment_id). It can be whatever $load_func() will accept. Other load_funcs with other load_args are certainly possible; add-on modules can freely define them.

value could also be whatever add-on modules want to use, but for the 'node'/'tid' subscription type defined in subscriptions_taxonomy.module value needs to be one tid.

$node->subscriptions_currentstatus is used to save whether a node is already published before being updated or not.

If the user editing a node has the administer nodes permission, then he can uncheck the Send subscriptions notifications checkbox and this causes $node->subscriptions_notify to be FALSE. In that case, notifications are suppressed.

is_new is saved in {subscriptions_queue} and later used to set the !is_new variable so that mail templates can look different depending on whether we're sending a notification for a new post or for an updated or commented post.

We do NOT want "to prevent future repeat notifications". If a node is updated and or commented, we want to send notifications again (if the subscriptions have on_update and/or on_comment enabled). If those are disabled, then only node creation generates notifications.

I can't really help you with the workflow-specific stuff, but I think there's a lot more to enabling actions.

mudd’s picture

Ah, that clears up a lot for me. yeah, I figured that whatever uses load_args would have to be rewritten...

I don't see where to uncheck the Send subscriptions notifications. In the admin settings I have disabled both "Sends a notification when an item is updated" and "Sends a notification when an item receives a comment or reply" -- I tried checking them but still don't see this option on the edit form -- I assume that's where it is (I'm looking with admin user ($uid = 1). Oh, and I have the three visibility controls hidden too.

The comments you've given me so far help enormously, so a big thanks! :)

salvis’s picture

It's under Publishing options on node/NID/edit.

salvis’s picture

Status: Active » Closed (outdated)