New plugin: Subscribe To Comments

8 views
Skip to first unread message

Steve Love

unread,
Jan 11, 2009, 11:55:08 PM1/11/09
to habari-dev
Hi, everyone. I started using Habari about a week ago (loving it) and
saw the need for a "Subscribe to Comments" plugin, so I'm giving it a
shot. (If one already exists I'd love to know!)

I've got an alpha release that seems to work well and would love to
have some adventurous souls test it out (or just look over the code).
Just to be on the safe side, you may not want to use this on a
production environment. You can download it here:
http://stevelove.org/projects/plugins/subscribe-to-comments/

There's a readme file included that talks about usage.

Here's what I particularly would like help with:
1. I'm currently using inline SQL where I know I should be using
something like Posts::get(array('info' => array('blah')). I just can't
seem to get it to work, perhaps because of the next item.

2. I originally wanted to add a new column to the comments table
during activation. Couldn't get that to work, so I changed gears and
now I'm adding entries to the postinfo table, but I'm not really happy
about the way this is done. Since the name column is unique, I use it
to store the subscriber's email address and set the value column to
"SUBSCRIBE". Is that why I can't grab subscribers with Posts::get(array
('info' => array('value' => 'SUBSCRIBE', 'post_id' => $comment-
>post_id))) ?

3. There is no way to unsubscribe. Anyone have suggestions for
implementing this?

4. Any other comments or pointers would be helpful. :)

Thanks in advance,
Steve

Arthus Erea

unread,
Jan 12, 2009, 12:08:28 AM1/12/09
to habar...@googlegroups.com
What about storing the subscription info in the comment_info table.

You could store a boolean "subscribed" attribute.

Then whenever a new comment is posted on a post, parse through all the
commenters on that post, then email all those who have the
"subscribed" attribute turned on.

I've been thinking about writing a plugin like this for a while, but I
want to get the Email stuff nailed down so it is standardized.

Scott Merrill

unread,
Jan 12, 2009, 8:59:14 AM1/12/09
to habar...@googlegroups.com
On Sun, Jan 11, 2009 at 11:55 PM, Steve Love <stev...@gmail.com> wrote:
> Hi, everyone. I started using Habari about a week ago (loving it) and
> saw the need for a "Subscribe to Comments" plugin, so I'm giving it a
> shot. (If one already exists I'd love to know!)

Mike Lietz and I have kicked around a few ideas for such a plugin. I'd
written some of the necessary underpinnings, but got hung up on
user-facing stuff.

> Here's what I particularly would like help with:
> 1. I'm currently using inline SQL where I know I should be using
> something like Posts::get(array('info' => array('blah')). I just can't
> seem to get it to work, perhaps because of the next item.

Let's tackle these in order.

// Is this email address already subscribed to the comments on this post?
$query = "SELECT value
FROM " . DB::table( 'postinfo' ) . "
WHERE name=?";
$result = DB::get_row( $query, array($comment->email) );
$subscribed = ( ! empty($result) );

Our info records should permit you to query for this value directly.
You have a comment object, so from that you need to fetch the
corresponding post object. Then you can interrogate that post's
postinfo records.

$post= Post::get( array( 'id' => $comment->post-id ) );
$subscribed= $post->info->{$comment->email};

Note I'm note sure if the above will work, or if you need to stuff the
email into a temporary (non-object) variable.

Similarly, for writing to a postinfo value:
$query = "INSERT INTO " . DB::table( 'postinfo' ) . "
(post_id, name, type, value)
VALUES (".$comment->post_id.", '".$comment->email."', 0, '".$s2c."')";
DB::query( $query );

$post->info->{$comment->email} = 'SUBSCRIBE';
$post->info->update(); // this is what commits your changes to the DB

> 2. I originally wanted to add a new column to the comments table
> during activation. Couldn't get that to work, so I changed gears and
> now I'm adding entries to the postinfo table, but I'm not really happy
> about the way this is done. Since the name column is unique, I use it
> to store the subscriber's email address and set the value column to
> "SUBSCRIBE". Is that why I can't grab subscribers with Posts::get(array
> ('info' => array('value' => 'SUBSCRIBE', 'post_id' => $comment-
>>post_id))) ?

Posts::get() returns post objects. Post objects do provide access to
their associated postinfo objects, but it sounds to me like you want
the postinfo objects exclusively, so you shouldn't be querying for
them using Posts::get()

Incidentally, in my subscribe-to-comments efforts, I simply stored an
array of all the subscribed email addresses in a single postinfo
record. When a new subscriber signed up, their addresses was appended
to the array. This has its own set of benefits and drawbacks.

> 3. There is no way to unsubscribe. Anyone have suggestions for
> implementing this?

There is a way, but it's not very classy. Essentially, you need to
create an empty post, and then have a handler to generate your content
when that post is requested. It's not entirely intuitive.

> 4. Any other comments or pointers would be helpful. :)

