fu: Close-up of Fu, bringing a scoop of water to her mouth (Default)
fu ([personal profile] fu) wrote in [site community profile] changelog2010-04-29 07:10 am

[dw-free] sort alpha by keyword on allpics/editpics page

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

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

Allow to sort icons by either upload order (current behavior/default) or by
keyword.

On your /icons page, the keyword sort results in multiple instances of the
icon showing up. Icons with no keywords (but have a nominal keyword of
"pic#1234") are sorted to the bottom of the list.

When doing /editicons, the keyword sort sorts only by the first keyword on
the list.

Patch by [personal profile] allen.

Files modified:
  • cgi-bin/LJ/User.pm
  • cgi-bin/LJ/Userpic.pm
  • htdocs/allpics.bml
  • htdocs/allpics.bml.text
  • htdocs/editicons.bml
  • htdocs/editicons.bml.text
--------------------------------------------------------------------------------
diff -r 079aa6f23dae -r a8d460b97735 cgi-bin/LJ/User.pm
--- a/cgi-bin/LJ/User.pm	Wed Apr 28 23:47:49 2010 -0700
+++ b/cgi-bin/LJ/User.pm	Thu Apr 29 00:14:59 2010 -0700
@@ -5590,6 +5590,13 @@ sub allpics_base {
     return $u->journal_base . "/icons";;
 }
 
