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