why

g.d.o is having problems with spam submission. A human spammer registers, writes spam content, and many people get the spam delivered by mail because of the og subscription feature.

If any module allows spammers to post content and have the content delivered by mail by ourselves to legitimate users, it is a great incentive to spammers to abuse the web site further.

Beside the captcha module that can stop spambots, the site should offer as little incentive as possible to human spammers.

what

Thus the following feature request is critical for a subscriptions.module on today's internet:

Have a delayed mail delivery feature that could ensure that no spam email is ever sent.
The delayed mail delivery would only kick in for untrusted/new users. A permission should be added so that known/trusted users can post and have the notification related to their post sent to the subscribers without delay.

For all other users, the content of the mail notification would have to be reviewed by one of a group of moderators (having the permission "moderate notifications" or something like that). The moderator would have 3 choices:
- the notification is full of spam. Don't send it. If the moderator has sufficient permission he can then deleted the actual node/comment with spam, and block the user.
- the notification is ok: apply a check mark and the notification is sent. If the moderator has sufficient permission, he can add the permission "don't review the content of the notification" to the user so further contributions won't need to be moderated.
- the notification is ok. check it and send it, but keep the user on the watch list for a little while longer.

After a while, all known users would have sufficient permission that no moderator review is necessary. Only new users would be monitored for a while.

Note: this feature is independent from the usual node/comment moderation workflow. I.e. a new comment could be set to be published on the web site straight away, without prior moderation, but the goal is not to send any notification by mail until we have a certain level of assurance that we are not helping spammers.

we don't get black listed

This feature is critical for another reason: if we send email notification that contain spam, and the mails get flaged, reported as such by the recipient, the web server's IP may be black listed and ALL email emanating from the site will be blocked.

modularity

og.module could very well benefit from a similar feature.
Subscriptions.module should implement this feature with modularity in mind.
The main features that subscriptions should implement, and delayed delivery for notification caused by the contributions of users without the proper permission.
Then, the delayed notification could be processed in different ways: put on a moderation list for human review, or passed on to a 3rd module (like spam.module) for automatic evaluation: is it spam? is it not? or is it border line and needs human review?
Thus the subscriptions / spam module would do part of the moderator's job.

Comments

salvis’s picture

Thank you for the well thought-out feature request. I clearly see the need.

Previewing the notifications is not feasible. For example, comments are always merged with pending new or update notifications, so spam and non-spam could be in the same preview. For the same reason not all users get the same notifications in the same context, e.g. some users may already have received the new notification and will get the comment separately, and others with larger (or offset) Send Intervals would get them merged.

I'd rather see posts and comments getting links like
-- release notifications (where notifications are held)
-- hold notifications (where notifications are auto-released)
with the former going to a confirmation form with a checkbox like
[ ] auto-release notifications for this user

Is a notification moderation queue form needed?

Is auditing of moderator actions needed? history of poster qualifications?

beginner’s picture

It sounds good.

When I said "Previewing the notifications", it was a shortcut for saying: don't send any notification that includes the content of a new comment or a new node by an untrusted member before review.
In case of batched notifications, the notification should only include the comments by trusted members, and exclude that of the untrusted one, until sending a notification about it has been approved.

What you propose sound good.

A moderation queue sounds necessary for the moderator's convenience: he would have a list of all comments and nodes (already published on the site) for which the notification is on hold.

The auditing of moderator action is not required, but it could be a nice additional touch. It could be a follow up feature request.

The history of the poster qualification should be obvious by looking at his tracker page (user/$nid/tracker), which a moderator would have access to. What would only be required is a direct link from the moderation queue to the user tracker page. Easy.

salvis’s picture

I've been thinking about this and I found that my original idea was a bit too simple for various reasons, e.g. it doesn't provide a way to get items off the queue in all cases, and it's cumbersome to use because it involves reloads. Whatever interface it is, it will appear below every post and every comment that has notifications in the queue, so it really ought to work well!

You recently mentioned on the dev list that you're getting into JS/AJAX — maybe we can do something together here. I'm thinking of a dynamic interface like the following:

Notifications: discard | 0 | HELD | 0 | release auto-release

Clicking discard would turn it into

Notifications:            | 1 | DISCARDED | 0 |

Clicking release or auto-release (the latter would need an additional permission) instead would turn it into either of

Notifications:    recall | 0 | RELEASED | 1 | auto-release
Notifications:    recall | 0 | AUTO-RELEASED | 1 |

A second post would then show up (depending on how the first one was treated) as either of

Notifications: discard | 1 | HELD | 0 | release auto-release
Notifications: discard | 0 | HELD | 1 | release auto-release
Notifications:   recall | 0 | AUTO-RELEASED | 2 |

Clicking on recall would change it to

Notifications:            | 1 | RECALLED | 2 |

