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