In my work on the WordPress subscribe2 plugin, I learned the hard way
that hosting providers are absolutely insane when it comes to email.
Some hosts will limit the total number of messages you can send per
day. Some hosts will limit the total number of addresses any one
message can handle.

Your code sends one email per subscriber, which is likely to work on
some hosts, and should work fine on low-volume sites. If several
hundred people subscribe to a single post, and that post gets frequent
comment activity, your code may result in a nastygram from the hosting
provider to the account owner.

Of course, you can't simply BCC everyone, either, because hosts like
Dreamhost limit you to a total of 30 recipients per message. I
included huge amounts of code into susbcribe2 to account for Dreamhost
and other similarly broken hosting providers.

Other than those caveats, it looks like this is a fine start!

It might be worth taking a peek at our nascent ACL work. You could
conceivably create a new account when someone subscribes to a post.
You could then construct an admin form using FormUI to permit a user
to manage their subscriptions. Their account should only have access
to this subscription management interface, and not have access to
create new posts, etc.

Cheers,
Scott

ringmaster

unread,
Jan 12, 2009, 10:41:28 AM1/12/09
to habari-dev
On Jan 12, 8:59 am, "Scott Merrill" <ski...@skippy.net> wrote:
...
>
> $post->info->{$comment->email} = 'SUBSCRIBE';
> $post->info->update(); // this is what commits your changes to the DB

And all of that code looks fine.

What I would suggest, though, is what Arthus was talking about in his
prior message. Habari has a commentinfo table that allows you to
store metadata with each comment, just like you can with each post.
So while you're subscribing to a post, your comment is actually
attached to the post, and the comment has info on it saying that
you're subscribed. This is better for a couple of reasons.

The first reason has to do with a limitation of postinfo. As you
noticed, postinfo keys (like commentinfo keys) are unique. So you
can't have multiple "subscribed" values unless they're stored in an
array in a single key.

You can do this:
$post->info->subscribed = array('al...@example.com',
'b...@example.com');

But then you can't easily query for posts to which al...@example.com
is subscribed because the array value is serialized into the table.

You've also noticed that using the email address as the key works, but
it's messy, since you can't easily query for all of the subscribers to
a single post.

If you instead store subscription status in commentinfo, things become
a bit easier.

To enable a subscription via comment:
$comment->info->subscribed = true;

And then when you want a list of users who are subscribed to a post
(for when a new comment is added):
$cc = array();
foreach($post->comments as $comment) {
if($comment->info->subscribed) {
$cc[] = $comment;
}
}

And you're mostly done. To unsubscribe, you'd need to unset (or set
to false) the subscribed value for every comment belonging to a
commenter with a specific email address on a specific post.

This might work, and if not, it's close:
$comments = Comments::get(array('email'=>$unsub_email, 'post_id'=>
$post_id));
foreach($comments as $comment) {
unset($comment->info->subscribed);
$comment->info->commit();
}

Some of this could be made more efficient via direct queries, but the
API makes it easy. And we could expand on this API in the future, for
example, to make it easy to find posts where the comments have a
specific value for a commentinfo key. (In other words, a Posts::get()
that returns posts to which an email address has subscribed.)