If we could do this in place, without a reload, that'd be pretty neat!

So, can you write me a widget that fills a <div> with HTML, including a few links that perform some actions and refill the <div>? It'll have to work as multiple instances on the same page and needs to have an anchor and a fall-back non-JS interface. Keeping a constant width in the first column would be a nice touch.

beginner’s picture

Hi,
You speak about the details about the UI, while I am still worrying about the inner implementation.
To reply quickly to your comments: I am certainly not an ajax guru. I wrote this for my own benefit, because I knew really little. A simple AHAH call is now easy for Drupal 6, though.

Coming back to the internal implementation. I am not yet familiar with how subscriptions.module works internally.
Imagine the following situation:

We have a node N, with comments C.
A new comment C1 is posted by user U1, who is a trusted user (no need for moderation).
C2 is posted by U2, a new user.
C3 is posted by U3, another untrusted user.
C4 is posted by U4, a trusted user. 

User U10 had received notifications of new comments up to C0 on that node, and needs to be notified of comments C1 to C4.
User U11 has received notifications of new comments up to C1 on that node, and needs to be notified of comments C2 to C4.

What happens
- in the current implementation of subscriptions.module ?
- after this feature is implemented ?

Is user U10 notified of C1 and C4, but not of C2 and C3?
Assuming everything is ok, is he notified of C2 and C3 after the moderation process?
Or is he notified of C1 only?
Or not at all, until the comments in this nodes are moderated?
Is U11 notified of anything at all?

How do you tag internally which change (new comment) has been notified to whom?
Do you use timestamps: check the last time the users were sent notifications, and see what content has been added since that time?

Before dealing with the details of the UI, can you explain the current internal process?
What changes would be required in the internal process for this feature to work?

I would be happy to help with preparing and testing a patch. I am very grateful that you are seriously considering this critical feature.

salvis’s picture

I am certainly not an ajax guru.

But you are way ahead of me. So, if you'll take care of the UI, I'll take care of the implementation.

Designing the details of the UI helps me find the use cases and thus the requirements for the inner workings.

The entries in {subscriptions_queue} are by recipient and by node/comment. When someone creates (or updates) a node or comment, then every matching subscription puts an entry into the queue for each subscribed user.

cron consumes the entries in FIFO manner. For every entry, when it loads a node, the queue is automatically scanned for comment entries for that node and user. From this information a notification is assembled (node + comments, for this user) and sent, and the comment entries as well as all other queue items with the same load_function and load_args (and user) are removed. The actual implementation is a little more involved, but these are the essential parts.

So, in the current implementation,
queue item U10/C1 is picked up, N loaded, queue items U10/C1..4 fetched, comments loaded, notification assembled and sent, and the queue items U10/C1..4 are removed.

Then
queue item U11/C2 is picked up, N loaded, queue items U10/C2..4 fetched, comments loaded, notification assembled and sent, and the queue items U11/C2..4 are removed. U11/C1 had already been removed during the previous cron run according to your set-up.

***

For the new implementation we have to decide whether moderators receive advance notification of unreleased posts. I tend to say no. Notification moderation needs to be organized so that it's done in a timely manner, and I think it can only work well when enough people patrol the site frequently enough.

The trust state needs to be stored with the author, i.e. in {subscriptions_user}. Before items (nodes as well as comments) are inserted into the queue they need to be checked against the trust state of the author. The trust state would be one of (AUTO-RELEASE, HOLD, and maybe DISCARD) and translates directly into initial actions taken when content is created. The count of good and bad posts would have to be stored there as well for efficient access. We'd have
U1: AUTO-RELEASE
U2: HOLD
U3: HOLD
U4: AUTO-RELEASE

To store the moderation state, we need a new table {subscriptions_moderate}:
nid,cid: primary key (cid=0 for the node)
state: RECALLED/DISCARDED/HELD

Items not in the table would be RELEASED (or AUTO-RELEASED, depending on the author).

At creation time, C1 and C4 would be queued and C2 and C3 entered into {subscriptions_moderate} as HELD. So, C1 and C4 would be notified as usual and C2 and C3 not. When the moderator releases C2 and C3, then they are entered into the queue and notified as usual. If he auto-releases C2, then {subscriptions_user} is updated and all held content of U2 is queued.

We might sort the comments that are assembled into one notification try to preserve chronological order as well as possible, but we can't avoid C4 being notified before C2 and C3, if cron runs in the meantime.

When the moderator discards C2, it's updated in {subscriptions_moderate} only. When he releases and recalls it, then queue entries are removed also. Notifications that have already gone out can't really be recalled, of course, but "recall" describes the intent.

It wouldn't make sense to notify comments for a node before that node's notifications are released. Thus all comments would need to be implicitly put on hold (and blocked from moderation) as long as the node needs moderation. When the node is released, its comments could be dealt with as if they had just been created.