+# clears the internally cached mapping of userpics to keywords for this
+# User
+sub clear_userpic_kw_map {
+    my $self = $_[0];
+
+    $self->{picid_kw_map} = undef;
+}
 
 sub get_userpic_count {
     my $u = shift or return undef;
@@ -5598,6 +5605,25 @@ sub get_userpic_count {
     return $count;
 }
 
+# gets a mapping from userpic ids to keywords for this User.
+sub get_userpic_kw_map {
+    my $self = shift;
+
+    if ( $self->{picid_kw_map} ) {
+        return $self->{picid_kw_map};
+    }
+
+    my $picinfo = LJ::get_userpic_info( $self->userid, {load_comments => 0} );
+    my $keywords = {};
+    foreach my $keyword ( keys %{$picinfo->{kw}} ) {
+        my $picid = $picinfo->{kw}->{$keyword}->{picid};
+        $keywords->{$picid} = [] unless $keywords->{$picid};
+        push @{$keywords->{$picid}}, $keyword if ( $keyword && $picid );
+    }
+
+    $self->{picid_kw_map} = $keywords;
+    return $keywords;
+}
 
 # <LJFUNC>
 # name: LJ::User::mogfs_userpic_key
diff -r 079aa6f23dae -r a8d460b97735 cgi-bin/LJ/Userpic.pm
--- a/cgi-bin/LJ/Userpic.pm	Wed Apr 28 23:47:49 2010 -0700
+++ b/cgi-bin/LJ/Userpic.pm	Thu Apr 29 00:14:59 2010 -0700
@@ -429,18 +429,9 @@ sub keywords {
 
     croak "Invalid opts passed to LJ::Userpic::keywords" if keys %opts;
 
-    my $picinfo = LJ::get_userpic_info($self->{userid}, {load_comments => 0});
+    my $u = $self->owner;
 
-    # $picinfo is a hashref of userpic data
-    # keywords are stored in the "kw" field in the format keyword => {hash of some picture info}
-
-    # create a hash of picids => keywords
-    my $keywords = {};
-    foreach my $keyword (keys %{$picinfo->{kw}}) {
-        my $picid = $picinfo->{kw}->{$keyword}->{picid};
-        $keywords->{$picid} = [] unless $keywords->{$picid};
-        push @{$keywords->{$picid}}, $keyword if ($keyword && $picid);
-    }
+    my $keywords = $u->get_userpic_kw_map();
 
     # return keywords for this picid
     my @pickeywords = $keywords->{$self->id} ? @{$keywords->{$self->id}} : ();
@@ -921,6 +912,9 @@ sub set_keywords {
                 undef, @data );
     }
 
+    # clear the userpic-keyword map.
+    $u->clear_userpic_kw_map;
+
     # Let the user know about any we didn't save
     # don't throw until the end or nothing will be saved!
     if (@kw_errors) {
@@ -1036,6 +1030,65 @@ sub set_fullurl {
     return 1;
 }
 
+# Sorts the given list of Userpics.
+sub sort {
+    my ( $class, $userpics ) = @_;
+    
+    return () unless ( $userpics && ref $userpics );
+
+    my %kwhash;
+    my %nokwhash;
+
+    for my $pic ( @$userpics ) {
+        my $pickw = $pic->keywords( raw => 1 );
+        if ( $pickw ) {
+            $kwhash{$pickw} = $pic;
+        } else {
+            $pickw = $pic->keywords;
+            $nokwhash{$pickw} = $pic;
+        }
+    }
+    my @sortedkw = sort keys %kwhash;
+    my @sortednokw = sort keys %nokwhash;
+
+    my @sortedpics;
+    foreach my $kw ( @sortedkw ) {
+        push @sortedpics, $kwhash{$kw};
+    }
+    foreach my $kw ( @sortednokw ) {
+        push @sortedpics, $nokwhash{$kw};
+    }
+
+    return @sortedpics;
+}
+
+# Organizes the given userpics by keyword.  Returns an array of hashes,
+# with values of keyword and userpic.
+sub separate_keywords {
+    my ( $class, $userpics ) = @_;
+
+    return () unless ( $userpics && ref $userpics );
+
+    my @userpic_array;
+    my @nokw_array;
+    foreach my $userpic ( @$userpics ) {
+        my @keywords = $userpic->keywords( raw => 1 );
+        foreach my $keyword ( @keywords ) {
+            if ( $keyword ) {
+                push @userpic_array, { keyword => $keyword, userpic => $userpic };
+            } else {
+                $keyword = $userpic->keywords;
+                push @nokw_array, { keyword => $keyword, userpic => $userpic };
+            }
+        } 
+    }
+        
+    @userpic_array = sort { $a->{keyword} cmp $b->{keyword} } @userpic_array;
+    push @userpic_array, sort { $a->{keyword} cmp $b->{keyword} } @nokw_array;
+
+    return @userpic_array;
+}
+
 ####
 # error classes:
 
diff -r 079aa6f23dae -r a8d460b97735 htdocs/allpics.bml
--- a/htdocs/allpics.bml	Wed Apr 28 23:47:49 2010 -0700
+++ b/htdocs/allpics.bml	Thu Apr 29 00:14:59 2010 -0700
@@ -15,7 +15,6 @@ _c?>
 <?_code
 {
 # This page displays all of the user's userpics along with all user-contributed text
-# FIXME: A lot more of this functionality should be coming from the Userpic.pm class
 
     use strict;
     use vars qw(%GET $title $body $head);
@@ -106,41 +105,36 @@ _c?>
 
     #### show pictures
 
-    my $info = LJ::get_userpic_info( $u, { load_comments => 1, load_urls => 1, load_descriptions => 1 } );
-
-    my %keywords = ();
-    while (my ($kw, $pic) = each %{$info->{'kw'}}) {
-        LJ::text_out( BML::ebml( \$kw ) );
-        push @{$keywords{$pic->{'picid'}}}, $kw;
+    my $keyword_sort =  $GET{'keywordSort'};
+    if ( $keyword_sort ) {
+        $getextra = $getextra ? $getextra . "&keywordSort=1" : "?keywordSort=1";
     }
 
-    my %comments = ();
-    while (my ($pic, $comment) = each %{$info->{'comment'}}) {
-        LJ::text_out( BML::ebml( \$comment ) );
-        $comments{$pic} = $comment;
-    }
-
-    my %descriptions = ();
-    while ( my ($pic, $description) = each %{$info->{description}}) {
-        LJ::text_out( BML::ebml( \$description ) );
-        $descriptions{$pic} = $description;
-    }
-    
     my $piccount = 0;
-
-    my @allpics;
-    my $defaultpicid = $u ? $u->{'defaultpicid'} : undef;
 
     # allow support to view inactive userpics for debugging
     my $view_inactive = $GET{inactive} && $remote &&
                         ( $remote->has_priv( "supportviewscreened" ) || $remote->has_priv( "supporthelp" ) );
     $view_inactive ||= $can_manage;
 
-    push @allpics, $info->{'pic'}->{$u->{'defaultpicid'}} if $defaultpicid;
-    push @allpics, map { $info->{'pic'}->{$_} } sort { $a <=> $b }
-                grep { $_ != $defaultpicid && ($info->{'pic'}->{$_}->{'state'} eq 'N' || $view_inactive) &&
-                           $info->{'pic'}->{$_}->{'state'} ne 'X' }
-                keys %{$info->{'pic'}};
+    my @allpics = LJ::Userpic->load_user_userpics($u);
+    if ( @allpics ) {
+        if ( $keyword_sort ) {
+            @allpics = LJ::Userpic->separate_keywords( \@allpics );
+        } else {
+            my @newpics;
+            my $default_pic;
+            foreach my $pic ( @allpics ) {
+                my $keyword = $pic->keywords;
+                if ( $pic->is_default ) {
+                    $default_pic = { keyword => $keyword, userpic => $pic };
+                } elsif ( $pic->state eq 'N' || ( $view_inactive && $pic->state ne 'X' ) ) {
+                    push @newpics, { keyword => $keyword, userpic => $pic };
+                }
+            }
+            @allpics = ( $default_pic, @newpics );
+        }
+    }
 
     # pagination code
 
@@ -166,11 +160,14 @@ _c?>
         @pics = @allpics;
     }
 
-    foreach my $pic (@pics) {
+    foreach my $userpic_hash ( @allpics ) {
 
-        #  load the userpic object for each pic
-        my $userpic = LJ::Userpic->new( $u, $pic->{picid} );
+        # if we have no default pic
+        next unless $userpic_hash;
 
+        my $userpic = $userpic_hash->{userpic};
+        my $keywords = $userpic_hash->{keyword};
+        
         if ($piccount++ == 0) {
             $body .= "<?h1 $ML{'.current'} h1?><?p ";
             if ($can_manage) {
@@ -184,76 +181,84 @@ _c?>
             } else {
                 $body .= BML::ml('.pics2', {'user' => LJ::ljuser($u), 'aopts' => "href='$LJ::SITEROOT/manage/subscriptions/user?journal=$u->{user}'"});
             }
-
+            
+            # show sort options
+            $body .= "<p>";
+            $body .= $keyword_sort ? '<a href = "' . $u->allpics_base . '">' . "$ML{'.sort.default'}</a>" : "$ML{'.sort.default'}";
+            $body .=" | ";
+            $body .= $keyword_sort ? "$ML{'.sort.keyword'}" : '<a href = "' . $u->allpics_base . '?keywordSort=1">' . "$ML{'.sort.keyword'}</a>";
+            $body .= "</p>\n";
+            
             $body .= " p?><table class='allpics'>";
         }
-
+        
         ### Keywords
-        my $eh_keywords = join(", ", sort { lc($a) cmp lc($b) } @{$keywords{$pic->{'picid'}}||[]});
-        $eh_keywords = LJ::ehtml($eh_keywords);
-
+        my $eh_keywords = LJ::ehtml( $keywords );
+        
         if ($piccount % 2 == 1) {
             $body .= "<tr>";
         }
-
+        
         $body .= "<td class='pic'>";
-
-        my ($apre, $apost);
-        if ($pic->{'url'}) {
-            $apre = "<a href='" . LJ::ehtml($pic->{'url'}) . "'>";
+        
+        my ( $apre, $apost );
+        if ( $userpic->url ) {
+            $apre = "<a href='" . LJ::ehtml( $userpic->url ) . "'>";
             $apost = "</a>";
         }
-
+        
         # for each image, we know the picid, get the html imgtag
         $body .= "$apre" . $userpic->imgtag . $apost . "</td><td class='desc'>";
-
-        if ($u->{'defaultpicid'} == $pic->{'picid'}) {
+        
+        if ( $userpic->is_default ) {
             $body .= "$ML{'.default'}<br />";
         }
-
-        if ($view_inactive && $pic->{'state'} eq 'I') {
+        
+        if ($view_inactive && $userpic->state eq 'I') {
             $body .= "<em>[$ML{'userpic.inactive'}]</em>&nbsp;" . LJ::help_icon('userpic_inactive') . "<br />";
         }
-
-        if ($eh_keywords) {
+        
+        if ( $eh_keywords ) {
             $body .= "<strong>$ML{'.keywords'}</strong> $eh_keywords<br />";
         }
-
+        
         # Comments
-        my $eh_comments = $comments{$pic->{'picid'}};
-        if ($eh_comments) {
+        my $eh_comments = $userpic->comment;
+        if ( $eh_comments ) {
+            LJ::text_out( BML::ebml( \$eh_comments ) );
             LJ::CleanHTML::clean(\$eh_comments, {
                 'wordlength' => 40,
                 'addbreaks' => 0,
                 'tablecheck' => 1,
                 'mode' => 'deny',
             });
-
+            
             $body .= "<strong>$ML{'.comment'}</strong> $eh_comments<br />\n";
         }
         
-        # Descriptions
-        my $eh_descriptions = $descriptions{$pic->{picid}};
-        if ($eh_descriptions) {
-            LJ::CleanHTML::clean(\$eh_descriptions, {
+        # Description
+        my $eh_description = $userpic->description;
+        if ($eh_description) {
+            LJ::text_out( BML::ebml( \$eh_description ) );
+            LJ::CleanHTML::clean(\$eh_description, {
                 wordlength => 40,
                 addbreaks => 0,
                 tablecheck => 1,
                 mode => 'deny',
             });
             
-            $body .= "<strong>$ML{'.description'}</strong> $eh_descriptions\n";
+            $body .= "<strong>$ML{'.description'}</strong> $eh_description\n";
         }
-
+        
         $body .= "</td>";
-
+        
         if ($piccount % 2 == 1) {
             $body .= "<td class='blank'></td>";
         } else {
             $body .= "</tr>\n";
         } 
     }
-
+    
     if ($piccount) {
         if ($piccount % 2 == 1) {
             # finish off this row.
@@ -274,7 +279,7 @@ _c?>
             $body = "<?h1 $ML{'.nopics.title'} h1?><?p ". BML::ml('.nopics.text.other2', {'aopts' => "href='$LJ::SITEROOT/manage/subscriptions/user?journal=$u->{user}'", username => $u->ljuser_display }). " p?>";
         }
     }
- 
+    
     return;
 }
 _code?><?page
diff -r 079aa6f23dae -r a8d460b97735 htdocs/allpics.bml.text
--- a/htdocs/allpics.bml.text	Wed Apr 28 23:47:49 2010 -0700
+++ b/htdocs/allpics.bml.text	Thu Apr 29 00:14:59 2010 -0700
@@ -29,5 +29,9 @@
 
 .pics.owner=Here are the userpics for [[user]].
 
+.sort.default=Upload Order
+
+.sort.keyword=Keyword Order
+
 .title=User Pictures
 
diff -r 079aa6f23dae -r a8d460b97735 htdocs/editicons.bml
--- a/htdocs/editicons.bml	Wed Apr 28 23:47:49 2010 -0700
+++ b/htdocs/editicons.bml	Thu Apr 29 00:14:59 2010 -0700
@@ -61,6 +61,12 @@ use strict;
 
     # extra arguments for get requests
     my $getextra = $authas ne $remote->{'user'} ? "?authas=$authas" : '';
+
+    # see if we're sorting by default order or by keyword
+    my $keyword_sort =  $GET{'keywordSort'};
+    if ( $keyword_sort ) {
+        $getextra = $getextra ? $getextra . "&keywordSort=1" : "?keywordSort=1";
+    }
 
     my $returl = LJ::CleanHTML::canonical_url($POST{'ret'});
     my $picurl = LJ::CleanHTML::canonical_url($POST{'urlpic'});
@@ -491,12 +497,24 @@ use strict;
                 $body .= "<p>".BML::ml('cprod.editpics.text7.v1',{ "num" => $max })."</p>";
             }
         }
+
+        # show sort options
+        $body .= "<p>";
+        $body .= $keyword_sort ? '<a href = "/editicons">' . "$ML{'.sort.default'}</a>" : "$ML{'.sort.default'}";
+        $body .=" | ";
+        $body .= $keyword_sort ? "$ML{'.sort.keyword'}" : '<a href = "/editicons?keywordSort=1">' . "$ML{'.sort.keyword'}</a>";
+        $body .= "</p>\n";
+
         $body .= "</div>";
 
         $body .= "<div id='list_userpics' style='width: 98%; float: left;'>";
         my $display_rename = LJ::theschwartz() ? 1 : 0;
 
-        foreach my $pic (@userpics) {
+        if ( $keyword_sort ) {
+            @userpics = LJ::Userpic->sort( \@userpics );
+        }
+        
+        foreach my $pic ( @userpics ) {
             my $pid = $pic->id;
             
             $body .= "<div class='pkg userpic_wrapper'>";
diff -r 079aa6f23dae -r a8d460b97735 htdocs/editicons.bml.text
--- a/htdocs/editicons.bml.text	Wed Apr 28 23:47:49 2010 -0700
+++ b/htdocs/editicons.bml.text	Thu Apr 29 00:14:59 2010 -0700
@@ -124,6 +124,10 @@
 
 .restriction.keywords.faq=Picture will be unusable if <a [[aopts]]>keywords</a> are not supplied
 
+.sort.default=Upload Order
+
+.sort.keyword=Keyword Order
+
 .title=Edit Userpics
 
 .title3|notes=This is the new page title.  .title is used by multisearch.bml.
--------------------------------------------------------------------------------

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