> > 3. There is no way to unsubscribe. Anyone have suggestions for
> > implementing this?
>
> There is a way, but it's not very classy. Essentially, you need to
> create an empty post, and then have a handler to generate your content
> when that post is requested. It's not entirely intuitive.

I'm not sure if you're talking about UI or logic here, but I'll take
on logic for a moment.

It's possible to grab the rewrite rule for posts and add a thing to
the end so that you can take a couple of arguments.
This is the default rule regex:
%^(?P<slug>[^/]+)(?:/page/(?P<page>\d+))?/?$%i

And you'd create a new rule that uses parts of it to create this:
%^(?P<slug>[^/]+)/unsubscribe/(?P<emailhash>[\da-f]+)/?$%i

The new rule responds to URLs that look like this:
/my-post-slug/unsubscribe/38f1ac6

You can dispatch URLs that match the new rule to a handler in your
plugin. If the hash is included (it's optional), code would search
for an email in the comments that matched the hash. If it doesn't
find a matching address, it displays an error. If it does find a
matching address, it displays a confirmation form. Upon confirming
that the user really wanted to unsubscribe, unsubscribe that address.
This format of URL (with the hash) would be useful for including in
the sent emails, so that users could two-click their way to
unsubscription.

If no hash accompanies the URL, the plugin could instead display a
template with a form that allowed the user to enter their email
address to unsubscribe, and then do the search that way. This format
would be useful for adding a link near the comment form to
unsubscribe.

I think the actual display of the form is what skippy's talking
about. What he's saying (it took me a minute to figure out) is that
you can't easily create a template that fits into a site layout to
display only static content. Here's what he proposes...

When you handle the request, you create a new post, fill it with your
static content, and then send it to display like it was any other
post.

Perhaps it would look something like this:
$form = $this->get_unsub_confirmation_form(); // Returns FormUI
$post = new Post();
$post->title = 'Unsubscribe to ' . $orig_post_title;
$post->content = $form->get();
$post->type = Post::type('page');
$theme->post = $post;
$theme->act_display_post();
exit;

As an aside: There definitely needs to be a better way to do this.
Should we perhaps institute some template like "static.php" that
outputs generic content? A full implementation of this template may
look like this:

<?php $theme->display('header'); ?>
<?php echo $content; ?>
<?php $theme->display('footer'); ?>

I digress.

> > 4. Any other comments or pointers would be helpful. :)

I haven't looked to see if you have, but if you have not and are
interested, sharing your work via the -extras repository is an easy
way to get collaborative help on your plugin. It also automatically
creates builds for distribution on habariproject.org. Or not, it's
entirely up to you.

Owen

Chris J. Davis

unread,
Jan 12, 2009, 10:51:37 AM1/12/09
to habar...@googlegroups.com
I am +1 on the addition of a static template. This could make a number
of tasks less tedious in the future. There is always someone who wants
to display static content via a dynamically generated system.

Chris

ringmaster

unread,
Jan 12, 2009, 11:09:13 AM1/12/09
to habari-dev
On Jan 12, 10:51 am, "Chris J. Davis" <c...@chrisjdavis.org> wrote:
> I am +1 on the addition of a static template. This could make a number  
> of tasks less tedious in the future. There is always someone who wants  
> to display static content via a dynamically generated system.

Also converting the comment form to FormUI would be helpful with this
and other plugins.

Owen

Chris J. Davis

unread,
Jan 12, 2009, 11:13:05 AM1/12/09
to habar...@googlegroups.com
Yep, as you have suggested before as I remember.

I will see what I can do on that front today.

Sean Coates

unread,
Jan 12, 2009, 11:16:36 AM1/12/09
to habar...@googlegroups.com
> Also converting the comment form to FormUI would be helpful with this
> and other plugins.

Indeed.
I had to use some nasty hacks to get around this in the honeypot plugin.

S

Steve Love

unread,
Jan 13, 2009, 12:22:23 AM1/13/09
to habari-dev
Thanks for the pointers, Scott, and also for the word of caution on
the emails. I considered the possible impact of sending one email per
subscriber on high-traffic sites but wasn't sure of any better
alternatives. I thought maybe a queue might help, but a very active
post could create quite a backlog. Is there any sense in sending
emails with 10 or so BCC recipients at a time?