Is user U10 notified of C1 and C4, but not of C2 and C3?
Assuming everything is ok, is he notified of C2 and C3 after the moderation process?
Or is he notified of C1 only?
Or not at all, until the comments in this nodes are moderated?
Is U11 notified of anything at all?

yes
yes
no
no
yes, C4 right away, and C2 and C3 after release

How do you tag internally which change (new comment) has been notified to whom?

No. As explained above, the queue works as a ToNotify list at the most granular (and redundant!) level. Every processed notification removes the items that would lead to duplicate notifications (to the same user). So this essentially takes care of itself. This design is due to chx, btw.

Do you use timestamps: check the last time the users were sent notifications, and see what content has been added since that time?

The timestamps are only used to delay processing, if a Send Interval > ASAP was selected. This automatically has the effect, that multiple comments may accumulate in the queue, and when the time has come, they'll be merged.

Before dealing with the details of the UI, can you explain the current internal process?
What changes would be required in the internal process for this feature to work?

Good enough? This requires very little changes in the current code. A hook_queue_subscription() is probably all that's needed, so that a module can filter events before they are entered into the queue. Just about everything else can be handled externally.

There's plenty of data for administrative interfaces (moderation queue, etc.), and if anything else is needed, we can easily add a column somewhere.

Does this make sense? Have I missed anything?

***

Back to the UI:
The problem is that we need such a widget for every node and comment, which means there could be tens of them on a page, and if the same author has made multiple comments, then it would be nice to have some synchronization among that author's widgets. Reloading the page would automatically take care of that, but reloading the page for each and every comment just won't cut it. We need something better, and not just for D6.

Having all these widgets visible all of the time is not very attractive either, so if we could hide them behind a JS link (unless moderation is needed) and only pop them up on request, that would be really nice.

This could become an add-on module of its own or an optional component of the Subscriptions distribution. If you want to take a stab at it on your own we'll certainly help. Otherwise I can do the internal stuff, but I'd need you to do a usable JS UI. And I'd appreciate help with whatever administrative interface is needed.

P.S. I'll be away for a week starting Saturday and pretty busy in the week after that, so I won't be able to do much in the next few days, but I see the need to implement it and will work on it.

chx’s picture

Hmm, i do not readily see the need for a new table, why not just add a hold flag to the subscriptions_queue table. The mail module could check that only hold = 0 mails are sent. A subscriptions_moderation module can update this flag accordingly. (subscriptions_content_node_load and subscriptions_content_comment_load tells us which entries are nodes / comments). We could add a module_invoke to subscriptions_queue calling subscriptions_moderation_queue which would conviniently deliver a 0 for us if the module is not on. This is backend and it's fairly easy. About the UI, no idea.

chx’s picture

An alter like hook also works, pretty much the same as a dedicated module invoke.

salvis’s picture

Storing the HELD state in {subscriptions_queue} would mean that you can't display moderation controls on comments, because retrieving the state would be very inefficient. chx sees no need for that, just a moderation queue with a list of subjects and links to the nodes/comments. If that is sufficient, then we can save a lot of effort.

beginner’s picture

For the UI, I thing something like the comment approval queue would be enough: ?q=admin/content/comment/list/approval

So, we only need to add a field in {subscriptions_queue} as suggested above.

beginner’s picture

Re: #5, I forgot: thanks Salvis for the detailed explanation and comments. :)

I am working on a patch + new module.

I need some quick feedback here:

/**
 * Implementation of hook_perm()
 */
function subscriptions_moderation_perm() {
  return array('send subscription mails without moderation', 'moderate subscription mails');
}

The second perm, 'moderate subscription mails' is for moderators. Their task is simple: keep an eye on spammers, and promote trusted users so the subscription mails do not need to be moderated in the future.

The users who have the first perm, 'send subscription mails without moderation', won't have their content moderated by this module. It is a perm, so it must be associated to a role (e.g. "registered users" don't have this perm, but "trusted members" do have it).

In order to use this perm efficiently within the module, I need to know which role has it. For this, I must have a setting page, where the role (e.g. "trusted members") is selected and saved in a variable. The role should have only this one, or few additional perms: it is a basic role only to differentiate new registered members from known, trusted members.

The subscriptions_moderation.module would add the said role to the trusted users.

=====

The other approach would be to create our own user table with the fields: uid (primary) + moderate (bool). It's simpler for the administrator, but requires more coding.

I prefer the first approach.
What do you think?

salvis’s picture

Since we have to add 'hold' to {subscriptions_queue} (default 0), we may as well add 'moderate' to {subscriptions_user} (again default 0). The defaults ensure that everything runs normally if subscriptions_moderate is not enabled.

