[dw-free] Implement delete_trust_group method
[commit: http://hg.dwscoalition.org/dw-free/rev/dfd18646e8e7]
http://bugs.dwscoalition.org/show_bug.cgi?id=370
Add delete_trust_group method. Deprecate editfriends and editfriendgroups
protocol modes.
Patch by
mark.
Files modified:
http://bugs.dwscoalition.org/show_bug.cgi?id=370
Add delete_trust_group method. Deprecate editfriends and editfriendgroups
protocol modes.
Patch by
![[staff profile]](https://www.dreamwidth.org/img/silk/identity/user_staff.png)
Files modified:
- bin/test/test-wtf
- cgi-bin/DW/User/Edges/WatchTrust.pm
- cgi-bin/ljprotocol.pl
- cgi-bin/taglib.pl
-------------------------------------------------------------------------------- diff -r d655bcb8c70f -r dfd18646e8e7 bin/test/test-wtf --- a/bin/test/test-wtf Fri Mar 06 17:26:56 2009 -0800 +++ b/bin/test/test-wtf Sat Mar 07 06:07:58 2009 +0000 @@ -419,6 +419,30 @@ push @tests, [ 'test contains', sub return 1; } ]; + +################################################################################ +push @tests, [ 'delete trust group 3', sub +{ + # have to create a group with a known id for these tests + $u1->edit_trust_group( id => 3, groupname => 'bar group 3', _force_create => 1 ); + + $u1->trust_group_contains( $u2, 3 ) == 1 or return 0; + $u1->trust_group_contains( $u2, 4 ) == 1 or return 0; + $u1->trust_group_contains( $u2, 5 ) == 0 or return 0; + + # now delete the group + $u1->delete_trust_group( name => 'bar group 3' ) or return 0; + + $u1->trust_group_contains( $u2, 3 ) == 0 or return 0; + $u1->trust_group_contains( $u2, 4 ) == 1 or return 0; + $u1->trust_group_contains( $u2, 5 ) == 0 or return 0; + + # validate group doesn't fetch + $u1->trust_groups( name => 'bar group 3' ) and return 0; + + return 1; +} ]; + ################################################################################ push @tests, [ 'clear groups', sub { diff -r d655bcb8c70f -r dfd18646e8e7 cgi-bin/DW/User/Edges/WatchTrust.pm --- a/cgi-bin/DW/User/Edges/WatchTrust.pm Fri Mar 06 17:26:56 2009 -0800 +++ b/cgi-bin/DW/User/Edges/WatchTrust.pm Sat Mar 07 06:07:58 2009 +0000 @@ -772,6 +772,95 @@ sub edit_trust_group { *LJ::User::edit_trust_group = \&edit_trust_group; +# deletes a trust_group, arguments is a hash of options +# id => NNN, delete by id (1..60) +# name => "ZZZ", or delete by name +# +# specify either the id or the name. note that deletion is a rather permanent +# option that will remove this group from all entries that are secured to it +# as well as remove this bit from all trustmasks. +# +# returns 1/0. +# +sub delete_trust_group { + my ( $u, %opts ) = @_; + $u = LJ::want_user( $u ) or confess 'invalid user object'; + + # use existing accessor to figure out what group they mean + my $grp = $u->trust_groups( id => $opts{id}, name => $opts{name} ); + return 0 unless $grp; + + # set bit to remove + my $bit = $grp->{groupnum}+0; + return 0 unless $bit >= 1 && $bit <= 60; + + # remove the bits for deleted groups from all friends groupmasks + my $dbh = LJ::get_db_writer() + or return 0; + $dbh->do( + q{UPDATE wt_edges SET groupmask = groupmask & ~(1 << ?) WHERE from_userid = ?}, + undef, $bit, $u->id + ); + return 0 if $dbh->err; + + # remove all posts from allowing that group: + my @posts_to_clean = @{ $u->selectcol_arrayref( + q{SELECT jitemid FROM logsec2 WHERE journalid = ? AND allowmask & (1 << ?)}, + undef, $u->id, $bit + ) || [] }; + + # now clean the posts while we can, this is a loop so we can do it in blocks of twenty + # as it's somewhat hard on the database to do this enmasse + my $userid = $u->id; # convenience + while ( @posts_to_clean ) { + my @batch = splice( @posts_to_clean, 0, 50 ); + + # actually updates the entries. note that we do not return an error here because + # it's not the end of the world if one of these fails... + my $in = join ',', @batch; + $u->do("UPDATE log2 SET allowmask=allowmask & ~(1 << $bit) ". + "WHERE journalid=$userid AND jitemid IN ($in) AND security='usemask'"); + $u->do("UPDATE logsec2 SET allowmask=allowmask & ~(1 << $bit) ". + "WHERE journalid=$userid AND jitemid IN ($in)"); + + foreach my $id (@batch) { + LJ::MemCache::delete([$userid, "log2:$userid:$id"]); + } + LJ::MemCache::delete([$userid, "log2lt:$userid"]); + } + + # notify the tags system so it can do its thing + LJ::Tags::deleted_trust_group( $u, $bit ); + + # iterate over everybody in this group and remove the bit + foreach my $tid ( keys %{ $u->trust_group_list( id => $bit ) || {} } ) { + $dbh->do( + q{UPDATE wt_edges SET groupmask = groupmask & ~(1 << ?) WHERE from_userid = ? AND to_userid = ?}, + undef, $bit, $u->id, $tid + ); + + # don't forget memcache + LJ::MemCache::delete( [$userid, "trustmask:$userid:$tid"] ); + } + + # finally remove the trust group, huzzah + $dbh->do( + q{DELETE FROM trust_groups WHERE userid = ? AND groupnum = ?}, + undef, $u->id, $bit + ); + return 0 if $dbh->err; + + # invalidate memcache of friends/groups + LJ::memcache_kill( $u->id, "trust_group" ); + LJ::memcache_kill( $u->id, "wt_list" ); + + # sister mary of the holy hand grenade says hi and apologies if any of the + # above failed. we think it worked by this point, though. + return 1; +} +*LJ::User::delete_trust_group = \&delete_trust_group; + + # alters a trustmask to munge someone's group membership # # $u->edit_trustmask( $otheru, ARGUMENTS ) diff -r d655bcb8c70f -r dfd18646e8e7 cgi-bin/ljprotocol.pl --- a/cgi-bin/ljprotocol.pl Fri Mar 06 17:26:56 2009 -0800 +++ b/cgi-bin/ljprotocol.pl Sat Mar 07 06:07:58 2009 +0000 @@ -2247,352 +2247,14 @@ sub getevents return $res; } -sub editfriends -{ - my ($req, $err, $flags) = @_; - - # TODO(mark): most of this code can move to the new trust groups editing - # sort of thing, but for now just say we're deprecated - return fail( $err, 504 ); - - return undef unless authenticate($req, $err, $flags); - - my $u = $flags->{'u'}; - my $userid = $u->{'userid'}; - my $dbh = LJ::get_db_writer(); - my $sth; - - return fail($err,306) unless $dbh; - - # do not let locked people do this - return fail($err, 308) if $u->{statusvis} eq 'L'; - - my $res = {}; - - ## first, figure out who the current friends are to save us work later - my %curfriend; - my $friend_count = 0; - # TAG:FR:protocol:editfriends1 - $sth = $dbh->prepare("SELECT u.user FROM useridmap u, friends f ". - "WHERE u.userid=f.friendid AND f.userid=$userid"); - $sth->execute; - while (my ($friend) = $sth->fetchrow_array) { - $curfriend{$friend} = 1; - $friend_count++; - } - $sth->finish; - - # perform the deletions - DELETEFRIEND: - foreach (@{$req->{'delete'}}) - { - my $deluser = LJ::canonical_username($_); - next DELETEFRIEND unless $curfriend{$deluser}; - - $u->remove_edge( LJ::get_userid( $deluser ), watch => {}, trust => {} ); - $friend_count--; - } - - my $error_flag = 0; - my $friends_added = 0; - my $fail = sub { - LJ::memcache_kill($userid, "friends"); - return fail($err, $_[0], $_[1]); - }; - - # only people, shared journals, and owned syn feeds can add friends - return $fail->(104, "Journal type cannot add friends") - unless ($u->{'journaltype'} eq 'P' || - $u->{'journaltype'} eq 'S' || - $u->{'journaltype'} eq 'I' || - ($u->{'journaltype'} eq "Y" && $u->password)); - - # Don't let suspended users add friend - return $fail->(305, "Suspended journals cannot add friends.") - if ($u->is_suspended); - - my $sclient = LJ::theschwartz(); - - # perform the adds - ADDFRIEND: - foreach my $fa (@{$req->{'add'}}) - { - unless (ref $fa eq "HASH") { - $fa = { 'username' => $fa }; - } - - my $aname = LJ::canonical_username($fa->{'username'}); - unless ($aname) { - $error_flag = 1; - next ADDFRIEND; - } - - $friend_count++ unless $curfriend{$aname}; - - my $err; -#TODO(mark): wtf - return $fail->(104, "$err") - unless 1;#$u->can_add_friends(\$err, { 'numfriends' => $friend_count, friend => $fa }); - - my $fg = $fa->{'fgcolor'} || "#000000"; - my $bg = $fa->{'bgcolor'} || "#FFFFFF"; - if ($fg !~ /^\#[0-9A-F]{6,6}$/i || $bg !~ /^\#[0-9A-F]{6,6}$/i) { - return $fail->(203, "Invalid color values"); - } - - my $row = LJ::load_user($aname); - my $currently_is_friend = LJ::is_friend($u, $row); - my $currently_is_banned = LJ::is_banned($u, $row); - - # XXX - on some errors we fail out, on others we continue and try adding - # any other users in the request. also, error message for redirect should - # point the user to the redirected username. - if (! $row) { - $error_flag = 1; - } elsif ($row->{'journaltype'} eq "R") { - return $fail->(154); - } elsif ($row->{'statusvis'} ne "V") { - $error_flag = 1; - } else { - $friends_added++; - my $added = { 'username' => $aname, - 'fullname' => $row->{'name'}, - }; - if ($req->{'ver'} >= 1) { - LJ::text_out(\$added->{'fullname'}); - } - push @{$res->{'added'}}, $added; - - my $qfg = LJ::color_todb($fg); - my $qbg = LJ::color_todb($bg); - - my $friendid = $row->{'userid'}; - - my $gmask = $fa->{'groupmask'}; - if (! $gmask && $curfriend{$aname}) { - # if no group mask sent, use the existing one if this is an existing friend - # TAG:FR:protocol:editfriends3_getmask - my $sth = $dbh->prepare("SELECT groupmask FROM friends ". - "WHERE userid=$userid AND friendid=$friendid"); - $sth->execute; - $gmask = $sth->fetchrow_array; - } - - # force bit 0 and 61 on, since people who use this old path are going to - # be forced to watch+trust. - $gmask |= ( 1 & ( 1 << 61 ) ); - - # TAG:FR:protocol:editfriends4_addeditfriend - $dbh->do("REPLACE INTO friends (userid, friendid, fgcolor, bgcolor, groupmask) ". - "VALUES ($userid, $friendid, $qfg, $qbg, $gmask)"); - return $fail->(501,$dbh->errstr) if $dbh->err; - - my $memkey = [$userid,"frgmask:$userid:$friendid"]; - LJ::MemCache::set($memkey, $gmask+0, time()+60*15); - LJ::memcache_kill($friendid, 'friendofs'); - LJ::memcache_kill($friendid, 'friendofs2'); - - if ($sclient && !$currently_is_friend && !$currently_is_banned) { - my @jobs; - push @jobs, LJ::Event::Befriended->new(LJ::load_userid($friendid), LJ::load_userid($userid))->fire_job - if !$LJ::DISABLED{esn}; - - push @jobs, TheSchwartz::Job->new( - funcname => "LJ::Worker::FriendChange", - arg => [$userid, 'add', $friendid], - ) unless $LJ::DISABLED{'friendchange-schwartz'}; - - $sclient->insert_jobs(@jobs) if @jobs; - } - - LJ::run_hooks('befriended', LJ::load_userid($userid), LJ::load_userid($friendid)); - } - } - - return $fail->(104) if $error_flag; - - # invalidate memcache of friends - LJ::memcache_kill($userid, "friends"); - LJ::memcache_kill($userid, "friends2"); - - return $res; +# deprecated +sub editfriends { + return fail( $_[1], 504 ); } -sub editfriendgroups -{ - my ($req, $err, $flags) = @_; - - # TODO(mark): most of this code can move to the new trust groups editing - # sort of thing, but for now just say we're deprecated - return fail( $err, 504 ); - - return undef unless authenticate($req, $err, $flags); - - my $u = $flags->{'u'}; - my $userid = $u->{'userid'}; - my ($db, $fgtable, $bmax, $cmax) = $u->{dversion} > 5 ? - ($u->writer, 'friendgroup2', LJ::BMAX_GRPNAME2, LJ::CMAX_GRPNAME2) : - (LJ::get_db_writer(), 'friendgroup', LJ::BMAX_GRPNAME, LJ::CMAX_GRPNAME); - my $sth; - - return fail($err,306) unless $db; - - # do not let locked people do this - return fail($err, 308) if $u->{statusvis} eq 'L'; - - my $res = {}; - - ## make sure tree is how we want it - $req->{'groupmasks'} = {} unless - (ref $req->{'groupmasks'} eq "HASH"); - $req->{'set'} = {} unless - (ref $req->{'set'} eq "HASH"); - $req->{'delete'} = [] unless - (ref $req->{'delete'} eq "ARRAY"); - - # Keep track of what bits are already set, so we can know later - # whether to INSERT or UPDATE. - my %bitset; - my $groups = LJ::get_friend_group($userid); - foreach my $bit (keys %{$groups || {}}) { - $bitset{$bit} = 1; - } - - ## before we perform any DB operations, validate input text - # (groups' names) for correctness so we can fail gracefully - if ($LJ::UNICODE) { - foreach my $bit (keys %{$req->{'set'}}) - { - my $name = $req->{'set'}->{$bit}->{'name'}; - return fail($err,207,"non-ASCII names require a Unicode-capable client") - if $req->{'ver'} < 1 and not LJ::is_ascii($name); - return fail($err,208,"Invalid group names. Please see $LJ::SITEROOT/support/encodings.bml for more information.") - unless LJ::text_in($name); - } - } - - ## figure out deletions we'll do later - foreach my $bit (@{$req->{'delete'}}) - { - $bit += 0; - next unless ($bit >= 1 && $bit <= 60); - $bitset{$bit} = 0; # so later we replace into, not update. - } - - ## do additions/modifications ('set' hash) - my %added; - foreach my $bit (keys %{$req->{'set'}}) - { - $bit += 0; - next unless ($bit >= 1 && $bit <= 60); - my $sa = $req->{'set'}->{$bit}; - my $name = LJ::text_trim($sa->{'name'}, $bmax, $cmax); - - # can't end with a slash - $name =~ s!/$!!; - - # setting it to name is like deleting it. - unless ($name =~ /\S/) { - push @{$req->{'delete'}}, $bit; - next; - } - - my $qname = $db->quote($name); - my $qsort = defined $sa->{'sort'} ? ($sa->{'sort'}+0) : 50; - my $qpublic = $db->quote(defined $sa->{'public'} ? ($sa->{'public'}+0) : 0); - - if ($bitset{$bit}) { - # so update it - my $sets; - if (defined $sa->{'public'}) { - $sets .= ", is_public=$qpublic"; - } - $db->do("UPDATE $fgtable SET groupname=$qname, sortorder=$qsort ". - "$sets WHERE userid=$userid AND groupnum=$bit"); - } else { - $db->do("REPLACE INTO $fgtable (userid, groupnum, ". - "groupname, sortorder, is_public) VALUES ". - "($userid, $bit, $qname, $qsort, $qpublic)"); - } - $added{$bit} = 1; - } - - - ## do deletions ('delete' array) - my $dbcm = LJ::get_cluster_master($u); - - # ignore bits that aren't integers or that are outside 1-60 range - my @delete_bits = grep {$_ >= 1 and $_ <= 60} map {$_+0} @{$req->{'delete'}}; - my $delete_mask = 0; - foreach my $bit (@delete_bits) { - $delete_mask |= (1 << $bit) - } - - # remove the bits for deleted groups from all friends groupmasks - my $dbh = LJ::get_db_writer(); - if ($delete_mask) { - # TAG:FR:protocol:editfriendgroups_removemasks - $dbh->do("UPDATE friends". - " SET groupmask = groupmask & ~$delete_mask". - " WHERE userid = $userid"); - } - - foreach my $bit (@delete_bits) - { - # remove all posts from allowing that group: - my @posts_to_clean = (); - $sth = $dbcm->prepare("SELECT jitemid FROM logsec2 WHERE journalid=$userid AND allowmask & (1 << $bit)"); - $sth->execute; - while (my ($id) = $sth->fetchrow_array) { push @posts_to_clean, $id; } - while (@posts_to_clean) { - my @batch; - if (scalar(@posts_to_clean) < 20) { - @batch = @posts_to_clean; - @posts_to_clean = (); - } else { - @batch = splice(@posts_to_clean, 0, 20); - } - - my $in = join(",", @batch); - $u->do("UPDATE log2 SET allowmask=allowmask & ~(1 << $bit) ". - "WHERE journalid=$userid AND jitemid IN ($in) AND security='usemask'"); - $u->do("UPDATE logsec2 SET allowmask=allowmask & ~(1 << $bit) ". - "WHERE journalid=$userid AND jitemid IN ($in)"); - - foreach my $id (@batch) { - LJ::MemCache::delete([$userid, "log2:$userid:$id"]); - } - LJ::MemCache::delete([$userid, "log2lt:$userid"]); - } - LJ::Tags::deleted_friend_group($u, $bit); - LJ::run_hooks('delete_friend_group', $u, $bit); - - # remove the friend group, unless we just added it this transaction - unless ($added{$bit}) { - $db->do("DELETE FROM $fgtable WHERE ". - "userid=$userid AND groupnum=$bit"); - } - } - - ## change friends' masks - # TAG:FR:protocol:editfriendgroups_changemasks - foreach my $friend (keys %{$req->{'groupmasks'}}) - { - my $mask = int($req->{'groupmasks'}->{$friend}) | 1; - my $friendid = LJ::get_userid($dbh, $friend); - - $dbh->do("UPDATE friends SET groupmask=$mask ". - "WHERE userid=$userid AND friendid=?", - undef, $friendid); - LJ::MemCache::set([$userid, "frgmask:$userid:$friendid"], $mask); - } - - # invalidate memcache of friends/groups - LJ::memcache_kill($userid, "friends"); - LJ::memcache_kill($userid, "fgrp"); - - # return value for this is nothing. - return {}; +# deprecated +sub editfriendgroups { + return fail( $_[1], 504 ); } sub sessionexpire { diff -r d655bcb8c70f -r dfd18646e8e7 cgi-bin/taglib.pl --- a/cgi-bin/taglib.pl Fri Mar 06 17:26:56 2009 -0800 +++ b/cgi-bin/taglib.pl Sat Mar 07 06:07:58 2009 +0000 @@ -1292,7 +1292,7 @@ sub set_usertag_display { } # <LJFUNC> -# name: LJ::Tags::deleted_friend_group +# name: LJ::Tags::deleted_trust_group # class: tags # des: Called from ljprotocol when a friends group is deleted. # args: uobj, bit @@ -1300,7 +1300,7 @@ sub set_usertag_display { # des-bit: The id (1..60) of the friends group being deleted. # returns: 1 of success undef on failure. # </LJFUNC> -sub deleted_friend_group { +sub deleted_trust_group { my $u = LJ::want_user(shift); my $bit = shift() + 0; return undef unless $u && $bit >= 1 && $bit <= 60; --------------------------------------------------------------------------------