I seem to remember the subscribe2 plugin making use of a crontab. Did
you ever come up with any best practices for sending out subscriber
emails?

I'd also thought of storing subscriber addresses in an array in a
single record, but decided against it fearing one glitch while
inserting new data could destroy the whole record. Maybe I'm being too
paranoid though.

Thanks again,
Steve

On Jan 12, 6:59 am, "Scott Merrill" <ski...@skippy.net> wrote:

Steve Love

unread,
Jan 13, 2009, 12:29:39 AM1/13/09
to habari-dev
Makes sense to me. I'll work on it this weekend.

Steve

Steve Love

unread,
Jan 13, 2009, 12:34:50 AM1/13/09
to habari-dev
I will see what I can do using the commentinfo table instead. Thanks.

And thanks for the suggestions regarding unsubscribing. I was actually
looking for ideas for both UI and logic, so all comments on that have
been very helpful.

I am definitely interested in sharing the plugin in the extras
repository. How do I go about this?

Steve

On Jan 12, 8:41 am, ringmaster <epit...@gmail.com> wrote:
> On Jan 12, 8:59 am, "Scott Merrill" <ski...@skippy.net> wrote:
> ...
>
>
>
> > $post->info->{$comment->email} = 'SUBSCRIBE';
> > $post->info->update(); // this is what commits your changes to the DB
>
> And all of that code looks fine.
>
> What I would suggest, though, is what Arthus was talking about in his
> prior message.  Habari has a commentinfo table that allows you to
> store metadata with each comment, just like you can with each post.
> So while you're subscribing to a post, your comment is actually
> attached to the post, and the comment has info on it saying that
> you're subscribed.  This is better for a couple of reasons.
>
> The first reason has to do with a limitation of postinfo.  As you
> noticed, postinfo keys (like commentinfo keys) are unique.  So you
> can't have multiple "subscribed" values unless they're stored in an
> array in a single key.
>
> You can do this:
> $post->info->subscribed = array('al...@example.com',
> '...@example.com');

Michael Harris

unread,
Jan 13, 2009, 1:43:31 AM1/13/09
to habar...@googlegroups.com
2009/1/13 Steve Love <stev...@gmail.com>:

>
> I am definitely interested in sharing the plugin in the extras
> repository. How do I go about this?

Register an account on Trac,
http://trac.habariproject.org/habari/register, then ask for access to
-extras, either here or on IRC, giving your Trac username.

--
Michael C. Harris, School of CS&IT, RMIT University
http://twofishcreative.com/michael/blog
IRC: michaeltwofish #habari

Scott Merrill

unread,
Jan 13, 2009, 9:27:19 AM1/13/09
to habar...@googlegroups.com
On Tue, Jan 13, 2009 at 12:22 AM, Steve Love <stev...@gmail.com> wrote:
> Thanks for the pointers, Scott, and also for the word of caution on
> the emails. I considered the possible impact of sending one email per
> subscriber on high-traffic sites but wasn't sure of any better
> alternatives. I thought maybe a queue might help, but a very active
> post could create quite a backlog. Is there any sense in sending
> emails with 10 or so BCC recipients at a time?

I think it's worth batching subscribers together in the BCC field. In
my experience, I never found a hosting provider that imposed a limit
less than 30, so 30 BCC recipients per batch makes a pretty good
default. It maximizes the number of recipients per SMTP transaction,
it doesn't look like spam, and most blogs should probably be able to
deliver to all recipients with a single message.

> I seem to remember the subscribe2 plugin making use of a crontab. Did
> you ever come up with any best practices for sending out subscriber
> emails?

The cron stuff in subscribe2 was to send a daily digest of posts each
day, rather than one email per post. I have no information on how
widely used this feature was. I wouldn't spend a lot of time adding
such functionality at this time.

Cheers,
Scott

Reply all
Reply to author
Forward
0 new messages