The benefit of having a permission would be that core could be used for administering it when you initially set this up. Later on, having a column in {subscriptions_user} seems much simpler and more compact, but at some point you'll probably want to know, which of your users have auto-release, so the role-based approach has the advantage again.

Besides, the admin might choose to give this perm to a few other roles (maybe even most roles) from the start.

So I prefer the first approach, too.

You'll have to provide some instructions and suggest a good (short) name for the role, ideally right on the setting page, so that admins who go there will know what to do.

beginner’s picture

Hi,
Sorry for the delay. I went away for the New Year holiday. With the New Year celebration behind us, I come back here :)

The new subscriptions_moderation.module is currently almost empty. Most importantly, it implement the permission 'trusted not to spam' (which is much clearer than what I proposed above).

I added a 'moderate' column in {subscriptions_queue}. If the module is enabled and the the user doesn't have the permission 'trusted not to spam', the moderate flag is set to 1. Afterwards, all the other operations by the subscriptions modules ignore everything in {subscriptions_queue} where the flag is on.

The next step is obviously to complete the new module to perform the three following actions:
1 - list all the entries to moderate.
2 - for each entry, either set the flag to 0 (so it will be included in the next mailing) or delete it.
3 - offer to give a specific role (with the perm 'trusted not to spam') to the users as we go along the moderation process.

But here I run again into some implementation problems.

First, I misunderstood the meaning of the column 'author_uid' in {subscriptions_queue}. I thought I could use it to select all entries by the same author, but now I see that most of the time, the field has the value -1, and only the author's uid in specific circumstances.

Also, now that I understand a bit more the module, I see that a single comment / node will create dozens or more rows in the table, one per recipient and subscription method.

I am currently left wondering:
A - how to sort the list, group the rows that correspond to the same piece of content, and then group the pieces of content per actual author.
B- how to present this to the moderator (we are back to the UI problem discussed above. Knowing better the module, salvis foresaw the problem that I figure out only now).

So, we need to make a decission as this point:

C- Either carry on in this direction (see attached files), and find a way to go through the list of items to moderate in {subscriptions_queue} in a logical manner.

