fu: Close-up of Fu, bringing a scoop of water to her mouth (Default)
fu ([personal profile] fu) wrote in [site community profile] changelog2011-08-19 03:55 am

[dw-free] enforce limits on number of users that can be watched or trusted

[commit: http://hg.dwscoalition.org/dw-free/rev/170812b5791e]

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

Enforce limits, and tweak user interface to give appropriate error messages
or otherwise tweak behavior when you've hit your limit.

Patch by [personal profile] kareila.

Files modified:
  • bin/upgrading/en.dat
  • cgi-bin/DW/Console/Command/ManageCircle.pm
  • cgi-bin/DW/User/Edges/WatchTrust.pm
  • cgi-bin/LJ/User.pm
  • htdocs/manage/circle/add.bml
  • htdocs/manage/circle/edit.bml
  • htdocs/manage/circle/edit.bml.text
--------------------------------------------------------------------------------
diff -r d1b4f5148736 -r 170812b5791e bin/upgrading/en.dat
--- a/bin/upgrading/en.dat	Fri Aug 19 11:44:02 2011 +0800
+++ b/bin/upgrading/en.dat	Fri Aug 19 11:55:21 2011 +0800
@@ -822,6 +822,12 @@
 Error|notes=typically used inside an H1 tag to announce an error.
 Error=Error
 
+error.adduser.limit=You have reached your limit of [[maxnum]] accounts in your circle.
+
+error.adduser.rate=You are trying to watch or trust too many accounts in too short a period of time.
+
+error.adduser.suspended=Suspended users cannot add accounts to their circles.
+
 error.badpassword2=You typed the wrong password. If you make too many wrong login attempts in a row, your account access will be temporarily blocked, so you may want to <a [[aopts]]>reset your password</a> if you aren't sure what it is.
 
 error.blocked=Sorry, you are currently blocked from reaching the page you are attempting to visit.  The block type is: [[blocktype]].  Please email us at [[email]] and reference this message for further assistance.
diff -r d1b4f5148736 -r 170812b5791e cgi-bin/DW/Console/Command/ManageCircle.pm
--- a/cgi-bin/DW/Console/Command/ManageCircle.pm	Fri Aug 19 11:44:02 2011 +0800
+++ b/cgi-bin/DW/Console/Command/ManageCircle.pm	Fri Aug 19 11:55:21 2011 +0800
@@ -55,10 +55,16 @@
     return $self->error( 'You must be logged in, dude!' )
         unless $remote;
 
+    my $edge_err;
+
     if ( $cmd eq 'add_read' ) {
-        $remote->add_edge( $to_u, watch => {
-            nonotify => $remote->watches( $to_u ) ? 1 : 0,
-        } );
+        if ( $remote->can_watch( $to_u, errref => \$edge_err ) ) {
+            $remote->add_edge( $to_u, watch => {
+                nonotify => $remote->watches( $to_u ) ? 1 : 0,
+            } );
+        } else {
+            return $self->error( "Error: $edge_err" );
+        }
 
     } elsif ( $cmd eq 'del_read' ) {
         $remote->remove_edge( $to_u, watch => {
@@ -72,10 +78,14 @@
         my $existing_mask = $remote->trustmask( $to_u );
         $mask |= $existing_mask;
 
-        $remote->add_edge( $to_u, trust => {
-            mask => $mask,
-            nonotify => $remote->trusts( $to_u ) ? 1 : 0,
-        } );
+        if ( $remote->can_trust( $to_u, errref => \$edge_err ) ) {
+            $remote->add_edge( $to_u, trust => {
+                mask => $mask,
+                nonotify => $remote->trusts( $to_u ) ? 1 : 0,
+            } );
+        } else {
+            return $self->error( "Error: $edge_err" );
+        }
 
     } elsif ( $cmd eq 'del_access' ) {
         $remote->remove_edge( $to_u, trust => {
diff -r d1b4f5148736 -r 170812b5791e cgi-bin/DW/User/Edges/WatchTrust.pm
--- a/cgi-bin/DW/User/Edges/WatchTrust.pm	Fri Aug 19 11:44:02 2011 +0800
+++ b/cgi-bin/DW/User/Edges/WatchTrust.pm	Fri Aug 19 11:55:21 2011 +0800
@@ -1027,6 +1027,9 @@
         }
     }
 
+    # check limits
+    return 0 unless _can_add_wt_edge( $u, $errref, { target => $tu } );
+
     # okay, good to go!
     return 1;
 }
@@ -1063,6 +1066,9 @@
         }
     }
 
+    # check limits
+    return 0 unless _can_add_wt_edge( $u, $errref, { target => $tu } );
+
     # okay, good to go!
     return 1;
 }
