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.
--------------------------------------------------------------------------------
turlough: The Girl & Party Poison (Grace Jeanette & Gerard Way) high-fiveing, on the set of Na Na Na, Sept 2011 ((mcr) yeah!)

[personal profile] turlough 2010-04-29 03:26 pm (UTC)(link)
\o/ \o/ \o/ \o/ \o/
grlnamedlucifer: A red-scarved reveur from The Night Circus ([p&p] can you stand this much awesomenes)

[personal profile] grlnamedlucifer 2010-05-01 01:29 am (UTC)(link)
<3!