D- Or, decide that everything in {subscriptions_queue} is already pre-moderated (i.e. don't add the 'moderate' field). Thus, every new piece of content that requires moderation is first entered in different table. When the moderator goes through the items in this new table (better organized for this specific purpose), the {subscriptions_queue} is populated, i.e. hours after the fact.
I've got the feeling that this would be too complicated overall...

salvis’s picture

No problem, I'm not leaving. :-)

I see this more like

 function subscriptions_queue($event) {
   global $user;
   $event += array(
     'uid' => $user->uid,
     'load_args' => '',
   );
+  $moderate = module_invoke_all('subscriptions_moderation_queue', $event);
+  $moderate = !empty($moderate);
   if (is_array($event['load_args'])) {
    $event['load_args'] = serialize($event['load_args']);
   }

Then subscriptions_moderation or any other interested module can implement that hook and return a 1 to put on moderation the notifications that result from any event.

The rest will be pretty much as you prepared it (except that some of the AND moderate = 0's aren't needed).

I hope to have the next beta ready on Wednesday, including these changes (unless we decide something else, see below).

First, I misunderstood the meaning of the column 'author_uid' in {subscriptions_queue}. I thought I could use it to select all entries by the same author, but now I see that most of the time, the field has the value -1, and only the author's uid in specific circumstances.

'author_uid' in {subscriptions_queue} is just a copy of 'author_uid' in {subscriptions}, which is either -1 (for any author) or a uid (for author-specific subscriptions). You'll have to load the node or comment to get the author, but you need to do that anyway, don't you?

Also, now that I understand a bit more the module, I see that a single comment / node will create dozens or more rows in the table, one per recipient and subscription method.

Yes, that's part of what I tried to explain in #5.

I am currently left wondering:
A - how to sort the list, group the rows that correspond to the same piece of content, and then group the pieces of content per actual author.

Group by load_function and load_args. That's what subscriptions_mail() does to avoid sending duplicates. I think you're interested only in the 'subscriptions_content_node_load' and 'subscriptions_content_comment_load' load functions, and their load_args is nothing but nid and cid, respectively.

B- how to present this to the moderator (we are back to the UI problem discussed above. Knowing better the module, salvis foresaw the problem that I figure out only now).

I don't think it's that bad. You can create the moderation list directly from the nids and cids, or if you want to get fancy, you can group them by author uids.

So, we need to make a decission as this point:

C- Either carry on in this direction (see attached files), and find a way to go through the list of items to moderate in {subscriptions_queue} in a logical manner.

D- Or, decide that everything in {subscriptions_queue} is already pre-moderated (i.e. don't add the 'moderate' field). Thus, every new piece of content that requires moderation is first entered in different table. When the moderator goes through the items in this new table (better organized for this specific purpose), the {subscriptions_queue} is populated, i.e. hours after the fact.
I've got the feeling that this would be too complicated overall...

So far I see no obstacles to C. Until now we only deal with normal nodes and comments that can be fully assembled with 'subscriptions_content_node_load' and 'subscriptions_content_comment_load', and I assume that subscriptions_og will continue that tradition. I'm not sure what we may see in that area in the future, but we should be able to deal with it when it gets here.

D is also a possibility; this would mean snatching and serializing the event in hook_subscriptions_moderation_queue() and feeding it back into subscriptions_queue() upon approval.

Now that I think about it, I like this even better, because you'd be able to extract and store whatever information you want, right at the source (only for the events that do need moderation, of course), and Subscriptions would not be impacted at all, i.e. not have any held rows in {subscriptions_queue}.

We might even rename the hook to just hook_subscriptions_queue() — this would open up other interesting possibilities, including feeding back altered events immediately in a recursive call.

Or we might implement a hook_subscriptions_queue_alter() and allow unsetting the event, as chx suggested in #7. This might be more in line with expected behavior in Drupal.

What do you (or anyone) think of this?

beginner’s picture

I am not sure how a hook_?_alter() would work (I still deserve my handle), so no comment on that.

What I started implementing is what's described in #6 (i.e. no use for a separate table).
If we do D (#12), then we would need a whole new table just to make some things a bit easier (sorting through the content to moderate, etc.).

So, I am not too sure on this point.

For the rest, your comments re. hook_subscriptions_queue() are good. I forgot about that bit and I'll change that in the next patch... as soon as we decide on C/D.

beginner’s picture

Maybe I can try along the same route (C), and see how difficult it is to sort through the table.

chx’s picture

After some thought I am siding with salvis on D. Let's do that. Less overhead for subscriptions itself.

salvis’s picture

The only thing special about an alter hook is that you take your parameter(s) by reference and you're allowed to change the object of interest. (It's an array in our case.) IOW, you define your hook function as

function subscriptions_moderation_subscriptions_queue_alter(&$event)

and you can change the $event as you like (as long as you don't break Subscriptions, of course). Other alter hooks might not like this, but here we specifically allow you to even set the $event = null, and Subscriptions will just forget about it.

So, you get an $event, which includes absolutely all the information that Subscriptions has (including $node and possibly $comment). If it does not need moderation, you just let it pass. Otherwise serialize($event) into a LONGTEXT database field, along with any other information that will be useful for the moderation queue, and set $event = null.

When the moderator releases the notification, then you simply call subscriptions_queue(unserialize($event)) and Subscriptions will never know what happened in between. You're completely free to implement whatever you want, and your only point of contact with the rest of Subscriptions is the $event array.

Obviously, when you call subscriptions_queue() yourself, your hook will also be called again, and you should let the $event pass this time. You could for example add an element like $event['subscriptions_moderation'] = 1, so that you can recognize the situation.

The next beta release will have hook_subscriptions_queue_alter(), but you can put it in yourself and start exploring this route right away:

function subscriptions_queue($event) {
  global $user;
  $event += array(
    'uid' => $user->uid,
    'load_args' => '',
  );
+  
+  foreach (module_implements('subscriptions_queue_alter') as $module) {
+    $function = $module .'_subscriptions_queue_alter';
+    $function($event);
+    if (empty($event)) {
+      return;  // $event was cleared, never mind.
+    }
+  }
+  
  if (is_array($event['load_args'])) {
    $event['load_args'] = serialize($event['load_args']);
  }

No other change is needed.

salvis’s picture

The alter hook is in BETA11.

salvis’s picture

@beginner: Have you had a chance to look at this?

beginner’s picture

Assigned: Unassigned » beginner

No, I haven't. But I am working on it today. I'll upload later in the day what I have.

beginner’s picture

Status: Active » Needs work
StatusFileSize
new3.89 KB
new868 bytes
new140 bytes

Thanks salvis for the alter hook in the main module.

I am attempting to create a first, bare bone moderation module. I am having some problems with the form with the list of content to moderate.
I copied from comment_admin() and comment_admin_overview(), hoping to get a result similar to ?q=admin/content/comment but the form layout is a disaster in my implementation. I've been stuck on this for a couple of hours and I don't see what's fundamentally different from the comment.module.

I"ll be busy/away tomorrow and the day after. Hopefully I can finish this off Sunday.

salvis’s picture

function moderate_mailings_uninstall() {
  if (db_table_exists('subscriptions')) {
    db_query("DROP TABLE {subscriptions}");
  }
}

Lurkers: Don't try to install (and especially uninstall) this!!!

The moderate_mailings_uninstall() function will destroy your Subscriptions installation!

@beginner: Maybe you want to call it something like subscriptions_moderate, to make it clear that it's a Subscriptions add-on?

beginner’s picture

Oops! re. hook_uninstall()!! It's a too hasty copy/paste.

I purposefully called the module something different. I did not want to call it subscriptions_something. I work a lot from the konsole, and use command line autocompletion a lot, and it is really a pain when there are so many files with similar names at the beginning.

beginner’s picture

What I still don't get is at what stage the $form output by comment_admin_overview() is put into a table.
I keep banging my head on this problem.

salvis’s picture

theme_comment_admin_overview()

beginner’s picture

Oh, that's it! :) Thanks.
This is a feature of FAPI that I didn't know existed (theme_hook() called on the name of the form).

beginner’s picture

StatusFileSize
new7.97 KB
new885 bytes

Ok, the basic functionality works (thanks salvis). I still need to polish a few things which I shall do in the next couple of days.

I post what I have just in case someone can't wait to have a look. The code still needs work.

beginner’s picture

Version: 6.x-1.x-dev » 5.x-2.0-beta11
Status: Needs work » Needs review
StatusFileSize
new10.3 KB

There you are. Nothing fancy, but it works.

.info is in #21
.install is in #27

zilla’s picture

brilliant - and delighted to see this. in an effort to make it even more user-centric and directly benefit from user activity, would it be possible to incorporate the d6 implementation of the "flag content" module such that any "flagged content" is automatically removed from any subscription output?

this would be a great way to allow active site users to police content violations (particular in school sites or youth-oriented sites) and make sure that no flagged content goes out to subscribers of a node/item until the item has been unflagged...

just a thought. turns users 'optionally' into part of the clean-up for the spam problem...

beginner’s picture

This is not how this module works. The new content by untrusted users is put aside as soon as the content is posted. By the time the members get to flag content, the content itself has already been either queued for the subscription mailing (and it's too late to retrieve it from there), or queued for human moderation.

And this is for D5. There is no D6 version of any subscription module yet.

zilla’s picture

okay, i follow you. perhaps a feature request (for flag content - not subscriptions - would be to change usertype if flagged, basically revert the person to 'untrusted' until a flagged item is unflagged)

as for d6, just saw this: http://ftp.drupal.org/files/projects/subscriptions-6.x-1.x-dev.tar.gz (direct link - or go to modules, filter by d6)

salvis’s picture

I'm sorry about the confusion re: 6.x-1.x-dev. I was educated in http://drupal.org/node/231966 that the MAIN releases are supposed to be converted to -dev nodes and I needed this done for some other modules and thought it would be a good occasion to have subscriptions-MAIN converted, too, not realizing that this would change the versions tags here on the site...

There is no D6 version anywhere yet, not even on my local disk. Please ignore 6.x-1.x-dev, it's the same as the latest BETA for Drupal 5!

zilla’s picture

thanks salvis - i almost put it up and likely woulda been bombarding the support forums if it bugged! you may want to add that note above to the project page because when it showed up yesterday, probably tons of people downloaded it...

salvis’s picture

@beginner: Thank you for your work! I'm sorry this 6.x mess erupted in your thread...

I haven't had time to take a look yet, but I will. However, moderate_mailings.install.txt is still unchanged.

Lurkers: Don't try to install this yet — #27 will still trash Subscriptions when you uninstall it!!!

beginner’s picture

StatusFileSize
new885 bytes

Oops! Very sorry about that. You told me already. Here the updated .install.

beginner’s picture

StatusFileSize
new893 bytes

oops again. Wrong file.

beginner’s picture

case 'pgsql' remains to be done. I'll do it after review if there is no objection to the DB schema.

beginner’s picture

Since we are still in beta releases, I hope this can be included in beta12, so that more users can test it. I'll follow up all bug reports (if any) with patches.
Also, it would be nice if you could update the component list, so that each module has its own component. Thus it will be easier for me to follow up on all issues related to this new module.

chx’s picture

Status: Needs review » Needs work

First of all, let's decide whether it's a generic or a content-only solution. Never forget that subscription is a generic framework, you might subscribe to private messages for example. This solution is currently content focused.

Second, all this fiddling with roles is not necessary. You provide a permission , check the permission with user_access and that's it. Let core handle the rest.

zilla’s picture

i also agree (with #39 right above) - seems that moderation should be a separate module and could possibly grow to support other areas of notification (like other site messaging tools, notifications etc)...or maybe i'm missing something...perhaps include with subscriptions project page as 'suggested other modules: moderate mailings' so that people will clearly see it, but then on moderate mailings list specific dependencies and so on...that way people can isolate the workflow around moderation and hammer it out independent of larger 'subscriptions' module issues.

beginner’s picture

Status: Needs work » Needs review

@chx: If you had any objection about what I was trying to achieve with this module, you could have said so before I coded the bloody thing. The module does exactly what I set out to do, which I discussed and explained at length earlier in the thread.

About the roles and permission, see the earlier comments, and read the code.

Also, I fully took into consideration all the comments you made earlier and already changed the whole module accordingly.

Besides, a hook alter is already part of the main module: the idea was that any kind of module would be able to be plugged in and implement their own moderation strategy. This module is only the first one implementing this hook.

Again, the module does exactly what it was supposed to do.

@zilla: please read the whole thread. If you have another idea in mind, it is perfectly ok if you open a new feature request where you can provide your own patch. Please don't hijack this issue.

chx’s picture

I thought I have valid concerns -- I am not against this "bloody" thing, I just would like to see a few things fixed. I have re-read the code and the user-role is fine, after all, sorry -- you do check the permission with user_access as I asked where it's appropriate. On the same line, we have empty($event['moderate_mailings_done'] how is this supposed to work...? Who fills this key?

Next up, this is minor but you can see in for eg. watchdog that we use auto_increment / serial instead of db_next_id when we do not care about the value of the id. Speaking of serial, a postgresql install would be nice.

After you unserialize, you are using content instead of data, $content['node'] ought to be $data->node, $content['comment'] ought to be $data->comment.

There is concern that still stands: subscriptions is a generic framework, you might subscribe to private messages for example. This solution is currently content focused. It is fine that it only deals with content, we have subscriptions_content.module too. But, then this module needs to be renamed and some sorts of checking added so it won't try to process anything but content.

Thanks for the hard work.

beginner’s picture

Status: Needs review » Needs work

@chx: thanks for your feedback. I completely misread your comment in #39. Apparently Zilla did, too, in #40, which didn't help. I thought you were speaking about a complete change of direction for this module, proposing to do something other, "more generic" than what was previously explained and, I though, agreed earlier in the thread. So, apparently, I misunderstood what you meant.

$event['moderate_mailings_done'] is set by this module for the reason correctly foreseen by salvis in #17.

About renaming the module, I am open to suggestions: any name that does not start with s.

This module targets only content, not private messages or anything else (at the very least not at the beginning). I am not too sure what check would be necessary to process any other content than the one targeted. Suggestions?

I'll update the module to take into account the other minor stuff your mention.

beginner’s picture

Status: Needs work » Needs review
StatusFileSize
new10.38 KB
new1.6 KB

changes done:
* removed db_next_id, using auto_increment.
* did pgsql.
* $content['node'] ought to be $data->node : very good catch, thanks. fixed.

I am not against extending this module to moderate private messages, too, but I fail to see how one could subscribe to PMs. Also, from a privacy point of view, it is trickier to ask moderators to read other people's PM... I don't have a good mental image of what could be done more, compared to moderating the content as I already do here.
Maybe this can be committed more or less as is, and follow up in a new issue, to see if it's feasible to extend the module.

Also, should this module only moderate normal content (nodes and comments), I am not sure what check to add so that it doesn't catch any other content. The check would go in moderate_mailings_subscriptions_queue_alter(&$event).
Currently, we have:

  // Don't moderate anything if the user is a trusted one,
  // or if the content has already been moderated ($event['moderate_mailings_done'] == TRUE).
  if (!empty($event['moderate_mailings_done']) || user_access('trusted not to spam')) {
    return;
  }

Am I supposed to put something like this?

  // Don't moderate anything if the user is a trusted one,
  // or if the content has already been moderated ($event['moderate_mailings_done'] == TRUE)
 // or if the content is neither a node nor a comment..
  if (!empty($event['moderate_mailings_done']) || user_access('trusted not to spam') || $event['module'] != 'node') {
    return;
  }
salvis’s picture

Thank you for the fixes and for all your work.

I think limiting the module to content (nodes and comments) is just fine at this point, but I'll let chx answer the question on the best method to differentiate content from other subscription items.

It's hard to come up with an appropriate name — at first I thought, "Subscriptions" needed to be part of the name, but maybe not. I keep coming back to moderate_content_mailings ("Content Mail Moderation"). chx?

Is there a reason for not using l() in moderate_mailings_admin_settings_form()? The link is wrong.

Please add the dependency on subscriptions_content to the info file.

beginner’s picture

StatusFileSize
new11.1 KB
new170 bytes

mailing_content_moderation.module ?
moderate_mailing_content.module ?

in moderate_mailings_admin_settings_form(), we cannot use l() because the link is part of a longer string. But I forgot to use url(). Fixed, thanks.

.info file updated.

I also added a check in moderate_mailings_admin_settings_form() to make sure that the roles listed in the form have the proper perm (trusted not to spam). If no role have this perm, a warning is displayed.

salvis’s picture

What's wrong with l()? I'm using it on admin/settings/subscriptions, and it works just fine.

beginner’s picture

The problem is with translation. Using url() gives more flexibility to the translators. I was told this was the way to insert a link in a long string.

chx’s picture

What this module does? Moderates content mailings. Hence, moderate_content_mailing. That one is good. Checking for node/comment, just check whether $event['node'] exists.

beginner’s picture

Version: 5.x-2.0-beta11 » 5.x-2.0-beta12
StatusFileSize
new11.39 KB
new1.7 KB
new172 bytes

Ok. Name changed, check added.

salvis’s picture

One more thought — sorry this comes in pieces...

The menu item keeps bothering me a bit:

# ...
# Site information
# Site maintenance
# Subscriptions
# Subscriptions (spam deterrent moderation)

This is only used once in the entire lifetime of the site and it takes up a lot of menu real estate. Could it either become a submenu item under "Subscriptions" or a subtab on admin/settings/subscriptions? As long as we don't have any other add-ins competing for the same name, I'd be fine with having a subtab "Moderation" with a heavy weight.

However, this would mean changing the admin/settings/moderate_mailings path to admin/settings/subscriptions/moderation.

salvis’s picture

Status: Needs review » Needs work

Ah, you've changed the menu item in #50, but it's even longer now:

# Moderate content mailing (spam deterrent moderation)

It takes four lines now. I don't think we can do that...

Here's a grammar question: my strings use the term "subscriptions notifications" (= multiple mailings caused by multiple subscriptions, two times plural form). You use "subscription mailings" — should the first word be plural or singular?

At this point, Subscriptions only supports mail as medium, but the design is such that subscriptions_mail.module could be replaced by some other module. That's why I'm using "notifications" rather than "mailings," and your module is independent of subscriptions_mail, so it should follow the same terminology.

This means the module should be renamed again, to moderate_content_notifications. I realize that you've only just renamed it and I should have noticed that before, so I'll do the renaming for you...

salvis’s picture

Component: Code » moderate_content_notifications
Status: Needs work » Needs review
StatusFileSize
new183 bytes
new1.79 KB
new11.71 KB

Here are the resulting files. We're planning to release RC1 tomorrow evening (European time) — let me know before then if you need any last-minute changes.

beginner’s picture

Hi, Thanks for updating the module, and correcting the grammar and stuff...

About the setting page, I can't find it anymore, neither on the main admin panel, nor in the subscriptions setting page.

You argue that it is only used once in the entire lifetime of the site. Frankly, most of the elements in the Settings section are only used once. How often do you enable/disable clean urls? How often do you modify the Time and Date settings? What about most of the others? ;) So the argument is not valid.

Having said that, I don't mind having it in a tab if that's what you prefer... as long as it exists:

    $items[] = array(
      'path' => 'admin/settings/subscriptions/moderate_content_notifications',
      'title' => t('Content notifications (spam deterrent moderation)'), // <-----------the title is a bit long
      'type' => MENU_LOCAL_TASK,             // <------------------ Add this line.
      'description' => t('Set which basic role is trusted not to spam.'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array('moderate_content_notifications_admin_settings_form'),
      'access' => user_access('access administration pages'),
    );

The title is a bit long to use in a tab title. In this context, I think "Spam deterrent settings" should be enough.
It is much shorter and also explicit.

beginner’s picture

... Thinking about it a bit more, I personally still prefer to have the link to the setting page on the main admin panel. First, as I said, the argument about how often we need to access the setting page is not valid. That's why all the setting pages of most modules are grouped in the same section which is listed last. Once the web site set, the whole Settings section can be ignored.

The advantage of having the link on the main panel, is that it's much easier to find once the module is enabled. Too often, after enabling a module, I am left clicking around trying to find what setting pages there are. Why let the user have to guess where the setting page is... or whether there is any...?

But I won't insist further and let you take the final decision. At the end, I am happy either way. It's a fairly minor detail after all! :)

salvis’s picture

You can find it

  • on admin/by-module
  • at Administer ›› Site configuration ›› Subscriptions ›› Content notifications
  • via the link in the message on admin/content/moderate_content_notifications

The links you mention are one-time-use indeed, but they're part of Drupal core and thus privileged. We want people who install Drupal for the first time to find all the Site configuration pages, and in a logical sequence.

However, I don't think us lowly contrib developers are supposed to litter the menu with monster entries...

salvis’s picture

Version: 5.x-2.0-beta12 » 5.x-2.0-rc1

moderate_content_notifications is included in RC1. Thank you, beginner!

I'd still like to get some feedback, if possible...

salvis’s picture

Status: Needs review » Fixed

Ok, 2.0 is out and moderate_content_notifications along with it.

@beginner: Please keep an eye on the moderate_content_notifications subqueue.

Thanks again!

beginner’s picture

Thanks.
ok, but I'm not subscribed to all issues. Contact me if a bug comes along...

Anonymous’s picture

Status: Fixed » Closed (fixed)

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