Committer: dnikolaev
LJSUP-8686: Programming Ability to add to friends page content by tagU trunk/bin/upgrading/proplists.dat U trunk/cgi-bin/LJ/Console/Command/Friend.pm U trunk/cgi-bin/LJ/Entry.pm A trunk/cgi-bin/LJ/FriendsTags.pm U trunk/cgi-bin/LJ/S2/FriendsPage.pm U trunk/cgi-bin/ljlib.pl U trunk/cgi-bin/ljviews.pl U trunk/htdocs/friends/add.bml U trunk/htdocs/friends/add.bml.text U trunk/htdocs/friends/edit.bml U trunk/htdocs/friends/edit.bml.text
Modified: trunk/bin/upgrading/proplists.dat =================================================================== --- trunk/bin/upgrading/proplists.dat 2011-05-25 11:06:59 UTC (rev 19130) +++ trunk/bin/upgrading/proplists.dat 2011-05-26 02:17:19 UTC (rev 19131) @@ -1650,3 +1650,11 @@ des: Given tokens counter prettyname: Given counter sortorder: 110 + +userproplist.friends_tags: + datatype: blobchar + des: Set of tags, which filter friendspage + indexed: 0 + multihomed: 0 + prettyname: Friendspage filtrating tags + Modified: trunk/cgi-bin/LJ/Console/Command/Friend.pm =================================================================== --- trunk/cgi-bin/LJ/Console/Command/Friend.pm 2011-05-25 11:06:59 UTC (rev 19130) +++ trunk/cgi-bin/LJ/Console/Command/Friend.pm 2011-05-26 02:17:19 UTC (rev 19131) @@ -3,13 +3,14 @@ use strict; use base qw(LJ::Console::Command); use Carp qw(croak); +use LJ::FriendsTags; sub cmd { "friend" } sub desc { "List your friends or add/remove a user from your friends list." } sub args_desc { [ - 'command' => "Either 'list' to list friend, 'add' to add a friend, or 'remove' to remove a friend.", + 'command' => "Either 'list' to list friend, 'list_tags' to view friends page filter tags, 'add' to add a friend, or 'remove' to remove a friend.", 'user' => "The username of the person to add or remove when using the add or remove command.", 'group' => "Optional; when using 'add', adds the user to this friend group. It must already exist.", 'fgcolor' => "Optional; when using 'add', specifies the foreground color. Must be of form 'fgcolor=#hex'", @@ -27,27 +28,74 @@ unless $command && scalar(@args) <= 3; return $self->error("Invalid command. Must be one of: 'list', 'add', or 'remove'.") - unless $command =~ /^(?:list|add|remove)$/; + unless $command =~ /^(?:list|list_tags|add|remove)$/; my $remote = LJ::get_remote(); - if ($command eq "list") { - # TAG:FR:console:friend:getfriends + if ($command eq 'list' || $command eq 'list_tags') { + + my $fu = LJ::load_user($user); + return $self->error("Invalid username: $user") + unless $fu; + + unless (LJ::check_priv($remote, 'canview', 'friends')) { + return $self->error("You are not allowed to view other's friends") + if $fu->id != $remote->id; + } + my $dbh = LJ::get_db_reader(); - my $sth = $dbh->prepare("SELECT u.user, u.name, u.statusvis, u.journaltype FROM user u, friends f ". + my $sth = $dbh->prepare("SELECT u.userid, u.user, u.name, u.statusvis, u.journaltype FROM user u, friends f ". "WHERE u.userid=f.friendid AND f.userid=? ORDER BY u.user"); - $sth->execute($remote->id); + $sth->execute($fu->id); - $self->info(sprintf("%-15s S T Name", "User")); - $self->info("-" x 58); + if ($command eq 'list') { + $self->info(sprintf("%-15s S T Name", "User")); + $self->info("-" x 58); - while (my ($username, $name, $statusvis, $type) = $sth->fetchrow_array) { - $statusvis = "" if $statusvis eq "V"; - $type = "" if $type eq "P"; + while (my ($userid, $username, $name, $statusvis, $type) = $sth->fetchrow_array) { + $statusvis = "" if $statusvis eq "V"; + $type = "" if $type eq "P"; + $self->info(sprintf("%-15s %1s %1s %s", $username, $statusvis, $type, $name)); + } + } + elsif ($command eq 'list_tags') { + my $tags_by_friendid = LJ::FriendsTags->load_tags($fu); - $self->info(sprintf("%-15s %1s %1s %s", $username, $statusvis, $type, $name)); + my $get_tags_str = sub { + my $tags_data = shift; + + my $tags_str = ''; + return '' unless $tags_data && ref($tags_data) eq 'ARRAY'; + my ($mode, $tags_list) = @$tags_data; + $mode ||= 'A'; + $tags_list = [] unless $tags_list && ref($tags_list) eq 'ARRAY'; + return ($mode eq 'D' ? '- ' : '+ ') . join(', ', @$tags_list); + }; + + $self->info(sprintf("%-15s S T Tags", "User")); + $self->info("-" x 58); + + while (my ($userid, $username, $name, $statusvis, $type) = $sth->fetchrow_array) { + $statusvis = "" if $statusvis eq "V"; + $type ="" if $type eq "P"; + + $self->info(sprintf("%-15s %1s %1s %s", $username, $statusvis, $type, + $get_tags_str->($tags_by_friendid->{$userid}))); + + delete $tags_by_friendid->{$userid}; + } + + if (keys %$tags_by_friendid) { + $self->info(""); + $self->info("--- Not Friends " . "-" x 42); + while (my ($friendid, $tags_data) = each %$tags_by_friendid) { + my $u = LJ::load_userid($friendid); + my $username = $u ? $u->user : "userid=$friendid"; + $self->info(sprintf("%-15s %s", $username, $get_tags_str->($tags_data))); + } + } } - + return 1; } Modified: trunk/cgi-bin/LJ/Entry.pm =================================================================== --- trunk/cgi-bin/LJ/Entry.pm 2011-05-25 11:06:59 UTC (rev 19130) +++ trunk/cgi-bin/LJ/Entry.pm 2011-05-26 02:17:19 UTC (rev 19131) @@ -1925,13 +1925,13 @@ my $log = LJ::get_log2_recent_log($opts->{'userid'}, $opts->{'clusterid'}, $opts->{'update'}, $opts->{'notafter'}, $opts->{events_date}); - my $left = $opts->{'itemshow'}; + ## UNUSED: my $left = $opts->{'itemshow'}; my $notafter = $opts->{'notafter'}; my $remote = $opts->{'remote'}; my %mask_for_remote = (); # jid => mask for $remote foreach my $item (@$log) { - last unless $left; + ## UNUSED: last unless $left; last if $notafter and $item->{'rlogtime'} > $notafter; next unless $remote || $item->{'security'} eq 'public'; next if $item->{'security'} eq 'private' Added: trunk/cgi-bin/LJ/FriendsTags.pm =================================================================== --- trunk/cgi-bin/LJ/FriendsTags.pm (rev 0) +++ trunk/cgi-bin/LJ/FriendsTags.pm 2011-05-26 02:17:19 UTC (rev 19131) @@ -0,0 +1,144 @@ +package LJ::FriendsTags; + +use constant ALLOW => 'A'; +use constant DENY => 'D'; +use constant MAX_FRIENDSTAGS_SIZE => 65535; + +# +# { +# friendid1 => [ +# ('A' | 'D'), +# [ tag1, tag2, ... ] +# ], +# ... +# } +# + +sub load { + my ($class, $remote) = @_; + + my $prop = $remote->prop('friends_tags'); + $prop = LJ::text_uncompress($prop); + + my $data; + if ($prop) { + eval { $data = LJ::JSON->from_json($prop); }; + } + + unless ($data && ref($data) eq 'HASH') { + $data = {}; + } + + my $self = bless { + _u => $remote, + _data => $data, + }, $class; + + return $self; +} + +sub load_tags { + my ($class, $remote) = @_; + + my $self = $class->load($remote); + my $data = $self->{_data}; + return $data; +} + +sub is_allow_mode { + my ($class, $mode) = @_; + + return 0 if $mode && lc($mode) eq lc(DENY); + return 1; +} + +sub normalize_mode { + my ($class, $mode) = @_; + + return $class->is_allow_mode($mode) ? ALLOW : DENY; +} + +sub save { + my ($self) = @_; + + while (my ($friendid, $arr) = each %{$self->{_data}}) { + unless (LJ::is_friend($self->{_u}, $friendid)) { + delete $self->{_data}->{$friendid}; + } + } + + my $prop = LJ::JSON->to_json($self->{_data}); + $prop = LJ::text_compress($prop); + + if (length($prop) <= MAX_FRIENDSTAGS_SIZE) { + return $self->{_u}->set_prop(friends_tags => $prop); + } else { + # BML::ml('widget.addalias.too.long'); + return 0; + } +} + +sub get_tags { + my ($self, $friendid) = @_; + + my $ret = $self->{_data}->{$friendid}; + return (undef, []) unless $ret && ref($ret) eq 'ARRAY'; + my $mode = $ret->[0]; + my $tags = $ret->[1]; + $tags = [] unless $tags && ref($tags) eq 'ARRAY'; + return ($mode, $tags); +} + +sub set { + my ($self, $friendid, $mode, $tags_str) = @_; + + my $data = $self->{_data}; + + $mode = $self->normalize_mode($mode); + + $tags_str = '' unless defined $tags_str; + $tags_str =~ s/^\s+//s; + $tags_str =~ s/\s+$//s; + + if (length($tags_str) > 0) { + my %tags = map { lc($_) => 1 } split /\s*,\s*/, $tags_str; + if (keys %tags) { + $data->{$friendid} = [ $mode, [ sort grep { length($_) > 0 } keys %tags ] ]; + } else { + delete $data->{$friendid}; + } + } else { + delete $data->{$friendid}; + } + + $self->{_data} = $data; +} + +sub filter_func { + my ($self, $friendid) = @_; + + my ($mode, $tags) = $self->get_tags($friendid); + return undef unless defined $mode && @$tags; + my %tags = map { $_ => 1 } @$tags; + + if ($mode eq DENY) { + return sub { + return 1 unless @_; + foreach (@_) { + return 0 if exists $tags{$_}; + } + return 1; + }; + } else { + return sub { + return 0 unless @_; + foreach (@_) { + return 1 if exists $tags{$_}; + } + return 0; + }; + } +} + +1; + Modified: trunk/cgi-bin/LJ/S2/FriendsPage.pm =================================================================== --- trunk/cgi-bin/LJ/S2/FriendsPage.pm 2011-05-25 11:06:59 UTC (rev 19130) +++ trunk/cgi-bin/LJ/S2/FriendsPage.pm 2011-05-26 02:17:19 UTC (rev 19131) @@ -166,6 +166,7 @@ 'friendsoffriends' => $opts->{'view'} eq "friendsfriends", 'dateformat' => 'S2', 'events_date' => $events_date, + $get->{notags} ? () : 'filter_by_tags' => LJ::FriendsTags->load($remote), }); # warn "[FriendsPage=$user] Items loaded. elapsed=" . Time::HiRes::tv_interval( $t0, [Time::HiRes::gettimeofday]) . " sec"; Modified: trunk/cgi-bin/ljlib.pl =================================================================== --- trunk/cgi-bin/ljlib.pl 2011-05-25 11:06:59 UTC (rev 19130) +++ trunk/cgi-bin/ljlib.pl 2011-05-26 02:17:19 UTC (rev 19131) @@ -55,6 +55,7 @@ LJ::GraphicPreviews LJ::Vertical LJ::Browse + LJ::FriendsTags ); use LJ::TimeUtil; @@ -916,6 +917,28 @@ if (@newitems) { + if ($opts->{'filter_by_tags'}) { + die "Invalid type of parameter filter_by_tags: " . ref($opts->{filter_by_tags}) . "\n" + unless UNIVERSAL::isa($opts->{filter_by_tags}, 'LJ::FriendsTags'); + + my $filter_func = $opts->{'filter_by_tags'}->filter_func($friendid); + if ($filter_func) { + my $idsbycluster = {}; + foreach (@newitems) { + push @{ $idsbycluster->{$_->{clusterid}} }, [ $_->{ownerid}, $_->{itemid} ]; + } + my $logtags = LJ::Tags::get_logtagsmulti($idsbycluster); + my $get_entry_tags = sub { + my ($jid, $jitemid) = @_; + my $entry_tags = $logtags->{"$jid $jitemid"}; + return () unless $entry_tags; + return values %$entry_tags; + }; + + @newitems = grep { $filter_func->($get_entry_tags->($_->{ownerid}, $_->{itemid})) } @newitems; + } + } + push @items, @newitems; $itemsleft--; # we'll need at least one less for the next friend Modified: trunk/cgi-bin/ljviews.pl =================================================================== --- trunk/cgi-bin/ljviews.pl 2011-05-25 11:06:59 UTC (rev 19130) +++ trunk/cgi-bin/ljviews.pl 2011-05-26 02:17:19 UTC (rev 19131) @@ -1067,6 +1067,7 @@ require "ljlang.pl"; require "cleanhtml.pl"; +use LJ::FriendsTags; # the creator for the 'lastn' view: sub create_view_lastn @@ -1728,6 +1729,7 @@ 'showtypes' => $get->{'show'}, 'friendsoffriends' => $opts->{'view'} eq "friendsfriends", 'events_date' => $events_date, + $get->{notags} ? () : 'filter_by_tags' => LJ::FriendsTags->load($remote), }); while ($_ = each %friends) { Modified: trunk/htdocs/friends/add.bml =================================================================== --- trunk/htdocs/friends/add.bml 2011-05-25 11:06:59 UTC (rev 19130) +++ trunk/htdocs/friends/add.bml 2011-05-26 02:17:19 UTC (rev 19131) @@ -19,7 +19,7 @@ my $userid = $u->{userid}; $body = ""; - LJ::need_res(qw(stc/addfriend.css js/core.js js/dom.js js/colorpicker.js)); + LJ::need_res(qw(stc/addfriend.css stc/entry.css js/core.js js/dom.js js/colorpicker.js js/jquery/jquery.lj.itemlist.js js/jquery/jquery.dependentLists.js js/jquery-ui-autocomplete.js js/jquery.tmpl.js js/addfriend.js)); LJ::set_active_crumb('addfriend'); @@ -69,14 +69,17 @@ "ver" => $LJ::PROTOCOL_VER, }; + my $friend_tags_str; if ($POST{'action:delete'}) { $req->{"editfriend_delete_$user"} = 1; + $friend_tags_str = undef; } else { $req->{"editfriend_add_1_user"} = $user; $req->{"editfriend_add_1_fg"} = $POST{'editfriend_add_1_fg'}; $req->{"editfriend_add_1_bg"} = $POST{'editfriend_add_1_bg'}; $req->{"editfriend_add_1_groupmask"} = $gmask; + $friend_tags_str = $POST{'friend_tags'}; } my %res = (); @@ -89,6 +92,12 @@ ## user has been added. setting alias for him LJ::set_alias($u, $POST{'user_note'}); } + + my $friends_tags = LJ::FriendsTags->load($remote); + $friends_tags->set($u->{userid}, $POST{friend_tags_mode} || 'A', $friend_tags_str); + unless ($friends_tags->save()) { + $body .= '<br>' . BML::ml('.error.friendstags', { aopts => "href='/friends/add.bml?user=$user'" }) . '<br><br>'; + } $body .= LJ::get_ads({ location => 'bml.friends.add/main', @@ -246,44 +255,86 @@ $body .= '</div>'; } + my $to_lower_case = sub { + my $s = shift; + return Encode::encode('utf-8', lc(Encode::decode('utf-8', $s))); + }; + my $user_tags_map = $u->tags || {}; + $user_tags_map = { + map { + $to_lower_case->($_->{name}) => ( + $_->{uses} < 2 ? + '' : + ($_->{uses} > 999 ? '999+' : $_->{uses}) + ) + } + values %$user_tags_map + }; + + my $friends_tags = LJ::FriendsTags->load($remote); + my ($friend_tags_mode, $friend_tags_list) = $friends_tags->get_tags($u->{userid}); + my $friend_tags_map = {}; + $friend_tags_list = [ map { $to_lower_case->($_) } @$friend_tags_list ]; + $body .= '<div class="b-addfriend-option b-addfriend-tags">'; $body .= '<h3 class="b-addfriend-subhead">' . $ML{'.tags.read'} . '</h3>'; - $body .= '<p class="b-addfriend-notice">' . $ML{'.tags.text'} . '</p>'; + $body .= '<p class="b-addfriend-notice">' . BML::ml('.tags.text', { ljuser => LJ::ljuser($u), tagscount => scalar(keys %$user_tags_map) }) . '</p>'; $body .= '<dl class="b-addfriend-showmore">'; $body .= '<dt class="b-addfriend-showmore-head"><i class="i-addfriend-showmore-arr"></i>' . $ML{'.tags.select'} . '</dt>'; $body .= '<dd class="b-addfriend-showmore-content">'; - $body .= '<p><input type="radio" name="tags_include" id="tags_including" checked="checked" /><label for="tags_including">' . $ML{'.tags.including'} . '</label><br /><input type="radio" name="tags_include" id="tags_excluding" /><label for="tags_excluding">' . $ML{'.tags.excluding'} . '</label></p>'; + $body .= '<p>'; + $body .= '<input type="radio" name="friend_tags_mode" id="tags_including" value="A" ' . + (LJ::FriendsTags->is_allow_mode($friend_tags_mode) ? 'checked="checked" ' : '') . + '/><label for="tags_including">' . $ML{'.tags.including'} . '</label><br />'; + $body .= '<input type="radio" name="friend_tags_mode" id="tags_excluding" value="D" ' . + (LJ::FriendsTags->is_allow_mode($friend_tags_mode) ? '' : 'checked="checked" ') . + '/><label for="tags_excluding">' . $ML{'.tags.excluding'} . '</label>'; + $body .= '</p>'; $body .= '<h4>' . $ML{'.tags.selected.by.you'} . '</h4>'; - $body .= '<ul class="b-pending-users">'; - $body .= '<li><span><a target="_blank" href="http://tema.livejournal.com/tag/дебилы">дебилы</a> (2)<i title="Remove tag from list" class="i-pending-close"></i></span>,</li>'; - $body .= '<li><span><a target="_blank" href="http://tema.livejournal.com/tag/девственность">девственность</a> (3)<i title="Remove tag from list" class="i-pending-close"></i></span>,</li>'; - $body .= '<li><span><a target="_blank" href="http://tema.livejournal.com/tag/дезодорант">дезодорант</a> (2)<i title="Remove tag from list" class="i-pending-close"></i></span></li>'; + $body .= '<ul class="b-pending-users" id="selectedTagsList">'; + + { + my $not_last = scalar(@$friend_tags_list); + foreach (@$friend_tags_list) { + $body .= '<li><span><a target="_blank" href="#">' . LJ::ehtml($_) . '</a> ' . ($user_tags_map->{$_} ? "($user_tags_map->{$_})" : "") . '<i title="Remove tag from list" class="i-pending-close"></i></span>' . (--$not_last ? ',' : '') . '</li>'; + $friend_tags_map->{$_} = 1; + } + } + $body .= '</ul>'; $body .= '<p class="i-bubble b-bubble-alert b-bubble-noarrow">' . $ML{'.tags.remove.tags'} . '</p>'; $body .= '<h4>' . $ML{'.tags.select.more'} . '</h4>'; - $body .= '<p><input type="text" class="b-addfriend-input" placeholder="' . $ML{'.tags.input.tag'} . '" /> <input type="button" value="Select" /></p>'; + $body .= '<p><input type="text" class="b-addfriend-input" id="searchTag" autocomplete="off" placeholder="' . $ML{'.tags.input.tag'} . '" /> <input id="selectTag" type="button" value="Select" /></p>'; + $body .= '<input type="hidden" id="selectedTags" name="friend_tags">'; $body .= '<p class="i-bubble b-bubble-alert b-bubble-noarrow">' . $ML{'.tags.find.tags'} . '</p>'; $body .= '<ul class="b-pending-users b-addfriend-alltags">'; - $body .= '<li><span><a target="_blank" href="http://tema.livejournal.com/tag/дебилы">дебилы</a> (2)<i title="Remove tag from list" class="i-pending-close"></i></span>,</li>'; - $body .= '<li><span><a target="_blank" href="http://tema.livejournal.com/tag/девственность">девственность</a> (3)<i title="Remove tag from list" class="i-pending-close"></i></span>,</li>'; - $body .= '<li><span><a target="_blank" href="http://tema.livejournal.com/tag/дебилы">дебилы</a> (2)<i title="Remove tag from list" class="i-pending-close"></i></span>,</li>'; - $body .= '<li><span><a target="_blank" href="http://tema.livejournal.com/tag/девственность">девственность</a> (3)<i title="Remove tag from list" class="i-pending-close"></i></span>,</li>'; - $body .= '<li><span><a target="_blank" href="http://tema.livejournal.com/tag/дебилы">дебилы</a> (2)<i title="Remove tag from list" class="i-pending-close"></i></span>,</li>'; - $body .= '<li><span><a target="_blank" href="http://tema.livejournal.com/tag/девственность">девственность</a> (3)<i title="Remove tag from list" class="i-pending-close"></i></span>,</li>'; - $body .= '<li><span><a target="_blank" href="http://tema.livejournal.com/tag/дебилы">дебилы</a> (2)<i title="Remove tag from list" class="i-pending-close"></i></span>,</li>'; - $body .= '<li><span><a target="_blank" href="http://tema.livejournal.com/tag/девственность">девственность</a> (3)<i title="Remove tag from list" class="i-pending-close"></i></span>,</li>'; - $body .= '<li><span><a target="_blank" href="http://tema.livejournal.com/tag/дебилы">дебилы</a> (2)<i title="Remove tag from list" class="i-pending-close"></i></span>,</li>'; - $body .= '<li><span><a target="_blank" href="http://tema.livejournal.com/tag/девственность">девственность</a> (3)<i title="Remove tag from list" class="i-pending-close"></i></span>,</li>'; - $body .= '<li><span><a target="_blank" href="http://tema.livejournal.com/tag/дезодорант">дезодорант</a> (2)<i title="Remove tag from list" class="i-pending-close"></i></span></li>'; + + { + my @tags = sort grep { !$friend_tags_map->{$_} } keys %$user_tags_map; + my $not_last = scalar(@tags); + foreach my $tag (@tags) { + $body .= '<li><span><a target="_blank" href="#">' . LJ::ehtml($tag) . '</a> ' . ($user_tags_map->{$tag} ? "($user_tags_map->{$tag})" : "") . '<i title="Remove tag from list" class="i-pending-close"></i></span>' . (--$not_last ? ',' : '') . '</li>'; + } + } + $body .= '</ul>'; $body .= '</dd>'; $body .= '</dl>'; $body .= '</div>'; + + $body .= '<script id="tagTmpl" type="text/x-jquery-tmpl"> + <li> + <span class="ljappippu_pers"> + <a href="#">${tagName}</a> + (-) + {{if hasCloseButton }} + <i title="Remove tag from list" class="i-pending-close" ></i> + {{/if}} + </span> + </li> +</script>'; - - - $body .= '<div class="b-addfriend-option b-addfriend-colors">'; $body .= '<h3 class="b-addfriend-subhead">' . $ML{'.colors.select.user'} . '</h3>'; $body .= '<p class="b-addfriend-notice">' . BML::ml('.colors.text', {'user'=>$user}) . '</p>'; Modified: trunk/htdocs/friends/add.bml.text =================================================================== --- trunk/htdocs/friends/add.bml.text 2011-05-25 11:06:59 UTC (rev 19130) +++ trunk/htdocs/friends/add.bml.text 2011-05-26 02:17:19 UTC (rev 19131) @@ -78,6 +78,8 @@ .error.too.long|staleness=1 .error.too.long=You've reached the maximum number of the notes. +.error.friendstags=Error while saving friends tags. Perhaps you've reached the maximum number of the friends tags. Please, <a [[aopts]]>retry</a> operation. + .error1.title=Add Friend .error2.text2=Invalid or missing username given. To add a friend, go to the <a [[aopts]]>edit friends</a> page. @@ -130,5 +132,5 @@ .tags.selected.by.you=Selected by you -.tags.text=You can read all entries by default or select entries with defe +.tags.text=[[ljuser]] used [[tagscount]] tags.<br>You can read all entries by default or select entries with definitely tags. Modified: trunk/htdocs/friends/edit.bml =================================================================== --- trunk/htdocs/friends/edit.bml 2011-05-25 11:06:59 UTC (rev 19130) +++ trunk/htdocs/friends/edit.bml 2011-05-26 02:17:19 UTC (rev 19131) @@ -155,8 +155,11 @@ $ret .= "<th style='text-align:left;'>$ML{'.editfriends.alias'}</th>"; } + $ret .= "<th style='text-align:left;'>$ML{'.editfriends.tagsonly'}</th>"; $ret .= "</tr>"; + my $friends_tags = LJ::FriendsTags->load($remote); + my $j = 11; # to uniquely distinguish 'friend-of' checkboxes, 1-10 are reserved for friend-add boxes foreach my $f (@friends) { my $who = $f->{'username'}; @@ -216,6 +219,20 @@ $ret .= "</td>"; } + + $ret .= "<td style='text-align: left;'>"; + my ($friend_tags_mode, $friend_tags_list) = $friends_tags->get_tags($who_u->{userid}); + if ($friends{$who} && $friends{$who}->{rel} ne 'FO') { + if ($friend_tags_list && ref($friend_tags_list) eq 'ARRAY' && scalar(@$friend_tags_list) > 0) { + my $friend_tags_str = LJ::ehtml(join(', ', @$friend_tags_list)); + $ret .= "<a href='/friends/add.bml?user=$who_u->{user}'>" . + $friend_tags_str . + "</a>"; + } else { + $ret .= "(<a href='/friends/add.bml?user=$who_u->{user}'>" . BML::ml('.selecttags.text') . "</a>)"; + } + } + $ret .= "</td>"; $ret .= "</tr>\n"; $j++; @@ -309,12 +326,15 @@ $request{$_} = $POST{$_}; } + my %friends_page_tags; + # now flip the logic and process the deletions foreach (grep { /^editfriend_has/} keys %POST) { my $who = $POST{$_}; $request{"editfriend_delete_${who}"} = 1 unless $POST{"editfriend_keep_${who}"}; + my $who_u = LJ::load_user($who); } - + # set new aliases for friends if ($remote->get_cap('aliases')) { my (@aliases, $error); Modified: trunk/htdocs/friends/edit.bml.text =================================================================== --- trunk/htdocs/friends/edit.bml.text 2011-05-25 11:06:59 UTC (rev 19130) +++ trunk/htdocs/friends/edit.bml.text 2011-05-26 02:17:19 UTC (rev 19131) @@ -22,6 +22,8 @@ .editfriends.name=Name +.editfriends.tagsonly=Show only entries with tags + .editfriends.text=You can add or remove anyone from your Friends list at any time. The green arrows ([[img1]]) indicate that someone is on your Friends list, while the blue arrows ([[img2]]) indicate that you're on their Friends list. .editfriends.username=Username @@ -51,6 +53,8 @@ .name=Name +.selecttags.text=select tags + .success.editfriends=Manage your Friends list .success.editgroups=Edit custom friends groups