Add revamped 'Site and Journal Search' feature.

        Patch by mark. 

diffs (672 lines):

diff -r eca92edcffa8 -r 111ac3e6b53c bin/upgrading/en.dat
--- a/bin/upgrading/en.dat      Sun Oct 11 17:12:18 2009 +0000
+++ b/bin/upgrading/en.dat      Tue Oct 13 05:24:02 2009 +0000
@@ -2209,11 +2209,11 @@ menunav.explore.interests=Interests
 
 menunav.explore.directorysearch=Directory Search
 
-menunav.explore.journalsearch=Journal Search
-
 menunav.explore.randomjournal=Random Journal
 
 menunav.explore.randomcommunity=Random Community
+
+menunav.explore.sitesearch=Site and Journal Search
 
 menunav.explore.faq=FAQ
 
@@ -2449,6 +2449,14 @@ setting.adultcontent.option.self=This jo
 
 setting.adultcontentreason.label=Adult Content Reason
 
+setting.allowsearchby.label=Allow Search By
+
+setting.allowsearchby.sel.a=Allow everybody to search my journal
+
+setting.allowsearchby.sel.f=Allow people on my access list to search my 
journal (default)
+
+setting.allowsearchby.sel.n=Allow nobody else (only me) to search my journal
+
 setting.bio.desc|notes=There shouldn't be a description unless the page 
specifies one
 setting.bio.desc=_none
 
@@ -2682,6 +2690,12 @@ setting.gender.error.wrongtype=Only pers
 setting.gender.error.wrongtype=Only personal or identity journals can have a 
gender.
 
 setting.gender.question=What's your gender?
+
+setting.globalsearch.label=Global Search Inclusion
+
+setting.globalsearch.sel.no=No, do not allow my content to be displayed
+
+setting.globalsearch.sel.yes=Yes, display my public content in search results 
(default)
 
 setting.graphicpreviews.label=Graphic Previews
 
diff -r eca92edcffa8 -r 111ac3e6b53c bin/upgrading/proplists.dat
--- a/bin/upgrading/proplists.dat       Sun Oct 11 17:12:18 2009 +0000
+++ b/bin/upgrading/proplists.dat       Tue Oct 13 05:24:02 2009 +0000
@@ -518,6 +518,14 @@ userproplist.no_mail_alias:
   multihomed: 0
   prettyname: Disable site e-mail alias
 
+userproplist.opt_allowsearchby:
+  cldversion: 4
+  datatype: char
+  des: A = everybody, F = access list only, N = nobody (me only).
+  indexed: 0
+  multihomed: 0
+  prettyname: Allow Search By
+
 userproplist.opt_bdaymail:
   cldversion: 0
   datatype: bool
@@ -525,6 +533,14 @@ userproplist.opt_bdaymail:
   indexed: 1
   multihomed: 0
   prettyname: Get Birthday Reminders
+
+userproplist.opt_blockglobalsearch:
+  cldversion: 4
+  datatype: char
+  des: Y: don't allow global search, N: do allow
+  indexed: 0
+  multihomed: 0
+  prettyname: Block Global Search of Content
 
 userproplist.opt_blockrobots:
   cldversion: 4