@@ -1079,7 +1085,7 @@
     my ($u, $err, $opts) = @_;
 
     if ($u->is_suspended) {
-        $$err = "Suspended journals cannot add friends.";
+        $$err = LJ::Lang::ml( "error.adduser.suspended" );
         return 0;
     }
 
@@ -1087,15 +1093,15 @@
     my $fr_count = $opts->{'numfriends'} || $u->circle_userids;
     my $maxfriends = $u->count_maxfriends;
     if ($fr_count >= $maxfriends) {
-        $$err = "You have reached your limit of $maxfriends friends.";
+        $$err = LJ::Lang::ml( "error.adduser.limit", { maxnum => $maxfriends } );
         return 0;
     }
 
     # are they trying to add friends too quickly?
 
     # don't count mutual friends
-    if (exists($opts->{friend})) {
-        my $fr_user = $opts->{friend};
+    if ( defined $opts->{target} ) {
+        my $fr_user = $opts->{target};
         # we needed LJ::User object, not just a hash.
         if (ref($fr_user) eq 'HASH') {
             $fr_user = LJ::load_user($fr_user->{username});
@@ -1108,7 +1114,7 @@
     }
 
     unless ($u->rate_log('addfriend', 1)) {
-        $$err = "You are trying to add too many friends in too short a period of time.";
+        $$err = LJ::Lang::ml( "error.adduser.rate" );
         return 0;
     }
 
diff -r d1b4f5148736 -r 170812b5791e cgi-bin/LJ/User.pm
--- a/cgi-bin/LJ/User.pm	Fri Aug 19 11:44:02 2011 +0800
+++ b/cgi-bin/LJ/User.pm	Fri Aug 19 11:55:21 2011 +0800
@@ -2256,7 +2256,7 @@
 }
 
 sub count_maxfriends {
-    return $_[0]->get_cap( 'friends' );
+    return $_[0]->get_cap( 'maxfriends' );
 }
 
 sub count_max_interests {
diff -r d1b4f5148736 -r 170812b5791e htdocs/manage/circle/add.bml
--- a/htdocs/manage/circle/add.bml	Fri Aug 19 11:44:02 2011 +0800
+++ b/htdocs/manage/circle/add.bml	Fri Aug 19 11:55:21 2011 +0800
@@ -205,7 +205,9 @@
    # users that aren't visible can only be removed, not modified
    if ($u->is_visible) {
 
-    if ( $remote->can_trust( $u ) ) {
+    my ( $w_err, $t_err );
+
+    if ( $trusted || $remote->can_trust( $u, errref => \$t_err ) ) {
         $body .= LJ::html_check({
             name => "add_trust",
             id => "add_trust",
@@ -214,7 +216,7 @@
         }) . "<br />";
     }
 
-    if ( $remote->can_watch( $u ) ) {
+    if ( $watched || $remote->can_watch( $u, errref => \$w_err ) ) {
         $body .= LJ::html_check({
             name => "add_watch",
             id => "add_watch",
@@ -223,6 +225,16 @@
         });
     }
 
+    if ( $w_err && $t_err ) {
+        # if we get here, the user isn't trusted or watched,
+        # and can_trust and can_watch both failed, which means
+        # no checkboxes can be displayed.
+
+        $title = $ML{'Error'};
+        $body .= "<?p $w_err p?>";
+        return;
+    }
+
     my @trust_groups = $remote->trust_groups;
     my @content_filters = $remote->content_filters;
 
diff -r d1b4f5148736 -r 170812b5791e htdocs/manage/circle/edit.bml
--- a/htdocs/manage/circle/edit.bml	Fri Aug 19 11:44:02 2011 +0800
+++ b/htdocs/manage/circle/edit.bml	Fri Aug 19 11:55:21 2011 +0800
@@ -47,8 +47,7 @@
 
     # no post, show edit form
     unless ( LJ::did_post() ) {
-        my @trusted_userids = $u->trusted_userids;
-        my %is_trusted_userid = map { $_ => 1 } @trusted_userids;
+        my $trust_list = $u->trust_list;
         my $watch_list = $u->watch_list;
         my @trusted_by_userids = $u->trusted_by_userids;
         my %is_trusted_by_userid = map { $_ => 1 } @trusted_by_userids;
@@ -57,7 +56,8 @@
         my @member_of_userids = $u->member_of_userids;
         my %is_member_of_userid = map { $_ => 1 } @member_of_userids;
 
-        my $us = LJ::load_userids( @trusted_userids, keys %$watch_list, @trusted_by_userids, @watched_by_userids, @member_of_userids );
+        my @all_circle_userids = ( keys %$trust_list, keys %$watch_list, @trusted_by_userids, @watched_by_userids, @member_of_userids );
+        my $us = LJ::load_userids( @all_circle_userids );
 
         $ret .= "<form method='post' name='editFriends' action='edit$getextra'>\n";
         $ret .= LJ::form_auth();
@@ -69,7 +69,7 @@
         my $standoutbox .= "<?standout " . BML::ml( '.circle.standout', { aopts1 => "href='#editpeople'", aopts2 => "href='#editcomms'", aopts3 => "href='#editfeeds'" }) . "standout?><br />\n\n";
 
         # does the circle exist
-        if ( @trusted_userids || keys %$watch_list || @trusted_by_userids || @watched_by_userids || @member_of_userids ) {
+        if ( @all_circle_userids ) {
             my ( @person_userids, @comm_userids, @feed_userids );
 
             # get sorted arrays
@@ -130,7 +130,7 @@
                     # subscription status
                     my $iscontent = 0; # tracks whether the table cell has content
                     $ret .= "<td>";
-                    if ( $u->can_watch( $other_u ) || $watch_list->{$uid} ) {
+                    if ( $watch_list->{$uid} || $u->can_watch( $other_u ) ) {
                         $ret .= LJ::html_check({
                             name => "editfriend_edit_${uid}_watch",
                             value => 1,
@@ -142,16 +142,15 @@
                     }
 
                     if ($type eq "people") {
-                        if ( $other_u->can_watch( $u ) ) {
-                            my $watched = $is_watched_by_userid{$uid} ? 1 : 0;
-                            $ret .= " | ";
-                            if ( $watched ) {
-                                $ret .= LJ::img( 'circle_yes', '' );
-                                $ret .= $ML{'.circle.subscribe.y'}
-                            } else {
-                                $ret .= LJ::img( 'circle_no', '' );
-                                $ret .= $ML{'.circle.subscribe.n'}
-                            }
+                        if ( $is_watched_by_userid{$uid} ) {
+                            $ret .= " | " if $iscontent;
+                            $ret .= LJ::img( 'circle_yes', '' );
+                            $ret .= $ML{'.circle.subscribe.y'};
+                            $iscontent = 1;
+                        } elsif ( $other_u->can_watch( $u ) ) {
+                            $ret .= " | " if $iscontent;
+                            $ret .= LJ::img( 'circle_no', '' );
+                            $ret .= $ML{'.circle.subscribe.n'};
                             $iscontent = 1;
                         }
                     }
@@ -163,11 +162,11 @@
                     # ...and access/membership
                     $iscontent = 0; # same as before
                     $iscontent = 1 if $type eq "feeds"; # slightly hacky workaround; feeds don't have this table cell
-                    if ( $u->can_trust( $other_u ) || $is_trusted_userid{$uid} ) {
+                    if ( $trust_list->{$uid} || $u->can_trust( $other_u ) ) {
                         $ret .= LJ::html_check({
                             name => "editfriend_edit_${uid}_trust",
                             value => 1,
-                            selected => $is_trusted_userid{$uid} ? 1 : 0,
+                            selected => $trust_list->{$uid} ? 1 : 0,
                             id => "editfriend_edit_${uid}_trust",
                         });
                         $ret .= "<label for='editfriend_edit_${uid}_trust'>" . $ML{'.circle.access'} . "</label>";
@@ -175,16 +174,15 @@
                     }
 
                     if ($type eq "people") {
-                        if ( $other_u->can_trust( $u ) ) {
-                            my $trusted = $is_trusted_by_userid{$uid} ? 1 : 0;
-                            $ret .= " | ";
-                            if ( $trusted ) {
-                                $ret .= LJ::img( 'circle_yes', '' );
-                                $ret .= $ML{'.circle.access.y'}
-                            } else {
-                                $ret .= LJ::img( 'circle_no', '' );
-                                $ret .= $ML{'.circle.access.n'}
-                            }
+                        if ( $is_trusted_by_userid{$uid} ) {
+                            $ret .= " | " if $iscontent;
+                            $ret .= LJ::img( 'circle_yes', '' );
+                            $ret .= $ML{'.circle.access.y'};
+                            $iscontent = 1;
+                        } elsif ( $other_u->can_trust( $u ) ) {
+                            $ret .= " | " if $iscontent;
+                            $ret .= LJ::img( 'circle_no', '' );
+                            $ret .= $ML{'.circle.access.n'};
                             $iscontent = 1;
                         }
                     } elsif ($type eq "comms") {
@@ -263,14 +261,29 @@
         $ret .= "<?h2 $ML{'.addrelationships.head'} h2?>\n";
         $ret .= "<?p " . BML::ml('.addrelationships.text', { sitename => $LJ::SITENAMESHORT, aopts => "href='$LJ::SITEROOT/manage/circle/invite'" }) . " p?>";
 
+        if ( $u->circle_userids >= $u->count_maxfriends ) {
+            # different message if account upgrade is possible
+            my $acttype = DW::Pay::get_account_type( $u );
+            my $noup = ( $acttype eq "seed" || $acttype eq "premium" ) ? 1 : 0;
+            my $warnml = $noup ? '.addrelationships.warning.noupgrade' : '.addrelationships.warning.canupgrade';
+            $ret .= "<?p " . BML::ml( $warnml, { maxnum => $u->count_maxfriends, aopts => "href='$LJ::SITEROOT/shop/account?for=self'" } ) . " p?>";
+        }
+
+        my $show_watch_col = $u->can_watch ? 1 : 0;
+        my $show_trust_col = $u->can_trust ? 1 : 0;
+        my $show_colors = ( $show_watch_col || keys %$watch_list ) ? 1 : 0;
+        # still let them edit colors for existing circle, even if they can't make new subscriptions
+
         $ret .= "<br />";
         $ret .= "<div align='center'><table id='addfriends'>\n";
         $ret .= "<tr><th>$ML{'.circle.username'}</th>";
-        $ret .= "<th>$ML{'.circle.trust'}</th>";
-        $ret .= "<th>$ML{'.circle.watch'}</th><th>$ML{'.foreground'}</th><th>$ML{'.background'}</th>\n";
+        $ret .= "<th>$ML{'.circle.trust'}</th>" if $show_trust_col;
+        $ret .= "<th>$ML{'.circle.watch'}</th>" if $show_watch_col;
+        $ret .= "<th>$ML{'.foreground'}</th><th>$ML{'.background'}</th>\n" if $show_colors;
 
         my @color = ();
-        if ( $u->can_watch ) {
+
+        if ( $show_colors ) {
             # load the colors
             LJ::load_codes({ "color" => \@color });
 
@@ -310,22 +323,24 @@
                                     'onfocus' => "setFriend($i);" });
             $ret .= "</td>";
 
-            if ( $u->can_trust ) {
+            if ( $show_trust_col ) {
                 $ret .= "<td style='text-align: center;'>";
                 $ret .= LJ::html_check({ name => "editfriend_add_${i}_trust",
                                          value => 1,
                                          onfocus => "setFriend($i);" });
                 $ret .= "</td>";
-            } else {
-                $ret .= "<td>$ML{'.circle.na'}</td>";
             }
 
-            if ( $u->can_watch ) {
+            if ( $show_watch_col ) {
                 $ret .= "<td style='text-align: center;'>";
                 $ret .= LJ::html_check({ name => "editfriend_add_${i}_watch",
                                          value => 1,
                                          onfocus => "setFriend($i);" });
-                $ret .= "</td><td>";
+                $ret .= "</td>";
+            }
+
+            if ( $show_colors ) {
+                $ret .= "<td>";
                 $ret .= LJ::html_select({ 'name' => "editfriend_add_${i}_fg",
                                           'selected' => '#000000',
                                           'onchange' => "updatePreview(); return true;",
@@ -338,10 +353,6 @@
                                           'onfocus' => "setFriend($i);" },
                                           map { lc($_->{'code'}), $_->{'item'} } @color );
                 $ret .= "</td>";
-            } else {
-                $ret .= "<td>$ML{'.circle.na'}</td>";
-                $ret .= "<td>$ML{'.circle.na'}</td>";
-                $ret .= "<td>$ML{'.circle.na'}</td>";
             }
 
             $ret .= "</tr>\n";
@@ -365,7 +376,7 @@
         # interface, since anyone who's in both the add and edit interfaces should
         # only be proccessed via the add interface and not by the edit interface
         my %userid_processed;
-        
+
         #  Maintain a list of invalid userids for display to the user
         my @not_user;
 
diff -r d1b4f5148736 -r 170812b5791e htdocs/manage/circle/edit.bml.text
--- a/htdocs/manage/circle/edit.bml.text	Fri Aug 19 11:44:02 2011 +0800
+++ b/htdocs/manage/circle/edit.bml.text	Fri Aug 19 11:55:21 2011 +0800
@@ -3,6 +3,10 @@
 
 .addrelationships.text=Enter the [[sitename]] account names or OpenID URLs of people you want to add to your Circle. You can also use this form to change the colors associated with an account you subscribe to.  To return to the default colors, select a Black foreground on a White background.
 
+.addrelationships.warning.canupgrade=<b><i>Note: you will not be able to add more users to your circle, because you have met or exceeded your limit of [[maxnum]] [[?maxnum|journal|journals]] in your circle. You may still use the following form to edit the colors associated with an account if it is already in your circle, or you can <a [[aopts]]>upgrade your account</a> in order to increase the maximum size of your circle.</i></b>
+
+.addrelationships.warning.noupgrade=<b><i>Note: you will not be able to add more users to your circle, because you have met or exceeded your limit of [[maxnum]] [[?maxnum|journal|journals]] in your circle. You may still use the following form to edit the colors associated with an account if it is already in your circle.</i></b>
+
 .background=Background
 
 .bgcolor=Background Color:
@@ -57,7 +61,7 @@
 
 .circle.none=(None)
 
-.circle.standout=Jump to: <a [[aopts1]]>People</a> | <a [[aopts2]]>Communities</a> | <a [[aopts3]]>Feeds</a> 
+.circle.standout=Jump to: <a [[aopts1]]>People</a> | <a [[aopts2]]>Communities</a> | <a [[aopts3]]>Feeds</a>
 
 .circle.status=Circle status
 
--------------------------------------------------------------------------------

Post a comment in response:

This account has disabled anonymous posting.
If you don't have an account you can create one now.
HTML doesn't work in the subject.
More info about formatting

If you are unable to use this captcha for any reason, please contact us by email at support@dreamwidth.org