mark: A photo of Mark kneeling on top of the Taal Volcano in the Philippines. It was a long hike. (Default)
Mark Smith ([staff profile] mark) wrote in [site community profile] changelog2009-02-24 08:23 am

[dw-free] Fix up WTF-related ESN events

[commit: http://hg.dwscoalition.org/dw-free/rev/1ad1d37c54ab]

http://bugs.dwscoalition.org/show_bug.cgi?id=317

Add ESN events for adding/removing someone from your circle. This patch
also changes the calling convention of the remove_edge method for edge
manipulation, so beware.

Patch by [personal profile] janinedog.

--------------------------------------------------------------------------------
diff -r 9ae6cc535deb -r 1ad1d37c54ab bin/upgrading/en.dat
--- a/bin/upgrading/en.dat	Tue Feb 24 08:17:37 2009 +0000
+++ b/bin/upgrading/en.dat	Tue Feb 24 08:23:54 2009 +0000
@@ -1136,6 +1136,26 @@ Enjoy!
 [[siteroot]]/
 .
 
+esn.addedtocircle.trusted.email_text<<
+Hi [[user]],
+
+[[poster]] has granted you access to their journal. You will now be able to read their locked entries.
+
+You can:
+.
+
+esn.addedtocircle.trusted.subject=[[who]] granted you access to their journal!
+
+esn.addedtocircle.watched.email_text<<
+Hi [[user]],
+
+[[poster]] has subscribed to your journal. They will now be able to read your[[entries]] entries on their reading list.
+
+You can:
+.
+
+esn.addedtocircle.watched.subject=[[who]] subscribed to your journal!
+
 esn.bday.subject=[[bdayuser]]'s birthday is coming up!
 
 esn.bday.email<<
@@ -1466,34 +1486,6 @@ esn.journal_new_entry.head_user=[[poster
 
 esn.journal_new_entry.about= titled "[[title]]"
 
-esn.befriended.subject=[[who]] added you as a friend!
-
-esn.befriended.email_text<<
-Hi [[user]],
-
-[[poster]] has added you to their Friends list. They will now be able to read your[[entries]] entries on their Friends page.
-
-You can:
-.
-
-esn.befriended.openid_email_text<<
-Hi [[user]],
-
-[[poster]] has added you to their Friends list.
-
-You can:
-.
-
-esn.defriended.subject=[[who]] removed you from their Friends list
-
-esn.defriended.email_text<<
-Hi [[user]],
-
-[[poster]] has removed you from their Friends list.
-
-You can:
-.
-
 esn.community_join_requst.email_text<<
 Hi [[maintainer]],
 
@@ -1532,7 +1524,9 @@ esn.join_community=[[openlink]]Join [[jo
 
 esn.read_user_entries=[[openlink]]Read [[poster]]'s recent entries[[closelink]]
 
-esn.add_friend=[[openlink]]Add [[journal]] to your Friends list[[closelink]]
+esn.add_trust=[[openlink]]Grant access to [[journal]][[closelink]]
+
+esn.add_watch=[[openlink]]Subscribe to [[journal]][[closelink]]
 
 esn.screened=This comment was screened.
 
@@ -1572,19 +1566,41 @@ You can:
 
 esn.public=public
 
-esn.remove_friend=[[openlink]]Remove [[postername]] from your Friends list[[closelink]]
+esn.remove_trust=[[openlink]]Remove access from [[postername]][[closelink]]
+
+esn.remove_watch=[[openlink]]Unsubscribe from [[postername]][[closelink]]
 
 esn.post_entry=[[openlink]]Post an entry[[closelink]]
 
-esn.edit_friends=[[openlink]]Edit Friends[[closelink]]
+esn.edit_friends=[[openlink]]Manage Circle[[closelink]]
 
-esn.edit_groups=[[openlink]]Edit Friends groups[[closelink]]
+esn.edit_groups=[[openlink]]Edit Filters[[closelink]]
 
 esn.read_journal=[[openlink]]Read [[postername]]'s journal[[closelink]]
 
 esn.view_profile=[[openlink]]View [[postername]]'s profile[[closelink]]
 
 esn.your_inbox=your Inbox
+
+esn.removedfromcircle.trusted.email_text<<
+Hi [[user]],
+
+[[poster]] has removed your access to their journal.  You will no longer be able to read their locked entries.
+
+You can:
+.
+
+esn.removedfromcircle.trusted.subject=[[who]] removed your access to their journal
+
+esn.removedfromcircle.watched.email_text<<
+Hi [[user]],
+
+[[poster]] has unsubscribed from your journal.  They will no longer be able to read your entries on their reading list.
+
+You can:
+.
+
+esn.removedfromcircle.watched.subject=[[who]] has unsubscribed from your journal
 
 entryform.adultcontent=Adult Content:
 
@@ -1840,9 +1856,9 @@ error.usernameinvalid=Username contains 
 
 error.usernamelong=Username is too long; cannot exceed 15 characters.
 
-event.befriended.me=Someone adds me as a friend
+event.addedtocircle.me=Someone adds me to their circle
 
-event.befriended.user=Someone adds [[user]] as a friend
+event.addedtocircle.user=Someone adds [[user]] to their circle
 
 event.birthday.me=One of the people on my access or subscription lists has an upcoming birthday
 
@@ -1851,10 +1867,6 @@ event.comm_invite=I receive an invitatio
 event.comm_invite=I receive an invitation to join a community
 
 event.community_join_requst=Someone requests membership in a community I maintain
-
-event.defriended.me=Someone removes me from their Friends list
-
-event.defriended.user=Someone removes [[user]] from their Friends list
 
 event.invited_friend_joins=Someone I invited creates a new journal
 
@@ -1947,6 +1959,10 @@ event.journal_new_entry.community=Someon
 event.journal_new_entry.community=Someone posts a new entry to [[user]]
 
 event.journal_new_entry.user=[[user]] posts a new entry.
+
+event.removedfromcircle.me=Someone removes me from their circle
+
+event.removedfromcircle.user=Someone removes [[user]] from their circle
 
 event.userpic_upload.me=One of the accounts I subscribe to uploads a new userpic
 
diff -r 9ae6cc535deb -r 1ad1d37c54ab cgi-bin/DW/Console/Command/ManageCircle.pm
--- a/cgi-bin/DW/Console/Command/ManageCircle.pm	Tue Feb 24 08:17:37 2009 +0000
+++ b/cgi-bin/DW/Console/Command/ManageCircle.pm	Tue Feb 24 08:23:54 2009 +0000
@@ -56,18 +56,29 @@ sub execute {
         unless $remote;
 
     if ( $cmd eq 'add_read' ) {
-        $remote->add_edge( $to_u, watch => { fgcolor => int(rand()*16777216), bgcolor => int(rand()*16777216) } );
+        $remote->add_edge( $to_u, watch => {
+            fgcolor => int(rand()*16777216),
+            bgcolor => int(rand()*16777216),
+            nonotify => $remote->watches( $to_u ) ? 1 : 0,
+        } );
 
     } elsif ( $cmd eq 'del_read' ) {
-        $remote->remove_edge( $to_u, qw/ watch / );
+        $remote->remove_edge( $to_u, watch => {
+            nonotify => $remote->watches( $to_u ) ? 0 : 1,
+        } );
 
     } elsif ( $cmd eq 'add_access' ) {
         my $mask = 0;
         $mask += ( 1 << $_ ) foreach @groups;
-        $remote->add_edge( $to_u, trust => { mask => $mask } );
+        $remote->add_edge( $to_u, trust => {
+            mask => $mask,
+            nonotify => $remote->trusts( $to_u ) ? 1 : 0,
+        } );
 
     } elsif ( $cmd eq 'del_access' ) {
-        $remote->remove_edge( $to_u, qw/ trust / );
+        $remote->remove_edge( $to_u, trust => {
+            nonotify => $remote->trusts( $to_u ) ? 0 : 1,
+        } );
 
     } elsif ( $cmd eq 'get_read' ) {
 
diff -r 9ae6cc535deb -r 1ad1d37c54ab cgi-bin/DW/User/Edges.pm
--- a/cgi-bin/DW/User/Edges.pm	Tue Feb 24 08:17:37 2009 +0000
+++ b/cgi-bin/DW/User/Edges.pm	Tue Feb 24 08:23:54 2009 +0000
@@ -186,7 +186,7 @@ sub add_edge {
 
 # removes an edge between two users
 sub remove_edge {
-    my ( $from_u, $to_u, @edges ) = @_;
+    my ( $from_u, $to_u, %edges ) = @_;
 
     # need u objects
     $from_u = LJ::want_user( $from_u );
@@ -194,20 +194,21 @@ sub remove_edge {
 
     # error check inputs
     return 0 unless $from_u && $to_u;
-    return 0 unless DW::User::Edges::validate_edges( \@edges );
+    return 0 unless DW::User::Edges::validate_edges( \%edges );
 
-    # try to remove the edges
-    my %edges_hr = ( map { $_ => 1 } @edges );
-    while ( my $key = shift @edges ) {
+    # now we try to remove these edges.  note that we do this in this way so that
+    # multiple edges can be consumed by one remove sub.
+    my @to_del = keys %edges;
+    while ( my $key = shift @to_del ) {
 
         # some modules will define multiple edges, and so one call to add_sub might
         # get rid of more than one edge, so we have to do this check to ensure that
         # the edge still exists
-        next unless $edges_hr{$key};
+        next unless $edges{$key};
 
         # simply calls an add_sub to handle the edge.  we expect them to remove the
         # edge from the hashref if they process it.
-        $DW::User::Edges::VALID_EDGES{$key}->{del_sub}->( $from_u, $to_u, \%edges_hr );
+        $DW::User::Edges::VALID_EDGES{$key}->{del_sub}->( $from_u, $to_u, \%edges );
     }
 
     # all good
diff -r 9ae6cc535deb -r 1ad1d37c54ab cgi-bin/DW/User/Edges/WatchTrust.pm
--- a/cgi-bin/DW/User/Edges/WatchTrust.pm	Tue Feb 24 08:17:37 2009 +0000
+++ b/cgi-bin/DW/User/Edges/WatchTrust.pm	Tue Feb 24 08:23:54 2009 +0000
@@ -120,27 +120,25 @@ sub _add_wt_edge {
 
     # fire notifications if we have theschwartz
     if ( my $sclient = LJ::theschwartz() ) {
+        my $notify = !$LJ::DISABLED{esn} &&
+            !$from_u->equals( $to_u ) &&
+            $from_u->is_visible &&
+            ( $from_u->is_personal || $from_u->is_identity ) &&
+            ( $to_u->is_personal || $to_u->is_identity ) &&
+            !$to_u->has_banned( $from_u ) ? 1 : 0;
+        my $trust_notify = $notify && !$trust_edge->{nonotify} ? 1 : 0;
+        my $watch_notify = $notify && !$watch_edge->{nonotify} ? 1 : 0;
 
-        # part of the criteria for whether to fire befriended event
-        my $skip_notify = ( $trust_edge->{nonotify} || $watch_edge->{nonotify} ) ? 1 : 0;
-        my $notify = !$LJ::DISABLED{esn} && !$skip_notify
-                     && $from_u->is_visible && $from_u->is_person;
-
-        # only fire event if the from_u is a person and not banned
-        if ( $notify && ! $to_u->is_banned( $from_u ) ) {
-# FIXME(mark): need a new event here instead of just Befriended
-#            $sclient->insert_jobs( LJ::Event::Befriended->new( $to_u, $from_u )->fire_job );
-        }
+        $sclient->insert_jobs( LJ::Event::AddedToCircle->new( $to_u, $from_u, 1 )->fire_job )
+            if $do_trust && $trust_notify;
+        $sclient->insert_jobs( LJ::Event::AddedToCircle->new( $to_u, $from_u, 2 )->fire_job )
+            if $do_watch && $watch_notify;
     }
 
     return 1;
 }
 
 # internal method to delete an edge
-#
-# FIXME: we should be able to accept an options here that says
-# 'please do not notify', skips theschwartz event ...
-#
 sub _del_wt_edge {
     my ( $from_u, $to_u, $edges ) = @_;
     $from_u = LJ::want_user( $from_u ) or return 0;
@@ -150,6 +148,10 @@ sub _del_wt_edge {
     my $de_watch = delete $edges->{watch};
     my $de_trust = delete $edges->{trust};
     return 1 unless $de_watch || $de_trust;
+
+    # now setup some helper variables
+    my $do_watch = $de_watch ? 1 : 0;
+    my $do_trust = $de_trust ? 1 : 0;
 
     # get what we know
     my $does_watch = $from_u->watches( $to_u );
@@ -194,24 +196,22 @@ sub _del_wt_edge {
     LJ::memcache_kill( $to_u, 'trusted_by' );
     LJ::MemCache::delete( [$from_u->id, "trustmask:" . $from_u->id . ":" . $to_u->id] );
 
-# TODO(mark): need to add this when we get the events sorted
-#    # part of the criteria for whether to fire defriended event
-#    my $notify = !$LJ::DISABLED{esn} && !$opts->{nonotify} && $u->is_visible && $u->is_person;
-#
-#    # delete friend-of memcache keys for anyone who was removed
-#    foreach my $fid (@del_ids) {
-#
-#        my $friendee = LJ::load_userid($fid);
-#        if ($sclient) {
-#            my @jobs;
-#
-#            # only fire event if the friender is a person and not banned and visible
-#            if ($notify && !$friendee->has_banned($u)) {
-#                push @jobs, LJ::Event::Defriended->new($friendee, $u)->fire_job;
-#            }
-#            $sclient->insert_jobs(@jobs);
-#        }
-#    }
+    # fire notifications if we have theschwartz
+    if ( my $sclient = LJ::theschwartz() ) {
+        my $notify = !$LJ::DISABLED{esn} &&
+            !$from_u->equals( $to_u ) &&
+            $from_u->is_visible &&
+            ( $from_u->is_personal || $from_u->is_identity ) &&
+            ( $to_u->is_personal || $to_u->is_identity ) &&
+            !$to_u->has_banned( $from_u ) ? 1 : 0;
+        my $trust_notify = $notify && !$de_trust->{nonotify} ? 1 : 0;
+        my $watch_notify = $notify && !$de_watch->{nonotify} ? 1 : 0;
+
+        $sclient->insert_jobs( LJ::Event::RemovedFromCircle->new( $to_u, $from_u, 1 )->fire_job )
+            if $do_trust && $trust_notify;
+        $sclient->insert_jobs( LJ::Event::RemovedFromCircle->new( $to_u, $from_u, 2 )->fire_job )
+            if $do_watch && $watch_notify;
+    }
 }
 
 ###############################################################################
diff -r 9ae6cc535deb -r 1ad1d37c54ab cgi-bin/LJ/Event.pm
--- a/cgi-bin/LJ/Event.pm	Tue Feb 24 08:17:37 2009 +0000
+++ b/cgi-bin/LJ/Event.pm	Tue Feb 24 08:23:54 2009 +0000
@@ -25,8 +25,10 @@ foreach my $event (@EVENTS) {
 #                                   ($ju,$jtalkid)   # TODO: should probably be ($ju,$jitemid,$jtalkid)
 #    LJ::Event::UserNewComment     -- a user left a new comment somewhere
 #                                   ($u,$journalid,$jtalkid)
-#    LJ::Event::Befriended         -- user $fromuserid added $u as a friend
-#                                   ($u,$fromuserid)
+#    LJ::Event::AddedToCircle      -- user $fromuserid added $u to their circle; $actionid is 1 (trust) or 2 (watch)
+#                                   ($u,$fromuserid,$actionid)
+#    LJ::Event::RemovedFromCircle  -- user $fromuserid removed $u to their circle; $actionid is 1 (trust) or 2 (watch)
+#                                   ($u,$fromuserid,$actionid)
 #    LJ::Event::CommunityInvite    -- user $fromuserid invited $u to join $commid community)
 #                                   ($u,$fromuserid, $commid)
 #    LJ::Event::InvitedFriendJoins -- user $u1 was invited to join by $u2 and created a journal
@@ -39,8 +41,6 @@ foreach my $event (@EVENTS) {
 #                                   ($u)
 #    LJ::Event::PollVote           -- $u1 voted in poll $p posted by $u
 #                                   ($u, $u1, $up)
-#    LJ::Event::Defriended         -- user $fromuserid removed $u as a friend
-#                                   ($u,$fromuserid)
 #    LJ::Event::UserMessageRecvd   -- user $u received message with ID $msgid from user $otherid
 #                                   ($u, $msgid, $otherid)
 #    LJ::Event::UserMessageSent    -- user $u sent message with ID $msgid to user $otherid
diff -r 9ae6cc535deb -r 1ad1d37c54ab cgi-bin/LJ/Event/AddedToCircle.pm
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cgi-bin/LJ/Event/AddedToCircle.pm	Tue Feb 24 08:23:54 2009 +0000
@@ -0,0 +1,200 @@
+#!/usr/bin/perl
+#
+# LJ::Event::AddedToCircle
+#
+# This is the event that's fired when someone adds another user to their circle.
+#
+# Authors:
+#      Janine Costanzo <janine@netrophic.com>
+#
+# Copyright (c) 2009 by Dreamwidth Studios, LLC.
+#
+# This program is free software; you may redistribute it and/or modify it under
+# the same terms as Perl itself.  For a copy of the license, please reference
+# 'perldoc perlartistic' or 'perldoc perlgpl'.
+#
+
+package LJ::Event::AddedToCircle;
+
+use strict;
+use Scalar::Util qw( blessed );
+use Carp qw( croak );
+use base 'LJ::Event';
+
+sub new {
+    my ( $class, $u, $fromu, $actionid ) = @_;
+
+    foreach ( $u, $fromu ) {
+        croak 'Not an LJ::User' unless blessed $_ && $_->isa("LJ::User");
+    }
+
+    croak 'Invalid actionid; must be 1 (trust) or 2 (watch)' unless $actionid == 1 || $actionid == 2;
+
+    return $class->SUPER::new( $u, $fromu->id, $actionid );
+}
+
+sub is_common { 0 }
+
+my @_ml_strings_en = qw(
+    esn.addedtocircle.trusted.subject
+    esn.addedtocircle.watched.subject
+    esn.addedtocircle.trusted.email_text
+    esn.addedtocircle.watched.email_text
+    esn.public
+    esn.add_trust
+    esn.add_watch
+    esn.read_journal
+    esn.view_profile
+    esn.edit_friends
+    esn.edit_groups
+);
+
+sub as_email_subject {
+    my ( $self, $u ) = @_;
+
+    if ( $self->trusted ) {
+        return LJ::Lang::get_text( $u->prop('browselang'), 'esn.addedtocircle.trusted.subject', undef, { who => $self->fromuser->display_username } );
+    } else { # watched
+        return LJ::Lang::get_text( $u->prop('browselang'), 'esn.addedtocircle.watched.subject', undef, { who => $self->fromuser->display_username } );
+    }
+}
+
+sub _as_email {
+    my ( $self, $u, $is_html ) = @_;
+
+    my $lang        = $u->prop('browselang');
+    my $user        = $is_html ? ($u->ljuser_display) : ($u->display_username);
+    my $poster      = $is_html ? ($self->fromuser->ljuser_display) : ($self->fromuser->display_username);
+    my $postername  = $self->fromuser->user;
+    my $journal_url = $self->fromuser->journal_base;
+    my $journal_profile = $self->fromuser->profile_url;
+
+    # Precache text lines
+    LJ::Lang::get_text_multi( $lang, undef, \@_ml_strings_en );
+
+    my $entries = $u->trusts( $self->fromuser ) ? "" : " " . LJ::Lang::get_text( $lang, 'esn.public', undef );
+
+    my $vars = {
+        who         => $self->fromuser->display_username,
+        poster      => $poster,
+        postername  => $poster,
+        journal     => $poster,
+        user        => $user,
+        entries     => $entries,
+    };
+
+    if ( $self->trusted ) {
+        return LJ::Lang::get_text( $lang, 'esn.addedtocircle.trusted.email_text', undef, $vars ) .
+            $self->format_options( $is_html, $lang, $vars,
+            {
+                'esn.add_trust'       => [ $u->trusts( $self->fromuser ) ? 0 : 1, "$LJ::SITEROOT/manage/circle/add.bml?user=$postername&action=access" ],
+                'esn.read_journal'    => [ 2, $journal_url ],
+                'esn.view_profile'    => [ 3, $journal_profile ],
+                'esn.edit_friends'    => [ 4, "$LJ::SITEROOT/manage/circle/edit.bml" ],
+                'esn.edit_groups'     => [ 5, "$LJ::SITEROOT/manage/circle/editfilters.bml" ],
+            }
+        );
+    } else { # watched
+        return LJ::Lang::get_text( $lang, 'esn.addedtocircle.watched.email_text', undef, $vars ) .
+            $self->format_options( $is_html, $lang, $vars,
+            {
+                'esn.add_watch'       => [ $u->watches( $self->fromuser ) ? 0 : 1, "$LJ::SITEROOT/manage/circle/add.bml?user=$postername&action=subscribe" ],
+                'esn.read_journal'    => [ 2, $journal_url ],
+                'esn.view_profile'    => [ 3, $journal_profile ],
+                'esn.edit_friends'    => [ 4, "$LJ::SITEROOT/manage/circle/edit.bml" ],
+                'esn.edit_groups'     => [ 5, "$LJ::SITEROOT/manage/circle/editfilters.bml" ],
+            }
+        );
+    }
+}
+
+sub as_email_string {
+    my ( $self, $u ) = @_;
+    return _as_email( $self, $u, 0 );
+}
+
+sub as_email_html {
+    my ( $self, $u ) = @_;
+    return _as_email( $self, $u, 1 );
+}
+
+sub fromuser {
+    my $self = shift;
+    return LJ::load_userid( $self->arg1 );
+}
+
+sub actionid {
+    my $self = shift;
+    return $self->arg2;
+}
+
+sub trusted {
+    my $self = shift;
+    return $self->actionid == 1 ? 1 : 0;
+}
+
+sub watched {
+    my $self = shift;
+    return $self->actionid == 2 ? 1 : 0;
+}
+
+sub as_html {
+    my $self = shift;
+
+    if ( $self->trusted ) {
+        return sprintf( "%s has granted you access to their journal.", $self->fromuser->ljuser_display );
+    } else { # watched
+        return sprintf( "%s has subscribed to your journal.", $self->fromuser->ljuser_display );
+    }
+}
+
+sub as_html_actions {
+    my ( $self ) = @_;
+
+    my $u = $self->u;
+    my $fromuser = $self->fromuser;
+
+    my $ret .= "<div class='actions'>";
+    if ( $self->trusted ) {
+        $ret .= $u->trusts( $fromuser ) ?
+            " <a href='" . $fromuser->profile_url . "'>View Profile</a>" :
+            " <a href='$LJ::SITEROOT/manage/circle/add.bml?user=" . $fromuser->user . "&action=access'>Grant Access</a>";
+    } else { # watched
+        $ret .= $u->watches( $fromuser ) ?
+            " <a href='" . $fromuser->profile_url . "'>View Profile</a>" :
+            " <a href='$LJ::SITEROOT/manage/circle/add.bml?user=" . $fromuser->user . "&action=subscribe'>Subscribe</a>";
+    }
+    $ret .= "</div>";
+
+    return $ret;
+}
+
+sub as_string {
+    my $self = shift;
+
+    if ( $self->trusted ) {
+        return sprintf( "%s has granted you access to their journal.", $self->fromuser->user );
+    } else { # watched
+        return sprintf( "%s has subscribed to your journal.", $self->fromuser->user );
+    }
+}
+
+sub subscription_as_html {
+    my ( $class, $subscr ) = @_;
+    my $journal = $subscr->journal or croak "No user";
+    my $journal_is_owner = $journal->equals( $subscr->owner );
+
+    if ( $journal_is_owner ) {
+        return BML::ml( 'event.addedtocircle.me' );   # "Someone adds me to their circle";
+    } else {
+        my $user = $journal->ljuser_display;
+        return BML::ml( 'event.addedtocircle.user', { user => $user } ); # "Someone adds $user to their circle";
+    }
+}
+
+sub content {
+    my ( $self, $target ) = @_;
+    return $self->as_html_actions;
+}
+
+1;
diff -r 9ae6cc535deb -r 1ad1d37c54ab cgi-bin/LJ/Event/Befriended.pm
--- a/cgi-bin/LJ/Event/Befriended.pm	Tue Feb 24 08:17:37 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,152 +0,0 @@
-package LJ::Event::Befriended;
-use strict;
-use Scalar::Util qw(blessed);
-use Carp qw(croak);
-use base 'LJ::Event';
-
-sub new {
-    my ($class, $u, $fromu) = @_;
-    foreach ($u, $fromu) {
-        croak 'Not an LJ::User' unless blessed $_ && $_->isa("LJ::User");
-    }
-
-    return $class->SUPER::new($u, $fromu->{userid});
-}
-
-sub is_common { 0 }
-
-my @_ml_strings_en = (
-    'esn.public',                       # 'public',
-    'esn.befriended.subject',           # '[[who]] added you as a friend!',
-    'esn.add_friend',                   # '[[openlink]]Add [[journal]] to your Friends list[[closelink]]',
-    'esn.read_journal',                 # '[[openlink]]Read [[postername]]\'s journal[[closelink]]',
-    'esn.view_profile',                 # '[[openlink]]View [[postername]]\'s profile[[closelink]]',
-    'esn.edit_friends',                 # '[[openlink]]Edit Friends[[closelink]]',
-    'esn.edit_groups',                  # '[[openlink]]Edit Friends groups[[closelink]]',
-    'esn.befriended.email_text',        # 'Hi [[user]],
-                                        #
-                                        #[[poster]] has added you to their Friends list. They will now be able to read your[[entries]] entries on their Friends page.
-                                        #
-                                        #You can:',
-    'esn.befriended.openid_email_text', # 'Hi [[user]],
-                                        #
-                                        #[[poster]] has added you to their Friends list.
-                                        #
-                                        #You can:',
-);
-
-sub as_email_subject {
-    my ($self, $u) = @_;
-    return LJ::Lang::get_text($u->prop('browselang'), 'esn.befriended.subject', undef, { who => $self->friend->display_username } );
-}
-
-sub _as_email {
-    my ($self, $u, $is_html) = @_;
-
-    my $lang        = $u->prop('browselang');
-    my $user        = $is_html ? ($u->ljuser_display) : ($u->display_username);
-    my $poster      = $is_html ? ($self->friend->ljuser_display) : ($self->friend->display_username);
-    my $postername  = $self->friend->user;
-    my $journal_url = $self->friend->journal_base;
-    my $journal_profile = $self->friend->profile_url;
-
-    # Precache text lines
-    LJ::Lang::get_text_multi($lang, undef, \@_ml_strings_en);
-
-    my $entries = LJ::is_friend($u, $self->friend) ? "" : " " . LJ::Lang::get_text($lang, 'esn.public', undef);
-    my $is_open_identity = $self->friend->openid_identity;
-
-    my $vars = {
-        who         => $self->friend->display_username,
-        poster      => $poster,
-        postername  => $poster,
-        journal     => $poster,
-        user        => $user,
-        entries     => $entries,
-    };
-
-    my $email_body_key = 'esn.befriended.' .
-        ($u->openid_identity ? 'openid_' : '' ) . 'email_text';
-
-    return LJ::Lang::get_text($lang, $email_body_key, undef, $vars) .
-        $self->format_options($is_html, $lang, $vars,
-        {
-            'esn.add_friend'      => [ LJ::is_friend($u, $self->friend) ? 0 : 1,
-                                            # Why not $self->friend->addfriend_url ?
-                                            "$LJ::SITEROOT/manage/circle/add.bml?user=$postername" ],
-            'esn.read_journal'    => [ $is_open_identity ? 0 : 2,
-                                            $journal_url ],
-            'esn.view_profile'    => [ 3, $journal_profile ],
-            'esn.edit_friends'    => [ 4, "$LJ::SITEROOT/manage/circle/edit.bml" ],
-            'esn.edit_groups'     => [ 5, "$LJ::SITEROOT/manage/circle/editfilters.bml" ],
-        }
-    );
-}
-
-sub as_email_string {
-    my ($self, $u) = @_;
-    return _as_email($self, $u, 0);
-}
-
-sub as_email_html {
-    my ($self, $u) = @_;
-    return _as_email($self, $u, 1);
-}
-
-sub friend {
-    my $self = shift;
-    return LJ::load_userid($self->arg1);
-}
-
-sub as_html {
-    my $self = shift;
-    return sprintf("%s has added you as a friend.",
-                   $self->friend->ljuser_display);
-}
-
-sub as_html_actions {
-    my ($self) = @_;
-
-    my $u = $self->u;
-    my $friend = $self->friend;
-    my $ret .= "<div class='actions'>";
-    $ret .= $u->is_friend($friend)
-            ? " <a href='" . $friend->profile_url . "'>View Profile</a>"
-            : " <a href='" . $friend->addfriend_url . "'>Add Friend</a>";
-    $ret .= "</div>";
-
-    return $ret;
-}
-
-sub as_string {
-    my $self = shift;
-    return sprintf("%s has added you as a friend.",
-                   $self->friend->{user});
-}
-
-sub as_sms {
-    my $self = shift;
-    return sprintf("%s has added you to their friends list. Reply with ADD %s to add them " .
-                   "to your friends list. Standard rates apply.",
-                   $self->friend->user, $self->friend->user);
-}
-
-sub subscription_as_html {
-    my ($class, $subscr) = @_;
-    my $journal = $subscr->journal or croak "No user";
-    my $journal_is_owner = LJ::u_equals($journal, $subscr->owner);
-
-    if ($journal_is_owner) {
-        return BML::ml('event.befriended.me');   # "Someone adds me as a friend";
-    } else {
-        my $user = $journal->ljuser_display;
-        return BML::ml('event.befriended.user', { user => $user } ); # "Someone adds $user as a friend";
-    }
-}
-
-sub content {
-    my ($self, $target) = @_;
-    return $self->as_html_actions;
-}
-
-1;
diff -r 9ae6cc535deb -r 1ad1d37c54ab cgi-bin/LJ/Event/CommunityInvite.pm
--- a/cgi-bin/LJ/Event/CommunityInvite.pm	Tue Feb 24 08:17:37 2009 +0000
+++ b/cgi-bin/LJ/Event/CommunityInvite.pm	Tue Feb 24 08:23:54 2009 +0000
@@ -25,7 +25,7 @@ my @_ml_strings = (
     'esn.manage_invitations',       # '[[openlink]]Manage your invitations[[closelink]]'
     'esn.read_last_comm_entries',   # '[[openlink]]Read the latest entries in [[journal]][[closelink]]'
     'esn.view_profile',             # '[[openlink]]View [[postername]]'s profile[[closelink]]',
-    'esn.add_friend',               # '[[openlink]]Add [[journal]] to your Friends list[[closelink]]'
+    'esn.add_watch',                # '[[openlink]]Subscribe to [[journal]][[closelink]]',
 );
 
 sub as_email_subject {
@@ -67,7 +67,7 @@ sub _as_email {
             'esn.manage_invitations'        => [ 1, "$LJ::SITEROOT/manage/invites.bml" ],
             'esn.read_last_comm_entries'    => [ 2, $community_url ],
             'esn.view_profile'              => [ 3, $community_profile ],
-            'esn.add_friend'                => [ $u->watches( $self->comm ) ? 0 : 4,
+            'esn.add_watch'                 => [ $u->watches( $self->comm ) ? 0 : 4,
                                                 "$LJ::SITEROOT/manage/circle/add.bml?user=$community_user&action=subscribe" ],
         }
     );
diff -r 9ae6cc535deb -r 1ad1d37c54ab cgi-bin/LJ/Event/Defriended.pm
--- a/cgi-bin/LJ/Event/Defriended.pm	Tue Feb 24 08:17:37 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,136 +0,0 @@
-package LJ::Event::Defriended;
-use strict;
-use Scalar::Util qw(blessed);
-use Carp qw(croak);
-use base 'LJ::Event';
-
-sub new {
-    my ($class, $u, $fromu) = @_;
-    foreach ($u, $fromu) {
-        croak 'Not an LJ::User' unless blessed $_ && $_->isa("LJ::User");
-    }
-
-    return $class->SUPER::new($u, $fromu->{userid});
-}
-
-sub is_common { 0 }
-
-my @_ml_strings_en = (
-    'esn.public',                   # 'public',
-    'esn.defriended.subject',       # '[[who]] removed you from their Friends list',
-    'esn.remove_friend',            # '[[openlink]]Remove [[postername]] from your Friends list[[closelink]]',
-    'esn.post_entry',               # '[[openlink]]Post an entry[[closelink]]',
-    'esn.edit_friends',             # '[[openlink]]Edit Friends[[closelink]]',
-    'esn.edit_groups',              # '[[openlink]]Edit Friends groups[[closelink]]',
-    'esn.defriended.email_text',    # 'Hi [[user]],
-                                    #
-                                    #[[poster]] has removed you from their Friends list.
-                                    #
-                                    #You can:',
-);
-
-sub as_email_subject {
-    my ($self, $u) = @_;
-
-    return LJ::Lang::get_text($u->prop('browselang'), 'esn.defriended.subject', undef, { who => $self->friend->display_username } );
-}
-
-sub _as_email {
-    my ($self, $u, $is_html) = @_;
-
-    my $lang        = $u->prop('browselang');
-    my $user        = $is_html ? ($u->ljuser_display) : ($u->user);
-    my $poster      = $is_html ? ($self->friend->ljuser_display) : ($self->friend->user);
-    my $postername  = $self->friend->user;
-    my $journal_url = $self->friend->journal_base;
-    my $journal_profile = $self->friend->profile_url;
-
-    # Precache text lines
-    LJ::Lang::get_text_multi($lang, undef, \@_ml_strings_en);
-
-    my $entries = LJ::is_friend($u, $self->friend) ? "" : " " . LJ::Lang::get_text($lang, 'esn.public', undef);
-
-    my $vars = {
-        who         => $self->friend->display_username,
-        poster      => $poster,
-        postername  => $postername,
-        user        => $user,
-        entries     => $entries,
-    };
-
-    return LJ::Lang::get_text($lang, 'esn.defriended.email_text', undef, $vars) .
-        $self->format_options($is_html, $lang, $vars,
-        {
-            'esn.remove_friend' => [ LJ::is_friend($u, $self->friend) ? 1 : 0,
-                                            "$LJ::SITEROOT/manage/circle/add.bml?user=$postername" ],
-            'esn.post_entry'    => [ 3, "$LJ::SITEROOT/update.bml" ],
-            'esn.edit_friends'  => [ 4, "$LJ::SITEROOT/manage/circle/edit.bml" ],
-            'esn.edit_groups'   => [ 5, "$LJ::SITEROOT/manage/circle/editfilters.bml" ],
-        }
-    );
-}
-
-sub as_email_string {
-    my ($self, $u) = @_;
-    return _as_email($self, $u, 0);
-}
-
-sub as_email_html {
-    my ($self, $u) = @_;
-    return _as_email($self, $u, 1);
-}
-
-# technically "former friend-of", but who's keeping track.
-sub friend {
-    my $self = shift;
-    return LJ::load_userid($self->arg1);
-}
-
-sub as_html {
-    my $self = shift;
-    return sprintf("%s has removed you from their Friends list.",
-                   $self->friend->ljuser_display);
-}
-
-sub as_html_actions {
-    my ($self) = @_;
-
-    my $u = $self->u;
-    my $friend = $self->friend;
-    my $ret .= "<div class='actions'>";
-    $ret .= " <a href='" . $friend->addfriend_url . "'>Remove friend</a>"
-        if LJ::is_friend($u, $friend);
-    $ret .= " <a href='" . $friend->profile_url . "'>View profile</a>";
-    $ret .= "</div>";
-
-    return $ret;
-}
-
-sub as_string {
-    my $self = shift;
-    return sprintf("%s has removed you from their Friends list.",
-                   $self->friend->{user});
-}
-
-sub subscription_as_html {
-    my ($class, $subscr) = @_;
-    my $journal = $subscr->journal or croak "No user";
-    my $journal_is_owner = LJ::u_equals($journal, $subscr->owner);
-    # "Someone removes $user from their Friends list"
-    # where $user may be also 'me'.
-    return BML::ml('event.defriended.' . ($journal_is_owner ? 'me' : 'user'), { user => $journal->ljuser_display });
-}
-
-# only users with the track_defriended cap can use this
-sub available_for_user  {
-    my ($class, $u, $subscr) = @_;
-    return $u->get_cap("track_defriended") ? 1 : 0;
-}
-
-sub content {
-    my ($self) = @_;
-
-    return $self->as_html_actions;
-}
-
-1;
diff -r 9ae6cc535deb -r 1ad1d37c54ab cgi-bin/LJ/Event/InvitedFriendJoins.pm
--- a/cgi-bin/LJ/Event/InvitedFriendJoins.pm	Tue Feb 24 08:17:37 2009 +0000
+++ b/cgi-bin/LJ/Event/InvitedFriendJoins.pm	Tue Feb 24 08:23:54 2009 +0000
@@ -16,7 +16,8 @@ sub is_common { 0 }
 
 my @_ml_strings = (
     'esn.invited_friend_joins.subject', # '[[who]] created a journal!'
-    'esn.add_friend',                   # '[[openlink]]Add [[journal]] to your Friends list[[closelink]]',
+    'esn.add_trust',                    # '[[openlink]]Grant access to [[journal]][[closelink]]',
+    'esn.add_watch',                    # '[[openlink]]Subscribe to [[journal]][[closelink]]',
     'esn.read_journal',                 # '[[openlink]]Read [[postername]]\'s journal[[closelink]]',
     'esn.view_profile',                 # '[[openlink]]View [[postername]]\'s profile[[closelink]]',
     'esn.invite_another_friend',        # '[[openlink]]Invite another friend[[closelink]]",
@@ -61,10 +62,11 @@ sub _as_email {
     return LJ::Lang::get_text($lang, 'esn.invited_friend_joins.email', undef, $vars) .
         $self->format_options($is_html, $lang, $vars,
         {
-            'esn.add_friend'            => [ 1, "$LJ::SITEROOT/manage/circle/add.bml?user=$newusername&action=subscribe" ], # Why not $self->friend->addfriend_url ?
-            'esn.read_journal'          => [ 2, $newuser_url ],
-            'esn.view_profile'          => [ 3, $newuser_profile ],
-            'esn.invite_another_friend' => [ 4, "$LJ::SITEROOT/manage/circle/invite.bml" ],
+            'esn.add_trust'             => [ 1, "$LJ::SITEROOT/manage/circle/add.bml?user=$newusername&action=access" ],
+            'esn.add_watch'             => [ 2, "$LJ::SITEROOT/manage/circle/add.bml?user=$newusername&action=subscribe" ],
+            'esn.read_journal'          => [ 3, $newuser_url ],
+            'esn.view_profile'          => [ 4, $newuser_profile ],
+            'esn.invite_another_friend' => [ 5, "$LJ::SITEROOT/manage/circle/invite.bml" ],
         }
     );
 }
@@ -85,7 +87,7 @@ sub as_html {
     return 'A friend you invited has created a journal.'
         unless $self->friend;
 
-    return sprintf "A friend you invited has created the journal %s", $self->friend->ljuser_display;
+    return sprintf "A friend you invited has created the journal %s.", $self->friend->ljuser_display;
 }
 
 sub as_html_actions {
@@ -104,7 +106,7 @@ sub as_string {
     return 'A friend you invited has created a journal.'
         unless $self->friend;
 
-    return sprintf "A friend you invited has created the journal %s", $self->friend->user;
+    return sprintf "A friend you invited has created the journal %s.", $self->friend->user;
 }
 
 sub as_sms {
diff -r 9ae6cc535deb -r 1ad1d37c54ab cgi-bin/LJ/Event/JournalNewEntry.pm
--- a/cgi-bin/LJ/Event/JournalNewEntry.pm	Tue Feb 24 08:17:37 2009 +0000
+++ b/cgi-bin/LJ/Event/JournalNewEntry.pm	Tue Feb 24 08:23:54 2009 +0000
@@ -136,7 +136,7 @@ my @_ml_strings_en = (
     'esn.read_recent_entries',                      # '[[openlink]]Read the recent entries in [[journal]][[closelink]]',
     'esn.join_community',                           # '[[openlink]]Join [[journal]] to read Members-only entries[[closelink]]',
     'esn.read_user_entries',                        # '[[openlink]]Read [[poster]]\'s recent entries[[closelink]]',
-    'esn.add_friend'                                # '[[openlink]]Add [[journal]] to your Friends list[[closelink]]',
+    'esn.add_watch'                                 # '[[openlink]]Subscribe to [[journal]][[closelink]]',
 );
 
 sub as_email_subject {
@@ -221,7 +221,7 @@ sub _as_email {
                                                     "$LJ::SITEROOT/community/join.bml?comm=$journal_user" ],
                 'esn.read_user_entries'     => [ ($self->entry->journal->is_comm) ? 0 : 4,
                                                     $journal_url ],
-                'esn.add_friend'            => [ $u->watches( $self->entry->journal ) ? 0 : 5,
+                'esn.add_watch'             => [ $u->watches( $self->entry->journal ) ? 0 : 5,
                                                     "$LJ::SITEROOT/manage/circle/add.bml?user=$journal_user&action=subscribe" ],
             });
 
diff -r 9ae6cc535deb -r 1ad1d37c54ab cgi-bin/LJ/Event/RemovedFromCircle.pm
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cgi-bin/LJ/Event/RemovedFromCircle.pm	Tue Feb 24 08:23:54 2009 +0000
@@ -0,0 +1,199 @@
+#!/usr/bin/perl
+#
+# LJ::Event::RemovedFromCircle
+#
+# This is the event that's fired when someone removes another user from their circle.
+#
+# Authors:
+#      Janine Costanzo <janine@netrophic.com>
+#
+# Copyright (c) 2009 by Dreamwidth Studios, LLC.
+#
+# This program is free software; you may redistribute it and/or modify it under
+# the same terms as Perl itself.  For a copy of the license, please reference
+# 'perldoc perlartistic' or 'perldoc perlgpl'.
+#
+
+package LJ::Event::RemovedFromCircle;
+
+use strict;
+use Scalar::Util qw( blessed );
+use Carp qw( croak );
+use base 'LJ::Event';
+
+sub new {
+    my ( $class, $u, $fromu, $actionid ) = @_;
+
+    foreach ( $u, $fromu ) {
+        croak 'Not an LJ::User' unless blessed $_ && $_->isa("LJ::User");
+    }
+
+    croak 'Invalid actionid; must be 1 (trust) or 2 (watch)' unless $actionid == 1 || $actionid == 2;
+
+    return $class->SUPER::new( $u, $fromu->id, $actionid );
+}
+
+sub is_common { 0 }
+
+my @_ml_strings_en = qw(
+    esn.removedfromcircle.trusted.subject
+    esn.removedfromcircle.watched.subject
+    esn.removedfromcircle.trusted.email_text
+    esn.removedfromcircle.watched.email_text
+    esn.remove_trust
+    esn.remove_watch
+    esn.post_entry
+    esn.edit_friends
+    esn.edit_groups
+);
+
+sub as_email_subject {
+    my ( $self, $u ) = @_;
+
+    if ( $self->trusted ) {
+        return LJ::Lang::get_text( $u->prop('browselang'), 'esn.removedfromcircle.trusted.subject', undef, { who => $self->fromuser->display_username } );
+    } else { # watched
+        return LJ::Lang::get_text( $u->prop('browselang'), 'esn.removedfromcircle.watched.subject', undef, { who => $self->fromuser->display_username } );
+    }
+}
+
+sub _as_email {
+    my ( $self, $u, $is_html ) = @_;
+
+    my $lang        = $u->prop( 'browselang' );
+    my $user        = $is_html ? $u->ljuser_display : $u->display_username;
+    my $poster      = $is_html ? $self->fromuser->ljuser_display : $self->fromuser->display_username;
+    my $postername  = $self->fromuser->user;
+    my $journal_url = $self->fromuser->journal_base;
+    my $journal_profile = $self->fromuser->profile_url;
+
+    # Precache text lines
+    LJ::Lang::get_text_multi( $lang, undef, \@_ml_strings_en );
+
+    my $vars = {
+        who         => $self->fromuser->display_username,
+        poster      => $poster,
+        postername  => $poster,
+        journal     => $poster,
+        user        => $user,
+    };
+
+    if ( $self->trusted ) {
+        return LJ::Lang::get_text( $lang, 'esn.removedfromcircle.trusted.email_text', undef, $vars ) .
+            $self->format_options( $is_html, $lang, $vars,
+            {
+                'esn.remove_trust'    => [ !$u->trusts( $self->fromuser ) ? 0 : 1, "$LJ::SITEROOT/manage/circle/add.bml?user=$postername" ],
+                'esn.post_entry'      => [ 2, "$LJ::SITEROOT/update.bml" ],
+                'esn.edit_friends'    => [ 3, "$LJ::SITEROOT/manage/circle/edit.bml" ],
+                'esn.edit_groups'     => [ 4, "$LJ::SITEROOT/manage/circle/editfilters.bml" ],
+            }
+        );
+    } else { # watched
+        return LJ::Lang::get_text( $lang, 'esn.removedfromcircle.watched.email_text', undef, $vars ) .
+            $self->format_options( $is_html, $lang, $vars,
+            {
+                'esn.remove_watch'    => [ !$u->watches( $self->fromuser ) ? 0 : 1, "$LJ::SITEROOT/manage/circle/add.bml?user=$postername" ],
+                'esn.post_entry'      => [ 2, "$LJ::SITEROOT/update.bml" ],
+                'esn.edit_friends'    => [ 3, "$LJ::SITEROOT/manage/circle/edit.bml" ],
+                'esn.edit_groups'     => [ 4, "$LJ::SITEROOT/manage/circle/editfilters.bml" ],
+            }
+        );
+    }
+}
+
+sub as_email_string {
+    my ( $self, $u ) = @_;
+    return _as_email( $self, $u, 0 );
+}
+
+sub as_email_html {
+    my ( $self, $u ) = @_;
+    return _as_email( $self, $u, 1 );
+}
+
+sub fromuser {
+    my $self = shift;
+    return LJ::load_userid( $self->arg1 );
+}
+
+sub actionid {
+    my $self = shift;
+    return $self->arg2;
+}
+
+sub trusted {
+    my $self = shift;
+    return $self->actionid == 1 ? 1 : 0;
+}
+
+sub watched {
+    my $self = shift;
+    return $self->actionid == 2 ? 1 : 0;
+}
+
+sub as_html {
+    my $self = shift;
+
+    if ( $self->trusted ) {
+        return sprintf( "%s has removed your access to their journal.", $self->fromuser->ljuser_display );
+    } else { # watched
+        return sprintf( "%s has unsubscribed from your journal.", $self->fromuser->ljuser_display );
+    }
+}
+
+sub as_html_actions {
+    my ( $self ) = @_;
+
+    my $u = $self->u;
+    my $fromuser = $self->fromuser;
+
+    my $ret .= "<div class='actions'>";
+    if ( $self->trusted ) {
+        $ret .= "<a href='$LJ::SITEROOT/manage/circle/add.bml?user=" . $fromuser->user . "'>Remove Access</a>"
+            if $u->trusts( $fromuser );
+        $ret .= " <a href='" . $fromuser->profile_url . "'>View Profile</a>";
+    } else { # watched
+        $ret .= "<a href='$LJ::SITEROOT/manage/circle/add.bml?user=" . $fromuser->user . "'>Unsubscribe</a>"
+            if $u->watches( $fromuser );
+        $ret .= " <a href='" . $fromuser->profile_url . "'>View Profile</a>";
+    }
+    $ret .= "</div>";
+
+    return $ret;
+}
+
+sub as_string {
+    my $self = shift;
+
+    if ( $self->trusted ) {
+        return sprintf( "%s has removed your access to their journal.", $self->fromuser->user );
+    } else { # watched
+        return sprintf( "%s has unsubscribed from your journal.", $self->fromuser->user );
+    }
+}
+
+sub subscription_as_html {
+    my ( $class, $subscr ) = @_;
+    my $journal = $subscr->journal or croak "No user";
+    my $journal_is_owner = $journal->equals( $subscr->owner );
+
+    if ( $journal_is_owner ) {
+        return BML::ml( 'event.removedfromcircle.me' );   # "Someone removes me from their circle";
+    } else {
+        my $user = $journal->ljuser_display;
+        return BML::ml( 'event.removedfromcircle.user', { user => $user } ); # "Someone removes $user from their circle";
+    }
+}
+
+# only users with the track_defriended cap can use this
+sub available_for_user  {
+    my ( $class, $u, $subscr ) = @_;
+    return $u->get_cap( 'track_defriended' ) ? 1 : 0;
+}
+
+sub content {
+    my ( $self, $target ) = @_;
+    return $self->as_html_actions;
+}
+
+1;
diff -r 9ae6cc535deb -r 1ad1d37c54ab cgi-bin/LJ/Event/UserMessageRecvd.pm
--- a/cgi-bin/LJ/Event/UserMessageRecvd.pm	Tue Feb 24 08:17:37 2009 +0000
+++ b/cgi-bin/LJ/Event/UserMessageRecvd.pm	Tue Feb 24 08:23:54 2009 +0000
@@ -53,7 +53,7 @@ sub _as_email {
         {
             'esn.view_profile'    => [ 1, $other_u->profile_url ],
             'esn.read_journal'    => [ 2, $other_u->journal_base ],
-            'esn.add_friend'      => [ $u->watches( $other_u ) ? 0 : 3,
+            'esn.add_watch'       => [ $u->watches( $other_u ) ? 0 : 3,
                                             "$LJ::SITEROOT/manage/circle/add.bml?user=$sender&action=subscribe" ],
         }
     );
diff -r 9ae6cc535deb -r 1ad1d37c54ab cgi-bin/LJ/NotificationInbox.pm
--- a/cgi-bin/LJ/NotificationInbox.pm	Tue Feb 24 08:17:37 2009 +0000
+++ b/cgi-bin/LJ/NotificationInbox.pm	Tue Feb 24 08:23:54 2009 +0000
@@ -136,7 +136,7 @@ sub befriended_items {
 sub befriended_items {
     my $self = shift;
 
-    my @events = ( 'Befriended' );
+    my @events = ( 'AddedToCircle' );
 
     return $self->subset_items(@events);
 }
@@ -674,7 +674,8 @@ sub usermsg_sent_event_count {
 # Methods that return Arrays of Event categories
 sub friend_event_list {
     my @events = qw(
-                    Befriended
+                    AddedToCircle
+                    RemovedFromCircle
                     InvitedFriendJoins
                     CommunityInvite
                     NewUserpic
@@ -685,11 +686,11 @@ sub friend_event_list {
 
 sub friendplus_event_list {
     my @events = qw(
-                    Befriended
+                    AddedToCircle
+                    RemovedFromCircle
                     InvitedFriendJoins
                     CommunityInvite
                     NewUserpic
-                    NewVGift
                     Birthday
                     );
     @events = (@events, (LJ::run_hook('friend_notification_types') || ()));
diff -r 9ae6cc535deb -r 1ad1d37c54ab cgi-bin/LJ/User.pm
--- a/cgi-bin/LJ/User.pm	Tue Feb 24 08:17:37 2009 +0000
+++ b/cgi-bin/LJ/User.pm	Tue Feb 24 08:23:54 2009 +0000
@@ -33,8 +33,6 @@ use Class::Autouse qw(
                       LJ::S2
                       IO::Socket::INET
                       Time::Local
-                      LJ::Event::Befriended
-                      LJ::Event::Defriended
                       LJ::BetaFeatures
                       LJ::S2Theme
                       );
diff -r 9ae6cc535deb -r 1ad1d37c54ab cgi-bin/communitylib.pl
--- a/cgi-bin/communitylib.pl	Tue Feb 24 08:17:37 2009 +0000
+++ b/cgi-bin/communitylib.pl	Tue Feb 24 08:23:54 2009 +0000
@@ -261,7 +261,7 @@ sub leave_community {
 
     # remove community membership
     return undef
-        unless $u->remove_edge( $cu, qw/ member / );
+        unless $u->remove_edge( $cu, member => 1 );
 
     # clear edges that effect this relationship
     foreach my $edge (qw(P N A M)) {
@@ -270,7 +270,7 @@ sub leave_community {
 
     # defriend user -> comm?
     return 1 unless $defriend;
-    $u->remove_friend($cu);
+    $u->remove_edge( $cu, watch => {} );
 
     # don't care if we failed the removal of comm from user's friends list...
     return 1;
diff -r 9ae6cc535deb -r 1ad1d37c54ab cgi-bin/ljdefaults.pl
--- a/cgi-bin/ljdefaults.pl	Tue Feb 24 08:17:37 2009 +0000
+++ b/cgi-bin/ljdefaults.pl	Tue Feb 24 08:23:54 2009 +0000
@@ -308,7 +308,7 @@
 
     unless (@LJ::EVENT_TYPES) {
         @LJ::EVENT_TYPES = qw (
-                               Befriended
+                               AddedToCircle
                                Birthday
                                JournalNewComment
                                JournalNewEntry
diff -r 9ae6cc535deb -r 1ad1d37c54ab cgi-bin/ljprotocol.pl
--- a/cgi-bin/ljprotocol.pl	Tue Feb 24 08:17:37 2009 +0000
+++ b/cgi-bin/ljprotocol.pl	Tue Feb 24 08:23:54 2009 +0000
@@ -9,7 +9,7 @@ use Class::Autouse qw(
                       LJ::Console
                       LJ::Event::JournalNewEntry
                       LJ::Event::UserNewEntry
-                      LJ::Event::Befriended
+                      LJ::Event::AddedToCircle
                       LJ::Entry
                       LJ::Poll
                       LJ::EventLogRecord::NewEntry
@@ -335,13 +335,13 @@ sub getinbox
     my $inbox = $u->notification_inbox or return fail($err, 500, "Cannot get user inbox");
 
     my %type_number = (
-        Befriended           => 1,
+        AddedToCircle        => 1,
         Birthday             => 2,
         CommunityInvite      => 3,
         CommunityJoinApprove => 4,
         CommunityJoinReject  => 5,
         CommunityJoinRequest => 6,
-        Defriended           => 7,
+        RemovedFromCircle    => 7,
         InvitedFriendJoins   => 8,
         JournalNewComment    => 9,
         JournalNewEntry      => 10,
@@ -2293,7 +2293,7 @@ sub editfriends
         my $deluser = LJ::canonical_username($_);
         next DELETEFRIEND unless $curfriend{$deluser};
 
-        $u->remove_edge( LJ::get_userid( $deluser ), qw/ watch trust / );
+        $u->remove_edge( LJ::get_userid( $deluser ), watch => {}, trust => {} );
         $friend_count--;
     }
 
diff -r 9ae6cc535deb -r 1ad1d37c54ab htdocs/community/leave.bml
--- a/htdocs/community/leave.bml	Tue Feb 24 08:17:37 2009 +0000
+++ b/htdocs/community/leave.bml	Tue Feb 24 08:23:54 2009 +0000
@@ -52,7 +52,7 @@
 
         # remove user's membership to community
         if ($watching && !$memberof) {
-            $remote->remove_edge( $cu, 'watch' );
+            $remote->remove_edge( $cu, watch => {} );
         } else {
             LJ::leave_community($remote, $cu, $FORM{removefriend});
         }
diff -r 9ae6cc535deb -r 1ad1d37c54ab htdocs/community/members.bml
--- a/htdocs/community/members.bml	Tue Feb 24 08:17:37 2009 +0000
+++ b/htdocs/community/members.bml	Tue Feb 24 08:23:54 2009 +0000
@@ -239,7 +239,7 @@ body<=
         foreach my $uid ( keys %{ $delete{member} || {} } ) {
             my $temp_u = LJ::load_userid( $uid )
                 or next;
-            $temp_u->remove_edge( $cid, qw/ member / );
+            $temp_u->remove_edge( $cid, member => 1 );
         }
 
         # create a closure to send a notification email to a user if they are
diff -r 9ae6cc535deb -r 1ad1d37c54ab htdocs/create.bml
--- a/htdocs/create.bml	Tue Feb 24 08:17:37 2009 +0000
+++ b/htdocs/create.bml	Tue Feb 24 08:23:54 2009 +0000
@@ -347,7 +347,7 @@ if ( $LJ::HUMAN_CHECK{create} && $mode e
          # if they are personal journals, we probably don't want to trust them anyway
          foreach my $friend (@LJ::INITIAL_OPTIONAL_FRIENDS) {
              my $friendu = LJ::load_user( $friend );
-             $nu->add_edge( $friendu, watch => {} ) if $friendu and $POST{"initial_optional_friend_$friend"};
+             $nu->add_edge( $friendu, watch => { nonotify => 1 } ) if $friendu && $POST{"initial_optional_friend_$friend"};
          }
 
          # Mark the turing test for deletion
diff -r 9ae6cc535deb -r 1ad1d37c54ab htdocs/manage/circle/add.bml
--- a/htdocs/manage/circle/add.bml	Tue Feb 24 08:17:37 2009 +0000
+++ b/htdocs/manage/circle/add.bml	Tue Feb 24 08:23:54 2009 +0000
@@ -65,12 +65,18 @@
             $gmask |= (1 << $bit);
         }
 
+        my $trusted_nonotify = $remote->trusts( $u ) ? 1 : 0;
+        my $watched_nonotify = $remote->watches( $u ) ? 1 : 0;
+
         if ( $POST{add_trust} ) {
             $remote->add_edge( $u, trust => {
                 mask => $gmask,
+                nonotify => $trusted_nonotify ? 1 : 0,
             });
         } else {
-            $remote->remove_edge( $u, 'trust' );
+            $remote->remove_edge( $u, trust => {
+                nonotify => $trusted_nonotify ? 0 : 1,
+            } );
         }
 
         if ( $POST{add_watch} ) {
@@ -79,9 +85,12 @@
             $remote->add_edge( $u, watch => {
                 fgcolor => $fg,
                 bgcolor => $bg,
+                nonotify => $watched_nonotify ? 1 : 0,
             } );
         } else {
-            $remote->remove_edge( $u, 'watch' );
+            $remote->remove_edge( $u, watch => {
+                nonotify => $watched_nonotify ? 0 : 1,
+            } );
         }
 
         $body = LJ::get_ads({
diff -r 9ae6cc535deb -r 1ad1d37c54ab htdocs/manage/circle/edit.bml
--- a/htdocs/manage/circle/edit.bml	Tue Feb 24 08:17:37 2009 +0000
+++ b/htdocs/manage/circle/edit.bml	Tue Feb 24 08:23:54 2009 +0000
@@ -256,12 +256,18 @@ body<=
                 my $other_u = LJ::load_user_or_identity( $POST{"editfriend_add_${num}_user"} );
                 next unless $other_u;
 
+                my $trusted_nonotify = $u->trusts( $other_u ) ? 1 : 0;
+                my $watched_nonotify = $u->watches( $other_u ) ? 1 : 0;
                 $userid_processed{$other_u->id} = 1;
 
                 if ( $POST{"editfriend_add_${num}_trust"} ) {
-                    $u->add_edge( $other_u, trust => {} );
+                    $u->add_edge( $other_u, trust => {
+                        nonotify => $trusted_nonotify ? 1 : 0,
+                    } );
                 } else {
-                    $u->remove_edge( $other_u, 'trust' );
+                    $u->remove_edge( $other_u, trust => {
+                        nonotify => $trusted_nonotify ? 0 : 1,
+                    } );
                 }
                 if ( $POST{"editfriend_add_${num}_watch"} ) {
                     my $fg = LJ::color_todb( $POST{"editfriend_add_${num}_fg"} );
@@ -269,9 +275,12 @@ body<=
                     $u->add_edge( $other_u, watch => {
                         fgcolor => $fg,
                         bgcolor => $bg,
+                        nonotify => $watched_nonotify ? 1 : 0,
                     } );
                 } else {
-                    $u->remove_edge( $other_u, 'watch' );
+                    $u->remove_edge( $other_u, watch => {
+                        nonotify => $watched_nonotify ? 0 : 1,
+                    } );
                 }
             } elsif ( $key =~ /^editfriend_edit_(\d+)_user/ ) {
                 my $uid = $1;
@@ -279,16 +288,27 @@ body<=
                 my $other_u = LJ::load_userid( $uid );
                 next unless $other_u && !$userid_processed{$uid};
 
+                my $trusted_nonotify = $u->trusts( $other_u ) ? 1 : 0;
+                my $watched_nonotify = $u->watches( $other_u ) ? 1 : 0;
+
                 if ( $POST{"editfriend_edit_${uid}_trust"} ) {
-                    $u->add_edge( $other_u, trust => {} );
+                    $u->add_edge( $other_u, trust => {
+                        nonotify => $trusted_nonotify ? 1 : 0,
+                    } );
                 } else {
-                    $u->remove_edge( $other_u, 'trust' );
+                    $u->remove_edge( $other_u, trust => {
+                        nonotify => $trusted_nonotify ? 0 : 1,
+                    } );
                 }
 
                 if ( $POST{"editfriend_edit_${uid}_watch"} ) {
-                    $u->add_edge( $other_u, watch => {} );
+                    $u->add_edge( $other_u, watch => {
+                        nonotify => $watched_nonotify ? 1 : 0,
+                    } );
                 } else {
-                    $u->remove_edge( $other_u, 'watch' );
+                    $u->remove_edge( $other_u, watch => {
+                        nonotify => $watched_nonotify ? 0 : 1,
+                    } );
                 }
             }
         }
diff -r 9ae6cc535deb -r 1ad1d37c54ab htdocs/manage/circle/editfilters.bml
--- a/htdocs/manage/circle/editfilters.bml	Tue Feb 24 08:17:37 2009 +0000
+++ b/htdocs/manage/circle/editfilters.bml	Tue Feb 24 08:23:54 2009 +0000
@@ -79,7 +79,10 @@
             next unless $trusted_u;
             my $groupmask = $POST{$post_key};
 
-            $u->add_edge( $trusted_u, trust => { mask => $groupmask } );
+            $u->add_edge( $trusted_u, trust => {
+                mask => $groupmask,
+                nonotify => 1,
+            } );
         }
 
         $body .= LJ::get_ads({ 
diff -r 9ae6cc535deb -r 1ad1d37c54ab htdocs/manage/settings/index.bml
--- a/htdocs/manage/settings/index.bml	Tue Feb 24 08:17:37 2009 +0000
+++ b/htdocs/manage/settings/index.bml	Tue Feb 24 08:23:54 2009 +0000
@@ -430,8 +430,8 @@ body<=
                         event => 'PollVote',
                         journal => $u,
                     ),
-                    'Befriended',
-                    'Defriended',
+                    'AddedToCircle',
+                    'RemovedFromCircle',
                     LJ::Subscription::Pending->new($u,
                         event => 'UserMessageRecvd',
                         journal => $u,
--------------------------------------------------------------------------------