Annotation of loncom/interface/lonsearchcat.pm, revision 1.212
1.98 harris41 1: # The LearningOnline Network with CAPA
1.108 harris41 2: # Search Catalog
3: #
1.212 ! matthew 4: # $Id: lonsearchcat.pm,v 1.211 2004/04/19 17:44:47 matthew Exp $
1.108 harris41 5: #
6: # Copyright Michigan State University Board of Trustees
7: #
8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
9: #
10: # LON-CAPA is free software; you can redistribute it and/or modify
11: # it under the terms of the GNU General Public License as published by
12: # the Free Software Foundation; either version 2 of the License, or
13: # (at your option) any later version.
1.98 harris41 14: #
1.108 harris41 15: # LON-CAPA is distributed in the hope that it will be useful,
16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18: # GNU General Public License for more details.
19: #
20: # You should have received a copy of the GNU General Public License
21: # along with LON-CAPA; if not, write to the Free Software
22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23: #
24: # /home/httpd/html/adm/gpl.txt
25: #
26: # http://www.lon-capa.org/
1.1 www 27: #
1.121 matthew 28: ###############################################################################
29: ###############################################################################
30:
31: =pod
32:
33: =head1 NAME
34:
1.140 matthew 35: lonsearchcat - LONCAPA Search Interface
1.121 matthew 36:
37: =head1 SYNOPSIS
38:
39: Search interface to LON-CAPAs digital library
40:
41: =head1 DESCRIPTION
42:
43: This module enables searching for a distributed browseable catalog.
1.104 harris41 44:
1.121 matthew 45: This is part of the LearningOnline Network with CAPA project
46: described at http://www.lon-capa.org.
47:
48: lonsearchcat presents the user with an interface to search the LON-CAPA
49: digital library. lonsearchcat also initiates the execution of a search
50: by sending the search parameters to LON-CAPA servers. The progress of
1.205 www 51: search (on a server basis) is displayed to the user in a separate window.
1.121 matthew 52:
53: =head1 Internals
54:
55: =over 4
56:
57: =cut
58:
59: ###############################################################################
1.98 harris41 60: ###############################################################################
1.121 matthew 61:
1.1 www 62: package Apache::lonsearchcat;
63:
64: use strict;
1.167 www 65: use Apache::Constants qw(:common :http);
1.6 harris41 66: use Apache::lonnet();
67: use Apache::File();
1.7 harris41 68: use CGI qw(:standard);
1.41 harris41 69: use Text::Query;
1.101 harris41 70: use GDBM_File;
1.112 harris41 71: use Apache::loncommon();
1.144 matthew 72: use Apache::lonmysql();
1.200 www 73: use Apache::lonmeta;
74: use Apache::lonhtmlcommon;
1.186 www 75: use Apache::lonlocal;
1.204 matthew 76: use LONCAPA::lonmetadata();
1.212 ! matthew 77: use HTML::Entities();
1.1 www 78:
1.121 matthew 79: ######################################################################
80: ######################################################################
1.196 matthew 81: ##
82: ## Global variables
83: ##
1.121 matthew 84: ######################################################################
85: ######################################################################
1.196 matthew 86: my %groupsearch_db; # Database hash used to save values for the
87: # groupsearch RAT interface.
88: my %persistent_db; # gdbm hash which holds data which is supposed to
89: # persist across calls to lonsearchcat.pm
1.121 matthew 90:
1.200 www 91: # The different view modes and associated functions
92:
93: my %Views = ("detailed" => \&detailed_citation_view,
94: "summary" => \&summary_view,
95: "fielded" => \&fielded_format_view,
96: "xml" => \&xml_sgml_view,
97: "compact" => \&compact_view);
1.101 harris41 98:
1.121 matthew 99: ######################################################################
100: ######################################################################
1.98 harris41 101: sub handler {
102: my $r = shift;
1.197 www 103: # &set_defaults();
1.196 matthew 104: #
105: # set form defaults
1.145 matthew 106: #
1.196 matthew 107: my $hidden_fields;# Hold all the hidden fields used to keep track
108: # of the search system state
109: my $importbutton; # button to take the selected results and go to group
110: # sorting
111: my $diropendb; # The full path to the (temporary) search database file.
112: # This is set and used in &handler() and is also used in
113: # &output_results().
114: my $bodytag; # LON-CAPA standard body tag, gotten from
115: # &Apache::lonnet::bodytag.
116: # No title, no table, just a <body> tag.
1.157 www 117:
118: my $loaderror=&Apache::lonnet::overloaderror($r);
119: if ($loaderror) { return $loaderror; }
120:
1.145 matthew 121: my $closebutton; # button that closes the search window
122: # This button is different for the RAT compared to
123: # normal invocation.
124: #
1.186 www 125: &Apache::loncommon::content_type($r,'text/html');
1.98 harris41 126: $r->send_http_header;
127: return OK if $r->header_only;
1.156 matthew 128: ##
129: ## Prevent caching of the search interface window. Hopefully this means
130: ## we will get the launch=1 passed in a little more.
131: &Apache::loncommon::no_cache($r);
1.145 matthew 132: ##
133: ## Pick up form fields passed in the links.
134: ##
135: &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
1.146 matthew 136: ['catalogmode','launch','acts','mode','form','element','pause',
1.158 matthew 137: 'phase','persistent_db_id','table','start','show',
1.191 albertel 138: 'cleargroupsort','titleelement']);
1.146 matthew 139: ##
140: ## The following is a trick - we wait a few seconds if asked to so
141: ## the daemon running the search can get ahead of the daemon
142: ## printing the results. We only need (theoretically) to do
143: ## this once, so the pause indicator is deleted
144: ##
145: if (exists($ENV{'form.pause'})) {
1.181 matthew 146: sleep(1);
1.146 matthew 147: delete($ENV{'form.pause'});
148: }
1.143 matthew 149: ##
150: ## Initialize global variables
151: ##
1.121 matthew 152: my $domain = $r->dir_config('lonDefDomain');
1.177 albertel 153: $diropendb= "/home/httpd/perl/tmp/$ENV{'user.domain'}_$ENV{'user.name'}_searchcat.db";
1.145 matthew 154: #
155: # set the name of the persistent database
1.146 matthew 156: # $ENV{'form.persistent_db_id'} can only have digits in it.
1.145 matthew 157: if (! exists($ENV{'form.persistent_db_id'}) ||
1.147 matthew 158: ($ENV{'form.persistent_db_id'} =~ /\D/) ||
159: ($ENV{'form.launch'} eq '1')) {
1.145 matthew 160: $ENV{'form.persistent_db_id'} = time;
161: }
1.155 matthew 162: $bodytag=&Apache::loncommon::bodytag(undef,undef,undef,1);
1.146 matthew 163: my $persistent_db_file = "/home/httpd/perl/tmp/".
1.145 matthew 164: &Apache::lonnet::escape($domain).
165: '_'.&Apache::lonnet::escape($ENV{'user.name'}).
166: '_'.$ENV{'form.persistent_db_id'}.'_persistent_search.db';
1.146 matthew 167: ##
1.209 matthew 168: &Apache::lonhtmlcommon::clear_breadcrumbs();
169: if (exists($ENV{'request.course.id'}) && $ENV{'request.course.id'} ne '') {
170: &Apache::lonhtmlcommon::add_breadcrumb
171: ({href=>'/adm/searchcat?'.
172: 'catalogmode='.$ENV{'form.catalogmode'}.
173: '&launch='.$ENV{'form.launch'}.
174: '&mode='.$ENV{'form.mode'},
175: text=>"Course and Catalog Search",
176: bug=>'Searching',});
177: } else {
178: &Apache::lonhtmlcommon::add_breadcrumb
179: ({href=>'/adm/searchcat?'.
180: 'catalogmode='.$ENV{'form.catalogmode'}.
181: '&launch='.$ENV{'form.launch'}.
182: '&mode='.$ENV{'form.mode'},
183: text=>"Catalog Search",
184: bug=>'Searching',});
185: }
186: #
1.152 matthew 187: if (! &get_persistent_form_data($persistent_db_file)) {
1.150 matthew 188: if ($ENV{'form.phase'} =~ /(run_search|results)/) {
189: &Apache::lonnet::logthis("lonsearchcat:Unable to recover data ".
190: "from $persistent_db_file");
191: $r->print(<<END);
192: <html>
193: <head><title>LON-CAPA Search Error</title></head>
1.155 matthew 194: $bodytag
1.150 matthew 195: We were unable to retrieve data describing your search. This is a serious
196: error and has been logged. Please alert your LON-CAPA administrator.
197: </body>
198: </html>
199: END
200: return OK;
201: }
1.147 matthew 202: }
1.124 matthew 203: ##
1.143 matthew 204: ## Clear out old values from groupsearch database
1.124 matthew 205: ##
1.146 matthew 206: untie %groupsearch_db if (tied(%groupsearch_db));
1.158 matthew 207: if (($ENV{'form.cleargroupsort'} eq '1') ||
208: (($ENV{'form.launch'} eq '1') &&
209: ($ENV{'form.catalogmode'} eq 'groupsearch'))) {
1.148 matthew 210: if (tie(%groupsearch_db,'GDBM_File',$diropendb,&GDBM_WRCREAT(),0640)) {
1.101 harris41 211: &start_fresh_session();
1.142 matthew 212: untie %groupsearch_db;
1.158 matthew 213: delete($ENV{'form.cleargroupsort'});
1.122 matthew 214: } else {
1.155 matthew 215: # This is a stupid error to give to the user.
216: # It really tells them nothing.
217: $r->print('<html><head></head>'.$bodytag.
218: 'Unable to tie hash to db file</body></html>');
1.101 harris41 219: return OK;
220: }
221: }
1.124 matthew 222: ##
1.150 matthew 223: ## Configure hidden fields
1.124 matthew 224: ##
1.145 matthew 225: $hidden_fields = '<input type="hidden" name="persistent_db_id" value="'.
1.150 matthew 226: $ENV{'form.persistent_db_id'}.'" />'."\n";
227: if (exists($ENV{'form.catalogmode'})) {
228: $hidden_fields .= '<input type="hidden" name="catalogmode" value="'.
229: $ENV{'form.catalogmode'}.'" />'."\n";
230: }
231: if (exists($ENV{'form.form'})) {
232: $hidden_fields .= '<input type="hidden" name="form" value="'.
233: $ENV{'form.form'}.'" />'."\n";
234: }
235: if (exists($ENV{'form.element'})) {
236: $hidden_fields .= '<input type="hidden" name="element" value="'.
237: $ENV{'form.element'}.'" />'."\n";
238: }
1.191 albertel 239: if (exists($ENV{'form.titleelement'})) {
240: $hidden_fields .= '<input type="hidden" name="titleelement" value="'.
241: $ENV{'form.titleelement'}.'" />'."\n";
242: }
1.150 matthew 243: if (exists($ENV{'form.mode'})) {
244: $hidden_fields .= '<input type="hidden" name="mode" value="'.
245: $ENV{'form.mode'}.'" />'."\n";
246: }
247: ##
248: ## Configure dynamic components of interface
1.146 matthew 249: ##
1.98 harris41 250: if ($ENV{'form.catalogmode'} eq 'interactive') {
1.150 matthew 251: $closebutton="<input type='button' name='close' value='CLOSE' ";
252: if ($ENV{'form.phase'} =~ /(results|run_search)/) {
253: $closebutton .="onClick='parent.close()'";
254: } else {
255: $closebutton .="onClick='self.close()'";
256: }
257: $closebutton .=">\n";
1.124 matthew 258: } elsif ($ENV{'form.catalogmode'} eq 'groupsearch') {
1.150 matthew 259: $closebutton="<input type='button' name='close' value='CLOSE' ";
260: if ($ENV{'form.phase'} =~ /(results|run_search)/) {
261: $closebutton .="onClick='parent.close()'";
262: } else {
263: $closebutton .="onClick='self.close()'";
264: }
265: $closebutton .= ">";
1.98 harris41 266: $importbutton=<<END;
267: <input type='button' name='import' value='IMPORT'
268: onClick='javascript:select_group()'>
269: END
1.146 matthew 270: } else {
271: $closebutton = '';
272: $importbutton = '';
1.98 harris41 273: }
1.124 matthew 274: ##
1.146 matthew 275: ## Sanity checks on form elements
1.124 matthew 276: ##
1.146 matthew 277: if (!defined($ENV{'form.viewselect'})) {
1.150 matthew 278: if (($ENV{'form.catalogmode'} eq 'groupsearch') ||
279: ($ENV{'form.catalogmode'} eq 'interactive')) {
280: $ENV{'form.viewselect'} ="Compact View";
281: } else {
282: $ENV{'form.viewselect'} ="Detailed Citation View";
283: }
1.146 matthew 284: }
1.149 matthew 285: $ENV{'form.phase'} = 'disp_basic' if (! exists($ENV{'form.phase'}));
1.151 matthew 286: $ENV{'form.show'} = 20 if (! exists($ENV{'form.show'}));
1.209 matthew 287: #
288: $ENV{'form.searchmode'} = 'basic';
289: if ($ENV{'form.phase'} eq 'adv_search' ||
290: $ENV{'form.phase'} eq 'disp_adv') {
291: $ENV{'form.searchmode'} = 'advanced';
292: } elsif ($ENV{'form.phase'} eq 'course_search') {
293: $ENV{'form.searchmode'} = 'course_search';
294: }
295: #
296: if ($ENV{'form.searchmode'} eq 'advanced') {
297: &Apache::lonhtmlcommon::add_breadcrumb
298: ({href=>'/adm/searchcat?phase=disp_adv&'.
299: 'catalogmode='.$ENV{'form.catalogmode'}.
300: '&launch='.$ENV{'form.launch'}.
301: '&mode='.$ENV{'form.mode'},
302: text=>"Advanced Search",
303: bug=>'Searching',});
304: } elsif ($ENV{'form.searchmode'} eq 'course search') {
305: &Apache::lonhtmlcommon::add_breadcrumb
306: ({href=>'/adm/searchcat?phase=disp_adv&'.
307: 'catalogmode='.$ENV{'form.catalogmode'}.
308: '&launch='.$ENV{'form.launch'}.
309: '&mode='.$ENV{'form.mode'},
310: text=>"Course Search",
311: bug=>'Searching',});
312: }
1.146 matthew 313: ##
314: ## Switch on the phase
315: ##
316: if ($ENV{'form.phase'} eq 'disp_basic') {
1.196 matthew 317: &print_basic_search_form($r,$closebutton,$hidden_fields);
1.146 matthew 318: } elsif ($ENV{'form.phase'} eq 'disp_adv') {
1.196 matthew 319: &print_advanced_search_form($r,$closebutton,$hidden_fields);
1.146 matthew 320: } elsif ($ENV{'form.phase'} eq 'results') {
1.196 matthew 321: &display_results($r,$importbutton,$closebutton,$diropendb);
1.151 matthew 322: } elsif ($ENV{'form.phase'} =~ /^(sort|run_search)$/) {
1.146 matthew 323: my ($query,$customquery,$customshow,$libraries,$pretty_string) =
324: &get_persistent_data($persistent_db_file,
325: ['query','customquery','customshow',
326: 'libraries','pretty_string']);
1.151 matthew 327: if ($ENV{'form.phase'} eq 'sort') {
328: &print_sort_form($r,$pretty_string);
329: } elsif ($ENV{'form.phase'} eq 'run_search') {
330: &run_search($r,$query,$customquery,$customshow,
331: $libraries,$pretty_string);
332: }
1.167 www 333: } elsif ($ENV{'form.phase'} eq 'course_search') {
334: &course_search($r);
1.146 matthew 335: } elsif(($ENV{'form.phase'} eq 'basic_search') ||
336: ($ENV{'form.phase'} eq 'adv_search')) {
337: # Set up table
338: if (! defined(&create_results_table())) {
1.198 www 339: my $errorstring=&Apache::lonmysql::get_error();
1.211 matthew 340: &Apache::lonnet::logthis('lonsearchcat.pm: Unable to create '.
341: 'needed table. lonmysql error:'.
342: $errorstring);
1.147 matthew 343: $r->print(<<END);
344: <html><head><title>Search Error</title></head>
1.155 matthew 345: $bodytag
1.147 matthew 346: Unable to create table in which to store search results.
347: The search has been aborted.
348: </body>
349: </html>
350: END
351: return OK;
1.146 matthew 352: }
1.147 matthew 353: delete($ENV{'form.launch'});
1.146 matthew 354: if (! &make_form_data_persistent($r,$persistent_db_file)) {
1.147 matthew 355: $r->print(<<END);
356: <html><head><title>Search Error</title></head>
1.155 matthew 357: $bodytag
1.147 matthew 358: Unable to properly store search information. The search has been aborted.
359: </body>
360: </html>
361: END
362: return OK;
1.146 matthew 363: }
1.145 matthew 364: #
1.139 matthew 365: # We are running a search
1.134 matthew 366: my ($query,$customquery,$customshow,$libraries) =
367: (undef,undef,undef,undef);
1.143 matthew 368: my $pretty_string;
1.146 matthew 369: if ($ENV{'form.phase'} eq 'basic_search') {
1.180 matthew 370: ($query,$pretty_string,$libraries) =
1.196 matthew 371: &parse_basic_search($r,$closebutton,$hidden_fields);
1.146 matthew 372: } else { # Advanced search
1.143 matthew 373: ($query,$customquery,$customshow,$libraries,$pretty_string)
1.196 matthew 374: = &parse_advanced_search($r,$closebutton,$hidden_fields);
1.134 matthew 375: return OK if (! defined($query));
376: }
1.152 matthew 377: &make_persistent({ query => $query,
1.146 matthew 378: customquery => $customquery,
379: customshow => $customshow,
380: libraries => $libraries,
381: pretty_string => $pretty_string },
382: $persistent_db_file);
1.145 matthew 383: ##
1.146 matthew 384: ## Print out the frames interface
1.145 matthew 385: ##
1.146 matthew 386: &print_frames_interface($r);
1.124 matthew 387: }
388: return OK;
389: }
1.98 harris41 390:
1.124 matthew 391: ######################################################################
392: ######################################################################
1.196 matthew 393: ##
394: ## Course Search
395: ##
396: ######################################################################
397: ######################################################################
398: { # Scope the course search to avoid global variables
399: #
400: # Variables For course search
401: my %alreadyseen;
402: my %hash;
403: my $totalfound;
1.124 matthew 404:
1.167 www 405: sub course_search {
406: my $r=shift;
1.203 www 407: my $bodytag=&Apache::loncommon::bodytag('Course Search').
408: &Apache::loncommon::help_open_bug('Searching');
1.167 www 409: my $pretty_search_string = '<b>'.$ENV{'form.courseexp'}.'</b>';
410: my $search_string = $ENV{'form.courseexp'};
411: my @New_Words;
412: if ($ENV{'form.crsrelated'}) {
413: ($search_string,@New_Words) = &related_version($ENV{'form.courseexp'});
414: if (@New_Words) {
1.200 www 415: $pretty_search_string .= ' '.&mt("with related words").": <b>@New_Words</b>.";
1.167 www 416: } else {
1.200 www 417: $pretty_search_string .= ' '.&mt('with no related words').".";
1.167 www 418: }
419: }
420: my $fulltext=$ENV{'form.crsfulltext'};
421: my @allwords=($search_string,@New_Words);
1.169 www 422: $totalfound=0;
1.167 www 423: $r->print('<html><head><title>LON-CAPA Course Search</title></head>'.
1.203 www 424: $bodytag.'<hr /><center><font size="+2" face="arial">'.$pretty_search_string.'</font></center><hr />');
1.167 www 425: $r->rflush();
426: # ======================================================= Go through the course
1.196 matthew 427: undef %alreadyseen;
428: %alreadyseen=();
1.169 www 429: my $c=$r->connection;
1.196 matthew 430: if (tie(%hash,'GDBM_File',$ENV{'request.course.fn'}.".db",
431: &GDBM_READER(),0640)) {
432: foreach (keys %hash) {
433: if ($c->aborted()) { last; }
434: if (($_=~/^src\_(.+)$/) && (!$alreadyseen{$hash{$_}})) {
435: &checkonthis($r,$hash{$_},0,$hash{'title_'.$1},$fulltext,
436: @allwords);
437: }
438: }
439: untie(%hash);
440: }
1.169 www 441: unless ($totalfound) {
1.187 www 442: $r->print('<p>'.&mt('No resources found').'.</p>');
1.169 www 443: }
1.167 www 444: # =================================================== Done going through course
445: $r->print('</body></html>');
446: }
447:
448: # =============================== This pulls up a resource and its dependencies
449:
450: sub checkonthis {
1.168 www 451: my ($r,$url,$level,$title,$fulltext,@allwords)=@_;
1.167 www 452: $alreadyseen{$url}=1;
453: $r->rflush();
454: my $result=&Apache::lonnet::metadata($url,'title').' '.
455: &Apache::lonnet::metadata($url,'subject').' '.
456: &Apache::lonnet::metadata($url,'abstract').' '.
457: &Apache::lonnet::metadata($url,'keywords');
458: if (($url) && ($fulltext)) {
1.168 www 459: $result.=&Apache::lonnet::ssi_body($url);
1.167 www 460: }
461: $result=~s/\s+/ /gs;
462: my $applies=0;
463: foreach (@allwords) {
464: if ($_=~/\w/) {
465: if ($result=~/$_/si) {
466: $applies++;
467: }
468: }
469: }
470: # Does this resource apply?
471: if ($applies) {
472: $r->print('<br />');
473: for (my $i=0;$i<=$level*5;$i++) {
474: $r->print(' ');
475: }
476: $r->print('<a href="'.$url.'" target="cat">'.
1.169 www 477: ($title?$title:$url).'</a><br />');
478: $totalfound++;
479: } elsif ($fulltext) {
480: $r->print(' .');
1.167 www 481: }
1.169 www 482: $r->rflush();
1.167 www 483: # Check also the dependencies of this one
484: my $dependencies=
485: &Apache::lonnet::metadata($url,'dependencies');
486: foreach (split(/\,/,$dependencies)) {
487: if (($_=~/^\/res\//) && (!$alreadyseen{$_})) {
1.168 www 488: &checkonthis($r,$_,$level+1,'',$fulltext,@allwords);
1.167 www 489: }
490: }
491: }
492:
1.196 matthew 493: sub untiehash {
494: if (tied(%hash)) {
495: untie(%hash);
496: }
497: }
498:
499: } # End of course search scoping
500:
1.207 matthew 501: sub search_html_header {
502: my $Str = <<ENDHEADER;
503: <html>
504: <head>
505: <title>The LearningOnline Network with CAPA</title>
506: <script type="text/javascript">
507: function openhelp(val) {
508: openhelpwin=open('/adm/help/searchcat.html','helpscreen',
509: 'scrollbars=1,width=600,height=300');
510: openhelpwin.focus();
511: }
512: </script>
513: </head>
514: ENDHEADER
515: return $Str;
516: }
517:
1.167 www 518: ######################################################################
519: ######################################################################
520:
1.124 matthew 521: =pod
522:
1.146 matthew 523: =item &print_basic_search_form()
1.124 matthew 524:
1.204 matthew 525: Prints the form for the basic search. Sorry the name is so cryptic.
1.124 matthew 526:
527: =cut
528:
529: ######################################################################
530: ######################################################################
1.204 matthew 531: sub print_basic_search_form {
1.196 matthew 532: my ($r,$closebutton,$hidden_fields) = @_;
1.203 www 533: my $bodytag=&Apache::loncommon::bodytag('Search').
1.209 matthew 534: &Apache::lonhtmlcommon::breadcrumbs(undef,'Searching','Finding_Resources',
535: undef,undef,! $ENV{'form.launch'});
1.207 matthew 536: my $scrout = &search_html_header().$bodytag;
537: if (&Apache::lonnet::allowed('bre',$ENV{'request.role.domain'})) {
538: my $Statement=&searchhelp();
539: $scrout.=(<<ENDDOCUMENT);
1.195 matthew 540: <form name="loncapa_search" method="post" action="/adm/searchcat">
1.146 matthew 541: <input type="hidden" name="phase" value="basic_search" />
1.145 matthew 542: $hidden_fields
1.124 matthew 543: <p>
1.193 sakharuk 544: $Statement.
1.124 matthew 545: </p>
546: <p>
547: <table>
548: <tr><td>
549: ENDDOCUMENT
1.207 matthew 550: $scrout.=' '.
1.208 matthew 551: &Apache::lonhtmlcommon::textbox('basicexp',
1.207 matthew 552: $ENV{'form.basicexp'},40).
553: ' ';
554: my $relatedcheckbox =
555: &Apache::lonhtmlcommon::checkbox('related',
556: $ENV{'form.related'});
557: my $domain = $r->dir_config('lonDefDomain');
558: my $domaincheckbox =
559: &Apache::lonhtmlcommon::checkbox('domains',
560: $ENV{'form.domains'});
561: my $srch=&mt('Search');
562: my $header=&mt('Advanced Search');
563: my $userelatedwords=&mt('use related words');
564: my $onlysearchdomain=&mt('only search domain');
565: my $view=&viewoptions();
566: $scrout.=<<END;
1.160 www 567: </td><td><a
1.190 matthew 568: href="/adm/searchcat?phase=disp_adv&catalogmode=$ENV{'form.catalogmode'}&launch=$ENV{'form.launch'}&mode=$ENV{'form.mode'}"
1.187 www 569: >$header</a></td></tr>
1.193 sakharuk 570: <tr><td>$relatedcheckbox $userelatedwords</td>
571: <td>$domaincheckbox $onlysearchdomain <b>$domain</b></td></tr>
1.141 matthew 572: </table>
1.124 matthew 573: </p>
1.200 www 574: $view
1.124 matthew 575: <p>
1.187 www 576: <input type="submit" name="basicsubmit" value='$srch' />
1.124 matthew 577: $closebutton
1.139 matthew 578: END
579: $scrout.=<<ENDDOCUMENT;
1.124 matthew 580: </p>
581: </form>
1.167 www 582: ENDDOCUMENT
1.180 matthew 583: }
584: if ($ENV{'request.course.id'}) {
1.187 www 585: my %lt=&Apache::lonlocal::texthash(
586: 'srch' => 'Search',
587: 'header' => 'Course Search',
588: 'note' => 'Enter terms or phrases, then press "Search" below',
1.200 www 589: 'use' => 'use related words',
590: 'full' =>'fulltext search (time consuming)'
1.187 www 591: );
1.180 matthew 592: $scrout.=(<<ENDCOURSESEARCH);
593: <hr />
1.187 www 594: <h1>$lt{'header'}</h1>
1.195 matthew 595: <form name="course_search" method="post" action="/adm/searchcat">
1.167 www 596: <input type="hidden" name="phase" value="course_search" />
597: $hidden_fields
598: <p>
1.187 www 599: $lt{'note'}.
1.167 www 600: </p>
601: <p>
602: <table>
603: <tr><td>
604: ENDCOURSESEARCH
1.180 matthew 605: $scrout.=' '.
1.200 www 606: &Apache::lonhtmlcommon::textbox('courseexp',
607: $ENV{'form.courseexp'},40);
1.180 matthew 608: my $crscheckbox =
1.200 www 609: &Apache::lonhtmlcommon::checkbox('crsfulltext',
610: $ENV{'form.crsfulltext'});
1.180 matthew 611: my $relcheckbox =
1.200 www 612: &Apache::lonhtmlcommon::checkbox('crsrelated',
613: $ENV{'form.crsrelated'});
1.180 matthew 614: $scrout.=(<<ENDENDCOURSE);
1.167 www 615: </td></tr>
1.200 www 616: <tr><td>$relcheckbox $lt{'use'}</td><td></td></tr>
617: <tr><td>$crscheckbox $lt{'full'}</td><td></td></tr>
1.167 www 618: </table><p>
1.187 www 619: <input type="submit" name="coursesubmit" value='$lt{'srch'}' />
1.167 www 620: </p>
621: ENDENDCOURSE
1.180 matthew 622: }
1.167 www 623: $scrout.=(<<ENDDOCUMENT);
1.124 matthew 624: </body>
625: </html>
626: ENDDOCUMENT
1.146 matthew 627: $r->print($scrout);
628: return;
1.124 matthew 629: }
630: ######################################################################
631: ######################################################################
632:
633: =pod
634:
635: =item &advanced_search_form()
636:
1.204 matthew 637: Prints the advanced search form.
1.124 matthew 638:
639: =cut
640:
641: ######################################################################
642: ######################################################################
1.146 matthew 643: sub print_advanced_search_form{
1.196 matthew 644: my ($r,$closebutton,$hidden_fields) = @_;
1.200 www 645: my %lt=&Apache::lonlocal::texthash('srch' => 'Search',
646: 'reset' => 'Reset',
647: 'help' => 'Help');
1.129 matthew 648: my $advanced_buttons = <<"END";
649: <p>
1.200 www 650: <input type="submit" name="advancedsubmit" value='$lt{"srch"}' />
651: <input type="reset" name="reset" value='$lt{"reset"}' />
1.129 matthew 652: $closebutton
1.200 www 653: <input type="button" value="$lt{'help'}" onClick="openhelp()" />
1.129 matthew 654: END
1.209 matthew 655: my $bodytag=&Apache::loncommon::bodytag('Advanced Catalog Search').
656: &Apache::lonhtmlcommon::breadcrumbs(undef,'Searching',
657: 'Finding_Resources',
658: undef,undef,
659: ! $ENV{'form.launch'});
1.201 www 660: my $searchhelp=&searchhelp();
1.207 matthew 661: my $scrout=&search_html_header();
662: $scrout .= <<"ENDHEADER";
1.154 www 663: $bodytag
1.200 www 664: <form method="post" action="/adm/searchcat" name="advsearch">
1.129 matthew 665: $advanced_buttons
1.124 matthew 666: ENDHEADER
1.208 matthew 667: $scrout.=(' 'x2).&viewoptions().'</p>'.$hidden_fields.
668: '<input type="hidden" name="phase" value="adv_search" />';
1.200 www 669: my %fields=&Apache::lonmeta::fieldnames();
1.208 matthew 670: #
671: $scrout.= '<p>'.$searchhelp.'</p>'.
672: "<table>\n";
673: my %related_word_search =
674: ('title'=>1,
675: 'author'=>0,
676: 'owner'=>0,
677: 'authorspace'=>0,
678: 'modifyinguser'=>0,
679: 'keywords'=>1,
680: 'notes'=>1,
681: 'abstract'=>1,
682: 'standards'=>1,
683: 'mime'=>1,
684: );
1.209 matthew 685: #
1.208 matthew 686: foreach my $field ('title','author','owner','authorspace','modifyinguser',
687: 'keywords','notes','abstract','standards','mime') {
688: $scrout.='<tr><td align="right">'.&titlefield($fields{$field}).'</td><td>'.
689: &Apache::lonmeta::prettyinput($field,
690: $ENV{'form.'.$field},
691: $field,
692: 'advsearch',
693: $related_word_search{$field},
694: '</td><td align="left">',
695: $ENV{'form.'.$field.'_related'},
696: 50);
697: if ($related_word_search{$field}) {
698: $scrout .= 'related words';
699: } else {
700: $scrout .= '</td><td> ';
701: }
702: $scrout .= '</td></tr>'.$/;
703: }
704: foreach my $field ('lowestgradelevel','highestgradelevel') {
705: $scrout.='<tr>'.
706: '<td align="right">'.&titlefield($fields{$field}).'</td>'.
707: '<td colspan="2">'.
708: &Apache::lonmeta::prettyinput($field,
709: $ENV{'form.'.$field},
710: $field,
711: 'advsearch',
712: 0).
713: '</td></tr>'.$/;
1.200 www 714: }
1.208 matthew 715: $scrout.='<tr><td align="right">'.
716: &titlefield(&mt('MIME Type Category')).'</td><td colspan="2">'.
1.202 www 717: &Apache::loncommon::filecategoryselect('category',
718: $ENV{'form.category'}).
1.208 matthew 719: '</td></tr>'.$/;
720: $scrout.='<tr><td align="right" valign="top">'.
721: &titlefield(&mt('Domains')).'</td><td colspan="2">'.
1.202 www 722: &Apache::loncommon::domain_select('domains',
723: $ENV{'form.domains'},1).
1.208 matthew 724: '</td></tr>'.$/;
725: $scrout .= "</table>\n<br />\n<table>\n";
1.204 matthew 726: my %dates=&Apache::lonlocal::texthash
727: ('creationdatestart' => 'Creation Date After',
728: 'creationdateend' => 'Creation Date Before',
729: 'lastrevisiondatestart' => 'Last Revision Date After',
730: 'lastrevisiondateend' => 'Last Revision Date Before');
1.208 matthew 731: foreach my $field (sort keys %dates) {
1.207 matthew 732: $scrout.='<tr>'.
1.208 matthew 733: '<td align="right">'.&titlefield($dates{$field}).'</td><td>'.
734: &Apache::lonhtmlcommon::date_setter('advsearch',$field,0,'',1).
735: '</td></tr>'.$/;
1.201 www 736: }
1.202 www 737: $scrout.="</table>\n";
1.124 matthew 738: $scrout.=<<ENDDOCUMENT;
1.129 matthew 739: $advanced_buttons
1.8 harris41 740: </form>
741: </body>
742: </html>
743: ENDDOCUMENT
1.146 matthew 744: $r->print($scrout);
745: return;
1.124 matthew 746: }
1.204 matthew 747:
1.200 www 748: ######################################################################
749: ######################################################################
750:
751: =pod
752:
1.204 matthew 753: =item &titlefield()
1.200 www 754:
755: Inputs: title text
756:
757: Outputs: titletext with font wrapper
758:
759: =cut
760:
761: ######################################################################
762: ######################################################################
763: sub titlefield {
764: my $title=shift;
1.208 matthew 765: return $title;
1.200 www 766: }
1.204 matthew 767:
1.200 www 768: ######################################################################
769: ######################################################################
770:
771: =pod
772:
1.204 matthew 773: =item viewoptiontext()
1.200 www 774:
775: Inputs: codename for view option
776:
777: Outputs: displayed text
778:
779: =cut
780:
781: ######################################################################
782: ######################################################################
783: sub viewoptiontext {
784: my $code=shift;
1.204 matthew 785: my %desc=&Apache::lonlocal::texthash
786: ('detailed' => "Detailed Citation View",
787: 'xml' => 'XML/SGML',
788: 'compact' => 'Compact View',
789: 'fielded' => 'Fielded Format',
790: 'summary' => 'Summary View');
1.200 www 791: return $desc{$code};
792: }
1.204 matthew 793:
794: ######################################################################
1.200 www 795: ######################################################################
796:
797: =pod
798:
1.204 matthew 799: =item viewoptions()
1.200 www 800:
801: Inputs: none
802:
803: Outputs: text for box with view options
804:
805: =cut
806:
807: ######################################################################
808: ######################################################################
809: sub viewoptions {
1.208 matthew 810: my $scrout="\n".'<nobr>';
811: if (! defined($ENV{'form.viewselect'})) {
812: $ENV{'form.viewselect'}='detailed';
813: }
1.200 www 814: $scrout.=&Apache::lonmeta::selectbox('viewselect',
815: $ENV{'form.viewselect'},
816: \&viewoptiontext,
817: sort(keys(%Views)));
1.208 matthew 818: $scrout.= ' ';
1.200 www 819: $scrout.=&Apache::lonmeta::selectbox('show',
820: $ENV{'form.show'},
821: undef,
822: (10,20,50,100,1000,10000));
1.208 matthew 823: $scrout .= (' 'x2).&mt('Records per Page').'</nobr>'.$/;
1.200 www 824: return $scrout;
1.201 www 825: }
826:
827: ######################################################################
1.204 matthew 828: ######################################################################
1.201 www 829:
830: =pod
831:
1.204 matthew 832: =item searchhelp()
1.201 www 833:
834: Inputs: none
835:
836: Outputs: return little blurb on how to enter searches
837:
838: =cut
839:
840: ######################################################################
841: ######################################################################
842: sub searchhelp {
843: return &mt('Enter terms or phrases separated by AND, OR, or NOT');
1.200 www 844: }
1.8 harris41 845:
1.121 matthew 846: ######################################################################
847: ######################################################################
848:
849: =pod
850:
1.204 matthew 851: =item &get_persistent_form_data()
1.145 matthew 852:
1.146 matthew 853: Inputs: filename of database
854:
855: Outputs: returns undef on database errors.
856:
857: This function is the reverse of &make_persistent() for form data.
1.145 matthew 858: Retrieve persistent data from %persistent_db. Retrieved items will have their
1.146 matthew 859: values unescaped. If a form value already exists in $ENV, it will not be
860: overwritten. Form values that are array references may have values appended
861: to them.
1.145 matthew 862:
863: =cut
864:
865: ######################################################################
866: ######################################################################
1.146 matthew 867: sub get_persistent_form_data {
868: my $filename = shift;
1.147 matthew 869: return 0 if (! -e $filename);
1.146 matthew 870: return undef if (! tie(%persistent_db,'GDBM_File',$filename,
1.148 matthew 871: &GDBM_READER(),0640));
1.146 matthew 872: #
873: # These make sure we do not get array references printed out as 'values'.
1.161 matthew 874: my %arrays_allowed = ('form.domains'=>1);
1.146 matthew 875: #
876: # Loop through the keys, looking for 'form.'
877: foreach my $name (keys(%persistent_db)) {
878: next if ($name !~ /^form./);
1.182 matthew 879: # Kludgification begins!
880: if ($name eq 'form.domains' &&
881: $ENV{'form.searchmode'} eq 'basic' &&
882: $ENV{'form.phase'} ne 'disp_basic') {
883: next;
884: }
885: # End kludge (hopefully)
1.152 matthew 886: next if (exists($ENV{$name}));
1.146 matthew 887: my @values = map {
888: &Apache::lonnet::unescape($_);
889: } split(',',$persistent_db{$name});
890: next if (@values <1);
1.152 matthew 891: if ($arrays_allowed{$name}) {
892: $ENV{$name} = [@values];
1.146 matthew 893: } else {
1.152 matthew 894: $ENV{$name} = $values[0] if ($values[0]);
1.146 matthew 895: }
896: }
897: untie (%persistent_db);
898: return 1;
899: }
1.181 matthew 900:
1.146 matthew 901: ######################################################################
902: ######################################################################
903:
904: =pod
905:
1.204 matthew 906: =item &get_persistent_data()
1.146 matthew 907:
908: Inputs: filename of database, ref to array of values to recover.
909:
910: Outputs: array of values. Returns undef on error.
911:
912: This function is the reverse of &make_persistent();
913: Retrieve persistent data from %persistent_db. Retrieved items will have their
914: values unescaped. If the item contains commas (before unescaping), the
915: returned value will be an array pointer.
916:
917: =cut
918:
919: ######################################################################
920: ######################################################################
921: sub get_persistent_data {
922: my $filename = shift;
923: my @Vars = @{shift()};
924: my @Values; # Return array
925: return undef if (! -e $filename);
926: return undef if (! tie(%persistent_db,'GDBM_File',$filename,
1.148 matthew 927: &GDBM_READER(),0640));
1.146 matthew 928: foreach my $name (@Vars) {
929: if (! exists($persistent_db{$name})) {
930: push @Values, undef;
931: next;
932: }
933: my @values = map {
934: &Apache::lonnet::unescape($_);
935: } split(',',$persistent_db{$name});
1.152 matthew 936: if (@values <= 1) {
1.146 matthew 937: push @Values,$values[0];
1.145 matthew 938: } else {
1.146 matthew 939: push @Values,\@values;
1.145 matthew 940: }
941: }
1.146 matthew 942: untie (%persistent_db);
943: return @Values;
1.145 matthew 944: }
945:
946: ######################################################################
947: ######################################################################
948:
949: =pod
950:
1.121 matthew 951: =item &make_persistent()
952:
1.146 matthew 953: Inputs: Hash of values to save, filename of persistent database.
954:
955: Store variables away to the %persistent_db.
1.145 matthew 956: Values will be escaped. Values that are array pointers will have their
1.205 www 957: elements escaped and concatenated in a comma separated string.
1.122 matthew 958:
1.121 matthew 959: =cut
960:
961: ######################################################################
962: ######################################################################
1.98 harris41 963: sub make_persistent {
1.133 matthew 964: my %save = %{shift()};
1.146 matthew 965: my $filename = shift;
966: return undef if (! tie(%persistent_db,'GDBM_File',
1.148 matthew 967: $filename,&GDBM_WRCREAT(),0640));
1.146 matthew 968: foreach my $name (keys(%save)) {
1.145 matthew 969: my @values = (ref($save{$name}) ? @{$save{$name}} : ($save{$name}));
970: # We handle array references, but not recursively.
971: my $store = join(',', map { &Apache::lonnet::escape($_); } @values );
972: $persistent_db{$name} = $store;
1.109 harris41 973: }
1.146 matthew 974: untie(%persistent_db);
975: return 1;
976: }
977:
978: ######################################################################
979: ######################################################################
980:
981: =pod
982:
983: =item &make_form_data_persistent()
984:
985: Inputs: filename of persistent database.
986:
987: Store most form variables away to the %persistent_db.
988: Values will be escaped. Values that are array pointers will have their
1.205 www 989: elements escaped and concatenated in a comma separated string.
1.146 matthew 990:
991: =cut
992:
993: ######################################################################
994: ######################################################################
995: sub make_form_data_persistent {
996: my $r = shift;
997: my $filename = shift;
998: my %save;
999: foreach (keys(%ENV)) {
1.150 matthew 1000: next if (!/^form/ || /submit/);
1.146 matthew 1001: $save{$_} = $ENV{$_};
1002: }
1.152 matthew 1003: return &make_persistent(\%save,$filename);
1.98 harris41 1004: }
1005:
1.122 matthew 1006: ######################################################################
1007: ######################################################################
1008:
1009: =pod
1010:
1.134 matthew 1011: =item &parse_advanced_search()
1012:
1013: Parse advanced search form and return the following:
1014:
1015: =over 4
1016:
1017: =item $query Scalar containing an SQL query.
1.126 matthew 1018:
1.134 matthew 1019: =item $customquery Scalar containing a custom query.
1020:
1021: =item $customshow Scalar containing commands to show custom metadata.
1022:
1023: =item $libraries_to_query Reference to array of domains to search.
1024:
1025: =back
1.122 matthew 1026:
1027: =cut
1028:
1029: ######################################################################
1030: ######################################################################
1.134 matthew 1031: sub parse_advanced_search {
1.196 matthew 1032: my ($r,$closebutton,$hidden_fields)=@_;
1.32 harris41 1033: my $fillflag=0;
1.143 matthew 1034: my $pretty_search_string = "<br />\n";
1.64 harris41 1035: # Clean up fields for safety
1036: for my $field ('title','author','subject','keywords','url','version',
1037: 'creationdatestart_month','creationdatestart_day',
1038: 'creationdatestart_year','creationdateend_month',
1039: 'creationdateend_day','creationdateend_year',
1040: 'lastrevisiondatestart_month','lastrevisiondatestart_day',
1041: 'lastrevisiondatestart_year','lastrevisiondateend_month',
1042: 'lastrevisiondateend_day','lastrevisiondateend_year',
1.161 matthew 1043: 'notes','abstract','extension','language','owner',
1.131 matthew 1044: 'custommetadata','customshow','category') {
1.101 harris41 1045: $ENV{"form.$field"}=~s/[^\w\/\s\(\)\=\-\"\']//g;
1.64 harris41 1046: }
1.117 matthew 1047: foreach ('mode','form','element') {
1048: # is this required? Hmmm.
1049: next unless (exists($ENV{"form.$_"}));
1050: $ENV{"form.$_"}=&Apache::lonnet::unescape($ENV{"form.$_"});
1051: $ENV{"form.$_"}=~s/[^\w\/\s\(\)\=\-\"\']//g;
1052: }
1.131 matthew 1053: # Preprocess the category form element.
1.161 matthew 1054: $ENV{'form.category'} = 'any' if (! defined($ENV{'form.category'}) ||
1055: ref($ENV{'form.category'}));
1056: #
1.90 harris41 1057: # Check to see if enough information was filled in
1.32 harris41 1058: for my $field ('title','author','subject','keywords','url','version',
1.161 matthew 1059: 'notes','abstract','category','extension','language',
1060: 'owner','custommetadata') {
1.40 harris41 1061: if (&filled($ENV{"form.$field"})) {
1.32 harris41 1062: $fillflag++;
1063: }
1064: }
1.212 ! matthew 1065: if (! $fillflag) {
1.204 matthew 1066: &output_blank_field_error($r,$closebutton,
1067: 'phase=disp_adv',$hidden_fields);
1.134 matthew 1068: return ;
1.32 harris41 1069: }
1.90 harris41 1070: # Turn the form input into a SQL-based query
1.39 harris41 1071: my $query='';
1.45 harris41 1072: my @queries;
1.143 matthew 1073: my $font = '<font color="#800000" face="helvetica">';
1.90 harris41 1074: # Evaluate logical expression AND/OR/NOT phrase fields.
1.58 harris41 1075: foreach my $field ('title','author','subject','notes','abstract','url',
1.200 www 1076: 'keywords','version','owner','standards') {
1.44 harris41 1077: if ($ENV{'form.'.$field}) {
1.142 matthew 1078: my $searchphrase = $ENV{'form.'.$field};
1.143 matthew 1079: $pretty_search_string .= $font."$field</font> contains <b>".
1080: $searchphrase."</b>";
1.142 matthew 1081: if ($ENV{'form.'.$field.'_related'}) {
1.143 matthew 1082: my @New_Words;
1083: ($searchphrase,@New_Words) = &related_version($searchphrase);
1084: if (@New_Words) {
1085: $pretty_search_string .= " with related words: ".
1086: "<b>@New_Words</b>.";
1087: } else {
1088: $pretty_search_string .= " with no related words.";
1089: }
1.142 matthew 1090: }
1.143 matthew 1091: $pretty_search_string .= "<br />\n";
1.142 matthew 1092: push @queries,&build_SQL_query($field,$searchphrase);
1.131 matthew 1093: }
1.44 harris41 1094: }
1.161 matthew 1095: #
1096: # Make the 'mime' from 'form.category' and 'form.extension'
1097: #
1098: my $searchphrase;
1099: if (exists($ENV{'form.category'}) &&
1100: $ENV{'form.category'} !~ /^\s*$/ &&
1101: $ENV{'form.category'} ne 'any') {
1102: my @extensions = &Apache::loncommon::filecategorytypes
1103: ($ENV{'form.category'});
1104: if (scalar(@extensions) > 0) {
1105: $searchphrase = join(' OR ',@extensions);
1106: }
1107: }
1108: if (exists($ENV{'form.extension'}) && $ENV{'form.extension'} !~ /^\s*$/) {
1109: $searchphrase .= ' OR ' if (defined($searchphrase));
1110: my @extensions = split(/,/,$ENV{'form.extension'});
1111: $searchphrase .= join(' OR ',@extensions);
1112: }
1113: if (defined($searchphrase)) {
1114: push @queries,&build_SQL_query('mime',$searchphrase);
1115: $pretty_search_string .=$font.'mime</font> contains <b>'.
1116: $searchphrase.'</b><br />';
1.135 matthew 1117: }
1.204 matthew 1118: #
1.90 harris41 1119: # Evaluate option lists
1.58 harris41 1120: if ($ENV{'form.language'} and $ENV{'form.language'} ne 'any') {
1.90 harris41 1121: push @queries,"(language like \"$ENV{'form.language'}\")";
1.143 matthew 1122: $pretty_search_string.=$font."language</font>= ".
1123: &Apache::loncommon::languagedescription($ENV{'form.language'}).
1124: "<br />\n";
1.58 harris41 1125: }
1126: if ($ENV{'form.copyright'} and $ENV{'form.copyright'} ne 'any') {
1.90 harris41 1127: push @queries,"(copyright like \"$ENV{'form.copyright'}\")";
1.143 matthew 1128: $pretty_search_string.=$font."copyright</font> = ".
1129: &Apache::loncommon::copyrightdescription($ENV{'form.copyright'}).
1130: "<br \>\n";
1.58 harris41 1131: }
1.143 matthew 1132: #
1.90 harris41 1133: # Evaluate date windows
1.60 harris41 1134: my $datequery=&build_date_queries(
1135: $ENV{'form.creationdatestart_month'},
1136: $ENV{'form.creationdatestart_day'},
1137: $ENV{'form.creationdatestart_year'},
1138: $ENV{'form.creationdateend_month'},
1139: $ENV{'form.creationdateend_day'},
1140: $ENV{'form.creationdateend_year'},
1141: $ENV{'form.lastrevisiondatestart_month'},
1142: $ENV{'form.lastrevisiondatestart_day'},
1143: $ENV{'form.lastrevisiondatestart_year'},
1144: $ENV{'form.lastrevisiondateend_month'},
1145: $ENV{'form.lastrevisiondateend_day'},
1146: $ENV{'form.lastrevisiondateend_year'},
1147: );
1.90 harris41 1148: # Test to see if date windows are legitimate
1.61 harris41 1149: if ($datequery=~/^Incorrect/) {
1.196 matthew 1150: &output_date_error($r,$datequery,$closebutton,$hidden_fields);
1.134 matthew 1151: return ;
1.143 matthew 1152: } elsif ($datequery) {
1153: # Here is where you would set up pretty_search_string to output
1154: # date query information.
1.60 harris41 1155: push @queries,$datequery;
1156: }
1.204 matthew 1157: #
1.90 harris41 1158: # Process form information for custom metadata querying
1.134 matthew 1159: my $customquery=undef;
1.204 matthew 1160: ##
1161: ## The custom metadata search was removed q long time ago mostly
1162: ## because I was unable to figureout exactly how it worked and could
1163: ## not imagine people actually using it. MH
1164: ##
1165: # if ($ENV{'form.custommetadata'}) {
1166: # $pretty_search_string .=$font."Custom Metadata Search</font>: <b>".
1167: # $ENV{'form.custommetadata'}."</b><br />\n";
1168: # $customquery=&build_custommetadata_query('custommetadata',
1169: # $ENV{'form.custommetadata'});
1170: # }
1.134 matthew 1171: my $customshow=undef;
1.204 matthew 1172: # if ($ENV{'form.customshow'}) {
1173: # $pretty_search_string .=$font."Custom Metadata Display</font>: <b>".
1174: # $ENV{'form.customshow'}."</b><br />\n";
1175: # $customshow=$ENV{'form.customshow'};
1176: # $customshow=~s/[^\w\s]//g;
1177: # my @fields=split(/\s+/,$customshow);
1178: # $customshow=join(" ",@fields);
1179: # }
1180: ##
1.132 matthew 1181: ## Deal with restrictions to given domains
1182: ##
1.180 matthew 1183: my ($libraries_to_query,$pretty_domains_string) =
1184: &parse_domain_restrictions();
1185: $pretty_search_string .= $pretty_domains_string."<br />\n";
1186: #
1187: if (@queries) {
1188: $query=join(" AND ",@queries);
1189: $query="select * from metadata where $query";
1190: } elsif ($customquery) {
1191: $query = '';
1192: }
1193: return ($query,$customquery,$customshow,$libraries_to_query,
1194: $pretty_search_string);
1195: }
1196:
1197: sub parse_domain_restrictions {
1.132 matthew 1198: my $libraries_to_query = undef;
1199: # $ENV{'form.domains'} can be either a scalar or an array reference.
1200: # We need an array.
1.180 matthew 1201: if (! exists($ENV{'form.domains'})) {
1202: return (undef,'');
1203: }
1204: my @allowed_domains;
1205: if (ref($ENV{'form.domains'})) {
1206: @allowed_domains = @{$ENV{'form.domains'}};
1207: } else {
1208: @allowed_domains = ($ENV{'form.domains'});
1209: }
1.204 matthew 1210: #
1.132 matthew 1211: my %domain_hash = ();
1.143 matthew 1212: my $pretty_domains_string;
1.132 matthew 1213: foreach (@allowed_domains) {
1214: $domain_hash{$_}++;
1215: }
1.143 matthew 1216: if ($domain_hash{'any'}) {
1.152 matthew 1217: $pretty_domains_string = "In all LON-CAPA domains.";
1.143 matthew 1218: } else {
1219: if (@allowed_domains > 1) {
1.152 matthew 1220: $pretty_domains_string = "In LON-CAPA domains:";
1.143 matthew 1221: } else {
1.152 matthew 1222: $pretty_domains_string = "In LON-CAPA domain ";
1.143 matthew 1223: }
1224: foreach (sort @allowed_domains) {
1.152 matthew 1225: $pretty_domains_string .= "<b>".$_."</b> ";
1.132 matthew 1226: }
1.143 matthew 1227: foreach (keys(%Apache::lonnet::libserv)) {
1228: if (exists($domain_hash{$Apache::lonnet::hostdom{$_}})) {
1229: push @$libraries_to_query,$_;
1230: }
1.132 matthew 1231: }
1232: }
1.180 matthew 1233: return ($libraries_to_query,$pretty_domains_string);
1.18 harris41 1234: }
1235:
1.122 matthew 1236: ######################################################################
1237: ######################################################################
1238:
1239: =pod
1240:
1.134 matthew 1241: =item &parse_basic_search()
1.122 matthew 1242:
1.134 matthew 1243: Parse the basic search form and return a scalar containing an sql query.
1.126 matthew 1244:
1.122 matthew 1245: =cut
1246:
1247: ######################################################################
1248: ######################################################################
1.134 matthew 1249: sub parse_basic_search {
1.145 matthew 1250: my ($r,$closebutton)=@_;
1.204 matthew 1251: #
1.64 harris41 1252: # Clean up fields for safety
1253: for my $field ('basicexp') {
1254: $ENV{"form.$field"}=~s/[^\w\s\(\)\-]//g;
1255: }
1.117 matthew 1256: foreach ('mode','form','element') {
1257: # is this required? Hmmm.
1258: next unless (exists($ENV{"form.$_"}));
1259: $ENV{"form.$_"}=&Apache::lonnet::unescape($ENV{"form.$_"});
1260: $ENV{"form.$_"}=~s/[^\w\/\s\(\)\=\-\"\']//g;
1261: }
1.180 matthew 1262: my ($libraries_to_query,$pretty_domains_string) =
1263: &parse_domain_restrictions();
1.204 matthew 1264: #
1265: # Check to see if enough of a query is filled in
1.26 harris41 1266: unless (&filled($ENV{'form.basicexp'})) {
1.151 matthew 1267: &output_blank_field_error($r,$closebutton,'phase=disp_basic');
1.24 harris41 1268: return OK;
1269: }
1.143 matthew 1270: my $pretty_search_string = '<b>'.$ENV{'form.basicexp'}.'</b>';
1.142 matthew 1271: my $search_string = $ENV{'form.basicexp'};
1.141 matthew 1272: if ($ENV{'form.related'}) {
1.143 matthew 1273: my @New_Words;
1274: ($search_string,@New_Words) = &related_version($ENV{'form.basicexp'});
1275: if (@New_Words) {
1276: $pretty_search_string .= " with related words: <b>@New_Words</b>.";
1277: } else {
1278: $pretty_search_string .= " with no related words.";
1279: }
1.141 matthew 1280: }
1.204 matthew 1281: #
1.90 harris41 1282: # Build SQL query string based on form page
1.39 harris41 1283: my $query='';
1.178 matthew 1284: my $concatarg=join(',',
1.124 matthew 1285: ('title', 'author', 'subject', 'notes', 'abstract',
1286: 'keywords'));
1.95 harris41 1287: $concatarg='title' if $ENV{'form.titleonly'};
1.178 matthew 1288: $query=&build_SQL_query('concat_ws(" ",'.$concatarg.')',$search_string);
1.180 matthew 1289: if (defined($pretty_domains_string) && $pretty_domains_string ne '') {
1290: $pretty_search_string .= ' '.$pretty_domains_string;
1291: }
1.143 matthew 1292: $pretty_search_string .= "<br />\n";
1.183 matthew 1293: my $final_query = 'SELECT * FROM metadata WHERE '.$query;
1.204 matthew 1294: # &Apache::lonnet::logthis($final_query);
1.183 matthew 1295: return ($final_query,$pretty_search_string,
1.180 matthew 1296: $libraries_to_query);
1.22 harris41 1297: }
1298:
1.122 matthew 1299: ######################################################################
1300: ######################################################################
1301:
1302: =pod
1303:
1.204 matthew 1304: =item &related_version()
1.142 matthew 1305:
1306: Modifies an input string to include related words. Words in the string
1307: are replaced with parenthesized lists of 'OR'd words. For example
1308: "torque" is replaced with "(torque OR word1 OR word2 OR ...)".
1309:
1310: Note: Using this twice on a string is probably silly.
1311:
1312: =cut
1313:
1314: ######################################################################
1315: ######################################################################
1316: sub related_version {
1317: my $search_string = shift;
1318: my $result = $search_string;
1.143 matthew 1319: my %New_Words = ();
1.142 matthew 1320: while ($search_string =~ /(\w+)/cg) {
1321: my $word = $1;
1322: next if (lc($word) =~ /\b(or|and|not)\b/);
1323: my @Words = &Apache::loncommon::get_related_words($word);
1.143 matthew 1324: @Words = ($#Words>4? @Words[0..4] : @Words);
1325: foreach (@Words) { $New_Words{$_}++;}
1326: my $replacement = join " OR ", ($word,@Words);
1.142 matthew 1327: $result =~ s/(\b)$word(\b)/$1($replacement)$2/g;
1328: }
1.143 matthew 1329: return $result,sort(keys(%New_Words));
1.142 matthew 1330: }
1331:
1332: ######################################################################
1333: ######################################################################
1334:
1335: =pod
1336:
1.122 matthew 1337: =item &build_SQL_query()
1338:
1.126 matthew 1339: Builds a SQL query string from a logical expression with AND/OR keywords
1340: using Text::Query and &recursive_SQL_query_builder()
1341:
1.122 matthew 1342: =cut
1343:
1344: ######################################################################
1345: ######################################################################
1.98 harris41 1346: sub build_SQL_query {
1347: my ($field_name,$logic_statement)=@_;
1348: my $q=new Text::Query('abc',
1349: -parse => 'Text::Query::ParseAdvanced',
1350: -build => 'Text::Query::Build');
1351: $q->prepare($logic_statement);
1352: my $matchexp=${$q}{'matchexp'}; chomp $matchexp;
1353: my $sql_query=&recursive_SQL_query_build($field_name,$matchexp);
1354: return $sql_query;
1355: }
1356:
1.122 matthew 1357: ######################################################################
1358: ######################################################################
1359:
1360: =pod
1361:
1362: =item &build_custommetadata_query()
1363:
1.126 matthew 1364: Constructs a custom metadata query using a rather heinous regular
1365: expression.
1366:
1.122 matthew 1367: =cut
1368:
1369: ######################################################################
1370: ######################################################################
1.98 harris41 1371: sub build_custommetadata_query {
1372: my ($field_name,$logic_statement)=@_;
1373: my $q=new Text::Query('abc',
1374: -parse => 'Text::Query::ParseAdvanced',
1375: -build => 'Text::Query::BuildAdvancedString');
1376: $q->prepare($logic_statement);
1377: my $matchexp=${$q}{'-parse'}{'-build'}{'matchstring'};
1378: # quick fix to change literal into xml tag-matching
1379: # will eventually have to write a separate builder module
1.122 matthew 1380: # wordone=wordtwo becomes\<wordone\>[^\<] *wordtwo[^\<]*\<\/wordone\>
1381: $matchexp =~ s/(\w+)\\=([\w\\\+]+)?# wordone=wordtwo is changed to
1382: /\\<$1\\>?# \<wordone\>
1383: \[\^\\<\]?# [^\<]
1384: \*$2\[\^\\<\]?# *wordtwo[^\<]
1385: \*\\<\\\/$1\\>?# *\<\/wordone\>
1386: /g;
1.98 harris41 1387: return $matchexp;
1388: }
1389:
1.122 matthew 1390: ######################################################################
1391: ######################################################################
1392:
1393: =pod
1394:
1395: =item &recursive_SQL_query_build()
1396:
1.126 matthew 1397: Recursively constructs an SQL query. Takes as input $dkey and $pattern.
1398:
1.122 matthew 1399: =cut
1400:
1401: ######################################################################
1402: ######################################################################
1.98 harris41 1403: sub recursive_SQL_query_build {
1404: my ($dkey,$pattern)=@_;
1405: my @matches=($pattern=~/(\[[^\]|\[]*\])/g);
1406: return $pattern unless @matches;
1407: foreach my $match (@matches) {
1.173 matthew 1408: $match=~/\[ (\w+)\s(.*) \]/;
1409: my ($key,$value)=($1,$2);
1410: my $replacement='';
1411: if ($key eq 'literal') {
1.178 matthew 1412: $replacement="($dkey LIKE \"\%$value\%\")";
1413: } elsif (lc($key) eq 'not') {
1414: $value=~s/LIKE/NOT LIKE/;
1.173 matthew 1415: # $replacement="($dkey not like $value)";
1416: $replacement="$value";
1417: } elsif ($key eq 'and') {
1418: $value=~/(.*[\"|\)]) ([|\(|\^].*)/;
1419: $replacement="($1 AND $2)";
1420: } elsif ($key eq 'or') {
1421: $value=~/(.*[\"|\)]) ([|\(|\^].*)/;
1422: $replacement="($1 OR $2)";
1.98 harris41 1423: }
1424: substr($pattern,
1.173 matthew 1425: index($pattern,$match),
1426: length($match),
1427: $replacement);
1.98 harris41 1428: }
1429: &recursive_SQL_query_build($dkey,$pattern);
1430: }
1.22 harris41 1431:
1.122 matthew 1432: ######################################################################
1433: ######################################################################
1434:
1435: =pod
1436:
1437: =item &build_date_queries()
1438:
1.126 matthew 1439: Builds a SQL logic query to check time/date entries.
1440: Also reports errors (check for /^Incorrect/).
1441:
1.122 matthew 1442: =cut
1443:
1444: ######################################################################
1445: ######################################################################
1.98 harris41 1446: sub build_date_queries {
1447: my ($cmonth1,$cday1,$cyear1,$cmonth2,$cday2,$cyear2,
1448: $lmonth1,$lday1,$lyear1,$lmonth2,$lday2,$lyear2)=@_;
1449: my @queries;
1450: if ($cmonth1 or $cday1 or $cyear1 or $cmonth2 or $cday2 or $cyear2) {
1451: unless ($cmonth1 and $cday1 and $cyear1 and
1452: $cmonth2 and $cday2 and $cyear2) {
1453: return "Incorrect entry for the creation date. You must specify ".
1454: "a starting month, day, and year and an ending month, ".
1455: "day, and year.";
1456: }
1457: my $cnumeric1=sprintf("%d%2d%2d",$cyear1,$cmonth1,$cday1);
1458: $cnumeric1+=0;
1459: my $cnumeric2=sprintf("%d%2d%2d",$cyear2,$cmonth2,$cday2);
1460: $cnumeric2+=0;
1461: if ($cnumeric1>$cnumeric2) {
1462: return "Incorrect entry for the creation date. The starting ".
1463: "date must occur before the ending date.";
1464: }
1465: my $cquery="(creationdate BETWEEN '$cyear1-$cmonth1-$cday1' AND '".
1466: "$cyear2-$cmonth2-$cday2 23:59:59')";
1467: push @queries,$cquery;
1468: }
1469: if ($lmonth1 or $lday1 or $lyear1 or $lmonth2 or $lday2 or $lyear2) {
1470: unless ($lmonth1 and $lday1 and $lyear1 and
1471: $lmonth2 and $lday2 and $lyear2) {
1472: return "Incorrect entry for the last revision date. You must ".
1473: "specify a starting month, day, and year and an ending ".
1474: "month, day, and year.";
1475: }
1476: my $lnumeric1=sprintf("%d%2d%2d",$lyear1,$lmonth1,$lday1);
1477: $lnumeric1+=0;
1478: my $lnumeric2=sprintf("%d%2d%2d",$lyear2,$lmonth2,$lday2);
1479: $lnumeric2+=0;
1480: if ($lnumeric1>$lnumeric2) {
1481: return "Incorrect entry for the last revision date. The ".
1482: "starting date must occur before the ending date.";
1483: }
1484: my $lquery="(lastrevisiondate BETWEEN '$lyear1-$lmonth1-$lday1' AND '".
1485: "$lyear2-$lmonth2-$lday2 23:59:59')";
1486: push @queries,$lquery;
1487: }
1488: if (@queries) {
1489: return join(" AND ",@queries);
1490: }
1491: return '';
1.18 harris41 1492: }
1.6 harris41 1493:
1.122 matthew 1494: ######################################################################
1495: ######################################################################
1496:
1.144 matthew 1497: =pod
1498:
1499: =item ©right_check()
1500:
1.204 matthew 1501: Inputs: $Metadata, a hash pointer of metadata for a resource.
1502:
1503: Returns: 1 if the resource is available to the user making the query,
1504: 0 otherwise.
1505:
1.144 matthew 1506: =cut
1507:
1508: ######################################################################
1509: ######################################################################
1510: sub copyright_check {
1511: my $Metadata = shift;
1512: # Check copyright tags and skip results the user cannot use
1513: my (undef,undef,$resdom,$resname) = split('/',
1514: $Metadata->{'url'});
1515: # Check for priv
1516: if (($Metadata->{'copyright'} eq 'priv') &&
1517: (($ENV{'user.name'} ne $resname) &&
1518: ($ENV{'user.domain'} ne $resdom))) {
1519: return 0;
1520: }
1521: # Check for domain
1522: if (($Metadata->{'copyright'} eq 'domain') &&
1523: ($ENV{'user.domain'} ne $resdom)) {
1524: return 0;
1525: }
1526: return 1;
1527: }
1528:
1.151 matthew 1529: ######################################################################
1530: ######################################################################
1531:
1532: =pod
1533:
1.204 matthew 1534: =item &ensure_db_and_table()
1.151 matthew 1535:
1536: Ensure we can get lonmysql to connect to the database and the table we
1537: need exists.
1538:
1539: Inputs: $r, table id
1540:
1541: Returns: undef on error, 1 if the table exists.
1542:
1543: =cut
1544:
1545: ######################################################################
1546: ######################################################################
1547: sub ensure_db_and_table {
1548: my ($r,$table) = @_;
1549: ##
1550: ## Sanity check the table id.
1551: ##
1552: if (! defined($table) || $table eq '' || $table =~ /\D/ ) {
1553: $r->print("Unable to retrieve search results. ".
1554: "Unable to determine the table results were stored in. ".
1555: "</body></html>");
1556: return undef;
1557: }
1558: ##
1559: ## Make sure we can connect and the table exists.
1560: ##
1561: my $connection_result = &Apache::lonmysql::connect_to_db();
1562: if (!defined($connection_result)) {
1563: $r->print("Unable to connect to the MySQL database where your results".
1564: " are stored. </body></html>");
1565: &Apache::lonnet::logthis("lonsearchcat: unable to get lonmysql to".
1566: " connect to database.");
1567: &Apache::lonnet::logthis(&Apache::lonmysql::get_error());
1568: return undef;
1569: }
1570: my $table_check = &Apache::lonmysql::check_table($table);
1571: if (! defined($table_check)) {
1572: $r->print("A MySQL error has occurred.</form></body></html>");
1573: &Apache::lonnet::logthis("lonmysql was unable to determine the status".
1574: " of table ".$table);
1575: return undef;
1576: } elsif (! $table_check) {
1577: $r->print("The table of results could not be found.");
1578: &Apache::lonnet::logthis("The user requested a table, ".$table.
1579: ", that could not be found.");
1580: return undef;
1581: }
1582: return 1;
1583: }
1584:
1585: ######################################################################
1586: ######################################################################
1587:
1588: =pod
1589:
1.204 matthew 1590: =item &print_sort_form()
1591:
1592: The sort feature is not implemented at this time. This form just prints
1593: a link to change the search query.
1.151 matthew 1594:
1595: =cut
1596:
1597: ######################################################################
1598: ######################################################################
1599: sub print_sort_form {
1600: my ($r,$pretty_query_string) = @_;
1.196 matthew 1601: my $bodytag=&Apache::loncommon::bodytag(undef,undef,undef,1);
1.151 matthew 1602: ##
1.187 www 1603: my %SortableFields=&Apache::lonlocal::texthash(
1604: id => 'Default',
1.151 matthew 1605: title => 'Title',
1606: author => 'Author',
1607: subject => 'Subject',
1608: url => 'URL',
1609: version => 'Version Number',
1610: mime => 'Mime type',
1611: lang => 'Language',
1612: owner => 'Owner/Publisher',
1613: copyright => 'Copyright',
1614: hostname => 'Host',
1615: creationdate => 'Creation Date',
1.187 www 1616: lastrevisiondate => 'Revision Date'
1.151 matthew 1617: );
1618: ##
1619: my $table = $ENV{'form.table'};
1620: return if (! &ensure_db_and_table($r,$table));
1621: ##
1622: ## Get the number of results
1623: ##
1624: my $total_results = &Apache::lonmysql::number_of_rows($table);
1625: if (! defined($total_results)) {
1626: $r->print("A MySQL error has occurred.</form></body></html>");
1627: &Apache::lonnet::logthis("lonmysql was unable to determine the number".
1628: " of rows in table ".$table);
1629: &Apache::lonnet::logthis(&Apache::lonmysql::get_error());
1630: return;
1631: }
1632: my $result;
1633: $result.=<<END;
1634: <html>
1635: <head>
1636: <script>
1637: function change_sort() {
1638: var newloc = "/adm/searchcat?phase=results";
1639: newloc += "&persistent_db_id=$ENV{'form.persistent_db_id'}";
1640: newloc += "&sortby=";
1641: newloc += document.forms.statusform.elements.sortby.value;
1642: parent.resultsframe.location= newloc;
1643: }
1644: </script>
1645: <title>Results</title>
1646: </head>
1.155 matthew 1647: $bodytag
1.151 matthew 1648: <form name="statusform" action="" method="post">
1.153 matthew 1649: <input type="hidden" name="Queue" value="" />
1.151 matthew 1650: END
1651:
1652: #<h2>Sort Results</h2>
1653: #Sort by: <select size="1" name="sortby" onchange="javascript:change_sort();">
1654: # $ENV{'form.sortby'} = 'id' if (! defined($ENV{'form.sortby'}));
1655: # foreach (keys(%SortableFields)) {
1656: # $result.="<option name=\"$_\"";
1657: # if ($_ eq $ENV{'form.sortby'}) {
1658: # $result.=" selected ";
1659: # }
1660: # $result.=" >$SortableFields{$_}</option>\n";
1661: # }
1662: # $result.="</select>\n";
1663: my $revise = &revise_button();
1664: $result.=<<END;
1665: <p>
1666: There are $total_results matches to your query. $revise
1667: </p><p>
1668: Search:$pretty_query_string
1669: </p>
1670: </form>
1671: </body>
1672: </html>
1673: END
1674: $r->print($result);
1675: return;
1676: }
1677:
1.144 matthew 1678: #####################################################################
1679: #####################################################################
1680:
1681: =pod
1682:
1683: =item MySQL Table Description
1684:
1685: MySQL table creation requires a precise description of the data to be
1686: stored. The use of the correct types to hold data is vital to efficient
1687: storage and quick retrieval of records. The columns must be described in
1688: the following format:
1689:
1690: =cut
1691:
1.170 matthew 1692: #####################################################################
1693: #####################################################################
1.204 matthew 1694: #
1695: # These should probably be scoped but I don't have time right now...
1696: #
1697: my @Datatypes;
1698: my @Fullindicies;
1.147 matthew 1699:
1.144 matthew 1700: ######################################################################
1701: ######################################################################
1702:
1703: =pod
1704:
1.146 matthew 1705: =item &create_results_table()
1706:
1707: Creates the table of search results by calling lonmysql. Stores the
1708: table id in $ENV{'form.table'}
1709:
1710: Inputs: none.
1711:
1712: Returns: the identifier of the table on success, undef on error.
1713:
1714: =cut
1715:
1716: ######################################################################
1717: ######################################################################
1.204 matthew 1718: sub set_up_table_structure {
1719: my ($datatypes,$fullindicies) =
1720: &LONCAPA::lonmetadata::describe_metadata_storage();
1.212 ! matthew 1721: # Copy the table description before modifying it...
! 1722: @Datatypes = @{$datatypes};
! 1723: unshift(@Datatypes,{name => 'id',
1.204 matthew 1724: type => 'MEDIUMINT',
1725: restrictions => 'UNSIGNED NOT NULL',
1726: primary_key => 'yes',
1727: auto_inc => 'yes' });
1728: @Fullindicies = @{$fullindicies};
1729: return;
1730: }
1731:
1.146 matthew 1732: sub create_results_table {
1.204 matthew 1733: &set_up_table_structure();
1.146 matthew 1734: my $table = &Apache::lonmysql::create_table
1.170 matthew 1735: ( { columns => \@Datatypes,
1.172 matthew 1736: FULLTEXT => [{'columns' => \@Fullindicies},],
1.146 matthew 1737: } );
1738: if (defined($table)) {
1739: $ENV{'form.table'} = $table;
1740: return $table;
1741: }
1742: return undef; # Error...
1743: }
1.148 matthew 1744:
1.146 matthew 1745: ######################################################################
1746: ######################################################################
1747:
1748: =pod
1749:
1.150 matthew 1750: =item Search Status update functions
1.144 matthew 1751:
1.150 matthew 1752: Each of the following functions changes the values of one of the
1753: input fields used to display the search status to the user. The names
1754: should be explanatory.
1.144 matthew 1755:
1.150 matthew 1756: Inputs: Apache request handler ($r), text to display.
1.148 matthew 1757:
1.150 matthew 1758: Returns: Nothing.
1.148 matthew 1759:
1760: =over 4
1761:
1762: =item &update_count_status()
1763:
1.150 matthew 1764: =item &update_status()
1.148 matthew 1765:
1.150 matthew 1766: =item &update_seconds()
1.148 matthew 1767:
1768: =back
1769:
1770: =cut
1771:
1772: ######################################################################
1773: ######################################################################
1774: sub update_count_status {
1775: my ($r,$text) = @_;
1776: $text =~ s/\'/\\\'/g;
1777: $r->print
1778: ("<script>document.statusform.count.value = ' $text'</script>\n");
1779: $r->rflush();
1780: }
1781:
1.150 matthew 1782: sub update_status {
1.148 matthew 1783: my ($r,$text) = @_;
1784: $text =~ s/\'/\\\'/g;
1785: $r->print
1.150 matthew 1786: ("<script>document.statusform.status.value = ' $text'</script>\n");
1.148 matthew 1787: $r->rflush();
1788: }
1789:
1.150 matthew 1790: sub update_seconds {
1.148 matthew 1791: my ($r,$text) = @_;
1792: $text =~ s/\'/\\\'/g;
1793: $r->print
1.150 matthew 1794: ("<script>document.statusform.seconds.value = ' $text'</script>\n");
1.148 matthew 1795: $r->rflush();
1796: }
1797:
1798: ######################################################################
1799: ######################################################################
1800:
1801: =pod
1802:
1.204 matthew 1803: =item &revise_button()
1.151 matthew 1804:
1805: Inputs: None
1806:
1807: Returns: html string for a 'revise search' button.
1808:
1809: =cut
1810:
1811: ######################################################################
1812: ######################################################################
1813: sub revise_button {
1814: my $revise_phase = 'disp_basic';
1815: $revise_phase = 'disp_adv' if ($ENV{'form.searchmode'} eq 'advanced');
1816: my $newloc = '/adm/searchcat'.
1817: '?persistent_db_id='.$ENV{'form.persistent_db_id'}.
1.158 matthew 1818: '&cleargroupsort=1'.
1.151 matthew 1819: '&phase='.$revise_phase;
1820: my $result = qq{<input type="button" value="Revise search" name="revise"} .
1821: qq{ onClick="parent.location='$newloc';" /> };
1822: return $result;
1823: }
1824:
1825: ######################################################################
1826: ######################################################################
1827:
1828: =pod
1829:
1.204 matthew 1830: =item &run_search()
1831:
1832: Executes a search query by sending it the the other servers and putting the
1833: results into MySQL.
1.144 matthew 1834:
1835: =cut
1836:
1837: ######################################################################
1838: ######################################################################
1839: sub run_search {
1.146 matthew 1840: my ($r,$query,$customquery,$customshow,$serverlist,$pretty_string) = @_;
1.196 matthew 1841: my $bodytag=&Apache::loncommon::bodytag(undef,undef,undef,1);
1.150 matthew 1842: my $connection = $r->connection;
1.144 matthew 1843: #
1.145 matthew 1844: # Timing variables
1845: #
1846: my $starttime = time;
1.151 matthew 1847: my $max_time = 30; # seconds for the search to complete
1.145 matthew 1848: #
1.146 matthew 1849: # Print run_search header
1850: #
1.151 matthew 1851: $r->print(<<END);
1852: <html>
1853: <head><title>Search Status</title></head>
1.155 matthew 1854: $bodytag
1.151 matthew 1855: <form name="statusform" action="" method="post">
1856: <input type="hidden" name="Queue" value="" />
1857: END
1858: # Check to see if $pretty_string has more than one carriage return.
1859: # Assume \n s are following <br /> s and truncate the value.
1860: # (there is probably a better way)...
1.152 matthew 1861: my @Lines = split /<br \/>/,$pretty_string;
1862: if (@Lines > 2) {
1863: $pretty_string = join '<br \>',(@Lines[0..2],'....<br />');
1.151 matthew 1864: }
1.187 www 1865: $r->print(&mt("Search").": ".$pretty_string);
1.146 matthew 1866: $r->rflush();
1867: #
1.145 matthew 1868: # Determine the servers we need to contact.
1.144 matthew 1869: my @Servers_to_contact;
1870: if (defined($serverlist)) {
1.152 matthew 1871: if (ref($serverlist) eq 'ARRAY') {
1872: @Servers_to_contact = @$serverlist;
1873: } else {
1874: @Servers_to_contact = ($serverlist);
1875: }
1.144 matthew 1876: } else {
1877: @Servers_to_contact = sort(keys(%Apache::lonnet::libserv));
1878: }
1879: my %Server_status;
1.146 matthew 1880: my $table =$ENV{'form.table'};
1.150 matthew 1881: if (! defined($table) || $table eq '' || $table =~ /\D/ ) {
1.147 matthew 1882: $r->print("Unable to determine table id to store search results in.".
1.148 matthew 1883: "The search has been aborted.</body></html>");
1.147 matthew 1884: return;
1885: }
1886: my $table_status = &Apache::lonmysql::check_table($table);
1887: if (! defined($table_status)) {
1888: $r->print("Unable to determine status of table.</body></html>");
1889: &Apache::lonnet::logthis("Bogus table id of $table for ".
1890: "$ENV{'user.name'} @ $ENV{'user.domain'}");
1891: &Apache::lonnet::logthis("lonmysql error = ".
1.144 matthew 1892: &Apache::lonmysql::get_error());
1.147 matthew 1893: return;
1894: }
1895: if (! $table_status) {
1.204 matthew 1896: &Apache::lonnet::logthis("lonmysql error = ".
1897: &Apache::lonmysql::get_error());
1898: &Apache::lonnet::logthis("lonmysql debug = ".
1899: &Apache::lonmysql::get_debug());
1900: &Apache::lonnet::logthis('table status = "'.$table_status.'"');
1.147 matthew 1901: $r->print("The table id,$table, we tried to use is invalid.".
1.148 matthew 1902: "The search has been aborted.</body></html>");
1.144 matthew 1903: return;
1904: }
1.145 matthew 1905: ##
1.146 matthew 1906: ## Prepare for the big loop.
1.144 matthew 1907: my $hitcountsum;
1908: my $server;
1909: my $status;
1.151 matthew 1910: my $revise = &revise_button();
1.148 matthew 1911: $r->print(<<END);
1912: <table>
1.151 matthew 1913: <tr><th>Status</th><th>Total Matches</th><th>Time Remaining</th><th></th></tr>
1.148 matthew 1914: <tr>
1.150 matthew 1915: <td><input type="text" name="status" value="" size="30" /></td>
1916: <td><input type="text" name="count" value="" size="10" /></td>
1917: <td><input type="text" name="seconds" value="" size="8" /></td>
1.151 matthew 1918: <td>$revise</td>
1.148 matthew 1919: </tr>
1920: </table>
1921: </form>
1922: END
1923: $r->rflush();
1.150 matthew 1924: my $time_remaining = $max_time - (time - $starttime) ;
1.210 matthew 1925: $time_remaining = 0 if ($time_remaining <0);
1.150 matthew 1926: my $last_time = $time_remaining;
1927: &update_seconds($r,$time_remaining);
1.178 matthew 1928: &update_status($r,'contacting '.$Servers_to_contact[0]);
1.159 matthew 1929: while (($time_remaining > 0) &&
1.144 matthew 1930: ((@Servers_to_contact) || keys(%Server_status))) {
1.212 ! matthew 1931: $time_remaining = $max_time - (time - $starttime) ;
1.210 matthew 1932: &update_seconds($r,$time_remaining);
1.144 matthew 1933: # Send out a search request if it needs to be done.
1934: if (@Servers_to_contact) {
1935: # Contact one server
1936: my $server = shift(@Servers_to_contact);
1.189 www 1937: &update_status($r,&mt('contacting').' '.$server);
1.144 matthew 1938: my $reply=&Apache::lonnet::metadata_query($query,$customquery,
1939: $customshow,[$server]);
1940: ($server) = keys(%$reply);
1941: $Server_status{$server} = $reply->{$server};
1942: } else {
1.150 matthew 1943: # wait a sec. to give time for files to be written
1944: # This sleep statement is here instead of outside the else
1945: # block because we do not want to pause if we have servers
1946: # left to contact.
1.183 matthew 1947: if (scalar (keys(%Server_status))) {
1948: &update_status($r,
1.189 www 1949: &mt('waiting on').' '.(join(' ',keys(%Server_status))));
1.183 matthew 1950: }
1.150 matthew 1951: sleep(1);
1.144 matthew 1952: }
1.159 matthew 1953: #
1954: # Loop through the servers we have contacted but do not
1955: # have results from yet, looking for results.
1.144 matthew 1956: while (my ($server,$status) = each(%Server_status)) {
1.150 matthew 1957: last if ($connection->aborted());
1.210 matthew 1958: $time_remaining = $max_time - (time - $starttime) ;
1959: $time_remaining = 0 if ($time_remaining < 0);
1960: if ($last_time - $time_remaining > 0) {
1961: $last_time = $time_remaining;
1962: &update_seconds($r,$time_remaining);
1963: }
1.144 matthew 1964: if ($status eq 'con_lost') {
1965: delete ($Server_status{$server});
1966: next;
1967: }
1968: $status=~/^([\.\w]+)$/;
1969: my $datafile=$r->dir_config('lonDaemons').'/tmp/'.$1;
1970: if (-e $datafile && ! -e "$datafile.end") {
1.189 www 1971: &update_status($r,&mt('Receiving results from').' '.$server);
1.144 matthew 1972: next;
1973: }
1.150 matthew 1974: last if ($connection->aborted());
1.144 matthew 1975: if (-e "$datafile.end") {
1.189 www 1976: &update_status($r,&mt('Reading results from').' '.$server);
1.144 matthew 1977: if (-z "$datafile") {
1978: delete($Server_status{$server});
1979: next;
1980: }
1981: my $fh;
1982: if (!($fh=Apache::File->new($datafile))) {
1.146 matthew 1983: $r->print("Unable to open search results file for ".
1.145 matthew 1984: "server $server. Omitting from search");
1.150 matthew 1985: delete($Server_status{$server});
1986: next;
1.144 matthew 1987: }
1988: # Read in the whole file.
1989: while (my $result = <$fh>) {
1.150 matthew 1990: last if ($connection->aborted());
1.144 matthew 1991: # handle custom fields? Someday we will!
1992: chomp($result);
1993: next unless $result;
1994: # Parse the result.
1995: my %Fields = &parse_raw_result($result,$server);
1996: $Fields{'hostname'} = $server;
1997: next if (! ©right_check(\%Fields));
1998: # Store the result in the mysql database
1999: my $result = &Apache::lonmysql::store_row($table,\%Fields);
2000: if (! defined($result)) {
1.146 matthew 2001: $r->print(&Apache::lonmysql::get_error());
1.144 matthew 2002: }
1.146 matthew 2003: # $r->print(&Apache::lonmysql::get_debug());
1.144 matthew 2004: $hitcountsum ++;
1.150 matthew 2005: $time_remaining = $max_time - (time - $starttime) ;
1.210 matthew 2006: $time_remaining = 0 if ($time_remaining < 0);
1.150 matthew 2007: if ($last_time - $time_remaining > 0) {
2008: &update_seconds($r,$time_remaining);
2009: $last_time = $time_remaining;
2010: }
2011: if ($hitcountsum % 50 == 0) {
2012: &update_count_status($r,$hitcountsum);
2013: }
1.144 matthew 2014: } # End of foreach (@results)
2015: $fh->close();
2016: # $server is only deleted if the results file has been
2017: # found and (successfully) opened. This may be a bad idea.
2018: delete($Server_status{$server});
2019: }
1.150 matthew 2020: last if ($connection->aborted());
1.148 matthew 2021: &update_count_status($r,$hitcountsum);
1.144 matthew 2022: }
1.150 matthew 2023: last if ($connection->aborted());
1.144 matthew 2024: # Finished looping through the servers
1.159 matthew 2025: $starttime = time if (@Servers_to_contact);
1.150 matthew 2026: $time_remaining = $max_time - (time - $starttime) ;
2027: if ($last_time - $time_remaining > 0) {
2028: $last_time = $time_remaining;
2029: &update_seconds($r,$time_remaining);
2030: }
1.144 matthew 2031: }
1.189 www 2032: &update_status($r,&mt('Search Complete').$server);
1.151 matthew 2033: &update_seconds($r,0);
1.204 matthew 2034: #
1.144 matthew 2035: &Apache::lonmysql::disconnect_from_db();
1.204 matthew 2036: #
1.144 matthew 2037: # We have run out of time or run out of servers to talk to and
2038: # results to get.
1.146 matthew 2039: $r->print("</body></html>");
1.153 matthew 2040: if ($ENV{'form.catalogmode'} ne 'groupsearch') {
2041: $r->print("<script>".
2042: "window.location='/adm/searchcat?".
2043: "phase=sort&".
2044: "persistent_db_id=$ENV{'form.persistent_db_id'}';".
2045: "</script>");
2046: }
1.144 matthew 2047: return;
2048: }
2049:
2050: ######################################################################
2051: ######################################################################
1.204 matthew 2052:
1.144 matthew 2053: =pod
2054:
1.204 matthew 2055: =item &prev_next_buttons()
2056:
2057: Returns html for the previous and next buttons on the search results page.
1.144 matthew 2058:
2059: =cut
2060:
2061: ######################################################################
2062: ######################################################################
1.146 matthew 2063: sub prev_next_buttons {
1.145 matthew 2064: my ($current_min,$show,$total,$parms) = @_;
2065: return '' if ($show eq 'all'); # No links if you get them all at once.
2066: my $links;
2067: ##
2068: ## Prev
2069: my $prev_min = $current_min - $show;
1.151 matthew 2070: $prev_min = 1 if $prev_min < 1;
1.145 matthew 2071: if ($prev_min < $current_min) {
1.204 matthew 2072: $links .=
2073: qq{<a href="/adm/searchcat?$parms&start=$prev_min&show=$show">}.
2074: &mt('prev').'</a>';
1.146 matthew 2075: } else {
1.204 matthew 2076: $links .= &mt('prev');
1.145 matthew 2077: }
2078: ##
2079: ## Pages.... Someday.
2080: ##
1.204 matthew 2081: $links .=
2082: qq{
2083: <a href="/adm/searchcat?$parms&start=$current_min&$show=$show">}.
2084: &mt('reload').'</a>';
1.145 matthew 2085: ##
2086: ## Next
2087: my $next_min = $current_min + $show;
1.146 matthew 2088: $next_min = $current_min if ($next_min > $total);
1.145 matthew 2089: if ($next_min != $current_min) {
1.204 matthew 2090: $links .=
2091: qq{
2092: <a href="/adm/searchcat?$parms&start=$next_min&show=$show">}.
2093: &mt('next').'</a>';
1.146 matthew 2094: } else {
1.204 matthew 2095: $links .= ' '.&mt('next');
1.144 matthew 2096: }
1.145 matthew 2097: return $links;
1.144 matthew 2098: }
1.204 matthew 2099:
1.144 matthew 2100: ######################################################################
2101: ######################################################################
2102:
2103: =pod
2104:
1.204 matthew 2105: =item &display_results()
2106:
2107: Prints the results out for selection and perusal.
1.144 matthew 2108:
2109: =cut
2110:
2111: ######################################################################
2112: ######################################################################
2113: sub display_results {
1.196 matthew 2114: my ($r,$importbutton,$closebutton,$diropendb) = @_;
1.150 matthew 2115: my $connection = $r->connection;
2116: $r->print(&search_results_header($importbutton,$closebutton));
1.144 matthew 2117: ##
2118: ## Set viewing function
2119: ##
2120: my $viewfunction = $Views{$ENV{'form.viewselect'}};
2121: if (!defined($viewfunction)) {
2122: $r->print("Internal Error - Bad view selected.\n");
2123: $r->rflush();
2124: return;
2125: }
2126: ##
1.158 matthew 2127: ## $checkbox_num is a count of the number of checkboxes output on the
2128: ## page this is used only during catalogmode=groupsearch.
2129: my $checkbox_num = 0;
2130: ##
1.144 matthew 2131: ## Get the catalog controls setup
2132: ##
1.146 matthew 2133: my $action = "/adm/searchcat?phase=results";
2134: ##
1.204 matthew 2135: ## Deal with groupsearch by opening the groupsearch db file.
1.146 matthew 2136: if ($ENV{'form.catalogmode'} eq 'groupsearch') {
2137: if (! tie(%groupsearch_db,'GDBM_File',$diropendb,
1.148 matthew 2138: &GDBM_WRCREAT(),0640)) {
1.150 matthew 2139: $r->print('Unable to store import results.</form></body></html>');
1.146 matthew 2140: $r->rflush();
2141: return;
2142: }
1.144 matthew 2143: }
1.145 matthew 2144: ##
2145: ## Prepare the table for querying
1.144 matthew 2146: my $table = $ENV{'form.table'};
1.151 matthew 2147: return if (! &ensure_db_and_table($r,$table));
1.145 matthew 2148: ##
2149: ## Get the number of results
2150: my $total_results = &Apache::lonmysql::number_of_rows($table);
2151: if (! defined($total_results)) {
1.150 matthew 2152: $r->print("A MySQL error has occurred.</form></body></html>");
1.145 matthew 2153: &Apache::lonnet::logthis("lonmysql was unable to determine the number".
2154: " of rows in table ".$table);
2155: &Apache::lonnet::logthis(&Apache::lonmysql::get_error());
2156: return;
2157: }
2158: ##
2159: ## Determine how many results we need to get
1.151 matthew 2160: $ENV{'form.start'} = 1 if (! exists($ENV{'form.start'}));
2161: $ENV{'form.show'} = 'all' if (! exists($ENV{'form.show'}));
1.146 matthew 2162: my $min = $ENV{'form.start'};
1.145 matthew 2163: my $max;
2164: if ($ENV{'form.show'} eq 'all') {
2165: $max = $total_results ;
2166: } else {
1.151 matthew 2167: $max = $min + $ENV{'form.show'} - 1;
1.146 matthew 2168: $max = $total_results if ($max > $total_results);
1.145 matthew 2169: }
2170: ##
2171: ## Output links (if necessary) for 'prev' and 'next' pages.
1.146 matthew 2172: $r->print
1.148 matthew 2173: ('<center>'.
1.146 matthew 2174: &prev_next_buttons($min,$ENV{'form.show'},$total_results,
2175: "table=".$ENV{'form.table'}.
2176: "&phase=results".
2177: "&persistent_db_id=".$ENV{'form.persistent_db_id'})
1.148 matthew 2178: ."</center>\n"
1.146 matthew 2179: );
1.150 matthew 2180: if ($total_results == 0) {
1.181 matthew 2181: $r->print('<meta HTTP-EQUIV="Refresh" CONTENT="1">'.
1.187 www 2182: '<h3>'.&mt('There are currently no results').'.</h3>'.
1.150 matthew 2183: "</form></body></html>");
2184: return;
2185: } else {
2186: $r->print
2187: ("<center>Results $min to $max out of $total_results</center>\n");
2188: }
1.145 matthew 2189: ##
2190: ## Get results from MySQL table
2191: my @Results = &Apache::lonmysql::get_rows($table,
1.151 matthew 2192: 'id>='.$min.' AND id<='.$max);
1.145 matthew 2193: ##
2194: ## Loop through the results and output them.
1.144 matthew 2195: foreach my $row (@Results) {
1.150 matthew 2196: if ($connection->aborted()) {
1.162 www 2197: &cleanup();
1.150 matthew 2198: return;
2199: }
1.144 matthew 2200: my %Fields = %{&parse_row(@$row)};
1.145 matthew 2201: my $output="<p>\n";
1.210 matthew 2202: if (! defined($Fields{'title'}) || $Fields{'title'} eq '') {
2203: $Fields{'title'} = 'Untitled';
2204: }
1.158 matthew 2205: my $prefix=&catalogmode_output($Fields{'title'},$Fields{'url'},
2206: $Fields{'id'},$checkbox_num++);
1.212 ! matthew 2207:
1.144 matthew 2208: # Render the result into html
1.150 matthew 2209: $output.= &$viewfunction($prefix,%Fields);
1.145 matthew 2210: # Print them out as they come in.
1.144 matthew 2211: $r->print($output);
2212: $r->rflush();
2213: }
2214: if (@Results < 1) {
1.187 www 2215: $r->print(&mt("There were no results matching your query"));
1.147 matthew 2216: } else {
2217: $r->print
1.148 matthew 2218: ('<center>'.
1.147 matthew 2219: &prev_next_buttons($min,$ENV{'form.show'},$total_results,
2220: "table=".$ENV{'form.table'}.
2221: "&phase=results".
2222: "&persistent_db_id=".
2223: $ENV{'form.persistent_db_id'})
1.148 matthew 2224: ."</center>\n"
1.147 matthew 2225: );
1.144 matthew 2226: }
1.150 matthew 2227: $r->print("</form></body></html>");
1.144 matthew 2228: $r->rflush();
1.150 matthew 2229: untie %groupsearch_db if (tied(%groupsearch_db));
1.144 matthew 2230: return;
2231: }
2232:
2233: ######################################################################
2234: ######################################################################
2235:
2236: =pod
2237:
1.158 matthew 2238: =item &catalogmode_output($title,$url,$fnum,$checkbox_num)
1.145 matthew 2239:
2240: Returns html needed for the various catalog modes. Gets inputs from
1.158 matthew 2241: $ENV{'form.catalogmode'}. Stores data in %groupsearch_db.
1.145 matthew 2242:
2243: =cut
2244:
2245: ######################################################################
2246: ######################################################################
2247: sub catalogmode_output {
2248: my $output = '';
1.158 matthew 2249: my ($title,$url,$fnum,$checkbox_num) = @_;
1.145 matthew 2250: if ($ENV{'form.catalogmode'} eq 'interactive') {
1.150 matthew 2251: $title=~ s/\'/\\\'/g;
1.145 matthew 2252: if ($ENV{'form.catalogmode'} eq 'interactive') {
2253: $output.=<<END
2254: <font size='-1'><INPUT TYPE="button" NAME="returnvalues" VALUE="SELECT"
2255: onClick="javascript:select_data('$title','$url')">
2256: </font>
2257: END
2258: }
1.150 matthew 2259: } elsif ($ENV{'form.catalogmode'} eq 'groupsearch') {
1.145 matthew 2260: $groupsearch_db{"pre_${fnum}_link"}=$url;
2261: $groupsearch_db{"pre_${fnum}_title"}=$title;
2262: $output.=<<END;
2263: <font size='-1'>
2264: <input type="checkbox" name="returnvalues" value="SELECT"
1.158 matthew 2265: onClick="javascript:queue($checkbox_num,$fnum)" />
1.145 matthew 2266: </font>
2267: END
2268: }
2269: return $output;
2270: }
2271: ######################################################################
2272: ######################################################################
2273:
2274: =pod
2275:
1.204 matthew 2276: =item &parse_row()
1.144 matthew 2277:
2278: Parse a row returned from the database.
2279:
2280: =cut
2281:
2282: ######################################################################
2283: ######################################################################
2284: sub parse_row {
2285: my @Row = @_;
2286: my %Fields;
1.204 matthew 2287: if (! scalar(@Datatypes)) {
2288: &set_up_table_structure();
2289: }
1.144 matthew 2290: for (my $i=0;$i<=$#Row;$i++) {
1.170 matthew 2291: $Fields{$Datatypes[$i]->{'name'}}=&Apache::lonnet::unescape($Row[$i]);
1.144 matthew 2292: }
2293: $Fields{'language'} =
1.198 www 2294: &Apache::loncommon::languagedescription($Fields{'language'});
1.144 matthew 2295: $Fields{'copyrighttag'} =
2296: &Apache::loncommon::copyrightdescription($Fields{'copyright'});
2297: $Fields{'mimetag'} =
2298: &Apache::loncommon::filedescription($Fields{'mime'});
2299: return \%Fields;
2300: }
1.126 matthew 2301:
2302: ###########################################################
2303: ###########################################################
2304:
2305: =pod
2306:
2307: =item &parse_raw_result()
2308:
2309: Takes a line from the file of results and parse it. Returns a hash
1.198 www 2310: with keys according to column labels
1.126 matthew 2311:
2312: In addition, the following tags are set by calling the appropriate
1.198 www 2313: lonnet function: 'language', 'copyrighttag', 'mimetag'.
1.126 matthew 2314:
2315: The 'title' field is set to "Untitled" if the title field is blank.
2316:
2317: 'abstract' and 'keywords' are truncated to 200 characters.
2318:
2319: =cut
2320:
2321: ###########################################################
2322: ###########################################################
2323: sub parse_raw_result {
2324: my ($result,$hostname) = @_;
1.204 matthew 2325: # conclude from self to others regarding fields
1.206 matthew 2326: my %Fields=&LONCAPA::lonmetadata::metadata_col_to_hash
2327: (map {
2328: &Apache::lonnet::unescape($_);
2329: } (split(/\,/,$result)) );
1.126 matthew 2330: return %Fields;
2331: }
2332:
2333: ###########################################################
2334: ###########################################################
2335:
2336: =pod
2337:
2338: =item &handle_custom_fields()
2339:
2340: =cut
2341:
2342: ###########################################################
2343: ###########################################################
2344: sub handle_custom_fields {
2345: my @results = @{shift()};
2346: my $customshow='';
2347: my $extrashow='';
2348: my @customfields;
2349: if ($ENV{'form.customshow'}) {
2350: $customshow=$ENV{'form.customshow'};
2351: $customshow=~s/[^\w\s]//g;
2352: my @fields=map {
2353: "<font color=\"#008000\">$_:</font><!-- $_ -->";
2354: } split(/\s+/,$customshow);
2355: @customfields=split(/\s+/,$customshow);
2356: if ($customshow) {
2357: $extrashow="<ul><li>".join("</li><li>",@fields)."</li></ul>\n";
2358: }
2359: }
2360: my $customdata='';
2361: my %customhash;
2362: foreach my $result (@results) {
2363: if ($result=~/^(custom\=.*)$/) { # grab all custom metadata
2364: my $tmp=$result;
2365: $tmp=~s/^custom\=//;
2366: my ($k,$v)=map {&Apache::lonnet::unescape($_);
2367: } split(/\,/,$tmp);
2368: $customhash{$k}=$v;
2369: }
2370: }
2371: return ($extrashow,\@customfields,\%customhash);
1.41 harris41 2372: }
2373:
1.122 matthew 2374: ######################################################################
2375: ######################################################################
2376:
1.125 matthew 2377: =pod
2378:
1.204 matthew 2379: =item &search_results_header()
1.125 matthew 2380:
1.130 matthew 2381: Output the proper html headers and javascript code to deal with different
2382: calling modes.
2383:
2384: Takes most inputs directly from %ENV, except $mode.
2385:
2386: =over 4
2387:
2388: =item $mode is either (at this writing) 'Basic' or 'Advanced'
2389:
2390: =back
1.126 matthew 2391:
1.130 matthew 2392: The following environment variables are checked:
1.126 matthew 2393:
2394: =over 4
2395:
2396: =item 'form.catalogmode'
2397:
2398: Checked for 'interactive' and 'groupsearch'.
2399:
2400: =item 'form.mode'
2401:
2402: Checked for existance & 'edit' mode.
2403:
2404: =item 'form.form'
2405:
1.191 albertel 2406: Contains the name of the form that has the input fields to set
2407:
1.126 matthew 2408: =item 'form.element'
2409:
1.191 albertel 2410: the name of the input field to put the URL into
2411:
2412: =item 'form.titleelement'
2413:
2414: the name of the input field to put the title into
2415:
1.126 matthew 2416: =back
2417:
1.125 matthew 2418: =cut
2419:
2420: ######################################################################
2421: ######################################################################
2422: sub search_results_header {
1.150 matthew 2423: my ($importbutton,$closebutton) = @_;
1.196 matthew 2424: my $bodytag=&Apache::loncommon::bodytag(undef,undef,undef,1);
1.125 matthew 2425: my $result = '';
2426: # output beginning of search page
2427: # conditional output of script functions dependent on the mode in
2428: # which the search was invoked
2429: if ($ENV{'form.catalogmode'} eq 'interactive'){
2430: if (! exists($ENV{'form.mode'}) || $ENV{'form.mode'} ne 'edit') {
2431: $result.=<<SCRIPT;
2432: <script type="text/javascript">
2433: function select_data(title,url) {
2434: changeTitle(title);
2435: changeURL(url);
1.150 matthew 2436: parent.close();
1.125 matthew 2437: }
2438: function changeTitle(val) {
1.153 matthew 2439: if (parent.opener.inf.document.forms.resinfo.elements.t) {
2440: parent.opener.inf.document.forms.resinfo.elements.t.value=val;
1.125 matthew 2441: }
2442: }
2443: function changeURL(val) {
1.153 matthew 2444: if (parent.opener.inf.document.forms.resinfo.elements.u) {
2445: parent.opener.inf.document.forms.resinfo.elements.u.value=val;
1.125 matthew 2446: }
2447: }
2448: </script>
2449: SCRIPT
2450: } elsif ($ENV{'form.mode'} eq 'edit') {
2451: my $form = $ENV{'form.form'};
2452: my $element = $ENV{'form.element'};
1.191 albertel 2453: my $titleelement = $ENV{'form.titleelement'};
2454: my $changetitle;
2455: if (!$titleelement) {
2456: $changetitle='function changeTitle(val) {}';
2457: } else {
2458: $changetitle=<<END;
2459: function changeTitle(val) {
2460: if (parent.targetwin.document) {
2461: parent.targetwin.document.forms["$form"].elements["$titleelement"].value=val;
2462: } else {
2463: var url = 'forms[\"$form\"].elements[\"$titleelement\"].value';
2464: alert("Unable to transfer data to "+url);
2465: }
2466: }
2467: END
2468: }
2469:
1.125 matthew 2470: $result.=<<SCRIPT;
2471: <script type="text/javascript">
2472: function select_data(title,url) {
2473: changeURL(url);
1.191 albertel 2474: changeTitle(title);
1.150 matthew 2475: parent.close();
1.125 matthew 2476: }
1.191 albertel 2477: $changetitle
1.125 matthew 2478: function changeURL(val) {
1.150 matthew 2479: if (parent.targetwin.document) {
2480: parent.targetwin.document.forms["$form"].elements["$element"].value=val;
1.125 matthew 2481: } else {
2482: var url = 'forms[\"$form\"].elements[\"$element\"].value';
2483: alert("Unable to transfer data to "+url);
2484: }
2485: }
2486: </script>
2487: SCRIPT
2488: }
2489: }
2490: $result.=<<SCRIPT if $ENV{'form.catalogmode'} eq 'groupsearch';
2491: <script type="text/javascript">
1.158 matthew 2492: function queue(checkbox_num,val) {
1.185 matthew 2493: if (document.forms.results.returnvalues.length != "undefined" &&
2494: typeof(document.forms.results.returnvalues.length) == "number") {
2495: if (document.forms.results.returnvalues[checkbox_num].checked) {
2496: parent.statusframe.document.forms.statusform.elements.Queue.value +='1a'+val+'b';
2497: } else {
2498: parent.statusframe.document.forms.statusform.elements.Queue.value +='0a'+val+'b';
2499: }
1.150 matthew 2500: } else {
1.185 matthew 2501: if (document.forms.results.returnvalues.checked) {
2502: parent.statusframe.document.forms.statusform.elements.Queue.value +='1a'+val+'b';
2503: } else {
2504: parent.statusframe.document.forms.statusform.elements.Queue.value +='0a'+val+'b';
2505: }
1.150 matthew 2506: }
1.125 matthew 2507: }
2508: function select_group() {
1.150 matthew 2509: parent.window.location=
1.125 matthew 2510: "/adm/groupsort?mode=$ENV{'form.mode'}&catalogmode=groupsearch&acts="+
1.150 matthew 2511: parent.statusframe.document.forms.statusform.elements.Queue.value;
1.125 matthew 2512: }
2513: </script>
2514: SCRIPT
1.130 matthew 2515: $result.=<<END;
2516: </head>
1.155 matthew 2517: $bodytag
1.150 matthew 2518: <form name="results" method="post" action="" >
2519: <input type="hidden" name="Queue" value="" />
2520: $importbutton
1.130 matthew 2521: END
1.125 matthew 2522: return $result;
2523: }
2524:
2525: ######################################################################
2526: ######################################################################
1.146 matthew 2527: sub search_status_header {
1.196 matthew 2528: my $bodytag=&Apache::loncommon::bodytag(undef,undef,undef,1);
1.146 matthew 2529: return <<ENDSTATUS;
2530: <html><head><title>Search Status</title></head>
1.155 matthew 2531: $bodytag
1.146 matthew 2532: <h3>Search Status</h3>
2533: Sending search request to LON-CAPA servers.<br />
2534: ENDSTATUS
2535: }
2536:
1.150 matthew 2537: sub results_link {
2538: my $basic_link = "/adm/searchcat?"."&table=".$ENV{'form.table'}.
2539: "&persistent_db_id=".$ENV{'form.persistent_db_id'};
2540: my $results_link = $basic_link."&phase=results".
1.151 matthew 2541: "&pause=1"."&start=1";
1.150 matthew 2542: return $results_link;
2543: }
2544:
1.146 matthew 2545: ######################################################################
2546: ######################################################################
2547: sub print_frames_interface {
2548: my $r = shift;
2549: my $basic_link = "/adm/searchcat?"."&table=".$ENV{'form.table'}.
2550: "&persistent_db_id=".$ENV{'form.persistent_db_id'};
2551: my $run_search_link = $basic_link."&phase=run_search";
1.150 matthew 2552: my $results_link = &results_link();
1.146 matthew 2553: my $result = <<"ENDFRAMES";
2554: <html>
2555: <head>
1.150 matthew 2556: <script>
2557: var targetwin = opener;
1.158 matthew 2558: var queue = '';
1.150 matthew 2559: </script>
1.146 matthew 2560: <title>LON-CAPA Digital Library Search Results</title>
2561: </head>
1.176 www 2562: <frameset rows="150,*">
1.146 matthew 2563: <frame name="statusframe" src="$run_search_link">
2564: <frame name="resultsframe" src="$results_link">
2565: </frameset>
2566: </html>
2567: ENDFRAMES
2568:
2569: $r->print($result);
2570: return;
2571: }
2572:
2573: ######################################################################
2574: ######################################################################
1.125 matthew 2575:
1.122 matthew 2576: =pod
2577:
2578: =item Metadata Viewing Functions
2579:
2580: Output is a HTML-ified string.
1.204 matthew 2581:
1.122 matthew 2582: Input arguments are title, author, subject, url, keywords, version,
2583: notes, short abstract, mime, language, creation date,
1.126 matthew 2584: last revision date, owner, copyright, hostname, and
1.122 matthew 2585: extra custom metadata to show.
2586:
2587: =over 4
2588:
2589: =item &detailed_citation_view()
2590:
2591: =cut
2592:
2593: ######################################################################
2594: ######################################################################
1.50 harris41 2595: sub detailed_citation_view {
1.150 matthew 2596: my ($prefix,%values) = @_;
1.192 albertel 2597: my $icon=&Apache::loncommon::icon($values{'url'});
1.50 harris41 2598: my $result=<<END;
1.192 albertel 2599: <b>$prefix<img src="$icon" /><a href="http://$ENV{'HTTP_HOST'}$values{'url'}"
1.150 matthew 2600: target='search_preview'>$values{'title'}</a></b>
1.56 harris41 2601: <p>
1.130 matthew 2602: <b>$values{'author'}</b>, <i>$values{'owner'}</i><br />
2603:
2604: <b>Subject: </b> $values{'subject'}<br />
2605: <b>Keyword(s): </b> $values{'keywords'}<br />
2606: <b>Notes: </b> $values{'notes'}<br />
2607: <b>MIME Type: </b> $values{'mimetag'}<br />
2608: <b>Language: </b> $values{'language'}<br />
1.198 www 2609: <b>Copyright/Distribution:</b> $values{'copyrighttag'}<br />
1.78 harris41 2610: </p>
1.126 matthew 2611: $values{'extrashow'}
1.78 harris41 2612: <p>
1.126 matthew 2613: $values{'shortabstract'}
1.50 harris41 2614: </p>
1.150 matthew 2615: <hr align='left' width='200' noshade />
1.50 harris41 2616: END
2617: return $result;
2618: }
2619:
1.122 matthew 2620: ######################################################################
2621: ######################################################################
2622:
2623: =pod
2624:
2625: =item &summary_view()
2626:
2627: =cut
2628: ######################################################################
2629: ######################################################################
1.50 harris41 2630: sub summary_view {
1.150 matthew 2631: my ($prefix,%values) = @_;
1.192 albertel 2632: my $icon=&Apache::loncommon::icon($values{'url'});
1.50 harris41 2633: my $result=<<END;
1.192 albertel 2634: $prefix<img src="$icon" /><a href="http://$ENV{'HTTP_HOST'}$values{'url'}"
1.126 matthew 2635: target='search_preview'>$values{'author'}</a><br />
2636: $values{'title'}<br />
2637: $values{'owner'} -- $values{'lastrevisiondate'}<br />
2638: $values{'copyrighttag'}<br />
2639: $values{'extrashow'}
1.50 harris41 2640: </p>
1.150 matthew 2641: <hr align='left' width='200' noshade />
1.50 harris41 2642: END
2643: return $result;
2644: }
2645:
1.122 matthew 2646: ######################################################################
2647: ######################################################################
2648:
2649: =pod
2650:
1.150 matthew 2651: =item &compact_view()
2652:
2653: =cut
2654:
2655: ######################################################################
2656: ######################################################################
2657: sub compact_view {
2658: my ($prefix,%values) = @_;
1.192 albertel 2659: my $icon=&Apache::loncommon::icon($values{'url'});
1.150 matthew 2660: my $result=<<END;
1.192 albertel 2661: $prefix <img src="$icon" /> <a href="http://$ENV{'HTTP_HOST'}$values{'url'}" target='search_preview'>
1.150 matthew 2662: $values{'title'}</a>
2663: <b>$values{'author'}</b><br />
2664: END
2665: return $result;
2666: }
2667:
2668:
2669: ######################################################################
2670: ######################################################################
2671:
2672: =pod
2673:
1.122 matthew 2674: =item &fielded_format_view()
2675:
2676: =cut
2677:
2678: ######################################################################
2679: ######################################################################
1.50 harris41 2680: sub fielded_format_view {
1.150 matthew 2681: my ($prefix,%values) = @_;
1.192 albertel 2682: my $icon=&Apache::loncommon::icon($values{'url'});
1.50 harris41 2683: my $result=<<END;
1.192 albertel 2684: $prefix <img src="$icon" />
1.126 matthew 2685: <b>URL: </b> <a href="http://$ENV{'HTTP_HOST'}$values{'url'}"
2686: target='search_preview'>$values{'url'}</a>
1.56 harris41 2687: <br />
1.126 matthew 2688: <b>Title:</b> $values{'title'}<br />
2689: <b>Author(s):</b> $values{'author'}<br />
2690: <b>Subject:</b> $values{'subject'}<br />
2691: <b>Keyword(s):</b> $values{'keywords'}<br />
2692: <b>Notes:</b> $values{'notes'}<br />
2693: <b>MIME Type:</b> $values{'mimetag'}<br />
2694: <b>Language:</b> $values{'language'}<br />
2695: <b>Creation Date:</b> $values{'creationdate'}<br />
2696: <b>Last Revision Date:</b> $values{'lastrevisiondate'}<br />
2697: <b>Publisher/Owner:</b> $values{'owner'}<br />
2698: <b>Copyright/Distribution:</b> $values{'copyrighttag'}<br />
2699: <b>Repository Location:</b> $values{'hostname'}<br />
2700: <b>Abstract:</b> $values{'shortabstract'}<br />
2701: $values{'extrashow'}
1.50 harris41 2702: </p>
1.150 matthew 2703: <hr align='left' width='200' noshade />
1.50 harris41 2704: END
2705: return $result;
2706: }
2707:
1.122 matthew 2708: ######################################################################
2709: ######################################################################
2710:
2711: =pod
2712:
2713: =item &xml_sgml_view()
2714:
2715: =back
2716:
2717: =cut
2718:
2719: ######################################################################
2720: ######################################################################
1.50 harris41 2721: sub xml_sgml_view {
1.150 matthew 2722: my ($prefix,%values) = @_;
1.212 ! matthew 2723: my $xml = <<END;
! 2724: <LonCapaResource>
! 2725: <url>$values{'url'}</url>
! 2726: <title>$values{'title'}</title>
! 2727: <author>$values{'author'}</author>
! 2728: <subject>$values{'subject'}</subject>
! 2729: <keywords>$values{'keywords'}</keywords>
! 2730: <notes>$values{'notes'}</notes>
! 2731: <mimeInfo>
! 2732: <mime>$values{'mime'}</mime>
! 2733: <mimetag>$values{'mimetag'}</mimetag>
! 2734: </mimeInfo>
! 2735: <languageInfo>
! 2736: <language>$values{'language'}</language>
! 2737: <languagetag>$values{'languagetag'}</languagetag>
! 2738: </languageInfo>
! 2739: <creationdate>$values{'creationdate'}</creationdate>
! 2740: <lastrevisiondate>$values{'lastrevisiondate'}</lastrevisiondate>
! 2741: <owner>$values{'owner'}</owner>
! 2742: <copyrightInfo>
! 2743: <copyright>$values{'copyright'}</copyright>
! 2744: <copyrighttag>$values{'copyrighttag'}</copyrighttag>
! 2745: </copyrightInfo>
! 2746: <repositoryLocation>$values{'hostname'}</repositoryLocation>
! 2747: <shortabstract>$values{'shortabstract'}</shortabstract>
! 2748: </LonCapaResource>
! 2749: END
! 2750: $xml = &HTML::Entities::encode($xml,'<>&');
1.50 harris41 2751: my $result=<<END;
1.150 matthew 2752: $prefix
1.56 harris41 2753: <pre>
1.212 ! matthew 2754: $xml
1.56 harris41 2755: </pre>
1.126 matthew 2756: $values{'extrashow'}
1.150 matthew 2757: <hr align='left' width='200' noshade />
1.50 harris41 2758: END
2759: return $result;
1.60 harris41 2760: }
2761:
1.122 matthew 2762: ######################################################################
2763: ######################################################################
2764:
2765: =pod
2766:
2767: =item &filled() see if field is filled.
2768:
2769: =cut
2770:
2771: ######################################################################
2772: ######################################################################
1.98 harris41 2773: sub filled {
2774: my ($field)=@_;
2775: if ($field=~/\S/ && $field ne 'any') {
1.204 matthew 2776: return 1;
2777: } else {
2778: return 0;
1.61 harris41 2779: }
1.60 harris41 2780: }
2781:
1.122 matthew 2782: ######################################################################
2783: ######################################################################
2784:
2785: =pod
2786:
2787: =item &output_blank_field_error()
2788:
1.151 matthew 2789: Output a complete page that indicates the user has not filled in enough
2790: information to do a search.
2791:
2792: Inputs: $r (Apache request handle), $closebutton, $parms.
2793:
2794: Returns: nothing
2795:
2796: $parms is extra information to include in the 'Revise search request' link.
2797:
1.122 matthew 2798: =cut
2799:
2800: ######################################################################
2801: ######################################################################
1.98 harris41 2802: sub output_blank_field_error {
1.196 matthew 2803: my ($r,$closebutton,$parms,$hidden_fields)=@_;
2804: my $bodytag=&Apache::loncommon::bodytag(undef,undef,undef,1);
1.98 harris41 2805: # make query information persistent to allow for subsequent revision
2806: $r->print(<<BEGINNING);
2807: <html>
2808: <head>
2809: <title>The LearningOnline Network with CAPA</title>
2810: BEGINNING
2811: $r->print(<<RESULTS);
2812: </head>
1.155 matthew 2813: $bodytag
1.98 harris41 2814: <img align='right' src='/adm/lonIcons/lonlogos.gif' />
2815: <h1>Search Catalog</h1>
2816: <form method="post" action="/adm/searchcat">
1.145 matthew 2817: $hidden_fields
1.151 matthew 2818: <a href="/adm/searchcat?$parms&persistent_db_id=$ENV{'form.persistent_db_id'}"
1.146 matthew 2819: >Revise search request</a>
1.98 harris41 2820: $closebutton
2821: <hr />
1.151 matthew 2822: <h3>Unactionable search query.</h3>
1.98 harris41 2823: <p>
1.151 matthew 2824: You did not fill in enough information for the search to be started.
2825: You need to fill in relevant fields on the search page in order
2826: for a query to be processed.
1.98 harris41 2827: </p>
2828: </body>
2829: </html>
2830: RESULTS
2831: }
2832:
1.122 matthew 2833: ######################################################################
2834: ######################################################################
2835:
2836: =pod
2837:
2838: =item &output_date_error()
2839:
2840: Output a full html page with an error message.
2841:
1.145 matthew 2842: Inputs:
2843:
2844: $r, the request pointer.
2845: $message, the error message for the user.
2846: $closebutton, the specialized close button needed for groupsearch.
2847:
1.122 matthew 2848: =cut
2849:
2850: ######################################################################
2851: ######################################################################
1.60 harris41 2852: sub output_date_error {
1.196 matthew 2853: my ($r,$message,$closebutton,$hidden_fields)=@_;
1.60 harris41 2854: # make query information persistent to allow for subsequent revision
1.196 matthew 2855: my $bodytag=&Apache::loncommon::bodytag(undef,undef,undef,1);
1.122 matthew 2856: $r->print(<<RESULTS);
1.60 harris41 2857: <html>
2858: <head>
2859: <title>The LearningOnline Network with CAPA</title>
2860: </head>
1.155 matthew 2861: $bodytag
1.98 harris41 2862: <img align='right' src='/adm/lonIcons/lonlogos.gif' />
1.60 harris41 2863: <h1>Search Catalog</h1>
2864: <form method="post" action="/adm/searchcat">
1.145 matthew 2865: $hidden_fields
1.60 harris41 2866: <input type='button' value='Revise search request'
1.98 harris41 2867: onClick='this.form.submit();' />
1.60 harris41 2868: $closebutton
1.98 harris41 2869: <hr />
1.151 matthew 2870: <h3>Error</h3>
1.60 harris41 2871: <p>
2872: $message
2873: </p>
2874: </body>
2875: </html>
2876: RESULTS
1.101 harris41 2877: }
2878:
1.122 matthew 2879: ######################################################################
2880: ######################################################################
2881:
2882: =pod
2883:
2884: =item &start_fresh_session()
2885:
1.142 matthew 2886: Cleans the global %groupsearch_db by removing all fields which begin with
1.122 matthew 2887: 'pre_' or 'store'.
2888:
2889: =cut
2890:
2891: ######################################################################
2892: ######################################################################
1.101 harris41 2893: sub start_fresh_session {
1.142 matthew 2894: delete $groupsearch_db{'mode_catalog'};
2895: foreach (keys %groupsearch_db) {
1.101 harris41 2896: if ($_ =~ /^pre_/) {
1.142 matthew 2897: delete $groupsearch_db{$_};
1.101 harris41 2898: }
2899: if ($_ =~ /^store/) {
1.142 matthew 2900: delete $groupsearch_db{$_};
1.101 harris41 2901: }
1.109 harris41 2902: }
1.3 harris41 2903: }
1.1 www 2904:
2905: 1;
1.162 www 2906:
2907: sub cleanup {
1.163 www 2908: if (tied(%groupsearch_db)) {
2909: unless (untie(%groupsearch_db)) {
2910: &Apache::lonnet::logthis('Failed cleanup searchcat: groupsearch_db');
2911: }
2912: }
1.167 www 2913: &untiehash();
1.162 www 2914: &Apache::lonmysql::disconnect_from_db();
2915: }
1.98 harris41 2916:
1.1 www 2917: __END__
1.105 harris41 2918:
1.121 matthew 2919: =pod
1.105 harris41 2920:
1.121 matthew 2921: =back
1.105 harris41 2922:
2923: =cut
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>