diff -r eca92edcffa8 -r 111ac3e6b53c bin/worker/sphinx-copier
--- a/bin/worker/sphinx-copier  Sun Oct 11 17:12:18 2009 +0000
+++ b/bin/worker/sphinx-copier  Tue Oct 13 05:24:02 2009 +0000
@@ -103,9 +103,9 @@ sub work {
     # already has most of their posts copied and they are just updating one or 
two.
     foreach my $jitemid ( @copy_jitemids ) {
         my $row = $dbfrom->selectrow_hashref(
-            qq{SELECT l.journalid, l.jitemid, l.posterid, l.security, 
l.eventtime, lt.subject, lt.event
-               FROM log2 l INNER JOIN logtext2 lt ON (l.journalid = 
lt.journalid AND l.jitemid = lt.jitemid)
-               WHERE l.journalid = ? AND l.jitemid = ?},
+            q{SELECT l.journalid, l.jitemid, l.posterid, l.security, 
l.allowmask, l.eventtime, lt.subject, lt.event
+              FROM log2 l INNER JOIN logtext2 lt ON (l.journalid = 
lt.journalid AND l.jitemid = lt.jitemid)
+              WHERE l.journalid = ? AND l.jitemid = ?},
             undef, $u->id, $jitemid
         );
         die $dbfrom->errstr if $dbfrom->err;
@@ -113,17 +113,25 @@ sub work {
         # have to do some more munging
         $row->{is_public} = $row->{security} eq 'public' ? 1 : 0;
         $row->{edittime} = $db_times->{$jitemid};
+        $row->{allowmask} = join ',', LJ::bit_breakdown( $row->{allowmask} );
+        $row->{allowpublic} = $u->include_in_global_search ? 1 : 0;
 
         # very important, the search engine can't index compressed crap...
         foreach ( qw/ subject event / ) {
             LJ::text_uncompress( \$row->{$_} );
+
+            # required, we store raw-bytes in our own database but the Sphinx 
system expects
+            # things to be proper UTF-8, this does it.
             $row->{$_} = Encode::decode( 'utf8', $row->{$_} );
         }
 
         # insert
-        $dbto->do( 'REPLACE INTO posts_raw (id, journal_id, jitemid, 
poster_id, is_public, date_posted, title, data, revtime) ' .
-                   'VALUES (NULL, ?, ?, ?, ?, UNIX_TIMESTAMP(?), ?, ?, ?)',
-                   undef, map { $row->{$_} } qw/ journalid jitemid posterid 
is_public eventtime subject event edittime /, );
+        $dbto->do(
+            q{REPLACE INTO posts_raw (id, journal_id, jitemid, poster_id, 
is_public, security_bits, allow_global_search, date_posted,
+                                      title, data, revtime)
+              VALUES (NULL, ?, ?, ?, ?, ?, ?, UNIX_TIMESTAMP(?), ?, ?, ?)},
+            undef, map { $row->{$_} } qw/ journalid jitemid posterid is_public 
allowmask allowpublic eventtime subject event edittime /
+        );
         die $dbto->errstr if $dbto->err;
 
         # let the viewer know what they missed
diff -r eca92edcffa8 -r 111ac3e6b53c bin/worker/sphinx-search-gm
--- a/bin/worker/sphinx-search-gm       Sun Oct 11 17:12:18 2009 +0000
+++ b/bin/worker/sphinx-search-gm       Tue Oct 13 05:24:02 2009 +0000
@@ -32,8 +32,10 @@ sub sphinx_search {
     my $job = $_[0];
 
     my $args = Storable::thaw( $job->arg ) || {};
-    return undef
-        unless $args->{userid} && $args->{query};
+    return undef unless $args->{query};
+
+    # a little sanity check, we don't currently allow a global search on 
non-public content
+    return undef if $args->{userid} == 0 && $args->{public} != 1;
 
     my $sx = Sphinx::Search->new();
     $sx->SetServer( @LJ::SPHINX_SEARCHD );
@@ -41,9 +43,15 @@ sub sphinx_search {
     $sx->SetMatchMode( SPH_MATCH_ALL )
        ->SetSortMode( SPH_SORT_RELEVANCE )
        ->SetMaxQueryTime( 15_000 )
-       ->SetFilter( 'journal_id', [ $args->{userid} ] )
        ->SetLimits( $args->{offset} || 0, 20 );
+    
+    # filter to a journal if we have a userid set; else, filter on 
allow_global_search items
+    $sx->SetFilter( 'journal_id', [ $args->{userid} ] )
+        if $args->{userid};
+    $sx->SetFilter( 'allow_global_search', [ 1 ] )
+        unless $args->{userid};
 
+    # further filter on public items or not
     $sx->SetFilter( 'is_public', [ 1 ] )
         if $args->{public};
 
diff -r eca92edcffa8 -r 111ac3e6b53c cgi-bin/DW/Logic/MenuNav.pm
--- a/cgi-bin/DW/Logic/MenuNav.pm       Sun Oct 11 17:12:18 2009 +0000
+++ b/cgi-bin/DW/Logic/MenuNav.pm       Tue Oct 13 05:24:02 2009 +0000
@@ -201,8 +201,8 @@ sub get_menu_navigation {
                 },
                 {
                     url => "$LJ::SITEROOT/search",
-                    text => "menunav.explore.journalsearch",
-                    display => $loggedin_ispaid && @LJ::SPHINX_SEARCHD ? 1 : 0,
+                    text => "menunav.explore.sitesearch",
+                    display => @LJ::SPHINX_SEARCHD ? 1 : 0,
                 },
                 {
                     url => "$LJ::SITEROOT/random",
diff -r eca92edcffa8 -r 111ac3e6b53c cgi-bin/DW/Logic/UserLinkBar.pm
--- a/cgi-bin/DW/Logic/UserLinkBar.pm   Sun Oct 11 17:12:18 2009 +0000
+++ b/cgi-bin/DW/Logic/UserLinkBar.pm   Tue Oct 13 05:24:02 2009 +0000
@@ -463,16 +463,13 @@ sub search {
 
     my $u = $self->{u};
     my $remote = $self->{remote};
-    my $user = $u->user;
 
     # don't show if search is disabled
     return undef unless
-        @LJ::SPHINX_SEARCHD &&
-        ( $u->is_community || ( $u->equals( $remote ) ) ) &&
-        $u->is_paid;
+        @LJ::SPHINX_SEARCHD && $u->allow_search_by( $remote );
 
     my $link = {
-        url => 'search' . ( $u->is_community ? "?search_user=" . $u->user : '' 
),
+        url => 'search?user=' . $u->user,
         image => 'search.png',
         text_ml => "userlinkbar.search",
         title_ml => "userlinkbar.search.title",
diff -r eca92edcffa8 -r 111ac3e6b53c cgi-bin/DW/Setting/AllowSearchBy.pm
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/cgi-bin/DW/Setting/AllowSearchBy.pm       Tue Oct 13 05:24:02 2009 +0000
@@ -0,0 +1,69 @@
+#!/usr/bin/perl
+#
+# DW::Setting::AllowSearchBy
+#
+# Module to set the opt_allowsearchby setting.
+#
+# Authors:
+#      Mark Smith <[email protected]>
+#
+# Copyright (c) 2009 by Dreamwidth Studios, LLC.
+#
+# This program is free software; you may redistribute it and/or modify it under
+# the same terms as Perl itself.  For a copy of the license, please reference
+# 'perldoc perlartistic' or 'perldoc perlgpl'.
+#
+
+package DW::Setting::AllowSearchBy;
+use base 'LJ::Setting';
+use strict;
+use warnings;
+
+sub should_render {
+    return $_[1] && $_[1]->is_person;
+}
+
+sub label {
+    return $_[0]->ml( 'setting.allowsearchby.label' );
+}
+
+sub option {
+    my ( $class, $u, $errs, $args ) = @_;
+    my $key = $class->pkgkey;
+
+    # the selection values are opposite the text, since the property is an
+    # opt-out property, it basically negates what we're trying to display to
+    # the user ... yes, it's confusing, sorry
+    my $sel = $class->get_arg( $args, "allowsearchby" ) ||  $u->prop( 
'opt_allowsearchby' ) || 'F';
+
+    my $ret .= LJ::html_select(
+        {
+            id       => "${key}allowsearchby",
+            name     => "${key}allowsearchby",
+            selected => $sel,
+        },
+
+        'A' => $class->ml( 'setting.allowsearchby.sel.a' ),
+        'F' => $class->ml( 'setting.allowsearchby.sel.f' ),
+        'N' => $class->ml( 'setting.allowsearchby.sel.n' ),
+    );
+
+    my $errdiv = $class->errdiv( $errs, 'allowsearchby' );
+    $ret .= "<br />$errdiv" if $errdiv;
+
+    return $ret;
+}
+
+sub save {
+    my ( $class, $u, $args ) = @_;
+
+    # must be defined and Y or N
+    my $val = $class->get_arg( $args, 'allowsearchby' ) || 0;
+    return unless defined $val && $val =~ /^[AFN]$/;
+
+    $u->set_prop( opt_allowsearchby => $val );
+
+    return 1;
+}
+
+1;
diff -r eca92edcffa8 -r 111ac3e6b53c cgi-bin/DW/Setting/GlobalSearch.pm
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/cgi-bin/DW/Setting/GlobalSearch.pm        Tue Oct 13 05:24:02 2009 +0000
@@ -0,0 +1,70 @@
+#!/usr/bin/perl
+#
+# DW::Setting::GlobalSearch
+#
+# Module to set the opt_blockglobalsearch setting.
+#
+# Authors:
+#      Mark Smith <[email protected]>
+#
+# Copyright (c) 2009 by Dreamwidth Studios, LLC.
+#
+# This program is free software; you may redistribute it and/or modify it under
+# the same terms as Perl itself.  For a copy of the license, please reference
+# 'perldoc perlartistic' or 'perldoc perlgpl'.
+#
+
+package DW::Setting::GlobalSearch;
+use base 'LJ::Setting';
+use strict;
+use warnings;
+
+sub should_render {
+    return $_[1] && ( $_[1]->is_person || $_[1]->is_community );
+}
+
+sub label {
+    return $_[0]->ml( 'setting.globalsearch.label' );
+}
+
+sub option {
+    my ( $class, $u, $errs, $args ) = @_;
+    my $key = $class->pkgkey;
+
+    # the selection values are opposite the text, since the property is an
+    # opt-out property, it basically negates what we're trying to display to
+    # the user ... yes, it's confusing, sorry
+    my $sel = $class->get_arg( $args, "globalsearch" );
+    $sel = $u->include_in_global_search ? 'N' : 'Y'
+        unless defined $sel && length $sel;
+
+    my $ret .= LJ::html_select(
+        {
+            id       => "${key}globalsearch",
+            name     => "${key}globalsearch",
+            selected => $sel,
+        },
+
+        'N' => $class->ml( 'setting.globalsearch.sel.yes' ),
+        'Y' => $class->ml( 'setting.globalsearch.sel.no' ),
+    );
+
+    my $errdiv = $class->errdiv( $errs, 'globalsearch' );
+    $ret .= "<br />$errdiv" if $errdiv;
+
+    return $ret;
+}
+
+sub save {
+    my ( $class, $u, $args ) = @_;
+
+    # must be defined and Y or N
+    my $val = $class->get_arg( $args, 'globalsearch' ) || 0;
+    return unless defined $val && $val =~ /^[YN]$/;
+
+    $u->set_prop( opt_blockglobalsearch => $val );
+
+    return 1;
+}
+
+1;
diff -r eca92edcffa8 -r 111ac3e6b53c cgi-bin/LJ/User.pm
--- a/cgi-bin/LJ/User.pm        Sun Oct 11 17:12:18 2009 +0000
+++ b/cgi-bin/LJ/User.pm        Tue Oct 13 05:24:02 2009 +0000
@@ -1736,6 +1736,31 @@ sub add_to_class {
 }
 
 
+# 1/0 whether the argument is allowed to search this journal
+sub allow_search_by {
+    my ( $u, $by ) = @_;
+    return 0 unless LJ::isu( $u ) && LJ::isu( $by );
+
+    # the person doing the search has to be an individual
+    return 0 unless $by->is_individual;
+
+    # someone in the equation has to be a paid account
+    return 0 unless $u->is_paid || $by->is_paid;
+
+    # allow searches if this is a community or it's us
+    return 1 if $u->is_community || $u->equals( $by );
+
+    # check the userprop for security access
+    my $whocan = $u->prop( 'opt_allowsearchby' );
+    return 1 if $whocan eq 'A';
+    return 1 if $whocan eq 'F' && $u->trusts( $by );
+    return 1 if $whocan eq 'N' && $u->equals( $by );
+
+    # failing the above, sorry, no search for you
+    return 0;
+}
+
+
 sub caps {
     my $u = shift;
     return $u->{caps};
@@ -1905,6 +1930,27 @@ sub in_class {
 sub in_class {
     my ($u, $class) = @_;
     return LJ::caps_in_group($u->{caps}, $class);
+}
+
+
+# 1/0; whether or not this account should be included in the global search
+# system.  this is used by the bin/worker/sphinx-copier mostly.
+sub include_in_global_search {
+    my $u = $_[0];
+
+    # only P/C accounts should be globally searched
+    return 0 unless $u->is_person || $u->is_community;
+
+    # default) check opt_blockglobalsearch and use that if it's defined
+    my $bgs = $u->prop( 'opt_blockglobalsearch' );
+    return $bgs eq 'Y' ? 0 : 1 if defined $bgs && length $bgs;
+
+    # fallback) use their robot blocking value if it's set
+    my $br = $u->prop( 'opt_blockrobots' );
+    return $br ? 0 : 1 if defined $br && length $br;
+
+    # allow search of this user's content
+    return 1;
 }
 
 
diff -r eca92edcffa8 -r 111ac3e6b53c htdocs/manage/settings/index.bml
--- a/htdocs/manage/settings/index.bml  Sun Oct 11 17:12:18 2009 +0000
+++ b/htdocs/manage/settings/index.bml  Tue Oct 13 05:24:02 2009 +0000
@@ -118,6 +118,8 @@ body<=
                 LJ::Setting::Display::BanUsers
                 DW::Setting::ProfileEmail
                 DW::Setting::RandomPaidGifts
+                DW::Setting::GlobalSearch
+                DW::Setting::AllowSearchBy
             )],
         },
         history => {
diff -r eca92edcffa8 -r 111ac3e6b53c htdocs/search.bml
--- a/htdocs/search.bml Sun Oct 11 17:12:18 2009 +0000
+++ b/htdocs/search.bml Tue Oct 13 05:24:02 2009 +0000
@@ -33,126 +33,147 @@ body<=
     my $remote = LJ::get_remote();
     return "<?needlogin?>" unless $remote;
 
-    # okay, now let's make sure the data is valid
-    my $su = LJ::load_user( $GET{search_user} || $remote->user );
-    return "Invalid search user.\n" unless $su;
-    return "You can't search that journal!\n"
-        unless $remote->equals( $su ) ||
-               $su->is_community;
+    # see what search mode...
+    my $su = LJ::load_user( $POST{mode} || $GET{user} );
+    my $q = LJ::strip_html( LJ::trim( $POST{query} ) );
 
-    # and make sure we're searching a paid account
-    return "Sorry, searches are only available for paid accounts.\n"
-        unless $su->is_paid;
+    # helper sub for returning the search form
+    my $search_form = sub {
+        my $ret = "<form method='post' action='$LJ::SITEROOT/search'>" . 
LJ::form_auth();
 
-    # for later
-    my $sulj = $su->ljuser_display;
-    my $suu = $su->user;
+        $ret .= LJ::html_check( { type => 'radio', selected => $su ? 0 : 1, id 
=> 'm-global', name => 'mode',
+                                  value => '', label => 'Site Search (Public 
Entries)' } );
 
-    # if they posted we might have to do something
-    if ( LJ::did_post() ) {
-        return "Failauth.\n" unless LJ::check_form_auth();
-        # and make sure we got a query
-        my $q = LJ::strip_html( LJ::trim( $POST{query} ) );
-        return "Query must be shorter than 255 characters, sorry!\n"
-            if length( $q ) > 255;
-        return "Please enter a search query. <a 
href='$LJ::SITEROOT/search'>Search again.</a>\n"
-            unless $q;
-
-        # if an offset, less than 1000 please
-        my $offset = $GET{offset} + 0;
-        return "Hey, that offset is nonsensical... :(\n"
-            if $offset < 0 || $offset > 1000;
-
-        # set a public only flag if this is not your journal
-        my $public = ( $remote->equals( $su ) || ( $su->is_community && 
$remote->member_of( $su ) ) ) ? 0 : 1;
-
-        # the arguments to the search
-        my $args = { userid => $su->id, query => $q, offset => $offset, public 
=> $public };
-        my $arg = Storable::nfreeze( $args );
-
-        # so we know that they're searching something valid, send to gearman
-        my $result;
-        my $task = Gearman::Task->new(
-            'sphinx_search', \$arg,
-            {
-                uniq => '-',
-                on_complete => sub {
-                    my $res = $_[0] or return undef;
-                    $result = Storable::thaw( $$res );
-                },
-            }
-        );
-
-        # setup the task set for gearman... really, isn't there a way to make 
this
-        # simpler?  oh well
-        my $ts = $gc->new_task_set();
-        $ts->add_task( $task );
-        $ts->wait( timeout => 20 );
-
-        # if we didn't get a result...
-        return "Sorry, we were unable to find a result in the time allotted.  
This may mean that ".
-               "the server is busy or down.  Please try your query again 
later.\n"
-            unless $result;
-
-        # if we didn't get any matches...
-        return "Sorry, we didn't find any matches for the search 
<strong>$q</strong>.  We looked for $result->{time} seconds, too!\n"
-            if $result->{total} <= 0;
-
-        # now we can process the results and do something fascinating!
-        my $matches = '';
-        foreach my $match ( @{ $result->{matches} } ) {
-            my $icon = {
-                    public => '',
-                    private => "<img 
src='$LJ::IMGPREFIX/silk/entry/private.png'>",
-                    usemask => "<img 
src='$LJ::IMGPREFIX/silk/entry/filtered.png'>",
-                    access => "<img 
src='$LJ::IMGPREFIX/silk/entry/locked.png'>",
-                }->{$match->{security}};
-
-            my $tags = join( ', ', map { "<strong>" . $match->{tags}->{$_} . 
"</strong>" } keys %{ $match->{tags} } );
-            $tags = "<br />Tags: $tags"
-                if $tags;
-
-            my $html = qq(<div class='searchres'>$icon <a 
href="$match->{url}">$match->{subject}</a><br />
-                          <span class='exc'>$match->{excerpt}</span>$tags<br 
/>Posted: <strong>$match->{eventtime}</strong><br /><br />
-                          </div>);
-            $matches .= $html;
+        my $tu = $su || $remote;
+        if ( $tu->allow_search_by( $remote ) ) {
+            $ret .= LJ::html_check( { type => 'radio', selected => $su ? 1 : 
0, id => 'm-user', name => 'mode',
+                                      value => $tu->user, label => "Journal 
Search: <strong>" . $tu->user . "</strong>", noescape => 1 } );
         }
 
-        # build the rest of the search page
-        my $ret = "<?p You have searched $sulj... p?>" . $matches;
-
-        # put some stats on the output
-        my $matchct = scalar( @{ $result->{matches} } );
-        my $skip = $offset > 0 ? " (skipped $offset)" : "";
-        $ret .= qq(<span class="stats">$matchct results displayed out of 
$result->{total} hits total$skip for <strong>$q</strong>.
-                   $result->{time} seconds.</span>);
-
-        if ( $result->{total} > ( $offset + $matchct ) ) {
-            my $offsetm = $offset + $matchct;
-            my $fa = LJ::form_auth();
-            $ret .= qq(<form method="post" 
action="$LJ::SITEROOT/search?search_user=$suu&offset=$offsetm">$fa
-                       <input type="hidden" name="query" value="$q"> <input 
type="submit" value="More Results..." />
-                       </form>);
-        }
+        $ret .= '<br /><input type="text" name="query" maxlength="255" 
size="60" value="' . LJ::ehtml( $q ) .
+                '"> <input type="submit" value="Search" /></form>';
 
         return $ret;
+    };
+
+    # an error redisplays the form, with an error message
+    my $error = sub {
+        return $search_form->() . "<br /><?p <strong>Error:</strong> $_[0] 
p?>";
+    };
+
+    # if no $su, then this is a public search, that's allowed.  but if it's a 
user,
+    # ensure that it's an account that we CAN search
+    return $error->( "You can't search that journal." )
+        if $su && ! $su->allow_search_by( $remote );
+
+    
################################################################################
+    
################################################################################
+
+    # give them the form to do a search if they haven't actually posted to us 
yet
+    unless ( LJ::did_post() ) {
+        # give them a form to enter their search options
+        return "<?p $LJ::SITENAME content search.  Please select where to 
search and enter your search terms. p?>" .
+               $search_form->() .
+               '</form><br /> <?p To control who can search your journal, and 
whether or not your public ' .
+               'entries appear in global search results, please adjust your <a 
href="' . $LJ::SITEROOT .
+               '/manage/settings/?cat=privacy">account privacy settings</a>. 
p?>';
     }
 
-    # give them a form to search...
-    my $fa = LJ::form_auth();
-    return <<EOF;
+    
################################################################################
+    
################################################################################
 
-<?p Searching $sulj ... p?>
+    # at this point, they have done a POST, which means they want to search 
something
+    return $error->( "Failauth." ) unless LJ::check_form_auth();
 
-<form method="post" action="$LJ::SITEROOT/search?search_user=$suu">
-$fa
-<input type="text" name="query" maxlength="255" size="60"> <input 
type="submit" value="Search!" />
-</form>
+    # and make sure we got a query
+    return $error->( "Query must be shorter than 255 characters, sorry!" )
+        if length( $q ) > 255;
+    return $error->( "Please enter a search query." )
+        unless $q;
 
-<?p This is a beta search.  Things may be slow or broken while we iron out the 
kinks
-in the system.  If you run into trouble, let us know! p?>
+    # if an offset, less than 1000 please
+    my $offset = $GET{offset} + 0;
+    return $error->( "Hey, that offset is nonsensical... :(" )
+        if $offset < 0 || $offset > 1000;
 
-EOF
+    # set a public only flag if this is not your journal or a community you 
are in
+    my $public = 1;
+    $public = 0 if $su && ( $remote->equals( $su ) || ( $su->is_community && 
$remote->member_of( $su ) ) );
+
+    # the arguments to the search (userid=0 implies global search)
+    my $args = { userid => $su ? $su->id : 0, query => $q, offset => $offset, 
public => $public };
+    my $arg = Storable::nfreeze( $args );
+
+    # so we know that they're searching something valid, send to gearman
+    my $result;
+    my $task = Gearman::Task->new(
+        'sphinx_search', \$arg,
+        {
+            uniq => '-',
+            on_complete => sub {
+                my $res = $_[0] or return undef;
+                $result = Storable::thaw( $$res );
+            },
+        }
+    );
+
+    # setup the task set for gearman... really, isn't there a way to make this
+    # simpler?  oh well
+    my $ts = $gc->new_task_set();
+    $ts->add_task( $task );
+    $ts->wait( timeout => 20 );
+
+    # if we didn't get a result...
+    return $error->( "Sorry, we were unable to find a result in the time 
allotted.  This may mean that ".
+                     "the server is busy or down.  Please try your query again 
later." )
+        unless $result;
+
+    # if we didn't get any matches...
+    return $error->( "Sorry, we didn't find any matches for the search 
<strong>$q</strong>.  We looked for $result->{time} seconds, too!" )
+        if $result->{total} <= 0;
+
+    # now we can process the results and do something fascinating!
+    my $matches = '<br /><br />';
+    foreach my $match ( @{ $result->{matches} } ) {
+        my $mu = LJ::load_userid( $match->{journal_id} );
+
+        my $icon = {
+                public => '',
+                private => "<img src='$LJ::IMGPREFIX/silk/entry/private.png'>",
+                usemask => "<img 
src='$LJ::IMGPREFIX/silk/entry/filtered.png'>",
+                access => "<img src='$LJ::IMGPREFIX/silk/entry/locked.png'>",
+            }->{$match->{security}};
+
+        my $tags = join( ', ', map { "<strong>" . $match->{tags}->{$_} . 
"</strong>" } keys %{ $match->{tags} } );
+        $tags = "<br />Tags: $tags"
+            if $tags;
+
+        my $mulj = $mu->ljuser_display;
+        my $html = qq(<div class='searchres'>$mulj: $icon <a 
href="$match->{url}">$match->{subject}</a><br />
+                      <span class='exc'>$match->{excerpt}</span>$tags<br 
/>Posted: <strong>$match->{eventtime}</strong><br /><br />
+                      </div>);
+        $matches .= $html;
+    }
+
+    # build the rest of the search page
+    my $ret = $search_form->() . $matches;
+
+    # put some stats on the output
+    my $matchct = scalar( @{ $result->{matches} } );
+    my $skip = $offset > 0 ? " (skipped $offset)" : "";
+    $ret .= qq(<span class="stats">$matchct results displayed out of 
$result->{total} hits total$skip for <strong>$q</strong>.
+               $result->{time} seconds.</span>);
+
+    if ( $result->{total} > ( $offset + $matchct ) ) {
+        my $offsetm = $offset + $matchct;
+        $ret .= "<form method='post' 
action='$LJ::SITEROOT/search?offset=$offsetm'>" . LJ::form_auth() .
+                "<input type='hidden' name='query' value='" . LJ::ehtml( $q ) 
. "'>" .
+                "<input type='hidden' name='mode' value='" . ( $su ? $su->user 
: '' ) . "'>" .
+                "<input type='submit' value='More Results...' />" .
+                "</form>";
+    }
+
+    return $ret;
 }
 _code?>
 <=body
_______________________________________________
dw-changelog mailing list
[email protected]
http://lists.dwscoalition.org/mailman/listinfo/dw-changelog

Reply via email to