Annotation of loncom/interface/loncommon.pm, revision 1.1178
1.10 albertel 1: # The LearningOnline Network with CAPA
1.1 albertel 2: # a pile of common routines
1.10 albertel 3: #
1.1178 ! raeburn 4: # $Id: loncommon.pm,v 1.1177 2014/02/20 00:56:15 raeburn Exp $
1.10 albertel 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: #
1.1 albertel 28:
29: # Makes a table out of the previous attempts
1.2 albertel 30: # Inputs result_from_symbread, user, domain, course_id
1.16 harris41 31: # Reads in non-network-related .tab files
1.1 albertel 32:
1.35 matthew 33: # POD header:
34:
1.45 matthew 35: =pod
36:
1.35 matthew 37: =head1 NAME
38:
39: Apache::loncommon - pile of common routines
40:
41: =head1 SYNOPSIS
42:
1.112 bowersj2 43: Common routines for manipulating connections, student answers,
44: domains, common Javascript fragments, etc.
1.35 matthew 45:
1.112 bowersj2 46: =head1 OVERVIEW
1.35 matthew 47:
1.112 bowersj2 48: A collection of commonly used subroutines that don't have a natural
49: home anywhere else. This collection helps remove
1.35 matthew 50: redundancy from other modules and increase efficiency of memory usage.
51:
52: =cut
53:
54: # End of POD header
1.1 albertel 55: package Apache::loncommon;
56:
57: use strict;
1.258 albertel 58: use Apache::lonnet;
1.46 matthew 59: use GDBM_File;
1.51 www 60: use POSIX qw(strftime mktime);
1.82 www 61: use Apache::lonmenu();
1.498 albertel 62: use Apache::lonenc();
1.117 www 63: use Apache::lonlocal;
1.685 tempelho 64: use Apache::lonnet();
1.139 matthew 65: use HTML::Entities;
1.334 albertel 66: use Apache::lonhtmlcommon();
67: use Apache::loncoursedata();
1.344 albertel 68: use Apache::lontexconvert();
1.444 albertel 69: use Apache::lonclonecourse();
1.1108 raeburn 70: use Apache::lonuserutils();
1.1110 raeburn 71: use Apache::lonuserstate();
1.479 albertel 72: use LONCAPA qw(:DEFAULT :match);
1.657 raeburn 73: use DateTime::TimeZone;
1.687 raeburn 74: use DateTime::Locale::Catalog;
1.1091 foxr 75: use Text::Aspell;
1.1094 raeburn 76: use Authen::Captcha;
77: use Captcha::reCAPTCHA;
1.1174 raeburn 78: use Crypt::DES;
79: use DynaLoader; # for Crypt::DES version
1.117 www 80:
1.517 raeburn 81: # ---------------------------------------------- Designs
82: use vars qw(%defaultdesign);
83:
1.22 www 84: my $readit;
85:
1.517 raeburn 86:
1.157 matthew 87: ##
88: ## Global Variables
89: ##
1.46 matthew 90:
1.643 foxr 91:
92: # ----------------------------------------------- SSI with retries:
93: #
94:
95: =pod
96:
1.648 raeburn 97: =head1 Server Side include with retries:
1.643 foxr 98:
99: =over 4
100:
1.648 raeburn 101: =item * &ssi_with_retries(resource,retries form)
1.643 foxr 102:
103: Performs an ssi with some number of retries. Retries continue either
104: until the result is ok or until the retry count supplied by the
105: caller is exhausted.
106:
107: Inputs:
1.648 raeburn 108:
109: =over 4
110:
1.643 foxr 111: resource - Identifies the resource to insert.
1.648 raeburn 112:
1.643 foxr 113: retries - Count of the number of retries allowed.
1.648 raeburn 114:
1.643 foxr 115: form - Hash that identifies the rendering options.
116:
1.648 raeburn 117: =back
118:
119: Returns:
120:
121: =over 4
122:
1.643 foxr 123: content - The content of the response. If retries were exhausted this is empty.
1.648 raeburn 124:
1.643 foxr 125: response - The response from the last attempt (which may or may not have been successful.
126:
1.648 raeburn 127: =back
128:
129: =back
130:
1.643 foxr 131: =cut
132:
133: sub ssi_with_retries {
134: my ($resource, $retries, %form) = @_;
135:
136:
137: my $ok = 0; # True if we got a good response.
138: my $content;
139: my $response;
140:
141: # Try to get the ssi done. within the retries count:
142:
143: do {
144: ($content, $response) = &Apache::lonnet::ssi($resource, %form);
145: $ok = $response->is_success;
1.650 www 146: if (!$ok) {
147: &Apache::lonnet::logthis("Failed ssi_with_retries on $resource: ".$response->is_success.', '.$response->code.', '.$response->message);
148: }
1.643 foxr 149: $retries--;
150: } while (!$ok && ($retries > 0));
151:
152: if (!$ok) {
153: $content = ''; # On error return an empty content.
154: }
155: return ($content, $response);
156:
157: }
158:
159:
160:
1.20 www 161: # ----------------------------------------------- Filetypes/Languages/Copyright
1.12 harris41 162: my %language;
1.124 www 163: my %supported_language;
1.1088 foxr 164: my %supported_codes;
1.1048 foxr 165: my %latex_language; # For choosing hyphenation in <transl..>
166: my %latex_language_bykey; # for choosing hyphenation from metadata
1.12 harris41 167: my %cprtag;
1.192 taceyjo1 168: my %scprtag;
1.351 www 169: my %fe; my %fd; my %fm;
1.41 ng 170: my %category_extensions;
1.12 harris41 171:
1.46 matthew 172: # ---------------------------------------------- Thesaurus variables
1.144 matthew 173: #
174: # %Keywords:
175: # A hash used by &keyword to determine if a word is considered a keyword.
176: # $thesaurus_db_file
177: # Scalar containing the full path to the thesaurus database.
1.46 matthew 178:
179: my %Keywords;
180: my $thesaurus_db_file;
181:
1.144 matthew 182: #
183: # Initialize values from language.tab, copyright.tab, filetypes.tab,
184: # thesaurus.tab, and filecategories.tab.
185: #
1.18 www 186: BEGIN {
1.46 matthew 187: # Variable initialization
188: $thesaurus_db_file = $Apache::lonnet::perlvar{'lonTabDir'}."/thesaurus.db";
189: #
1.22 www 190: unless ($readit) {
1.12 harris41 191: # ------------------------------------------------------------------- languages
192: {
1.158 raeburn 193: my $langtabfile = $Apache::lonnet::perlvar{'lonTabDir'}.
194: '/language.tab';
195: if ( open(my $fh,"<$langtabfile") ) {
1.356 albertel 196: while (my $line = <$fh>) {
197: next if ($line=~/^\#/);
198: chomp($line);
1.1088 foxr 199: my ($key,$code,$country,$three,$enc,$val,$sup,$latex)=(split(/\t/,$line));
1.158 raeburn 200: $language{$key}=$val.' - '.$enc;
201: if ($sup) {
202: $supported_language{$key}=$sup;
1.1088 foxr 203: $supported_codes{$key} = $code;
1.158 raeburn 204: }
1.1048 foxr 205: if ($latex) {
206: $latex_language_bykey{$key} = $latex;
1.1088 foxr 207: $latex_language{$code} = $latex;
1.1048 foxr 208: }
1.158 raeburn 209: }
210: close($fh);
211: }
1.12 harris41 212: }
213: # ------------------------------------------------------------------ copyrights
214: {
1.158 raeburn 215: my $copyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}.
216: '/copyright.tab';
217: if ( open (my $fh,"<$copyrightfile") ) {
1.356 albertel 218: while (my $line = <$fh>) {
219: next if ($line=~/^\#/);
220: chomp($line);
221: my ($key,$val)=(split(/\s+/,$line,2));
1.158 raeburn 222: $cprtag{$key}=$val;
223: }
224: close($fh);
225: }
1.12 harris41 226: }
1.351 www 227: # ----------------------------------------------------------- source copyrights
1.192 taceyjo1 228: {
229: my $sourcecopyrightfile = $Apache::lonnet::perlvar{'lonIncludes'}.
230: '/source_copyright.tab';
231: if ( open (my $fh,"<$sourcecopyrightfile") ) {
1.356 albertel 232: while (my $line = <$fh>) {
233: next if ($line =~ /^\#/);
234: chomp($line);
235: my ($key,$val)=(split(/\s+/,$line,2));
1.192 taceyjo1 236: $scprtag{$key}=$val;
237: }
238: close($fh);
239: }
240: }
1.63 www 241:
1.517 raeburn 242: # -------------------------------------------------------------- default domain designs
1.63 www 243: my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
1.517 raeburn 244: my $designfile = $designdir.'/default.tab';
245: if ( open (my $fh,"<$designfile") ) {
246: while (my $line = <$fh>) {
247: next if ($line =~ /^\#/);
248: chomp($line);
249: my ($key,$val)=(split(/\=/,$line));
250: if ($val) { $defaultdesign{$key}=$val; }
251: }
252: close($fh);
1.63 www 253: }
254:
1.15 harris41 255: # ------------------------------------------------------------- file categories
256: {
1.158 raeburn 257: my $categoryfile = $Apache::lonnet::perlvar{'lonTabDir'}.
258: '/filecategories.tab';
259: if ( open (my $fh,"<$categoryfile") ) {
1.356 albertel 260: while (my $line = <$fh>) {
261: next if ($line =~ /^\#/);
262: chomp($line);
263: my ($extension,$category)=(split(/\s+/,$line,2));
1.158 raeburn 264: push @{$category_extensions{lc($category)}},$extension;
265: }
266: close($fh);
267: }
268:
1.15 harris41 269: }
1.12 harris41 270: # ------------------------------------------------------------------ file types
271: {
1.158 raeburn 272: my $typesfile = $Apache::lonnet::perlvar{'lonTabDir'}.
273: '/filetypes.tab';
274: if ( open (my $fh,"<$typesfile") ) {
1.356 albertel 275: while (my $line = <$fh>) {
276: next if ($line =~ /^\#/);
277: chomp($line);
278: my ($ending,$emb,$mime,$descr)=split(/\s+/,$line,4);
1.158 raeburn 279: if ($descr ne '') {
280: $fe{$ending}=lc($emb);
281: $fd{$ending}=$descr;
1.351 www 282: if ($mime ne 'unk') { $fm{$ending}=$mime; }
1.158 raeburn 283: }
284: }
285: close($fh);
286: }
1.12 harris41 287: }
1.22 www 288: &Apache::lonnet::logthis(
1.705 tempelho 289: "<span style='color:yellow;'>INFO: Read file types</span>");
1.22 www 290: $readit=1;
1.46 matthew 291: } # end of unless($readit)
1.32 matthew 292:
293: }
1.112 bowersj2 294:
1.42 matthew 295: ###############################################################
296: ## HTML and Javascript Helper Functions ##
297: ###############################################################
298:
299: =pod
300:
1.112 bowersj2 301: =head1 HTML and Javascript Functions
1.42 matthew 302:
1.112 bowersj2 303: =over 4
304:
1.648 raeburn 305: =item * &browser_and_searcher_javascript()
1.112 bowersj2 306:
307: X<browsing, javascript>X<searching, javascript>Returns a string
308: containing javascript with two functions, C<openbrowser> and
309: C<opensearcher>. Returned string does not contain E<lt>scriptE<gt>
310: tags.
1.42 matthew 311:
1.648 raeburn 312: =item * &openbrowser(formname,elementname,only,omit) [javascript]
1.42 matthew 313:
314: inputs: formname, elementname, only, omit
315:
316: formname and elementname indicate the name of the html form and name of
317: the element that the results of the browsing selection are to be placed in.
318:
319: Specifying 'only' will restrict the browser to displaying only files
1.185 www 320: with the given extension. Can be a comma separated list.
1.42 matthew 321:
322: Specifying 'omit' will restrict the browser to NOT displaying files
1.185 www 323: with the given extension. Can be a comma separated list.
1.42 matthew 324:
1.648 raeburn 325: =item * &opensearcher(formname,elementname) [javascript]
1.42 matthew 326:
327: Inputs: formname, elementname
328:
329: formname and elementname specify the name of the html form and the name
330: of the element the selection from the search results will be placed in.
1.542 raeburn 331:
1.42 matthew 332: =cut
333:
334: sub browser_and_searcher_javascript {
1.199 albertel 335: my ($mode)=@_;
336: if (!defined($mode)) { $mode='edit'; }
1.453 albertel 337: my $resurl=&escape_single(&lastresurl());
1.42 matthew 338: return <<END;
1.219 albertel 339: // <!-- BEGIN LON-CAPA Internal
1.50 matthew 340: var editbrowser = null;
1.135 albertel 341: function openbrowser(formname,elementname,only,omit,titleelement) {
1.170 www 342: var url = '$resurl/?';
1.42 matthew 343: if (editbrowser == null) {
344: url += 'launch=1&';
345: }
346: url += 'catalogmode=interactive&';
1.199 albertel 347: url += 'mode=$mode&';
1.611 albertel 348: url += 'inhibitmenu=yes&';
1.42 matthew 349: url += 'form=' + formname + '&';
350: if (only != null) {
351: url += 'only=' + only + '&';
1.217 albertel 352: } else {
353: url += 'only=&';
354: }
1.42 matthew 355: if (omit != null) {
356: url += 'omit=' + omit + '&';
1.217 albertel 357: } else {
358: url += 'omit=&';
359: }
1.135 albertel 360: if (titleelement != null) {
361: url += 'titleelement=' + titleelement + '&';
1.217 albertel 362: } else {
363: url += 'titleelement=&';
364: }
1.42 matthew 365: url += 'element=' + elementname + '';
366: var title = 'Browser';
1.435 albertel 367: var options = 'scrollbars=1,resizable=1,menubar=0,toolbar=1,location=1';
1.42 matthew 368: options += ',width=700,height=600';
369: editbrowser = open(url,title,options,'1');
370: editbrowser.focus();
371: }
372: var editsearcher;
1.135 albertel 373: function opensearcher(formname,elementname,titleelement) {
1.42 matthew 374: var url = '/adm/searchcat?';
375: if (editsearcher == null) {
376: url += 'launch=1&';
377: }
378: url += 'catalogmode=interactive&';
1.199 albertel 379: url += 'mode=$mode&';
1.42 matthew 380: url += 'form=' + formname + '&';
1.135 albertel 381: if (titleelement != null) {
382: url += 'titleelement=' + titleelement + '&';
1.217 albertel 383: } else {
384: url += 'titleelement=&';
385: }
1.42 matthew 386: url += 'element=' + elementname + '';
387: var title = 'Search';
1.435 albertel 388: var options = 'scrollbars=1,resizable=1,menubar=0,toolbar=1,location=1';
1.42 matthew 389: options += ',width=700,height=600';
390: editsearcher = open(url,title,options,'1');
391: editsearcher.focus();
392: }
1.219 albertel 393: // END LON-CAPA Internal -->
1.42 matthew 394: END
1.170 www 395: }
396:
397: sub lastresurl {
1.258 albertel 398: if ($env{'environment.lastresurl'}) {
399: return $env{'environment.lastresurl'}
1.170 www 400: } else {
401: return '/res';
402: }
403: }
404:
405: sub storeresurl {
406: my $resurl=&Apache::lonnet::clutter(shift);
407: unless ($resurl=~/^\/res/) { return 0; }
408: $resurl=~s/\/$//;
409: &Apache::lonnet::put('environment',{'lastresurl' => $resurl});
1.646 raeburn 410: &Apache::lonnet::appenv({'environment.lastresurl' => $resurl});
1.170 www 411: return 1;
1.42 matthew 412: }
413:
1.74 www 414: sub studentbrowser_javascript {
1.111 www 415: unless (
1.258 albertel 416: (($env{'request.course.id'}) &&
1.302 albertel 417: (&Apache::lonnet::allowed('srm',$env{'request.course.id'})
418: || &Apache::lonnet::allowed('srm',$env{'request.course.id'}.
419: '/'.$env{'request.course.sec'})
420: ))
1.258 albertel 421: || ($env{'request.role'}=~/^(au|dc|su)/)
1.111 www 422: ) { return ''; }
1.74 www 423: return (<<'ENDSTDBRW');
1.776 bisitz 424: <script type="text/javascript" language="Javascript">
1.824 bisitz 425: // <![CDATA[
1.74 www 426: var stdeditbrowser;
1.999 www 427: function openstdbrowser(formname,uname,udom,clicker,roleflag,ignorefilter,courseadvonly) {
1.74 www 428: var url = '/adm/pickstudent?';
429: var filter;
1.558 albertel 430: if (!ignorefilter) {
431: eval('filter=document.'+formname+'.'+uname+'.value;');
432: }
1.74 www 433: if (filter != null) {
434: if (filter != '') {
435: url += 'filter='+filter+'&';
436: }
437: }
438: url += 'form=' + formname + '&unameelement='+uname+
1.999 www 439: '&udomelement='+udom+
440: '&clicker='+clicker;
1.111 www 441: if (roleflag) { url+="&roles=1"; }
1.793 raeburn 442: if (courseadvonly) { url+="&courseadvonly=1"; }
1.102 www 443: var title = 'Student_Browser';
1.74 www 444: var options = 'scrollbars=1,resizable=1,menubar=0';
445: options += ',width=700,height=600';
446: stdeditbrowser = open(url,title,options,'1');
447: stdeditbrowser.focus();
448: }
1.824 bisitz 449: // ]]>
1.74 www 450: </script>
451: ENDSTDBRW
452: }
1.42 matthew 453:
1.1003 www 454: sub resourcebrowser_javascript {
455: unless ($env{'request.course.id'}) { return ''; }
1.1004 www 456: return (<<'ENDRESBRW');
1.1003 www 457: <script type="text/javascript" language="Javascript">
458: // <![CDATA[
459: var reseditbrowser;
1.1004 www 460: function openresbrowser(formname,reslink) {
1.1005 www 461: var url = '/adm/pickresource?form='+formname+'&reslink='+reslink;
1.1003 www 462: var title = 'Resource_Browser';
463: var options = 'scrollbars=1,resizable=1,menubar=0';
1.1005 www 464: options += ',width=700,height=500';
1.1004 www 465: reseditbrowser = open(url,title,options,'1');
466: reseditbrowser.focus();
1.1003 www 467: }
468: // ]]>
469: </script>
1.1004 www 470: ENDRESBRW
1.1003 www 471: }
472:
1.74 www 473: sub selectstudent_link {
1.999 www 474: my ($form,$unameele,$udomele,$courseadvonly,$clickerid)=@_;
475: my $callargs = "'".&Apache::lonhtmlcommon::entity_encode($form)."','".
476: &Apache::lonhtmlcommon::entity_encode($unameele)."','".
477: &Apache::lonhtmlcommon::entity_encode($udomele)."'";
1.258 albertel 478: if ($env{'request.course.id'}) {
1.302 albertel 479: if (!&Apache::lonnet::allowed('srm',$env{'request.course.id'})
480: && !&Apache::lonnet::allowed('srm',$env{'request.course.id'}.
481: '/'.$env{'request.course.sec'})) {
1.111 www 482: return '';
483: }
1.999 www 484: $callargs.=",'".&Apache::lonhtmlcommon::entity_encode($clickerid)."'";
1.793 raeburn 485: if ($courseadvonly) {
486: $callargs .= ",'',1,1";
487: }
488: return '<span class="LC_nobreak">'.
489: '<a href="javascript:openstdbrowser('.$callargs.');">'.
490: &mt('Select User').'</a></span>';
1.74 www 491: }
1.258 albertel 492: if ($env{'request.role'}=~/^(au|dc|su)/) {
1.1012 www 493: $callargs .= ",'',1";
1.793 raeburn 494: return '<span class="LC_nobreak">'.
495: '<a href="javascript:openstdbrowser('.$callargs.');">'.
496: &mt('Select User').'</a></span>';
1.111 www 497: }
498: return '';
1.91 www 499: }
500:
1.1004 www 501: sub selectresource_link {
502: my ($form,$reslink,$arg)=@_;
503:
504: my $callargs = "'".&Apache::lonhtmlcommon::entity_encode($form)."','".
505: &Apache::lonhtmlcommon::entity_encode($reslink)."'";
506: unless ($env{'request.course.id'}) { return $arg; }
507: return '<span class="LC_nobreak">'.
508: '<a href="javascript:openresbrowser('.$callargs.');">'.
509: $arg.'</a></span>';
510: }
511:
512:
513:
1.653 raeburn 514: sub authorbrowser_javascript {
515: return <<"ENDAUTHORBRW";
1.776 bisitz 516: <script type="text/javascript" language="JavaScript">
1.824 bisitz 517: // <![CDATA[
1.653 raeburn 518: var stdeditbrowser;
519:
520: function openauthorbrowser(formname,udom) {
521: var url = '/adm/pickauthor?';
522: url += 'form='+formname+'&roledom='+udom;
523: var title = 'Author_Browser';
524: var options = 'scrollbars=1,resizable=1,menubar=0';
525: options += ',width=700,height=600';
526: stdeditbrowser = open(url,title,options,'1');
527: stdeditbrowser.focus();
528: }
529:
1.824 bisitz 530: // ]]>
1.653 raeburn 531: </script>
532: ENDAUTHORBRW
533: }
534:
1.91 www 535: sub coursebrowser_javascript {
1.1116 raeburn 536: my ($domainfilter,$sec_element,$formname,$role_element,$crstype,
537: $credits_element) = @_;
1.932 raeburn 538: my $wintitle = 'Course_Browser';
1.931 raeburn 539: if ($crstype eq 'Community') {
1.932 raeburn 540: $wintitle = 'Community_Browser';
1.909 raeburn 541: }
1.876 raeburn 542: my $id_functions = &javascript_index_functions();
543: my $output = '
1.776 bisitz 544: <script type="text/javascript" language="JavaScript">
1.824 bisitz 545: // <![CDATA[
1.468 raeburn 546: var stdeditbrowser;'."\n";
1.876 raeburn 547:
548: $output .= <<"ENDSTDBRW";
1.909 raeburn 549: function opencrsbrowser(formname,uname,udom,desc,extra_element,multflag,type,type_elem) {
1.91 www 550: var url = '/adm/pickcourse?';
1.895 raeburn 551: var formid = getFormIdByName(formname);
1.876 raeburn 552: var domainfilter = getDomainFromSelectbox(formname,udom);
1.128 albertel 553: if (domainfilter != null) {
554: if (domainfilter != '') {
555: url += 'domainfilter='+domainfilter+'&';
556: }
557: }
1.91 www 558: url += 'form=' + formname + '&cnumelement='+uname+
1.187 albertel 559: '&cdomelement='+udom+
560: '&cnameelement='+desc;
1.468 raeburn 561: if (extra_element !=null && extra_element != '') {
1.594 raeburn 562: if (formname == 'rolechoice' || formname == 'studentform') {
1.468 raeburn 563: url += '&roleelement='+extra_element;
564: if (domainfilter == null || domainfilter == '') {
565: url += '&domainfilter='+extra_element;
566: }
1.234 raeburn 567: }
1.468 raeburn 568: else {
569: if (formname == 'portform') {
570: url += '&setroles='+extra_element;
1.800 raeburn 571: } else {
572: if (formname == 'rules') {
573: url += '&fixeddom='+extra_element;
574: }
1.468 raeburn 575: }
576: }
1.230 raeburn 577: }
1.909 raeburn 578: if (type != null && type != '') {
579: url += '&type='+type;
580: }
581: if (type_elem != null && type_elem != '') {
582: url += '&typeelement='+type_elem;
583: }
1.872 raeburn 584: if (formname == 'ccrs') {
585: var ownername = document.forms[formid].ccuname.value;
586: var ownerdom = document.forms[formid].ccdomain.options[document.forms[formid].ccdomain.selectedIndex].value;
587: url += '&cloner='+ownername+':'+ownerdom;
588: }
1.293 raeburn 589: if (multflag !=null && multflag != '') {
590: url += '&multiple='+multflag;
591: }
1.909 raeburn 592: var title = '$wintitle';
1.91 www 593: var options = 'scrollbars=1,resizable=1,menubar=0';
594: options += ',width=700,height=600';
595: stdeditbrowser = open(url,title,options,'1');
596: stdeditbrowser.focus();
597: }
1.876 raeburn 598: $id_functions
599: ENDSTDBRW
1.1116 raeburn 600: if (($sec_element ne '') || ($role_element ne '') || ($credits_element ne '')) {
601: $output .= &setsec_javascript($sec_element,$formname,$role_element,
602: $credits_element);
1.876 raeburn 603: }
604: $output .= '
605: // ]]>
606: </script>';
607: return $output;
608: }
609:
610: sub javascript_index_functions {
611: return <<"ENDJS";
612:
613: function getFormIdByName(formname) {
614: for (var i=0;i<document.forms.length;i++) {
615: if (document.forms[i].name == formname) {
616: return i;
617: }
618: }
619: return -1;
620: }
621:
622: function getIndexByName(formid,item) {
623: for (var i=0;i<document.forms[formid].elements.length;i++) {
624: if (document.forms[formid].elements[i].name == item) {
625: return i;
626: }
627: }
628: return -1;
629: }
1.468 raeburn 630:
1.876 raeburn 631: function getDomainFromSelectbox(formname,udom) {
632: var userdom;
633: var formid = getFormIdByName(formname);
634: if (formid > -1) {
635: var domid = getIndexByName(formid,udom);
636: if (domid > -1) {
637: if (document.forms[formid].elements[domid].type == 'select-one') {
638: userdom=document.forms[formid].elements[domid].options[document.forms[formid].elements[domid].selectedIndex].value;
639: }
640: if (document.forms[formid].elements[domid].type == 'hidden') {
641: userdom=document.forms[formid].elements[domid].value;
1.468 raeburn 642: }
643: }
644: }
1.876 raeburn 645: return userdom;
646: }
647:
648: ENDJS
1.468 raeburn 649:
1.876 raeburn 650: }
651:
1.1017 raeburn 652: sub javascript_array_indexof {
1.1018 raeburn 653: return <<ENDJS;
1.1017 raeburn 654: <script type="text/javascript" language="JavaScript">
655: // <![CDATA[
656:
657: if (!Array.prototype.indexOf) {
658: Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
659: "use strict";
660: if (this === void 0 || this === null) {
661: throw new TypeError();
662: }
663: var t = Object(this);
664: var len = t.length >>> 0;
665: if (len === 0) {
666: return -1;
667: }
668: var n = 0;
669: if (arguments.length > 0) {
670: n = Number(arguments[1]);
1.1088 foxr 671: if (n !== n) { // shortcut for verifying if it is NaN
1.1017 raeburn 672: n = 0;
673: } else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0)) {
674: n = (n > 0 || -1) * Math.floor(Math.abs(n));
675: }
676: }
677: if (n >= len) {
678: return -1;
679: }
680: var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
681: for (; k < len; k++) {
682: if (k in t && t[k] === searchElement) {
683: return k;
684: }
685: }
686: return -1;
687: }
688: }
689:
690: // ]]>
691: </script>
692:
693: ENDJS
694:
695: }
696:
1.876 raeburn 697: sub userbrowser_javascript {
698: my $id_functions = &javascript_index_functions();
699: return <<"ENDUSERBRW";
700:
1.888 raeburn 701: function openuserbrowser(formname,uname,udom,ulast,ufirst,uemail,hideudom,crsdom,caller) {
1.876 raeburn 702: var url = '/adm/pickuser?';
703: var userdom = getDomainFromSelectbox(formname,udom);
704: if (userdom != null) {
705: if (userdom != '') {
706: url += 'srchdom='+userdom+'&';
707: }
708: }
709: url += 'form=' + formname + '&unameelement='+uname+
710: '&udomelement='+udom+
711: '&ulastelement='+ulast+
712: '&ufirstelement='+ufirst+
713: '&uemailelement='+uemail+
1.881 raeburn 714: '&hideudomelement='+hideudom+
715: '&coursedom='+crsdom;
1.888 raeburn 716: if ((caller != null) && (caller != undefined)) {
717: url += '&caller='+caller;
718: }
1.876 raeburn 719: var title = 'User_Browser';
720: var options = 'scrollbars=1,resizable=1,menubar=0';
721: options += ',width=700,height=600';
722: var stdeditbrowser = open(url,title,options,'1');
723: stdeditbrowser.focus();
724: }
725:
1.888 raeburn 726: function fix_domain (formname,udom,origdom,uname) {
1.876 raeburn 727: var formid = getFormIdByName(formname);
728: if (formid > -1) {
1.888 raeburn 729: var unameid = getIndexByName(formid,uname);
1.876 raeburn 730: var domid = getIndexByName(formid,udom);
731: var hidedomid = getIndexByName(formid,origdom);
732: if (hidedomid > -1) {
733: var fixeddom = document.forms[formid].elements[hidedomid].value;
1.888 raeburn 734: var unameval = document.forms[formid].elements[unameid].value;
735: if ((fixeddom != '') && (fixeddom != undefined) && (fixeddom != null) && (unameval != '') && (unameval != undefined) && (unameval != null)) {
736: if (domid > -1) {
737: var slct = document.forms[formid].elements[domid];
738: if (slct.type == 'select-one') {
739: var i;
740: for (i=0;i<slct.length;i++) {
741: if (slct.options[i].value==fixeddom) { slct.selectedIndex=i; }
742: }
743: }
744: if (slct.type == 'hidden') {
745: slct.value = fixeddom;
1.876 raeburn 746: }
747: }
1.468 raeburn 748: }
749: }
750: }
1.876 raeburn 751: return;
752: }
753:
754: $id_functions
755: ENDUSERBRW
1.468 raeburn 756: }
757:
758: sub setsec_javascript {
1.1116 raeburn 759: my ($sec_element,$formname,$role_element,$credits_element) = @_;
1.905 raeburn 760: my (@courserolenames,@communityrolenames,$rolestr,$courserolestr,
761: $communityrolestr);
762: if ($role_element ne '') {
763: my @allroles = ('st','ta','ep','in','ad');
764: foreach my $crstype ('Course','Community') {
765: if ($crstype eq 'Community') {
766: foreach my $role (@allroles) {
767: push(@communityrolenames,&Apache::lonnet::plaintext($role,$crstype));
768: }
769: push(@communityrolenames,&Apache::lonnet::plaintext('co'));
770: } else {
771: foreach my $role (@allroles) {
772: push(@courserolenames,&Apache::lonnet::plaintext($role,$crstype));
773: }
774: push(@courserolenames,&Apache::lonnet::plaintext('cc'));
775: }
776: }
777: $rolestr = '"'.join('","',@allroles).'"';
778: $courserolestr = '"'.join('","',@courserolenames).'"';
779: $communityrolestr = '"'.join('","',@communityrolenames).'"';
780: }
1.468 raeburn 781: my $setsections = qq|
782: function setSect(sectionlist) {
1.629 raeburn 783: var sectionsArray = new Array();
784: if ((sectionlist != '') && (typeof sectionlist != "undefined")) {
785: sectionsArray = sectionlist.split(",");
786: }
1.468 raeburn 787: var numSections = sectionsArray.length;
788: document.$formname.$sec_element.length = 0;
789: if (numSections == 0) {
790: document.$formname.$sec_element.multiple=false;
791: document.$formname.$sec_element.size=1;
792: document.$formname.$sec_element.options[0] = new Option('No existing sections','',false,false)
793: } else {
794: if (numSections == 1) {
795: document.$formname.$sec_element.multiple=false;
796: document.$formname.$sec_element.size=1;
797: document.$formname.$sec_element.options[0] = new Option('Select','',true,true);
798: document.$formname.$sec_element.options[1] = new Option('No section','',false,false)
799: document.$formname.$sec_element.options[2] = new Option(sectionsArray[0],sectionsArray[0],false,false);
800: } else {
801: for (var i=0; i<numSections; i++) {
802: document.$formname.$sec_element.options[i] = new Option(sectionsArray[i],sectionsArray[i],false,false)
803: }
804: document.$formname.$sec_element.multiple=true
805: if (numSections < 3) {
806: document.$formname.$sec_element.size=numSections;
807: } else {
808: document.$formname.$sec_element.size=3;
809: }
810: document.$formname.$sec_element.options[0].selected = false
811: }
812: }
1.91 www 813: }
1.905 raeburn 814:
815: function setRole(crstype) {
1.468 raeburn 816: |;
1.905 raeburn 817: if ($role_element eq '') {
818: $setsections .= ' return;
819: }
820: ';
821: } else {
822: $setsections .= qq|
823: var elementLength = document.$formname.$role_element.length;
824: var allroles = Array($rolestr);
825: var courserolenames = Array($courserolestr);
826: var communityrolenames = Array($communityrolestr);
827: if (elementLength != undefined) {
828: if (document.$formname.$role_element.options[5].value == 'cc') {
829: if (crstype == 'Course') {
830: return;
831: } else {
832: allroles[5] = 'co';
833: for (var i=0; i<6; i++) {
834: document.$formname.$role_element.options[i].value = allroles[i];
835: document.$formname.$role_element.options[i].text = communityrolenames[i];
836: }
837: }
838: } else {
839: if (crstype == 'Community') {
840: return;
841: } else {
842: allroles[5] = 'cc';
843: for (var i=0; i<6; i++) {
844: document.$formname.$role_element.options[i].value = allroles[i];
845: document.$formname.$role_element.options[i].text = courserolenames[i];
846: }
847: }
848: }
849: }
850: return;
851: }
852: |;
853: }
1.1116 raeburn 854: if ($credits_element) {
855: $setsections .= qq|
856: function setCredits(defaultcredits) {
857: document.$formname.$credits_element.value = defaultcredits;
858: return;
859: }
860: |;
861: }
1.468 raeburn 862: return $setsections;
863: }
864:
1.91 www 865: sub selectcourse_link {
1.909 raeburn 866: my ($form,$unameele,$udomele,$desc,$extra_element,$multflag,$selecttype,
867: $typeelement) = @_;
868: my $type = $selecttype;
1.871 raeburn 869: my $linktext = &mt('Select Course');
870: if ($selecttype eq 'Community') {
1.909 raeburn 871: $linktext = &mt('Select Community');
1.906 raeburn 872: } elsif ($selecttype eq 'Course/Community') {
873: $linktext = &mt('Select Course/Community');
1.909 raeburn 874: $type = '';
1.1019 raeburn 875: } elsif ($selecttype eq 'Select') {
876: $linktext = &mt('Select');
877: $type = '';
1.871 raeburn 878: }
1.787 bisitz 879: return '<span class="LC_nobreak">'
880: ."<a href='"
881: .'javascript:opencrsbrowser("'.$form.'","'.$unameele
882: .'","'.$udomele.'","'.$desc.'","'.$extra_element
1.909 raeburn 883: .'","'.$multflag.'","'.$type.'","'.$typeelement.'");'
1.871 raeburn 884: ."'>".$linktext.'</a>'
1.787 bisitz 885: .'</span>';
1.74 www 886: }
1.42 matthew 887:
1.653 raeburn 888: sub selectauthor_link {
889: my ($form,$udom)=@_;
890: return '<a href="javascript:openauthorbrowser('."'$form','$udom'".');">'.
891: &mt('Select Author').'</a>';
892: }
893:
1.876 raeburn 894: sub selectuser_link {
1.881 raeburn 895: my ($form,$unameelem,$domelem,$lastelem,$firstelem,$emailelem,$hdomelem,
1.888 raeburn 896: $coursedom,$linktext,$caller) = @_;
1.876 raeburn 897: return '<a href="javascript:openuserbrowser('."'$form','$unameelem','$domelem',".
1.888 raeburn 898: "'$lastelem','$firstelem','$emailelem','$hdomelem','$coursedom','$caller'".
1.881 raeburn 899: ');">'.$linktext.'</a>';
1.876 raeburn 900: }
901:
1.273 raeburn 902: sub check_uncheck_jscript {
903: my $jscript = <<"ENDSCRT";
904: function checkAll(field) {
905: if (field.length > 0) {
906: for (i = 0; i < field.length; i++) {
1.1093 raeburn 907: if (!field[i].disabled) {
908: field[i].checked = true;
909: }
1.273 raeburn 910: }
911: } else {
1.1093 raeburn 912: if (!field.disabled) {
913: field.checked = true;
914: }
1.273 raeburn 915: }
916: }
917:
918: function uncheckAll(field) {
919: if (field.length > 0) {
920: for (i = 0; i < field.length; i++) {
921: field[i].checked = false ;
1.543 albertel 922: }
923: } else {
1.273 raeburn 924: field.checked = false ;
925: }
926: }
927: ENDSCRT
928: return $jscript;
929: }
930:
1.656 www 931: sub select_timezone {
1.659 raeburn 932: my ($name,$selected,$onchange,$includeempty)=@_;
933: my $output='<select name="'.$name.'" '.$onchange.'>'."\n";
934: if ($includeempty) {
935: $output .= '<option value=""';
936: if (($selected eq '') || ($selected eq 'local')) {
937: $output .= ' selected="selected" ';
938: }
939: $output .= '> </option>';
940: }
1.657 raeburn 941: my @timezones = DateTime::TimeZone->all_names;
942: foreach my $tzone (@timezones) {
943: $output.= '<option value="'.$tzone.'"';
944: if ($tzone eq $selected) {
945: $output.=' selected="selected"';
946: }
947: $output.=">$tzone</option>\n";
1.656 www 948: }
949: $output.="</select>";
950: return $output;
951: }
1.273 raeburn 952:
1.687 raeburn 953: sub select_datelocale {
954: my ($name,$selected,$onchange,$includeempty)=@_;
955: my $output='<select name="'.$name.'" '.$onchange.'>'."\n";
956: if ($includeempty) {
957: $output .= '<option value=""';
958: if ($selected eq '') {
959: $output .= ' selected="selected" ';
960: }
961: $output .= '> </option>';
962: }
963: my (@possibles,%locale_names);
964: my @locales = DateTime::Locale::Catalog::Locales;
965: foreach my $locale (@locales) {
966: if (ref($locale) eq 'HASH') {
967: my $id = $locale->{'id'};
968: if ($id ne '') {
969: my $en_terr = $locale->{'en_territory'};
970: my $native_terr = $locale->{'native_territory'};
1.695 raeburn 971: my @languages = &Apache::lonlocal::preferred_languages();
1.687 raeburn 972: if (grep(/^en$/,@languages) || !@languages) {
973: if ($en_terr ne '') {
974: $locale_names{$id} = '('.$en_terr.')';
975: } elsif ($native_terr ne '') {
976: $locale_names{$id} = $native_terr;
977: }
978: } else {
979: if ($native_terr ne '') {
980: $locale_names{$id} = $native_terr.' ';
981: } elsif ($en_terr ne '') {
982: $locale_names{$id} = '('.$en_terr.')';
983: }
984: }
985: push (@possibles,$id);
986: }
987: }
988: }
989: foreach my $item (sort(@possibles)) {
990: $output.= '<option value="'.$item.'"';
991: if ($item eq $selected) {
992: $output.=' selected="selected"';
993: }
994: $output.=">$item";
995: if ($locale_names{$item} ne '') {
996: $output.=" $locale_names{$item}</option>\n";
997: }
998: $output.="</option>\n";
999: }
1000: $output.="</select>";
1001: return $output;
1002: }
1003:
1.792 raeburn 1004: sub select_language {
1005: my ($name,$selected,$includeempty) = @_;
1006: my %langchoices;
1007: if ($includeempty) {
1.1117 raeburn 1008: %langchoices = ('' => 'No language preference');
1.792 raeburn 1009: }
1010: foreach my $id (&languageids()) {
1011: my $code = &supportedlanguagecode($id);
1012: if ($code) {
1013: $langchoices{$code} = &plainlanguagedescription($id);
1014: }
1015: }
1.1117 raeburn 1016: %langchoices = &Apache::lonlocal::texthash(%langchoices);
1.970 raeburn 1017: return &select_form($selected,$name,\%langchoices);
1.792 raeburn 1018: }
1019:
1.42 matthew 1020: =pod
1.36 matthew 1021:
1.1088 foxr 1022:
1023: =item * &list_languages()
1024:
1025: Returns an array reference that is suitable for use in language prompters.
1026: Each array element is itself a two element array. The first element
1027: is the language code. The second element a descsriptiuon of the
1028: language itself. This is suitable for use in e.g.
1029: &Apache::edit::select_arg (once dereferenced that is).
1030:
1031: =cut
1032:
1033: sub list_languages {
1034: my @lang_choices;
1035:
1036: foreach my $id (&languageids()) {
1037: my $code = &supportedlanguagecode($id);
1038: if ($code) {
1039: my $selector = $supported_codes{$id};
1040: my $description = &plainlanguagedescription($id);
1041: push (@lang_choices, [$selector, $description]);
1042: }
1043: }
1044: return \@lang_choices;
1045: }
1046:
1047: =pod
1048:
1.648 raeburn 1049: =item * &linked_select_forms(...)
1.36 matthew 1050:
1051: linked_select_forms returns a string containing a <script></script> block
1052: and html for two <select> menus. The select menus will be linked in that
1053: changing the value of the first menu will result in new values being placed
1054: in the second menu. The values in the select menu will appear in alphabetical
1.609 raeburn 1055: order unless a defined order is provided.
1.36 matthew 1056:
1057: linked_select_forms takes the following ordered inputs:
1058:
1059: =over 4
1060:
1.112 bowersj2 1061: =item * $formname, the name of the <form> tag
1.36 matthew 1062:
1.112 bowersj2 1063: =item * $middletext, the text which appears between the <select> tags
1.36 matthew 1064:
1.112 bowersj2 1065: =item * $firstdefault, the default value for the first menu
1.36 matthew 1066:
1.112 bowersj2 1067: =item * $firstselectname, the name of the first <select> tag
1.36 matthew 1068:
1.112 bowersj2 1069: =item * $secondselectname, the name of the second <select> tag
1.36 matthew 1070:
1.112 bowersj2 1071: =item * $hashref, a reference to a hash containing the data for the menus.
1.36 matthew 1072:
1.609 raeburn 1073: =item * $menuorder, the order of values in the first menu
1074:
1.1115 raeburn 1075: =item * $onchangefirst, additional javascript call to execute for an onchange
1076: event for the first <select> tag
1077:
1078: =item * $onchangesecond, additional javascript call to execute for an onchange
1079: event for the second <select> tag
1080:
1.41 ng 1081: =back
1082:
1.36 matthew 1083: Below is an example of such a hash. Only the 'text', 'default', and
1084: 'select2' keys must appear as stated. keys(%menu) are the possible
1085: values for the first select menu. The text that coincides with the
1.41 ng 1086: first menu value is given in $menu{$choice1}->{'text'}. The values
1.36 matthew 1087: and text for the second menu are given in the hash pointed to by
1088: $menu{$choice1}->{'select2'}.
1089:
1.112 bowersj2 1090: my %menu = ( A1 => { text =>"Choice A1" ,
1091: default => "B3",
1092: select2 => {
1093: B1 => "Choice B1",
1094: B2 => "Choice B2",
1095: B3 => "Choice B3",
1096: B4 => "Choice B4"
1.609 raeburn 1097: },
1098: order => ['B4','B3','B1','B2'],
1.112 bowersj2 1099: },
1100: A2 => { text =>"Choice A2" ,
1101: default => "C2",
1102: select2 => {
1103: C1 => "Choice C1",
1104: C2 => "Choice C2",
1105: C3 => "Choice C3"
1.609 raeburn 1106: },
1107: order => ['C2','C1','C3'],
1.112 bowersj2 1108: },
1109: A3 => { text =>"Choice A3" ,
1110: default => "D6",
1111: select2 => {
1112: D1 => "Choice D1",
1113: D2 => "Choice D2",
1114: D3 => "Choice D3",
1115: D4 => "Choice D4",
1116: D5 => "Choice D5",
1117: D6 => "Choice D6",
1118: D7 => "Choice D7"
1.609 raeburn 1119: },
1120: order => ['D4','D3','D2','D1','D7','D6','D5'],
1.112 bowersj2 1121: }
1122: );
1.36 matthew 1123:
1124: =cut
1125:
1126: sub linked_select_forms {
1127: my ($formname,
1128: $middletext,
1129: $firstdefault,
1130: $firstselectname,
1131: $secondselectname,
1.609 raeburn 1132: $hashref,
1133: $menuorder,
1.1115 raeburn 1134: $onchangefirst,
1135: $onchangesecond
1.36 matthew 1136: ) = @_;
1137: my $second = "document.$formname.$secondselectname";
1138: my $first = "document.$formname.$firstselectname";
1139: # output the javascript to do the changing
1140: my $result = '';
1.776 bisitz 1141: $result.='<script type="text/javascript" language="JavaScript">'."\n";
1.824 bisitz 1142: $result.="// <![CDATA[\n";
1.36 matthew 1143: $result.="var select2data = new Object();\n";
1144: $" = '","';
1145: my $debug = '';
1146: foreach my $s1 (sort(keys(%$hashref))) {
1147: $result.="select2data.d_$s1 = new Object();\n";
1148: $result.="select2data.d_$s1.def = new String('".
1149: $hashref->{$s1}->{'default'}."');\n";
1.609 raeburn 1150: $result.="select2data.d_$s1.values = new Array(";
1.36 matthew 1151: my @s2values = sort(keys( %{ $hashref->{$s1}->{'select2'} } ));
1.609 raeburn 1152: if (ref($hashref->{$s1}->{'order'}) eq 'ARRAY') {
1153: @s2values = @{$hashref->{$s1}->{'order'}};
1154: }
1.36 matthew 1155: $result.="\"@s2values\");\n";
1156: $result.="select2data.d_$s1.texts = new Array(";
1157: my @s2texts;
1158: foreach my $value (@s2values) {
1159: push @s2texts, $hashref->{$s1}->{'select2'}->{$value};
1160: }
1161: $result.="\"@s2texts\");\n";
1162: }
1163: $"=' ';
1164: $result.= <<"END";
1165:
1166: function select1_changed() {
1167: // Determine new choice
1168: var newvalue = "d_" + $first.value;
1169: // update select2
1170: var values = select2data[newvalue].values;
1171: var texts = select2data[newvalue].texts;
1172: var select2def = select2data[newvalue].def;
1173: var i;
1174: // out with the old
1175: for (i = 0; i < $second.options.length; i++) {
1176: $second.options[i] = null;
1177: }
1178: // in with the nuclear
1179: for (i=0;i<values.length; i++) {
1180: $second.options[i] = new Option(values[i]);
1.143 matthew 1181: $second.options[i].value = values[i];
1.36 matthew 1182: $second.options[i].text = texts[i];
1183: if (values[i] == select2def) {
1184: $second.options[i].selected = true;
1185: }
1186: }
1187: }
1.824 bisitz 1188: // ]]>
1.36 matthew 1189: </script>
1190: END
1191: # output the initial values for the selection lists
1.1115 raeburn 1192: $result .= "<select size=\"1\" name=\"$firstselectname\" onchange=\"select1_changed();$onchangefirst\">\n";
1.609 raeburn 1193: my @order = sort(keys(%{$hashref}));
1194: if (ref($menuorder) eq 'ARRAY') {
1195: @order = @{$menuorder};
1196: }
1197: foreach my $value (@order) {
1.36 matthew 1198: $result.=" <option value=\"$value\" ";
1.253 albertel 1199: $result.=" selected=\"selected\" " if ($value eq $firstdefault);
1.119 www 1200: $result.=">".&mt($hashref->{$value}->{'text'})."</option>\n";
1.36 matthew 1201: }
1202: $result .= "</select>\n";
1203: my %select2 = %{$hashref->{$firstdefault}->{'select2'}};
1204: $result .= $middletext;
1.1115 raeburn 1205: $result .= "<select size=\"1\" name=\"$secondselectname\"";
1206: if ($onchangesecond) {
1207: $result .= ' onchange="'.$onchangesecond.'"';
1208: }
1209: $result .= ">\n";
1.36 matthew 1210: my $seconddefault = $hashref->{$firstdefault}->{'default'};
1.609 raeburn 1211:
1212: my @secondorder = sort(keys(%select2));
1213: if (ref($hashref->{$firstdefault}->{'order'}) eq 'ARRAY') {
1214: @secondorder = @{$hashref->{$firstdefault}->{'order'}};
1215: }
1216: foreach my $value (@secondorder) {
1.36 matthew 1217: $result.=" <option value=\"$value\" ";
1.253 albertel 1218: $result.=" selected=\"selected\" " if ($value eq $seconddefault);
1.119 www 1219: $result.=">".&mt($select2{$value})."</option>\n";
1.36 matthew 1220: }
1221: $result .= "</select>\n";
1222: # return $debug;
1223: return $result;
1224: } # end of sub linked_select_forms {
1225:
1.45 matthew 1226: =pod
1.44 bowersj2 1227:
1.973 raeburn 1228: =item * &help_open_topic($topic,$text,$stayOnPage,$width,$height,$imgid)
1.44 bowersj2 1229:
1.112 bowersj2 1230: Returns a string corresponding to an HTML link to the given help
1231: $topic, where $topic corresponds to the name of a .tex file in
1232: /home/httpd/html/adm/help/tex, with underscores replaced by
1233: spaces.
1234:
1235: $text will optionally be linked to the same topic, allowing you to
1236: link text in addition to the graphic. If you do not want to link
1237: text, but wish to specify one of the later parameters, pass an
1238: empty string.
1239:
1240: $stayOnPage is a value that will be interpreted as a boolean. If true,
1241: the link will not open a new window. If false, the link will open
1242: a new window using Javascript. (Default is false.)
1243:
1244: $width and $height are optional numerical parameters that will
1245: override the width and height of the popped up window, which may
1.973 raeburn 1246: be useful for certain help topics with big pictures included.
1247:
1248: $imgid is the id of the img tag used for the help icon. This may be
1249: used in a javascript call to switch the image src. See
1250: lonhtmlcommon::htmlareaselectactive() for an example.
1.44 bowersj2 1251:
1252: =cut
1253:
1254: sub help_open_topic {
1.973 raeburn 1255: my ($topic, $text, $stayOnPage, $width, $height, $imgid) = @_;
1.48 bowersj2 1256: $text = "" if (not defined $text);
1.44 bowersj2 1257: $stayOnPage = 0 if (not defined $stayOnPage);
1.1033 www 1258: $width = 500 if (not defined $width);
1.44 bowersj2 1259: $height = 400 if (not defined $height);
1260: my $filename = $topic;
1261: $filename =~ s/ /_/g;
1262:
1.48 bowersj2 1263: my $template = "";
1264: my $link;
1.572 banghart 1265:
1.159 www 1266: $topic=~s/\W/\_/g;
1.44 bowersj2 1267:
1.572 banghart 1268: if (!$stayOnPage) {
1.1033 www 1269: $link = "javascript:openMyModal('/adm/help/${filename}.hlp',$width,$height,'yes');";
1.1037 www 1270: } elsif ($stayOnPage eq 'popup') {
1271: $link = "javascript:void(open('/adm/help/${filename}.hlp', 'Help_for_$topic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
1.572 banghart 1272: } else {
1.48 bowersj2 1273: $link = "/adm/help/${filename}.hlp";
1274: }
1275:
1276: # Add the text
1.755 neumanie 1277: if ($text ne "") {
1.763 bisitz 1278: $template.='<span class="LC_help_open_topic">'
1279: .'<a target="_top" href="'.$link.'">'
1280: .$text.'</a>';
1.48 bowersj2 1281: }
1282:
1.763 bisitz 1283: # (Always) Add the graphic
1.179 matthew 1284: my $title = &mt('Online Help');
1.667 raeburn 1285: my $helpicon=&lonhttpdurl("/adm/help/help.png");
1.973 raeburn 1286: if ($imgid ne '') {
1287: $imgid = ' id="'.$imgid.'"';
1288: }
1.763 bisitz 1289: $template.=' <a target="_top" href="'.$link.'" title="'.$title.'">'
1290: .'<img src="'.$helpicon.'" border="0"'
1291: .' alt="'.&mt('Help: [_1]',$topic).'"'
1.973 raeburn 1292: .' title="'.$title.'" style="vertical-align:middle;"'.$imgid
1.763 bisitz 1293: .' /></a>';
1294: if ($text ne "") {
1295: $template.='</span>';
1296: }
1.44 bowersj2 1297: return $template;
1298:
1.106 bowersj2 1299: }
1300:
1301: # This is a quicky function for Latex cheatsheet editing, since it
1302: # appears in at least four places
1303: sub helpLatexCheatsheet {
1.1037 www 1304: my ($topic,$text,$not_author,$stayOnPage) = @_;
1.732 raeburn 1305: my $out;
1.106 bowersj2 1306: my $addOther = '';
1.732 raeburn 1307: if ($topic) {
1.1037 www 1308: $addOther = '<span>'.&help_open_topic($topic,&mt($text),$stayOnPage, undef, 600).'</span> ';
1.763 bisitz 1309: }
1310: $out = '<span>' # Start cheatsheet
1311: .$addOther
1312: .'<span>'
1.1037 www 1313: .&help_open_topic('Greek_Symbols',&mt('Greek Symbols'),$stayOnPage,undef,600)
1.763 bisitz 1314: .'</span> <span>'
1.1037 www 1315: .&help_open_topic('Other_Symbols',&mt('Other Symbols'),$stayOnPage,undef,600)
1.763 bisitz 1316: .'</span>';
1.732 raeburn 1317: unless ($not_author) {
1.763 bisitz 1318: $out .= ' <span>'
1.1037 www 1319: .&help_open_topic('Authoring_Output_Tags',&mt('Output Tags'),$stayOnPage,undef,600)
1.763 bisitz 1320: .'</span>';
1.732 raeburn 1321: }
1.763 bisitz 1322: $out .= '</span>'; # End cheatsheet
1.732 raeburn 1323: return $out;
1.172 www 1324: }
1325:
1.430 albertel 1326: sub general_help {
1327: my $helptopic='Student_Intro';
1328: if ($env{'request.role'}=~/^(ca|au)/) {
1329: $helptopic='Authoring_Intro';
1.907 raeburn 1330: } elsif ($env{'request.role'}=~/^(cc|co)/) {
1.430 albertel 1331: $helptopic='Course_Coordination_Intro';
1.672 raeburn 1332: } elsif ($env{'request.role'}=~/^dc/) {
1333: $helptopic='Domain_Coordination_Intro';
1.430 albertel 1334: }
1335: return $helptopic;
1336: }
1337:
1338: sub update_help_link {
1339: my ($topic,$component_help,$faq,$bug,$stayOnPage) = @_;
1340: my $origurl = $ENV{'REQUEST_URI'};
1341: $origurl=~s|^/~|/priv/|;
1342: my $timestamp = time;
1343: foreach my $datum (\$topic,\$component_help,\$faq,\$bug,\$origurl) {
1344: $$datum = &escape($$datum);
1345: }
1346:
1347: my $banner_link = "/adm/helpmenu?page=banner&topic=$topic&component_help=$component_help&faq=$faq&bug=$bug&origurl=$origurl&stamp=$timestamp&stayonpage=$stayOnPage";
1348: my $output .= <<"ENDOUTPUT";
1349: <script type="text/javascript">
1.824 bisitz 1350: // <![CDATA[
1.430 albertel 1351: banner_link = '$banner_link';
1.824 bisitz 1352: // ]]>
1.430 albertel 1353: </script>
1354: ENDOUTPUT
1355: return $output;
1356: }
1357:
1358: # now just updates the help link and generates a blue icon
1.193 raeburn 1359: sub help_open_menu {
1.430 albertel 1360: my ($topic,$component_help,$faq,$bug,$stayOnPage,$width,$height,$text)
1.552 banghart 1361: = @_;
1.949 droeschl 1362: $stayOnPage = 1;
1.430 albertel 1363: my $output;
1364: if ($component_help) {
1365: if (!$text) {
1366: $output=&help_open_topic($component_help,undef,$stayOnPage,
1367: $width,$height);
1368: } else {
1369: my $help_text;
1370: $help_text=&unescape($topic);
1371: $output='<table><tr><td>'.
1372: &help_open_topic($component_help,$help_text,$stayOnPage,
1373: $width,$height).'</td></tr></table>';
1374: }
1375: }
1376: my $banner_link = &update_help_link($topic,$component_help,$faq,$bug,$stayOnPage);
1377: return $output.$banner_link;
1378: }
1379:
1380: sub top_nav_help {
1381: my ($text) = @_;
1.436 albertel 1382: $text = &mt($text);
1.949 droeschl 1383: my $stay_on_page = 1;
1384:
1.1168 raeburn 1385: my ($link,$banner_link);
1386: unless ($env{'request.noversionuri'} =~ m{^/adm/helpmenu}) {
1387: $link = ($stay_on_page) ? "javascript:helpMenu('display')"
1388: : "javascript:helpMenu('open')";
1389: $banner_link = &update_help_link(undef,undef,undef,undef,$stay_on_page);
1390: }
1.201 raeburn 1391: my $title = &mt('Get help');
1.1168 raeburn 1392: if ($link) {
1393: return <<"END";
1.436 albertel 1394: $banner_link
1.1159 raeburn 1395: <a href="$link" title="$title">$text</a>
1.436 albertel 1396: END
1.1168 raeburn 1397: } else {
1398: return ' '.$text.' ';
1399: }
1.436 albertel 1400: }
1401:
1402: sub help_menu_js {
1.1154 raeburn 1403: my ($httphost) = @_;
1.949 droeschl 1404: my $stayOnPage = 1;
1.436 albertel 1405: my $width = 620;
1406: my $height = 600;
1.430 albertel 1407: my $helptopic=&general_help();
1.1154 raeburn 1408: my $details_link = $httphost.'/adm/help/'.$helptopic.'.hlp';
1.261 albertel 1409: my $nothing=&Apache::lonhtmlcommon::javascript_nothing();
1.331 albertel 1410: my $start_page =
1411: &Apache::loncommon::start_page('Help Menu', undef,
1412: {'frameset' => 1,
1413: 'js_ready' => 1,
1.1154 raeburn 1414: 'use_absolute' => $httphost,
1.331 albertel 1415: 'add_entries' => {
1.1168 raeburn 1416: 'border' => '0',
1.579 raeburn 1417: 'rows' => "110,*",},});
1.331 albertel 1418: my $end_page =
1419: &Apache::loncommon::end_page({'frameset' => 1,
1420: 'js_ready' => 1,});
1421:
1.436 albertel 1422: my $template .= <<"ENDTEMPLATE";
1423: <script type="text/javascript">
1.877 bisitz 1424: // <![CDATA[
1.253 albertel 1425: // <!-- BEGIN LON-CAPA Internal
1.430 albertel 1426: var banner_link = '';
1.243 raeburn 1427: function helpMenu(target) {
1428: var caller = this;
1429: if (target == 'open') {
1430: var newWindow = null;
1431: try {
1.262 albertel 1432: newWindow = window.open($nothing,"helpmenu","HEIGHT=$height,WIDTH=$width,resizable=yes,scrollbars=yes" )
1.243 raeburn 1433: }
1434: catch(error) {
1435: writeHelp(caller);
1436: return;
1437: }
1438: if (newWindow) {
1439: caller = newWindow;
1440: }
1.193 raeburn 1441: }
1.243 raeburn 1442: writeHelp(caller);
1443: return;
1444: }
1445: function writeHelp(caller) {
1.1168 raeburn 1446: caller.document.writeln('$start_page\\n<frame name="bannerframe" src="'+banner_link+'" marginwidth="0" marginheight="0" frameborder="0">\\n');
1447: caller.document.writeln('<frame name="bodyframe" src="$details_link" marginwidth="0" marginheight="0" frameborder="0">\\n$end_page');
1448: caller.document.close();
1449: caller.focus();
1.193 raeburn 1450: }
1.877 bisitz 1451: // END LON-CAPA Internal -->
1.253 albertel 1452: // ]]>
1.436 albertel 1453: </script>
1.193 raeburn 1454: ENDTEMPLATE
1455: return $template;
1456: }
1457:
1.172 www 1458: sub help_open_bug {
1459: my ($topic, $text, $stayOnPage, $width, $height) = @_;
1.258 albertel 1460: unless ($env{'user.adv'}) { return ''; }
1.172 www 1461: unless ($Apache::lonnet::perlvar{'BugzillaHost'}) { return ''; }
1462: $text = "" if (not defined $text);
1463: $stayOnPage=1;
1.184 albertel 1464: $width = 600 if (not defined $width);
1465: $height = 600 if (not defined $height);
1.172 www 1466:
1467: $topic=~s/\W+/\+/g;
1468: my $link='';
1469: my $template='';
1.379 albertel 1470: my $url=$Apache::lonnet::perlvar{'BugzillaHost'}.'enter_bug.cgi?product=LON-CAPA&bug_file_loc='.
1471: &escape($ENV{'REQUEST_URI'}).'&component='.$topic;
1.172 www 1472: if (!$stayOnPage)
1473: {
1474: $link = "javascript:void(open('$url', 'Bugzilla', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
1475: }
1476: else
1477: {
1478: $link = $url;
1479: }
1480: # Add the text
1481: if ($text ne "")
1482: {
1483: $template .=
1484: "<table bgcolor='#AA3333' cellspacing='1' cellpadding='1' border='0'><tr>".
1.705 tempelho 1485: "<td bgcolor='#FF5555'><a target=\"_top\" href=\"$link\"><span style=\"color:#FFFFFF;font-size:10pt;\">$text</span></a>";
1.172 www 1486: }
1487:
1488: # Add the graphic
1.179 matthew 1489: my $title = &mt('Report a Bug');
1.215 albertel 1490: my $bugicon=&lonhttpdurl("/adm/lonMisc/smallBug.gif");
1.172 www 1491: $template .= <<"ENDTEMPLATE";
1.436 albertel 1492: <a target="_top" href="$link" title="$title"><img src="$bugicon" border="0" alt="(Bug: $topic)" /></a>
1.172 www 1493: ENDTEMPLATE
1494: if ($text ne '') { $template.='</td></tr></table>' };
1495: return $template;
1496:
1497: }
1498:
1499: sub help_open_faq {
1500: my ($topic, $text, $stayOnPage, $width, $height) = @_;
1.258 albertel 1501: unless ($env{'user.adv'}) { return ''; }
1.172 www 1502: unless ($Apache::lonnet::perlvar{'FAQHost'}) { return ''; }
1503: $text = "" if (not defined $text);
1504: $stayOnPage=1;
1505: $width = 350 if (not defined $width);
1506: $height = 400 if (not defined $height);
1507:
1508: $topic=~s/\W+/\+/g;
1509: my $link='';
1510: my $template='';
1511: my $url=$Apache::lonnet::perlvar{'FAQHost'}.'/fom/cache/'.$topic.'.html';
1512: if (!$stayOnPage)
1513: {
1514: $link = "javascript:void(open('$url', 'FAQ-O-Matic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))";
1515: }
1516: else
1517: {
1518: $link = $url;
1519: }
1520:
1521: # Add the text
1522: if ($text ne "")
1523: {
1524: $template .=
1.173 www 1525: "<table bgcolor='#337733' cellspacing='1' cellpadding='1' border='0'><tr>".
1.705 tempelho 1526: "<td bgcolor='#448844'><a target=\"_top\" href=\"$link\"><span style=\"color:#FFFFFF; font-size:10pt;\">$text</span></a>";
1.172 www 1527: }
1528:
1529: # Add the graphic
1.179 matthew 1530: my $title = &mt('View the FAQ');
1.215 albertel 1531: my $faqicon=&lonhttpdurl("/adm/lonMisc/smallFAQ.gif");
1.172 www 1532: $template .= <<"ENDTEMPLATE";
1.436 albertel 1533: <a target="_top" href="$link" title="$title"><img src="$faqicon" border="0" alt="(FAQ: $topic)" /></a>
1.172 www 1534: ENDTEMPLATE
1535: if ($text ne '') { $template.='</td></tr></table>' };
1536: return $template;
1537:
1.44 bowersj2 1538: }
1.37 matthew 1539:
1.180 matthew 1540: ###############################################################
1541: ###############################################################
1542:
1.45 matthew 1543: =pod
1544:
1.648 raeburn 1545: =item * &change_content_javascript():
1.256 matthew 1546:
1547: This and the next function allow you to create small sections of an
1548: otherwise static HTML page that you can update on the fly with
1549: Javascript, even in Netscape 4.
1550:
1551: The Javascript fragment returned by this function (no E<lt>scriptE<gt> tag)
1552: must be written to the HTML page once. It will prove the Javascript
1553: function "change(name, content)". Calling the change function with the
1554: name of the section
1555: you want to update, matching the name passed to C<changable_area>, and
1556: the new content you want to put in there, will put the content into
1557: that area.
1558:
1559: B<Note>: Netscape 4 only reserves enough space for the changable area
1560: to contain room for the original contents. You need to "make space"
1561: for whatever changes you wish to make, and be B<sure> to check your
1562: code in Netscape 4. This feature in Netscape 4 is B<not> powerful;
1563: it's adequate for updating a one-line status display, but little more.
1564: This script will set the space to 100% width, so you only need to
1565: worry about height in Netscape 4.
1566:
1567: Modern browsers are much less limiting, and if you can commit to the
1568: user not using Netscape 4, this feature may be used freely with
1569: pretty much any HTML.
1570:
1571: =cut
1572:
1573: sub change_content_javascript {
1574: # If we're on Netscape 4, we need to use Layer-based code
1.258 albertel 1575: if ($env{'browser.type'} eq 'netscape' &&
1576: $env{'browser.version'} =~ /^4\./) {
1.256 matthew 1577: return (<<NETSCAPE4);
1578: function change(name, content) {
1579: doc = document.layers[name+"___escape"].layers[0].document;
1580: doc.open();
1581: doc.write(content);
1582: doc.close();
1583: }
1584: NETSCAPE4
1585: } else {
1586: # Otherwise, we need to use semi-standards-compliant code
1587: # (technically, "innerHTML" isn't standard but the equivalent
1588: # is really scary, and every useful browser supports it
1589: return (<<DOMBASED);
1590: function change(name, content) {
1591: element = document.getElementById(name);
1592: element.innerHTML = content;
1593: }
1594: DOMBASED
1595: }
1596: }
1597:
1598: =pod
1599:
1.648 raeburn 1600: =item * &changable_area($name,$origContent):
1.256 matthew 1601:
1602: This provides a "changable area" that can be modified on the fly via
1603: the Javascript code provided in C<change_content_javascript>. $name is
1604: the name you will use to reference the area later; do not repeat the
1605: same name on a given HTML page more then once. $origContent is what
1606: the area will originally contain, which can be left blank.
1607:
1608: =cut
1609:
1610: sub changable_area {
1611: my ($name, $origContent) = @_;
1612:
1.258 albertel 1613: if ($env{'browser.type'} eq 'netscape' &&
1614: $env{'browser.version'} =~ /^4\./) {
1.256 matthew 1615: # If this is netscape 4, we need to use the Layer tag
1616: return "<ilayer width='100%' id='${name}___escape' overflow='none'><layer width='100%' id='$name' overflow='none'>$origContent</layer></ilayer>";
1617: } else {
1618: return "<span id='$name'>$origContent</span>";
1619: }
1620: }
1621:
1622: =pod
1623:
1.648 raeburn 1624: =item * &viewport_geometry_js
1.590 raeburn 1625:
1626: Provides javascript object (Geometry) which can provide information about the viewport geometry for the client browser.
1627:
1628: =cut
1629:
1630:
1631: sub viewport_geometry_js {
1632: return <<"GEOMETRY";
1633: var Geometry = {};
1634: function init_geometry() {
1635: if (Geometry.init) { return };
1636: Geometry.init=1;
1637: if (window.innerHeight) {
1638: Geometry.getViewportHeight = function() { return window.innerHeight; };
1639: Geometry.getViewportWidth = function() { return window.innerWidth; };
1640: Geometry.getHorizontalScroll = function() { return window.pageXOffset; };
1641: Geometry.getVerticalScroll = function() { return window.pageYOffset; };
1642: }
1643: else if (document.documentElement && document.documentElement.clientHeight) {
1644: Geometry.getViewportHeight =
1645: function() { return document.documentElement.clientHeight; };
1646: Geometry.getViewportWidth =
1647: function() { return document.documentElement.clientWidth; };
1648:
1649: Geometry.getHorizontalScroll =
1650: function() { return document.documentElement.scrollLeft; };
1651: Geometry.getVerticalScroll =
1652: function() { return document.documentElement.scrollTop; };
1653: }
1654: else if (document.body.clientHeight) {
1655: Geometry.getViewportHeight =
1656: function() { return document.body.clientHeight; };
1657: Geometry.getViewportWidth =
1658: function() { return document.body.clientWidth; };
1659: Geometry.getHorizontalScroll =
1660: function() { return document.body.scrollLeft; };
1661: Geometry.getVerticalScroll =
1662: function() { return document.body.scrollTop; };
1663: }
1664: }
1665:
1666: GEOMETRY
1667: }
1668:
1669: =pod
1670:
1.648 raeburn 1671: =item * &viewport_size_js()
1.590 raeburn 1672:
1673: Provides a javascript function to set values of two form elements - width and height (elements are passed in as arguments to the javascript function) to the dimensions of the user's browser window.
1674:
1675: =cut
1676:
1677: sub viewport_size_js {
1678: my $geometry = &viewport_geometry_js();
1679: return <<"DIMS";
1680:
1681: $geometry
1682:
1683: function getViewportDims(width,height) {
1684: init_geometry();
1685: width.value = Geometry.getViewportWidth();
1686: height.value = Geometry.getViewportHeight();
1687: return;
1688: }
1689:
1690: DIMS
1691: }
1692:
1693: =pod
1694:
1.648 raeburn 1695: =item * &resize_textarea_js()
1.565 albertel 1696:
1697: emits the needed javascript to resize a textarea to be as big as possible
1698:
1699: creates a function resize_textrea that takes two IDs first should be
1700: the id of the element to resize, second should be the id of a div that
1701: surrounds everything that comes after the textarea, this routine needs
1702: to be attached to the <body> for the onload and onresize events.
1703:
1.648 raeburn 1704: =back
1.565 albertel 1705:
1706: =cut
1707:
1708: sub resize_textarea_js {
1.590 raeburn 1709: my $geometry = &viewport_geometry_js();
1.565 albertel 1710: return <<"RESIZE";
1711: <script type="text/javascript">
1.824 bisitz 1712: // <![CDATA[
1.590 raeburn 1713: $geometry
1.565 albertel 1714:
1.588 albertel 1715: function getX(element) {
1716: var x = 0;
1717: while (element) {
1718: x += element.offsetLeft;
1719: element = element.offsetParent;
1720: }
1721: return x;
1722: }
1723: function getY(element) {
1724: var y = 0;
1725: while (element) {
1726: y += element.offsetTop;
1727: element = element.offsetParent;
1728: }
1729: return y;
1730: }
1731:
1732:
1.565 albertel 1733: function resize_textarea(textarea_id,bottom_id) {
1734: init_geometry();
1735: var textarea = document.getElementById(textarea_id);
1736: //alert(textarea);
1737:
1.588 albertel 1738: var textarea_top = getY(textarea);
1.565 albertel 1739: var textarea_height = textarea.offsetHeight;
1740: var bottom = document.getElementById(bottom_id);
1.588 albertel 1741: var bottom_top = getY(bottom);
1.565 albertel 1742: var bottom_height = bottom.offsetHeight;
1743: var window_height = Geometry.getViewportHeight();
1.588 albertel 1744: var fudge = 23;
1.565 albertel 1745: var new_height = window_height-fudge-textarea_top-bottom_height;
1746: if (new_height < 300) {
1747: new_height = 300;
1748: }
1749: textarea.style.height=new_height+'px';
1750: }
1.824 bisitz 1751: // ]]>
1.565 albertel 1752: </script>
1753: RESIZE
1754:
1755: }
1756:
1757: =pod
1758:
1.256 matthew 1759: =head1 Excel and CSV file utility routines
1760:
1761: =cut
1762:
1763: ###############################################################
1764: ###############################################################
1765:
1766: =pod
1767:
1.1162 raeburn 1768: =over 4
1769:
1.648 raeburn 1770: =item * &csv_translate($text)
1.37 matthew 1771:
1.185 www 1772: Translate $text to allow it to be output as a 'comma separated values'
1.37 matthew 1773: format.
1774:
1775: =cut
1776:
1.180 matthew 1777: ###############################################################
1778: ###############################################################
1.37 matthew 1779: sub csv_translate {
1780: my $text = shift;
1781: $text =~ s/\"/\"\"/g;
1.209 albertel 1782: $text =~ s/\n/ /g;
1.37 matthew 1783: return $text;
1784: }
1.180 matthew 1785:
1786: ###############################################################
1787: ###############################################################
1788:
1789: =pod
1790:
1.648 raeburn 1791: =item * &define_excel_formats()
1.180 matthew 1792:
1793: Define some commonly used Excel cell formats.
1794:
1795: Currently supported formats:
1796:
1797: =over 4
1798:
1799: =item header
1800:
1801: =item bold
1802:
1803: =item h1
1804:
1805: =item h2
1806:
1807: =item h3
1808:
1.256 matthew 1809: =item h4
1810:
1811: =item i
1812:
1.180 matthew 1813: =item date
1814:
1815: =back
1816:
1817: Inputs: $workbook
1818:
1819: Returns: $format, a hash reference.
1820:
1.1057 foxr 1821:
1.180 matthew 1822: =cut
1823:
1824: ###############################################################
1825: ###############################################################
1826: sub define_excel_formats {
1827: my ($workbook) = @_;
1828: my $format;
1829: $format->{'header'} = $workbook->add_format(bold => 1,
1830: bottom => 1,
1831: align => 'center');
1832: $format->{'bold'} = $workbook->add_format(bold=>1);
1833: $format->{'h1'} = $workbook->add_format(bold=>1, size=>18);
1834: $format->{'h2'} = $workbook->add_format(bold=>1, size=>16);
1835: $format->{'h3'} = $workbook->add_format(bold=>1, size=>14);
1.255 matthew 1836: $format->{'h4'} = $workbook->add_format(bold=>1, size=>12);
1.246 matthew 1837: $format->{'i'} = $workbook->add_format(italic=>1);
1.180 matthew 1838: $format->{'date'} = $workbook->add_format(num_format=>
1.207 matthew 1839: 'mm/dd/yyyy hh:mm:ss');
1.180 matthew 1840: return $format;
1841: }
1842:
1843: ###############################################################
1844: ###############################################################
1.113 bowersj2 1845:
1846: =pod
1847:
1.648 raeburn 1848: =item * &create_workbook()
1.255 matthew 1849:
1850: Create an Excel worksheet. If it fails, output message on the
1851: request object and return undefs.
1852:
1853: Inputs: Apache request object
1854:
1855: Returns (undef) on failure,
1856: Excel worksheet object, scalar with filename, and formats
1857: from &Apache::loncommon::define_excel_formats on success
1858:
1859: =cut
1860:
1861: ###############################################################
1862: ###############################################################
1863: sub create_workbook {
1864: my ($r) = @_;
1865: #
1866: # Create the excel spreadsheet
1867: my $filename = '/prtspool/'.
1.258 albertel 1868: $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
1.255 matthew 1869: time.'_'.rand(1000000000).'.xls';
1870: my $workbook = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
1871: if (! defined($workbook)) {
1872: $r->log_error("Error creating excel spreadsheet $filename: $!");
1.928 bisitz 1873: $r->print(
1874: '<p class="LC_error">'
1875: .&mt('Problems occurred in creating the new Excel file.')
1876: .' '.&mt('This error has been logged.')
1877: .' '.&mt('Please alert your LON-CAPA administrator.')
1878: .'</p>'
1879: );
1.255 matthew 1880: return (undef);
1881: }
1882: #
1.1014 foxr 1883: $workbook->set_tempdir(LONCAPA::tempdir());
1.255 matthew 1884: #
1885: my $format = &Apache::loncommon::define_excel_formats($workbook);
1886: return ($workbook,$filename,$format);
1887: }
1888:
1889: ###############################################################
1890: ###############################################################
1891:
1892: =pod
1893:
1.648 raeburn 1894: =item * &create_text_file()
1.113 bowersj2 1895:
1.542 raeburn 1896: Create a file to write to and eventually make available to the user.
1.256 matthew 1897: If file creation fails, outputs an error message on the request object and
1898: return undefs.
1.113 bowersj2 1899:
1.256 matthew 1900: Inputs: Apache request object, and file suffix
1.113 bowersj2 1901:
1.256 matthew 1902: Returns (undef) on failure,
1903: Filehandle and filename on success.
1.113 bowersj2 1904:
1905: =cut
1906:
1.256 matthew 1907: ###############################################################
1908: ###############################################################
1909: sub create_text_file {
1910: my ($r,$suffix) = @_;
1911: if (! defined($suffix)) { $suffix = 'txt'; };
1912: my $fh;
1913: my $filename = '/prtspool/'.
1.258 albertel 1914: $env{'user.name'}.'_'.$env{'user.domain'}.'_'.
1.256 matthew 1915: time.'_'.rand(1000000000).'.'.$suffix;
1916: $fh = Apache::File->new('>/home/httpd'.$filename);
1917: if (! defined($fh)) {
1918: $r->log_error("Couldn't open $filename for output $!");
1.928 bisitz 1919: $r->print(
1920: '<p class="LC_error">'
1921: .&mt('Problems occurred in creating the output file.')
1922: .' '.&mt('This error has been logged.')
1923: .' '.&mt('Please alert your LON-CAPA administrator.')
1924: .'</p>'
1925: );
1.113 bowersj2 1926: }
1.256 matthew 1927: return ($fh,$filename)
1.113 bowersj2 1928: }
1929:
1930:
1.256 matthew 1931: =pod
1.113 bowersj2 1932:
1933: =back
1934:
1935: =cut
1.37 matthew 1936:
1937: ###############################################################
1.33 matthew 1938: ## Home server <option> list generating code ##
1939: ###############################################################
1.35 matthew 1940:
1.169 www 1941: # ------------------------------------------
1942:
1943: sub domain_select {
1944: my ($name,$value,$multiple)=@_;
1945: my %domains=map {
1.514 albertel 1946: $_ => $_.' '. &Apache::lonnet::domain($_,'description')
1.512 albertel 1947: } &Apache::lonnet::all_domains();
1.169 www 1948: if ($multiple) {
1949: $domains{''}=&mt('Any domain');
1.550 albertel 1950: $domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
1.287 albertel 1951: return &multiple_select_form($name,$value,4,\%domains);
1.169 www 1952: } else {
1.550 albertel 1953: $domains{'select_form_order'} = [sort {lc($a) cmp lc($b) } (keys(%domains))];
1.970 raeburn 1954: return &select_form($name,$value,\%domains);
1.169 www 1955: }
1956: }
1957:
1.282 albertel 1958: #-------------------------------------------
1959:
1960: =pod
1961:
1.519 raeburn 1962: =head1 Routines for form select boxes
1963:
1964: =over 4
1965:
1.648 raeburn 1966: =item * &multiple_select_form($name,$value,$size,$hash,$order)
1.282 albertel 1967:
1968: Returns a string containing a <select> element int multiple mode
1969:
1970:
1971: Args:
1972: $name - name of the <select> element
1.506 raeburn 1973: $value - scalar or array ref of values that should already be selected
1.282 albertel 1974: $size - number of rows long the select element is
1.283 albertel 1975: $hash - the elements should be 'option' => 'shown text'
1.282 albertel 1976: (shown text should already have been &mt())
1.506 raeburn 1977: $order - (optional) array ref of the order to show the elements in
1.283 albertel 1978:
1.282 albertel 1979: =cut
1980:
1981: #-------------------------------------------
1.169 www 1982: sub multiple_select_form {
1.284 albertel 1983: my ($name,$value,$size,$hash,$order)=@_;
1.169 www 1984: my %selected = map { $_ => 1 } ref($value)?@{$value}:($value);
1985: my $output='';
1.191 matthew 1986: if (! defined($size)) {
1987: $size = 4;
1.283 albertel 1988: if (scalar(keys(%$hash))<4) {
1989: $size = scalar(keys(%$hash));
1.191 matthew 1990: }
1991: }
1.734 bisitz 1992: $output.="\n".'<select name="'.$name.'" size="'.$size.'" multiple="multiple">';
1.501 banghart 1993: my @order;
1.506 raeburn 1994: if (ref($order) eq 'ARRAY') {
1995: @order = @{$order};
1996: } else {
1997: @order = sort(keys(%$hash));
1.501 banghart 1998: }
1999: if (exists($$hash{'select_form_order'})) {
2000: @order = @{$$hash{'select_form_order'}};
2001: }
2002:
1.284 albertel 2003: foreach my $key (@order) {
1.356 albertel 2004: $output.='<option value="'.&HTML::Entities::encode($key,'"<>&').'" ';
1.284 albertel 2005: $output.='selected="selected" ' if ($selected{$key});
2006: $output.='>'.$hash->{$key}."</option>\n";
1.169 www 2007: }
2008: $output.="</select>\n";
2009: return $output;
2010: }
2011:
1.88 www 2012: #-------------------------------------------
2013:
2014: =pod
2015:
1.970 raeburn 2016: =item * &select_form($defdom,$name,$hashref,$onchange)
1.88 www 2017:
2018: Returns a string containing a <select name='$name' size='1'> form to
1.970 raeburn 2019: allow a user to select options from a ref to a hash containing:
2020: option_name => displayed text. An optional $onchange can include
2021: a javascript onchange item, e.g., onchange="this.form.submit();"
2022:
1.88 www 2023: See lonrights.pm for an example invocation and use.
2024:
2025: =cut
2026:
2027: #-------------------------------------------
2028: sub select_form {
1.970 raeburn 2029: my ($def,$name,$hashref,$onchange) = @_;
2030: return unless (ref($hashref) eq 'HASH');
2031: if ($onchange) {
2032: $onchange = ' onchange="'.$onchange.'"';
2033: }
2034: my $selectform = "<select name=\"$name\" size=\"1\"$onchange>\n";
1.128 albertel 2035: my @keys;
1.970 raeburn 2036: if (exists($hashref->{'select_form_order'})) {
2037: @keys=@{$hashref->{'select_form_order'}};
1.128 albertel 2038: } else {
1.970 raeburn 2039: @keys=sort(keys(%{$hashref}));
1.128 albertel 2040: }
1.356 albertel 2041: foreach my $key (@keys) {
2042: $selectform.=
2043: '<option value="'.&HTML::Entities::encode($key,'"<>&').'" '.
2044: ($key eq $def ? 'selected="selected" ' : '').
1.970 raeburn 2045: ">".$hashref->{$key}."</option>\n";
1.88 www 2046: }
2047: $selectform.="</select>";
2048: return $selectform;
2049: }
2050:
1.475 www 2051: # For display filters
2052:
2053: sub display_filter {
1.1074 raeburn 2054: my ($context) = @_;
1.475 www 2055: if (!$env{'form.show'}) { $env{'form.show'}=10; }
1.477 www 2056: if (!$env{'form.displayfilter'}) { $env{'form.displayfilter'}='currentfolder'; }
1.1074 raeburn 2057: my $phraseinput = 'hidden';
2058: my $includeinput = 'hidden';
2059: my ($checked,$includetypestext);
2060: if ($env{'form.displayfilter'} eq 'containing') {
2061: $phraseinput = 'text';
2062: if ($context eq 'parmslog') {
2063: $includeinput = 'checkbox';
2064: if ($env{'form.includetypes'}) {
2065: $checked = ' checked="checked"';
2066: }
2067: $includetypestext = &mt('Include parameter types');
2068: }
2069: } else {
2070: $includetypestext = ' ';
2071: }
2072: my ($additional,$secondid,$thirdid);
2073: if ($context eq 'parmslog') {
2074: $additional =
2075: '<label><input type="'.$includeinput.'" name="includetypes"'.
2076: $checked.' name="includetypes" value="1" id="includetypes" />'.
2077: ' <span id="includetypestext">'.$includetypestext.'</span>'.
2078: '</label>';
2079: $secondid = 'includetypes';
2080: $thirdid = 'includetypestext';
2081: }
2082: my $onchange = "javascript:toggleHistoryOptions(this,'containingphrase','$context',
2083: '$secondid','$thirdid')";
2084: return '<span class="LC_nobreak"><label>'.&mt('Records: [_1]',
1.475 www 2085: &Apache::lonmeta::selectbox('show',$env{'form.show'},undef,
2086: (&mt('all'),10,20,50,100,1000,10000))).
1.714 bisitz 2087: '</label></span> <span class="LC_nobreak">'.
1.1074 raeburn 2088: &mt('Filter: [_1]',
1.477 www 2089: &select_form($env{'form.displayfilter'},
2090: 'displayfilter',
1.970 raeburn 2091: {'currentfolder' => 'Current folder/page',
1.477 www 2092: 'containing' => 'Containing phrase',
1.1074 raeburn 2093: 'none' => 'None'},$onchange)).' '.
2094: '<input type="'.$phraseinput.'" name="containingphrase" id="containingphrase" size="30" value="'.
2095: &HTML::Entities::encode($env{'form.containingphrase'}).
2096: '" />'.$additional;
2097: }
2098:
2099: sub display_filter_js {
2100: my $includetext = &mt('Include parameter types');
2101: return <<"ENDJS";
2102:
2103: function toggleHistoryOptions(setter,firstid,context,secondid,thirdid) {
2104: var firstType = 'hidden';
2105: if (setter.options[setter.selectedIndex].value == 'containing') {
2106: firstType = 'text';
2107: }
2108: firstObject = document.getElementById(firstid);
2109: if (typeof(firstObject) == 'object') {
2110: if (firstObject.type != firstType) {
2111: changeInputType(firstObject,firstType);
2112: }
2113: }
2114: if (context == 'parmslog') {
2115: var secondType = 'hidden';
2116: if (firstType == 'text') {
2117: secondType = 'checkbox';
2118: }
2119: secondObject = document.getElementById(secondid);
2120: if (typeof(secondObject) == 'object') {
2121: if (secondObject.type != secondType) {
2122: changeInputType(secondObject,secondType);
2123: }
2124: }
2125: var textItem = document.getElementById(thirdid);
2126: var currtext = textItem.innerHTML;
2127: var newtext;
2128: if (firstType == 'text') {
2129: newtext = '$includetext';
2130: } else {
2131: newtext = ' ';
2132: }
2133: if (currtext != newtext) {
2134: textItem.innerHTML = newtext;
2135: }
2136: }
2137: return;
2138: }
2139:
2140: function changeInputType(oldObject,newType) {
2141: var newObject = document.createElement('input');
2142: newObject.type = newType;
2143: if (oldObject.size) {
2144: newObject.size = oldObject.size;
2145: }
2146: if (oldObject.value) {
2147: newObject.value = oldObject.value;
2148: }
2149: if (oldObject.name) {
2150: newObject.name = oldObject.name;
2151: }
2152: if (oldObject.id) {
2153: newObject.id = oldObject.id;
2154: }
2155: oldObject.parentNode.replaceChild(newObject,oldObject);
2156: return;
2157: }
2158:
2159: ENDJS
1.475 www 2160: }
2161:
1.167 www 2162: sub gradeleveldescription {
2163: my $gradelevel=shift;
2164: my %gradelevels=(0 => 'Not specified',
2165: 1 => 'Grade 1',
2166: 2 => 'Grade 2',
2167: 3 => 'Grade 3',
2168: 4 => 'Grade 4',
2169: 5 => 'Grade 5',
2170: 6 => 'Grade 6',
2171: 7 => 'Grade 7',
2172: 8 => 'Grade 8',
2173: 9 => 'Grade 9',
2174: 10 => 'Grade 10',
2175: 11 => 'Grade 11',
2176: 12 => 'Grade 12',
2177: 13 => 'Grade 13',
2178: 14 => '100 Level',
2179: 15 => '200 Level',
2180: 16 => '300 Level',
2181: 17 => '400 Level',
2182: 18 => 'Graduate Level');
2183: return &mt($gradelevels{$gradelevel});
2184: }
2185:
1.163 www 2186: sub select_level_form {
2187: my ($deflevel,$name)=@_;
2188: unless ($deflevel) { $deflevel=0; }
1.167 www 2189: my $selectform = "<select name=\"$name\" size=\"1\">\n";
2190: for (my $i=0; $i<=18; $i++) {
2191: $selectform.="<option value=\"$i\" ".
1.253 albertel 2192: ($i==$deflevel ? 'selected="selected" ' : '').
1.167 www 2193: ">".&gradeleveldescription($i)."</option>\n";
2194: }
2195: $selectform.="</select>";
2196: return $selectform;
1.163 www 2197: }
1.167 www 2198:
1.35 matthew 2199: #-------------------------------------------
2200:
1.45 matthew 2201: =pod
2202:
1.1121 raeburn 2203: =item * &select_dom_form($defdom,$name,$includeempty,$showdomdesc,$onchange,$incdoms,$excdoms)
1.35 matthew 2204:
2205: Returns a string containing a <select name='$name' size='1'> form to
2206: allow a user to select the domain to preform an operation in.
2207: See loncreateuser.pm for an example invocation and use.
2208:
1.90 www 2209: If the $includeempty flag is set, it also includes an empty choice ("no domain
2210: selected");
2211:
1.743 raeburn 2212: If the $showdomdesc flag is set, the domain name is followed by the domain description.
2213:
1.910 raeburn 2214: The optional $onchange argument specifies what should occur if the domain selector is changed, e.g., 'this.form.submit()' if the form is to be automatically submitted.
2215:
1.1121 raeburn 2216: The optional $incdoms is a reference to an array of domains which will be the only available options.
2217:
2218: The optional $excdoms is a reference to an array of domains which will be excluded from the available options.
1.563 raeburn 2219:
1.35 matthew 2220: =cut
2221:
2222: #-------------------------------------------
1.34 matthew 2223: sub select_dom_form {
1.1121 raeburn 2224: my ($defdom,$name,$includeempty,$showdomdesc,$onchange,$incdoms,$excdoms) = @_;
1.872 raeburn 2225: if ($onchange) {
1.874 raeburn 2226: $onchange = ' onchange="'.$onchange.'"';
1.743 raeburn 2227: }
1.1121 raeburn 2228: my (@domains,%exclude);
1.910 raeburn 2229: if (ref($incdoms) eq 'ARRAY') {
2230: @domains = sort {lc($a) cmp lc($b)} (@{$incdoms});
2231: } else {
2232: @domains = sort {lc($a) cmp lc($b)} (&Apache::lonnet::all_domains());
2233: }
1.90 www 2234: if ($includeempty) { @domains=('',@domains); }
1.1121 raeburn 2235: if (ref($excdoms) eq 'ARRAY') {
2236: map { $exclude{$_} = 1; } @{$excdoms};
2237: }
1.743 raeburn 2238: my $selectdomain = "<select name=\"$name\" size=\"1\"$onchange>\n";
1.356 albertel 2239: foreach my $dom (@domains) {
1.1121 raeburn 2240: next if ($exclude{$dom});
1.356 albertel 2241: $selectdomain.="<option value=\"$dom\" ".
1.563 raeburn 2242: ($dom eq $defdom ? 'selected="selected" ' : '').'>'.$dom;
2243: if ($showdomdesc) {
2244: if ($dom ne '') {
2245: my $domdesc = &Apache::lonnet::domain($dom,'description');
2246: if ($domdesc ne '') {
2247: $selectdomain .= ' ('.$domdesc.')';
2248: }
2249: }
2250: }
2251: $selectdomain .= "</option>\n";
1.34 matthew 2252: }
2253: $selectdomain.="</select>";
2254: return $selectdomain;
2255: }
2256:
1.35 matthew 2257: #-------------------------------------------
2258:
1.45 matthew 2259: =pod
2260:
1.648 raeburn 2261: =item * &home_server_form_item($domain,$name,$defaultflag)
1.35 matthew 2262:
1.586 raeburn 2263: input: 4 arguments (two required, two optional) -
2264: $domain - domain of new user
2265: $name - name of form element
2266: $default - Value of 'default' causes a default item to be first
2267: option, and selected by default.
2268: $hide - Value of 'hide' causes hiding of the name of the server,
2269: if 1 server found, or default, if 0 found.
1.594 raeburn 2270: output: returns 2 items:
1.586 raeburn 2271: (a) form element which contains either:
2272: (i) <select name="$name">
2273: <option value="$hostid1">$hostid $servers{$hostid}</option>
2274: <option value="$hostid2">$hostid $servers{$hostid}</option>
2275: </select>
2276: form item if there are multiple library servers in $domain, or
2277: (ii) an <input type="hidden" name="$name" value="$hostid" /> form item
2278: if there is only one library server in $domain.
2279:
2280: (b) number of library servers found.
2281:
2282: See loncreateuser.pm for example of use.
1.35 matthew 2283:
2284: =cut
2285:
2286: #-------------------------------------------
1.586 raeburn 2287: sub home_server_form_item {
2288: my ($domain,$name,$default,$hide) = @_;
1.513 albertel 2289: my %servers = &Apache::lonnet::get_servers($domain,'library');
1.586 raeburn 2290: my $result;
2291: my $numlib = keys(%servers);
2292: if ($numlib > 1) {
2293: $result .= '<select name="'.$name.'" />'."\n";
2294: if ($default) {
1.804 bisitz 2295: $result .= '<option value="default" selected="selected">'.&mt('default').
1.586 raeburn 2296: '</option>'."\n";
2297: }
2298: foreach my $hostid (sort(keys(%servers))) {
2299: $result.= '<option value="'.$hostid.'">'.
2300: $hostid.' '.$servers{$hostid}."</option>\n";
2301: }
2302: $result .= '</select>'."\n";
2303: } elsif ($numlib == 1) {
2304: my $hostid;
2305: foreach my $item (keys(%servers)) {
2306: $hostid = $item;
2307: }
2308: $result .= '<input type="hidden" name="'.$name.'" value="'.
2309: $hostid.'" />';
2310: if (!$hide) {
2311: $result .= $hostid.' '.$servers{$hostid};
2312: }
2313: $result .= "\n";
2314: } elsif ($default) {
2315: $result .= '<input type="hidden" name="'.$name.
2316: '" value="default" />';
2317: if (!$hide) {
2318: $result .= &mt('default');
2319: }
2320: $result .= "\n";
1.33 matthew 2321: }
1.586 raeburn 2322: return ($result,$numlib);
1.33 matthew 2323: }
1.112 bowersj2 2324:
2325: =pod
2326:
1.534 albertel 2327: =back
2328:
1.112 bowersj2 2329: =cut
1.87 matthew 2330:
2331: ###############################################################
1.112 bowersj2 2332: ## Decoding User Agent ##
1.87 matthew 2333: ###############################################################
2334:
2335: =pod
2336:
1.112 bowersj2 2337: =head1 Decoding the User Agent
2338:
2339: =over 4
2340:
2341: =item * &decode_user_agent()
1.87 matthew 2342:
2343: Inputs: $r
2344:
2345: Outputs:
2346:
2347: =over 4
2348:
1.112 bowersj2 2349: =item * $httpbrowser
1.87 matthew 2350:
1.112 bowersj2 2351: =item * $clientbrowser
1.87 matthew 2352:
1.112 bowersj2 2353: =item * $clientversion
1.87 matthew 2354:
1.112 bowersj2 2355: =item * $clientmathml
1.87 matthew 2356:
1.112 bowersj2 2357: =item * $clientunicode
1.87 matthew 2358:
1.112 bowersj2 2359: =item * $clientos
1.87 matthew 2360:
1.1137 raeburn 2361: =item * $clientmobile
2362:
1.1141 raeburn 2363: =item * $clientinfo
2364:
1.87 matthew 2365: =back
2366:
1.157 matthew 2367: =back
2368:
1.87 matthew 2369: =cut
2370:
2371: ###############################################################
2372: ###############################################################
2373: sub decode_user_agent {
1.247 albertel 2374: my ($r)=@_;
1.87 matthew 2375: my @browsertype=split(/\&/,$Apache::lonnet::perlvar{"lonBrowsDet"});
2376: my %mathcap=split(/\&/,$$Apache::lonnet::perlvar{"lonMathML"});
2377: my $httpbrowser=$ENV{"HTTP_USER_AGENT"};
1.247 albertel 2378: if (!$httpbrowser && $r) { $httpbrowser=$r->header_in('User-Agent'); }
1.87 matthew 2379: my $clientbrowser='unknown';
2380: my $clientversion='0';
2381: my $clientmathml='';
2382: my $clientunicode='0';
1.1137 raeburn 2383: my $clientmobile=0;
1.87 matthew 2384: for (my $i=0;$i<=$#browsertype;$i++) {
2385: my ($bname,$match,$notmatch,$vreg,$minv,$univ)=split(/\:/,$browsertype[$i]);
2386: if (($httpbrowser=~/$match/i) && ($httpbrowser!~/$notmatch/i)) {
2387: $clientbrowser=$bname;
2388: $httpbrowser=~/$vreg/i;
2389: $clientversion=$1;
2390: $clientmathml=($clientversion>=$minv);
2391: $clientunicode=($clientversion>=$univ);
2392: }
2393: }
2394: my $clientos='unknown';
1.1141 raeburn 2395: my $clientinfo;
1.87 matthew 2396: if (($httpbrowser=~/linux/i) ||
2397: ($httpbrowser=~/unix/i) ||
2398: ($httpbrowser=~/ux/i) ||
2399: ($httpbrowser=~/solaris/i)) { $clientos='unix'; }
2400: if (($httpbrowser=~/vax/i) ||
2401: ($httpbrowser=~/vms/i)) { $clientos='vms'; }
2402: if ($httpbrowser=~/next/i) { $clientos='next'; }
2403: if (($httpbrowser=~/mac/i) ||
2404: ($httpbrowser=~/powerpc/i)) { $clientos='mac'; }
2405: if ($httpbrowser=~/win/i) { $clientos='win'; }
2406: if ($httpbrowser=~/embed/i) { $clientos='pda'; }
1.1137 raeburn 2407: if ($httpbrowser=~/(Android|iPod|iPad|iPhone|webOS|Blackberry|Windows Phone|Opera m(?:ob|in)|Fennec)/i) {
2408: $clientmobile=lc($1);
2409: }
1.1141 raeburn 2410: if ($httpbrowser=~ m{Firefox/(\d+\.\d+)}) {
2411: $clientinfo = 'firefox-'.$1;
2412: } elsif ($httpbrowser=~ m{chromeframe/(\d+\.\d+)\.}) {
2413: $clientinfo = 'chromeframe-'.$1;
2414: }
1.87 matthew 2415: return ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,
1.1141 raeburn 2416: $clientunicode,$clientos,$clientmobile,$clientinfo);
1.87 matthew 2417: }
2418:
1.32 matthew 2419: ###############################################################
2420: ## Authentication changing form generation subroutines ##
2421: ###############################################################
2422: ##
2423: ## All of the authform_xxxxxxx subroutines take their inputs in a
2424: ## hash, and have reasonable default values.
2425: ##
2426: ## formname = the name given in the <form> tag.
1.35 matthew 2427: #-------------------------------------------
2428:
1.45 matthew 2429: =pod
2430:
1.112 bowersj2 2431: =head1 Authentication Routines
2432:
2433: =over 4
2434:
1.648 raeburn 2435: =item * &authform_xxxxxx()
1.35 matthew 2436:
2437: The authform_xxxxxx subroutines provide javascript and html forms which
2438: handle some of the conveniences required for authentication forms.
2439: This is not an optimal method, but it works.
2440:
2441: =over 4
2442:
1.112 bowersj2 2443: =item * authform_header
1.35 matthew 2444:
1.112 bowersj2 2445: =item * authform_authorwarning
1.35 matthew 2446:
1.112 bowersj2 2447: =item * authform_nochange
1.35 matthew 2448:
1.112 bowersj2 2449: =item * authform_kerberos
1.35 matthew 2450:
1.112 bowersj2 2451: =item * authform_internal
1.35 matthew 2452:
1.112 bowersj2 2453: =item * authform_filesystem
1.35 matthew 2454:
2455: =back
2456:
1.648 raeburn 2457: See loncreateuser.pm for invocation and use examples.
1.157 matthew 2458:
1.35 matthew 2459: =cut
2460:
2461: #-------------------------------------------
1.32 matthew 2462: sub authform_header{
2463: my %in = (
2464: formname => 'cu',
1.80 albertel 2465: kerb_def_dom => '',
1.32 matthew 2466: @_,
2467: );
2468: $in{'formname'} = 'document.' . $in{'formname'};
2469: my $result='';
1.80 albertel 2470:
2471: #---------------------------------------------- Code for upper case translation
2472: my $Javascript_toUpperCase;
2473: unless ($in{kerb_def_dom}) {
2474: $Javascript_toUpperCase =<<"END";
2475: switch (choice) {
2476: case 'krb': currentform.elements[choicearg].value =
2477: currentform.elements[choicearg].value.toUpperCase();
2478: break;
2479: default:
2480: }
2481: END
2482: } else {
2483: $Javascript_toUpperCase = "";
2484: }
2485:
1.165 raeburn 2486: my $radioval = "'nochange'";
1.591 raeburn 2487: if (defined($in{'curr_authtype'})) {
2488: if ($in{'curr_authtype'} ne '') {
2489: $radioval = "'".$in{'curr_authtype'}."arg'";
2490: }
1.174 matthew 2491: }
1.165 raeburn 2492: my $argfield = 'null';
1.591 raeburn 2493: if (defined($in{'mode'})) {
1.165 raeburn 2494: if ($in{'mode'} eq 'modifycourse') {
1.591 raeburn 2495: if (defined($in{'curr_autharg'})) {
2496: if ($in{'curr_autharg'} ne '') {
1.165 raeburn 2497: $argfield = "'$in{'curr_autharg'}'";
2498: }
2499: }
2500: }
2501: }
2502:
1.32 matthew 2503: $result.=<<"END";
2504: var current = new Object();
1.165 raeburn 2505: current.radiovalue = $radioval;
2506: current.argfield = $argfield;
1.32 matthew 2507:
2508: function changed_radio(choice,currentform) {
2509: var choicearg = choice + 'arg';
2510: // If a radio button in changed, we need to change the argfield
2511: if (current.radiovalue != choice) {
2512: current.radiovalue = choice;
2513: if (current.argfield != null) {
2514: currentform.elements[current.argfield].value = '';
2515: }
2516: if (choice == 'nochange') {
2517: current.argfield = null;
2518: } else {
2519: current.argfield = choicearg;
2520: switch(choice) {
2521: case 'krb':
2522: currentform.elements[current.argfield].value =
2523: "$in{'kerb_def_dom'}";
2524: break;
2525: default:
2526: break;
2527: }
2528: }
2529: }
2530: return;
2531: }
1.22 www 2532:
1.32 matthew 2533: function changed_text(choice,currentform) {
2534: var choicearg = choice + 'arg';
2535: if (currentform.elements[choicearg].value !='') {
1.80 albertel 2536: $Javascript_toUpperCase
1.32 matthew 2537: // clear old field
2538: if ((current.argfield != choicearg) && (current.argfield != null)) {
2539: currentform.elements[current.argfield].value = '';
2540: }
2541: current.argfield = choicearg;
2542: }
2543: set_auth_radio_buttons(choice,currentform);
2544: return;
1.20 www 2545: }
1.32 matthew 2546:
2547: function set_auth_radio_buttons(newvalue,currentform) {
1.986 raeburn 2548: var numauthchoices = currentform.login.length;
2549: if (typeof numauthchoices == "undefined") {
2550: return;
2551: }
1.32 matthew 2552: var i=0;
1.986 raeburn 2553: while (i < numauthchoices) {
1.32 matthew 2554: if (currentform.login[i].value == newvalue) { break; }
2555: i++;
2556: }
1.986 raeburn 2557: if (i == numauthchoices) {
1.32 matthew 2558: return;
2559: }
2560: current.radiovalue = newvalue;
2561: currentform.login[i].checked = true;
2562: return;
2563: }
2564: END
2565: return $result;
2566: }
2567:
1.1106 raeburn 2568: sub authform_authorwarning {
1.32 matthew 2569: my $result='';
1.144 matthew 2570: $result='<i>'.
2571: &mt('As a general rule, only authors or co-authors should be '.
2572: 'filesystem authenticated '.
2573: '(which allows access to the server filesystem).')."</i>\n";
1.32 matthew 2574: return $result;
2575: }
2576:
1.1106 raeburn 2577: sub authform_nochange {
1.32 matthew 2578: my %in = (
2579: formname => 'document.cu',
2580: kerb_def_dom => 'MSU.EDU',
2581: @_,
2582: );
1.1106 raeburn 2583: my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.586 raeburn 2584: my $result;
1.1104 raeburn 2585: if (!$authnum) {
1.1105 raeburn 2586: $result = &mt('Under your current role you are not permitted to change login settings for this user');
1.586 raeburn 2587: } else {
2588: $result = '<label>'.&mt('[_1] Do not change login data',
2589: '<input type="radio" name="login" value="nochange" '.
2590: 'checked="checked" onclick="'.
1.281 albertel 2591: "javascript:changed_radio('nochange',$in{'formname'});".'" />').
2592: '</label>';
1.586 raeburn 2593: }
1.32 matthew 2594: return $result;
2595: }
2596:
1.591 raeburn 2597: sub authform_kerberos {
1.32 matthew 2598: my %in = (
2599: formname => 'document.cu',
2600: kerb_def_dom => 'MSU.EDU',
1.80 albertel 2601: kerb_def_auth => 'krb4',
1.32 matthew 2602: @_,
2603: );
1.586 raeburn 2604: my ($check4,$check5,$krbcheck,$krbarg,$krbver,$result,$authtype,
2605: $autharg,$jscall);
1.1106 raeburn 2606: my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.80 albertel 2607: if ($in{'kerb_def_auth'} eq 'krb5') {
1.772 bisitz 2608: $check5 = ' checked="checked"';
1.80 albertel 2609: } else {
1.772 bisitz 2610: $check4 = ' checked="checked"';
1.80 albertel 2611: }
1.165 raeburn 2612: $krbarg = $in{'kerb_def_dom'};
1.591 raeburn 2613: if (defined($in{'curr_authtype'})) {
2614: if ($in{'curr_authtype'} eq 'krb') {
1.772 bisitz 2615: $krbcheck = ' checked="checked"';
1.623 raeburn 2616: if (defined($in{'mode'})) {
2617: if ($in{'mode'} eq 'modifyuser') {
2618: $krbcheck = '';
2619: }
2620: }
1.591 raeburn 2621: if (defined($in{'curr_kerb_ver'})) {
2622: if ($in{'curr_krb_ver'} eq '5') {
1.772 bisitz 2623: $check5 = ' checked="checked"';
1.591 raeburn 2624: $check4 = '';
2625: } else {
1.772 bisitz 2626: $check4 = ' checked="checked"';
1.591 raeburn 2627: $check5 = '';
2628: }
1.586 raeburn 2629: }
1.591 raeburn 2630: if (defined($in{'curr_autharg'})) {
1.165 raeburn 2631: $krbarg = $in{'curr_autharg'};
2632: }
1.586 raeburn 2633: if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
1.591 raeburn 2634: if (defined($in{'curr_autharg'})) {
1.586 raeburn 2635: $result =
2636: &mt('Currently Kerberos authenticated with domain [_1] Version [_2].',
2637: $in{'curr_autharg'},$krbver);
2638: } else {
2639: $result =
2640: &mt('Currently Kerberos authenticated, Version [_1].',$krbver);
2641: }
2642: return $result;
2643: }
2644: }
2645: } else {
2646: if ($authnum == 1) {
1.784 bisitz 2647: $authtype = '<input type="hidden" name="login" value="krb" />';
1.165 raeburn 2648: }
2649: }
1.586 raeburn 2650: if (!$can_assign{'krb4'} && !$can_assign{'krb5'}) {
2651: return;
1.587 raeburn 2652: } elsif ($authtype eq '') {
1.591 raeburn 2653: if (defined($in{'mode'})) {
1.587 raeburn 2654: if ($in{'mode'} eq 'modifycourse') {
2655: if ($authnum == 1) {
1.1104 raeburn 2656: $authtype = '<input type="radio" name="login" value="krb" />';
1.587 raeburn 2657: }
2658: }
2659: }
1.586 raeburn 2660: }
2661: $jscall = "javascript:changed_radio('krb',$in{'formname'});";
2662: if ($authtype eq '') {
2663: $authtype = '<input type="radio" name="login" value="krb" '.
2664: 'onclick="'.$jscall.'" onchange="'.$jscall.'"'.
2665: $krbcheck.' />';
2666: }
2667: if (($can_assign{'krb4'} && $can_assign{'krb5'}) ||
1.1106 raeburn 2668: ($can_assign{'krb4'} && !$can_assign{'krb5'} &&
1.586 raeburn 2669: $in{'curr_authtype'} eq 'krb5') ||
1.1106 raeburn 2670: (!$can_assign{'krb4'} && $can_assign{'krb5'} &&
1.586 raeburn 2671: $in{'curr_authtype'} eq 'krb4')) {
2672: $result .= &mt
1.144 matthew 2673: ('[_1] Kerberos authenticated with domain [_2] '.
1.281 albertel 2674: '[_3] Version 4 [_4] Version 5 [_5]',
1.586 raeburn 2675: '<label>'.$authtype,
1.281 albertel 2676: '</label><input type="text" size="10" name="krbarg" '.
1.165 raeburn 2677: 'value="'.$krbarg.'" '.
1.144 matthew 2678: 'onchange="'.$jscall.'" />',
1.281 albertel 2679: '<label><input type="radio" name="krbver" value="4" '.$check4.' />',
2680: '</label><label><input type="radio" name="krbver" value="5" '.$check5.' />',
2681: '</label>');
1.586 raeburn 2682: } elsif ($can_assign{'krb4'}) {
2683: $result .= &mt
2684: ('[_1] Kerberos authenticated with domain [_2] '.
2685: '[_3] Version 4 [_4]',
2686: '<label>'.$authtype,
2687: '</label><input type="text" size="10" name="krbarg" '.
2688: 'value="'.$krbarg.'" '.
2689: 'onchange="'.$jscall.'" />',
2690: '<label><input type="hidden" name="krbver" value="4" />',
2691: '</label>');
2692: } elsif ($can_assign{'krb5'}) {
2693: $result .= &mt
2694: ('[_1] Kerberos authenticated with domain [_2] '.
2695: '[_3] Version 5 [_4]',
2696: '<label>'.$authtype,
2697: '</label><input type="text" size="10" name="krbarg" '.
2698: 'value="'.$krbarg.'" '.
2699: 'onchange="'.$jscall.'" />',
2700: '<label><input type="hidden" name="krbver" value="5" />',
2701: '</label>');
2702: }
1.32 matthew 2703: return $result;
2704: }
2705:
1.1106 raeburn 2706: sub authform_internal {
1.586 raeburn 2707: my %in = (
1.32 matthew 2708: formname => 'document.cu',
2709: kerb_def_dom => 'MSU.EDU',
2710: @_,
2711: );
1.586 raeburn 2712: my ($intcheck,$intarg,$result,$authtype,$autharg,$jscall);
1.1106 raeburn 2713: my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.591 raeburn 2714: if (defined($in{'curr_authtype'})) {
2715: if ($in{'curr_authtype'} eq 'int') {
1.586 raeburn 2716: if ($can_assign{'int'}) {
1.772 bisitz 2717: $intcheck = 'checked="checked" ';
1.623 raeburn 2718: if (defined($in{'mode'})) {
2719: if ($in{'mode'} eq 'modifyuser') {
2720: $intcheck = '';
2721: }
2722: }
1.591 raeburn 2723: if (defined($in{'curr_autharg'})) {
1.586 raeburn 2724: $intarg = $in{'curr_autharg'};
2725: }
2726: } else {
2727: $result = &mt('Currently internally authenticated.');
2728: return $result;
1.165 raeburn 2729: }
2730: }
1.586 raeburn 2731: } else {
2732: if ($authnum == 1) {
1.784 bisitz 2733: $authtype = '<input type="hidden" name="login" value="int" />';
1.586 raeburn 2734: }
2735: }
2736: if (!$can_assign{'int'}) {
2737: return;
1.587 raeburn 2738: } elsif ($authtype eq '') {
1.591 raeburn 2739: if (defined($in{'mode'})) {
1.587 raeburn 2740: if ($in{'mode'} eq 'modifycourse') {
2741: if ($authnum == 1) {
1.1104 raeburn 2742: $authtype = '<input type="radio" name="login" value="int" />';
1.587 raeburn 2743: }
2744: }
2745: }
1.165 raeburn 2746: }
1.586 raeburn 2747: $jscall = "javascript:changed_radio('int',$in{'formname'});";
2748: if ($authtype eq '') {
2749: $authtype = '<input type="radio" name="login" value="int" '.$intcheck.
2750: ' onchange="'.$jscall.'" onclick="'.$jscall.'" />';
2751: }
1.605 bisitz 2752: $autharg = '<input type="password" size="10" name="intarg" value="'.
1.586 raeburn 2753: $intarg.'" onchange="'.$jscall.'" />';
2754: $result = &mt
1.144 matthew 2755: ('[_1] Internally authenticated (with initial password [_2])',
1.586 raeburn 2756: '<label>'.$authtype,'</label>'.$autharg);
1.824 bisitz 2757: $result.="<label><input type=\"checkbox\" name=\"visible\" onclick='if (this.checked) { this.form.intarg.type=\"text\" } else { this.form.intarg.type=\"password\" }' />".&mt('Visible input').'</label>';
1.32 matthew 2758: return $result;
2759: }
2760:
1.1104 raeburn 2761: sub authform_local {
1.32 matthew 2762: my %in = (
2763: formname => 'document.cu',
2764: kerb_def_dom => 'MSU.EDU',
2765: @_,
2766: );
1.586 raeburn 2767: my ($loccheck,$locarg,$result,$authtype,$autharg,$jscall);
1.1106 raeburn 2768: my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.591 raeburn 2769: if (defined($in{'curr_authtype'})) {
2770: if ($in{'curr_authtype'} eq 'loc') {
1.586 raeburn 2771: if ($can_assign{'loc'}) {
1.772 bisitz 2772: $loccheck = 'checked="checked" ';
1.623 raeburn 2773: if (defined($in{'mode'})) {
2774: if ($in{'mode'} eq 'modifyuser') {
2775: $loccheck = '';
2776: }
2777: }
1.591 raeburn 2778: if (defined($in{'curr_autharg'})) {
1.586 raeburn 2779: $locarg = $in{'curr_autharg'};
2780: }
2781: } else {
2782: $result = &mt('Currently using local (institutional) authentication.');
2783: return $result;
1.165 raeburn 2784: }
2785: }
1.586 raeburn 2786: } else {
2787: if ($authnum == 1) {
1.784 bisitz 2788: $authtype = '<input type="hidden" name="login" value="loc" />';
1.586 raeburn 2789: }
2790: }
2791: if (!$can_assign{'loc'}) {
2792: return;
1.587 raeburn 2793: } elsif ($authtype eq '') {
1.591 raeburn 2794: if (defined($in{'mode'})) {
1.587 raeburn 2795: if ($in{'mode'} eq 'modifycourse') {
2796: if ($authnum == 1) {
1.1104 raeburn 2797: $authtype = '<input type="radio" name="login" value="loc" />';
1.587 raeburn 2798: }
2799: }
2800: }
1.165 raeburn 2801: }
1.586 raeburn 2802: $jscall = "javascript:changed_radio('loc',$in{'formname'});";
2803: if ($authtype eq '') {
2804: $authtype = '<input type="radio" name="login" value="loc" '.
2805: $loccheck.' onchange="'.$jscall.'" onclick="'.
2806: $jscall.'" />';
2807: }
2808: $autharg = '<input type="text" size="10" name="locarg" value="'.
2809: $locarg.'" onchange="'.$jscall.'" />';
2810: $result = &mt('[_1] Local Authentication with argument [_2]',
2811: '<label>'.$authtype,'</label>'.$autharg);
1.32 matthew 2812: return $result;
2813: }
2814:
1.1106 raeburn 2815: sub authform_filesystem {
1.32 matthew 2816: my %in = (
2817: formname => 'document.cu',
2818: kerb_def_dom => 'MSU.EDU',
2819: @_,
2820: );
1.586 raeburn 2821: my ($fsyscheck,$result,$authtype,$autharg,$jscall);
1.1106 raeburn 2822: my ($authnum,%can_assign) = &get_assignable_auth($in{'domain'});
1.591 raeburn 2823: if (defined($in{'curr_authtype'})) {
2824: if ($in{'curr_authtype'} eq 'fsys') {
1.586 raeburn 2825: if ($can_assign{'fsys'}) {
1.772 bisitz 2826: $fsyscheck = 'checked="checked" ';
1.623 raeburn 2827: if (defined($in{'mode'})) {
2828: if ($in{'mode'} eq 'modifyuser') {
2829: $fsyscheck = '';
2830: }
2831: }
1.586 raeburn 2832: } else {
2833: $result = &mt('Currently Filesystem Authenticated.');
2834: return $result;
2835: }
2836: }
2837: } else {
2838: if ($authnum == 1) {
1.784 bisitz 2839: $authtype = '<input type="hidden" name="login" value="fsys" />';
1.586 raeburn 2840: }
2841: }
2842: if (!$can_assign{'fsys'}) {
2843: return;
1.587 raeburn 2844: } elsif ($authtype eq '') {
1.591 raeburn 2845: if (defined($in{'mode'})) {
1.587 raeburn 2846: if ($in{'mode'} eq 'modifycourse') {
2847: if ($authnum == 1) {
1.1104 raeburn 2848: $authtype = '<input type="radio" name="login" value="fsys" />';
1.587 raeburn 2849: }
2850: }
2851: }
1.586 raeburn 2852: }
2853: $jscall = "javascript:changed_radio('fsys',$in{'formname'});";
2854: if ($authtype eq '') {
2855: $authtype = '<input type="radio" name="login" value="fsys" '.
2856: $fsyscheck.' onchange="'.$jscall.'" onclick="'.
2857: $jscall.'" />';
2858: }
2859: $autharg = '<input type="text" size="10" name="fsysarg" value=""'.
2860: ' onchange="'.$jscall.'" />';
2861: $result = &mt
1.144 matthew 2862: ('[_1] Filesystem Authenticated (with initial password [_2])',
1.281 albertel 2863: '<label><input type="radio" name="login" value="fsys" '.
1.586 raeburn 2864: $fsyscheck.'onchange="'.$jscall.'" onclick="'.$jscall.'" />',
1.605 bisitz 2865: '</label><input type="password" size="10" name="fsysarg" value="" '.
1.144 matthew 2866: 'onchange="'.$jscall.'" />');
1.32 matthew 2867: return $result;
2868: }
2869:
1.586 raeburn 2870: sub get_assignable_auth {
2871: my ($dom) = @_;
2872: if ($dom eq '') {
2873: $dom = $env{'request.role.domain'};
2874: }
2875: my %can_assign = (
2876: krb4 => 1,
2877: krb5 => 1,
2878: int => 1,
2879: loc => 1,
2880: );
2881: my %domconfig = &Apache::lonnet::get_dom('configuration',['usercreation'],$dom);
2882: if (ref($domconfig{'usercreation'}) eq 'HASH') {
2883: if (ref($domconfig{'usercreation'}{'authtypes'}) eq 'HASH') {
2884: my $authhash = $domconfig{'usercreation'}{'authtypes'};
2885: my $context;
2886: if ($env{'request.role'} =~ /^au/) {
2887: $context = 'author';
2888: } elsif ($env{'request.role'} =~ /^dc/) {
2889: $context = 'domain';
2890: } elsif ($env{'request.course.id'}) {
2891: $context = 'course';
2892: }
2893: if ($context) {
2894: if (ref($authhash->{$context}) eq 'HASH') {
2895: %can_assign = %{$authhash->{$context}};
2896: }
2897: }
2898: }
2899: }
2900: my $authnum = 0;
2901: foreach my $key (keys(%can_assign)) {
2902: if ($can_assign{$key}) {
2903: $authnum ++;
2904: }
2905: }
2906: if ($can_assign{'krb4'} && $can_assign{'krb5'}) {
2907: $authnum --;
2908: }
2909: return ($authnum,%can_assign);
2910: }
2911:
1.80 albertel 2912: ###############################################################
2913: ## Get Kerberos Defaults for Domain ##
2914: ###############################################################
2915: ##
2916: ## Returns default kerberos version and an associated argument
2917: ## as listed in file domain.tab. If not listed, provides
2918: ## appropriate default domain and kerberos version.
2919: ##
2920: #-------------------------------------------
2921:
2922: =pod
2923:
1.648 raeburn 2924: =item * &get_kerberos_defaults()
1.80 albertel 2925:
2926: get_kerberos_defaults($target_domain) returns the default kerberos
1.641 raeburn 2927: version and domain. If not found, it defaults to version 4 and the
2928: domain of the server.
1.80 albertel 2929:
1.648 raeburn 2930: =over 4
2931:
1.80 albertel 2932: ($def_version, $def_krb_domain) = &get_kerberos_defaults($target_domain);
2933:
1.648 raeburn 2934: =back
2935:
2936: =back
2937:
1.80 albertel 2938: =cut
2939:
2940: #-------------------------------------------
2941: sub get_kerberos_defaults {
2942: my $domain=shift;
1.641 raeburn 2943: my ($krbdef,$krbdefdom);
2944: my %domdefaults = &Apache::lonnet::get_domain_defaults($domain);
2945: if (($domdefaults{'auth_def'} =~/^krb(4|5)$/) && ($domdefaults{'auth_arg_def'} ne '')) {
2946: $krbdef = $domdefaults{'auth_def'};
2947: $krbdefdom = $domdefaults{'auth_arg_def'};
2948: } else {
1.80 albertel 2949: $ENV{'SERVER_NAME'}=~/(\w+\.\w+)$/;
2950: my $krbdefdom=$1;
2951: $krbdefdom=~tr/a-z/A-Z/;
2952: $krbdef = "krb4";
2953: }
2954: return ($krbdef,$krbdefdom);
2955: }
1.112 bowersj2 2956:
1.32 matthew 2957:
1.46 matthew 2958: ###############################################################
2959: ## Thesaurus Functions ##
2960: ###############################################################
1.20 www 2961:
1.46 matthew 2962: =pod
1.20 www 2963:
1.112 bowersj2 2964: =head1 Thesaurus Functions
2965:
2966: =over 4
2967:
1.648 raeburn 2968: =item * &initialize_keywords()
1.46 matthew 2969:
2970: Initializes the package variable %Keywords if it is empty. Uses the
2971: package variable $thesaurus_db_file.
2972:
2973: =cut
2974:
2975: ###################################################
2976:
2977: sub initialize_keywords {
2978: return 1 if (scalar keys(%Keywords));
2979: # If we are here, %Keywords is empty, so fill it up
2980: # Make sure the file we need exists...
2981: if (! -e $thesaurus_db_file) {
2982: &Apache::lonnet::logthis("Attempt to access $thesaurus_db_file".
2983: " failed because it does not exist");
2984: return 0;
2985: }
2986: # Set up the hash as a database
2987: my %thesaurus_db;
2988: if (! tie(%thesaurus_db,'GDBM_File',
1.53 albertel 2989: $thesaurus_db_file,&GDBM_READER(),0640)){
1.46 matthew 2990: &Apache::lonnet::logthis("Could not tie \%thesaurus_db to ".
2991: $thesaurus_db_file);
2992: return 0;
2993: }
2994: # Get the average number of appearances of a word.
2995: my $avecount = $thesaurus_db{'average.count'};
2996: # Put keywords (those that appear > average) into %Keywords
2997: while (my ($word,$data)=each (%thesaurus_db)) {
2998: my ($count,undef) = split /:/,$data;
2999: $Keywords{$word}++ if ($count > $avecount);
3000: }
3001: untie %thesaurus_db;
3002: # Remove special values from %Keywords.
1.356 albertel 3003: foreach my $value ('total.count','average.count') {
3004: delete($Keywords{$value}) if (exists($Keywords{$value}));
1.586 raeburn 3005: }
1.46 matthew 3006: return 1;
3007: }
3008:
3009: ###################################################
3010:
3011: =pod
3012:
1.648 raeburn 3013: =item * &keyword($word)
1.46 matthew 3014:
3015: Returns true if $word is a keyword. A keyword is a word that appears more
3016: than the average number of times in the thesaurus database. Calls
3017: &initialize_keywords
3018:
3019: =cut
3020:
3021: ###################################################
1.20 www 3022:
3023: sub keyword {
1.46 matthew 3024: return if (!&initialize_keywords());
3025: my $word=lc(shift());
3026: $word=~s/\W//g;
3027: return exists($Keywords{$word});
1.20 www 3028: }
1.46 matthew 3029:
3030: ###############################################################
3031:
3032: =pod
1.20 www 3033:
1.648 raeburn 3034: =item * &get_related_words()
1.46 matthew 3035:
1.160 matthew 3036: Look up a word in the thesaurus. Takes a scalar argument and returns
1.46 matthew 3037: an array of words. If the keyword is not in the thesaurus, an empty array
3038: will be returned. The order of the words returned is determined by the
3039: database which holds them.
3040:
3041: Uses global $thesaurus_db_file.
3042:
1.1057 foxr 3043:
1.46 matthew 3044: =cut
3045:
3046: ###############################################################
3047: sub get_related_words {
3048: my $keyword = shift;
3049: my %thesaurus_db;
3050: if (! -e $thesaurus_db_file) {
3051: &Apache::lonnet::logthis("Attempt to access $thesaurus_db_file ".
3052: "failed because the file does not exist");
3053: return ();
3054: }
3055: if (! tie(%thesaurus_db,'GDBM_File',
1.53 albertel 3056: $thesaurus_db_file,&GDBM_READER(),0640)){
1.46 matthew 3057: return ();
3058: }
3059: my @Words=();
1.429 www 3060: my $count=0;
1.46 matthew 3061: if (exists($thesaurus_db{$keyword})) {
1.356 albertel 3062: # The first element is the number of times
3063: # the word appears. We do not need it now.
1.429 www 3064: my (undef,@RelatedWords) = (split(/:/,$thesaurus_db{$keyword}));
3065: my (undef,$mostfrequentcount)=split(/\,/,$RelatedWords[0]);
3066: my $threshold=$mostfrequentcount/10;
3067: foreach my $possibleword (@RelatedWords) {
3068: my ($word,$wordcount)=split(/\,/,$possibleword);
3069: if ($wordcount>$threshold) {
3070: push(@Words,$word);
3071: $count++;
3072: if ($count>10) { last; }
3073: }
1.20 www 3074: }
3075: }
1.46 matthew 3076: untie %thesaurus_db;
3077: return @Words;
1.14 harris41 3078: }
1.1090 foxr 3079: ###############################################################
3080: #
3081: # Spell checking
3082: #
3083:
3084: =pod
3085:
1.1142 raeburn 3086: =back
3087:
1.1090 foxr 3088: =head1 Spell checking
3089:
3090: =over 4
3091:
3092: =item * &check_spelling($wordlist $language)
3093:
3094: Takes a string containing words and feeds it to an external
3095: spellcheck program via a pipeline. Returns a string containing
3096: them mis-spelled words.
3097:
3098: Parameters:
3099:
3100: =over 4
3101:
3102: =item - $wordlist
3103:
3104: String that will be fed into the spellcheck program.
3105:
3106: =item - $language
3107:
3108: Language string that specifies the language for which the spell
3109: check will be performed.
3110:
3111: =back
3112:
3113: =back
3114:
3115: Note: This sub assumes that aspell is installed.
3116:
3117:
3118: =cut
3119:
1.46 matthew 3120:
1.1090 foxr 3121: sub check_spelling {
3122: my ($wordlist, $language) = @_;
1.1091 foxr 3123: my @misspellings;
3124:
3125: # Generate the speller and set the langauge.
3126: # if explicitly selected:
1.1090 foxr 3127:
1.1091 foxr 3128: my $speller = Text::Aspell->new;
1.1090 foxr 3129: if ($language) {
1.1091 foxr 3130: $speller->set_option('lang', $language);
1.1090 foxr 3131: }
3132:
1.1091 foxr 3133: # Turn the word list into an array of words by splittingon whitespace
1.1090 foxr 3134:
1.1091 foxr 3135: my @words = split(/\s+/, $wordlist);
1.1090 foxr 3136:
1.1091 foxr 3137: foreach my $word (@words) {
3138: if(! $speller->check($word)) {
3139: push(@misspellings, $word);
1.1090 foxr 3140: }
3141: }
1.1091 foxr 3142: return join(' ', @misspellings);
3143:
1.1090 foxr 3144: }
3145:
1.61 www 3146: # -------------------------------------------------------------- Plaintext name
1.81 albertel 3147: =pod
3148:
1.112 bowersj2 3149: =head1 User Name Functions
3150:
3151: =over 4
3152:
1.648 raeburn 3153: =item * &plainname($uname,$udom,$first)
1.81 albertel 3154:
1.112 bowersj2 3155: Takes a users logon name and returns it as a string in
1.226 albertel 3156: "first middle last generation" form
3157: if $first is set to 'lastname' then it returns it as
3158: 'lastname generation, firstname middlename' if their is a lastname
1.81 albertel 3159:
3160: =cut
1.61 www 3161:
1.295 www 3162:
1.81 albertel 3163: ###############################################################
1.61 www 3164: sub plainname {
1.226 albertel 3165: my ($uname,$udom,$first)=@_;
1.537 albertel 3166: return if (!defined($uname) || !defined($udom));
1.295 www 3167: my %names=&getnames($uname,$udom);
1.226 albertel 3168: my $name=&Apache::lonnet::format_name($names{'firstname'},
3169: $names{'middlename'},
3170: $names{'lastname'},
3171: $names{'generation'},$first);
3172: $name=~s/^\s+//;
1.62 www 3173: $name=~s/\s+$//;
3174: $name=~s/\s+/ /g;
1.353 albertel 3175: if ($name !~ /\S/) { $name=$uname.':'.$udom; }
1.62 www 3176: return $name;
1.61 www 3177: }
1.66 www 3178:
3179: # -------------------------------------------------------------------- Nickname
1.81 albertel 3180: =pod
3181:
1.648 raeburn 3182: =item * &nickname($uname,$udom)
1.81 albertel 3183:
3184: Gets a users name and returns it as a string as
3185:
3186: ""nickname""
1.66 www 3187:
1.81 albertel 3188: if the user has a nickname or
3189:
3190: "first middle last generation"
3191:
3192: if the user does not
3193:
3194: =cut
1.66 www 3195:
3196: sub nickname {
3197: my ($uname,$udom)=@_;
1.537 albertel 3198: return if (!defined($uname) || !defined($udom));
1.295 www 3199: my %names=&getnames($uname,$udom);
1.68 albertel 3200: my $name=$names{'nickname'};
1.66 www 3201: if ($name) {
3202: $name='"'.$name.'"';
3203: } else {
3204: $name=$names{'firstname'}.' '.$names{'middlename'}.' '.
3205: $names{'lastname'}.' '.$names{'generation'};
3206: $name=~s/\s+$//;
3207: $name=~s/\s+/ /g;
3208: }
3209: return $name;
3210: }
3211:
1.295 www 3212: sub getnames {
3213: my ($uname,$udom)=@_;
1.537 albertel 3214: return if (!defined($uname) || !defined($udom));
1.433 albertel 3215: if ($udom eq 'public' && $uname eq 'public') {
3216: return ('lastname' => &mt('Public'));
3217: }
1.295 www 3218: my $id=$uname.':'.$udom;
3219: my ($names,$cached)=&Apache::lonnet::is_cached_new('namescache',$id);
3220: if ($cached) {
3221: return %{$names};
3222: } else {
3223: my %loadnames=&Apache::lonnet::get('environment',
3224: ['firstname','middlename','lastname','generation','nickname'],
3225: $udom,$uname);
3226: &Apache::lonnet::do_cache_new('namescache',$id,\%loadnames);
3227: return %loadnames;
3228: }
3229: }
1.61 www 3230:
1.542 raeburn 3231: # -------------------------------------------------------------------- getemails
1.648 raeburn 3232:
1.542 raeburn 3233: =pod
3234:
1.648 raeburn 3235: =item * &getemails($uname,$udom)
1.542 raeburn 3236:
3237: Gets a user's email information and returns it as a hash with keys:
3238: notification, critnotification, permanentemail
3239:
3240: For notification and critnotification, values are comma-separated lists
1.648 raeburn 3241: of e-mail addresses; for permanentemail, value is a single e-mail address.
1.542 raeburn 3242:
1.648 raeburn 3243:
1.542 raeburn 3244: =cut
3245:
1.648 raeburn 3246:
1.466 albertel 3247: sub getemails {
3248: my ($uname,$udom)=@_;
3249: if ($udom eq 'public' && $uname eq 'public') {
3250: return;
3251: }
1.467 www 3252: if (!$udom) { $udom=$env{'user.domain'}; }
3253: if (!$uname) { $uname=$env{'user.name'}; }
1.466 albertel 3254: my $id=$uname.':'.$udom;
3255: my ($names,$cached)=&Apache::lonnet::is_cached_new('emailscache',$id);
3256: if ($cached) {
3257: return %{$names};
3258: } else {
3259: my %loadnames=&Apache::lonnet::get('environment',
3260: ['notification','critnotification',
3261: 'permanentemail'],
3262: $udom,$uname);
3263: &Apache::lonnet::do_cache_new('emailscache',$id,\%loadnames);
3264: return %loadnames;
3265: }
3266: }
3267:
1.551 albertel 3268: sub flush_email_cache {
3269: my ($uname,$udom)=@_;
3270: if (!$udom) { $udom =$env{'user.domain'}; }
3271: if (!$uname) { $uname=$env{'user.name'}; }
3272: return if ($udom eq 'public' && $uname eq 'public');
3273: my $id=$uname.':'.$udom;
3274: &Apache::lonnet::devalidate_cache_new('emailscache',$id);
3275: }
3276:
1.728 raeburn 3277: # -------------------------------------------------------------------- getlangs
3278:
3279: =pod
3280:
3281: =item * &getlangs($uname,$udom)
3282:
3283: Gets a user's language preference and returns it as a hash with key:
3284: language.
3285:
3286: =cut
3287:
3288:
3289: sub getlangs {
3290: my ($uname,$udom) = @_;
3291: if (!$udom) { $udom =$env{'user.domain'}; }
3292: if (!$uname) { $uname=$env{'user.name'}; }
3293: my $id=$uname.':'.$udom;
3294: my ($langs,$cached)=&Apache::lonnet::is_cached_new('userlangs',$id);
3295: if ($cached) {
3296: return %{$langs};
3297: } else {
3298: my %loadlangs=&Apache::lonnet::get('environment',['languages'],
3299: $udom,$uname);
3300: &Apache::lonnet::do_cache_new('userlangs',$id,\%loadlangs);
3301: return %loadlangs;
3302: }
3303: }
3304:
3305: sub flush_langs_cache {
3306: my ($uname,$udom)=@_;
3307: if (!$udom) { $udom =$env{'user.domain'}; }
3308: if (!$uname) { $uname=$env{'user.name'}; }
3309: return if ($udom eq 'public' && $uname eq 'public');
3310: my $id=$uname.':'.$udom;
3311: &Apache::lonnet::devalidate_cache_new('userlangs',$id);
3312: }
3313:
1.61 www 3314: # ------------------------------------------------------------------ Screenname
1.81 albertel 3315:
3316: =pod
3317:
1.648 raeburn 3318: =item * &screenname($uname,$udom)
1.81 albertel 3319:
3320: Gets a users screenname and returns it as a string
3321:
3322: =cut
1.61 www 3323:
3324: sub screenname {
3325: my ($uname,$udom)=@_;
1.258 albertel 3326: if ($uname eq $env{'user.name'} &&
3327: $udom eq $env{'user.domain'}) {return $env{'environment.screenname'};}
1.212 albertel 3328: my %names=&Apache::lonnet::get('environment',['screenname'],$udom,$uname);
1.68 albertel 3329: return $names{'screenname'};
1.62 www 3330: }
3331:
1.212 albertel 3332:
1.802 bisitz 3333: # ------------------------------------------------------------- Confirm Wrapper
3334: =pod
3335:
1.1142 raeburn 3336: =item * &confirmwrapper($message)
1.802 bisitz 3337:
3338: Wrap messages about completion of operation in box
3339:
3340: =cut
3341:
3342: sub confirmwrapper {
3343: my ($message)=@_;
3344: if ($message) {
3345: return "\n".'<div class="LC_confirm_box">'."\n"
3346: .$message."\n"
3347: .'</div>'."\n";
3348: } else {
3349: return $message;
3350: }
3351: }
3352:
1.62 www 3353: # ------------------------------------------------------------- Message Wrapper
3354:
3355: sub messagewrapper {
1.369 www 3356: my ($link,$username,$domain,$subject,$text)=@_;
1.62 www 3357: return
1.441 albertel 3358: '<a href="/adm/email?compose=individual&'.
3359: 'recname='.$username.'&recdom='.$domain.
3360: '&subject='.&escape($subject).'&text='.&escape($text).'" '.
1.200 matthew 3361: 'title="'.&mt('Send message').'">'.$link.'</a>';
1.74 www 3362: }
1.802 bisitz 3363:
1.74 www 3364: # --------------------------------------------------------------- Notes Wrapper
3365:
3366: sub noteswrapper {
3367: my ($link,$un,$do)=@_;
3368: return
1.896 amueller 3369: "<a href='/adm/email?recordftf=retrieve&recname=$un&recdom=$do'>$link</a>";
1.62 www 3370: }
1.802 bisitz 3371:
1.62 www 3372: # ------------------------------------------------------------- Aboutme Wrapper
3373:
3374: sub aboutmewrapper {
1.1070 raeburn 3375: my ($link,$username,$domain,$target,$class)=@_;
1.447 raeburn 3376: if (!defined($username) && !defined($domain)) {
3377: return;
3378: }
1.1096 raeburn 3379: return '<a href="/adm/'.$domain.'/'.$username.'/aboutme"'.
1.1070 raeburn 3380: ($target?' target="'.$target.'"':'').($class?' class="'.$class.'"':'').' title="'.&mt("View this user's personal information page").'">'.$link.'</a>';
1.62 www 3381: }
3382:
3383: # ------------------------------------------------------------ Syllabus Wrapper
3384:
3385: sub syllabuswrapper {
1.707 bisitz 3386: my ($linktext,$coursedir,$domain)=@_;
1.208 matthew 3387: return qq{<a href="/public/$domain/$coursedir/syllabus">$linktext</a>};
1.61 www 3388: }
1.14 harris41 3389:
1.802 bisitz 3390: # -----------------------------------------------------------------------------
3391:
1.208 matthew 3392: sub track_student_link {
1.887 raeburn 3393: my ($linktext,$sname,$sdom,$target,$start,$only_body) = @_;
1.268 albertel 3394: my $link ="/adm/trackstudent?";
1.208 matthew 3395: my $title = 'View recent activity';
3396: if (defined($sname) && $sname !~ /^\s*$/ &&
3397: defined($sdom) && $sdom !~ /^\s*$/) {
1.268 albertel 3398: $link .= "selected_student=$sname:$sdom";
1.208 matthew 3399: $title .= ' of this student';
1.268 albertel 3400: }
1.208 matthew 3401: if (defined($target) && $target !~ /^\s*$/) {
3402: $target = qq{target="$target"};
3403: } else {
3404: $target = '';
3405: }
1.268 albertel 3406: if ($start) { $link.='&start='.$start; }
1.887 raeburn 3407: if ($only_body) { $link .= '&only_body=1'; }
1.554 albertel 3408: $title = &mt($title);
3409: $linktext = &mt($linktext);
1.448 albertel 3410: return qq{<a href="$link" title="$title" $target>$linktext</a>}.
3411: &help_open_topic('View_recent_activity');
1.208 matthew 3412: }
3413:
1.781 raeburn 3414: sub slot_reservations_link {
3415: my ($linktext,$sname,$sdom,$target) = @_;
3416: my $link ="/adm/slotrequest?command=showresv&origin=aboutme";
3417: my $title = 'View slot reservation history';
3418: if (defined($sname) && $sname !~ /^\s*$/ &&
3419: defined($sdom) && $sdom !~ /^\s*$/) {
3420: $link .= "&uname=$sname&udom=$sdom";
3421: $title .= ' of this student';
3422: }
3423: if (defined($target) && $target !~ /^\s*$/) {
3424: $target = qq{target="$target"};
3425: } else {
3426: $target = '';
3427: }
3428: $title = &mt($title);
3429: $linktext = &mt($linktext);
3430: return qq{<a href="$link" title="$title" $target>$linktext</a>};
3431: # FIXME uncomment when help item created: &help_open_topic('Slot_Reservation_History');
3432:
3433: }
3434:
1.508 www 3435: # ===================================================== Display a student photo
3436:
3437:
1.509 albertel 3438: sub student_image_tag {
1.508 www 3439: my ($domain,$user)=@_;
3440: my $imgsrc=&Apache::lonnet::studentphoto($domain,$user,'jpg');
3441: if (($imgsrc) && ($imgsrc ne '/adm/lonKaputt/lonlogo_broken.gif')) {
3442: return '<img src="'.$imgsrc.'" align="right" />';
3443: } else {
3444: return '';
3445: }
3446: }
3447:
1.112 bowersj2 3448: =pod
3449:
3450: =back
3451:
3452: =head1 Access .tab File Data
3453:
3454: =over 4
3455:
1.648 raeburn 3456: =item * &languageids()
1.112 bowersj2 3457:
3458: returns list of all language ids
3459:
3460: =cut
3461:
1.14 harris41 3462: sub languageids {
1.16 harris41 3463: return sort(keys(%language));
1.14 harris41 3464: }
3465:
1.112 bowersj2 3466: =pod
3467:
1.648 raeburn 3468: =item * &languagedescription()
1.112 bowersj2 3469:
3470: returns description of a specified language id
3471:
3472: =cut
3473:
1.14 harris41 3474: sub languagedescription {
1.125 www 3475: my $code=shift;
3476: return ($supported_language{$code}?'* ':'').
3477: $language{$code}.
1.126 www 3478: ($supported_language{$code}?' ('.&mt('interface available').')':'');
1.145 www 3479: }
3480:
1.1048 foxr 3481: =pod
3482:
3483: =item * &plainlanguagedescription
3484:
3485: Returns both the plain language description (e.g. 'Creoles and Pidgins, English-based (Other)')
3486: and the language character encoding (e.g. ISO) separated by a ' - ' string.
3487:
3488: =cut
3489:
1.145 www 3490: sub plainlanguagedescription {
3491: my $code=shift;
3492: return $language{$code};
3493: }
3494:
1.1048 foxr 3495: =pod
3496:
3497: =item * &supportedlanguagecode
3498:
3499: Returns the supported language code (e.g. sptutf maps to pt) given a language
3500: code.
3501:
3502: =cut
3503:
1.145 www 3504: sub supportedlanguagecode {
3505: my $code=shift;
3506: return $supported_language{$code};
1.97 www 3507: }
3508:
1.112 bowersj2 3509: =pod
3510:
1.1048 foxr 3511: =item * &latexlanguage()
3512:
3513: Given a language key code returns the correspondnig language to use
3514: to select the correct hyphenation on LaTeX printouts. This is undef if there
3515: is no supported hyphenation for the language code.
3516:
3517: =cut
3518:
3519: sub latexlanguage {
3520: my $code = shift;
3521: return $latex_language{$code};
3522: }
3523:
3524: =pod
3525:
3526: =item * &latexhyphenation()
3527:
3528: Same as above but what's supplied is the language as it might be stored
3529: in the metadata.
3530:
3531: =cut
3532:
3533: sub latexhyphenation {
3534: my $key = shift;
3535: return $latex_language_bykey{$key};
3536: }
3537:
3538: =pod
3539:
1.648 raeburn 3540: =item * ©rightids()
1.112 bowersj2 3541:
3542: returns list of all copyrights
3543:
3544: =cut
3545:
3546: sub copyrightids {
3547: return sort(keys(%cprtag));
3548: }
3549:
3550: =pod
3551:
1.648 raeburn 3552: =item * ©rightdescription()
1.112 bowersj2 3553:
3554: returns description of a specified copyright id
3555:
3556: =cut
3557:
3558: sub copyrightdescription {
1.166 www 3559: return &mt($cprtag{shift(@_)});
1.112 bowersj2 3560: }
1.197 matthew 3561:
3562: =pod
3563:
1.648 raeburn 3564: =item * &source_copyrightids()
1.192 taceyjo1 3565:
3566: returns list of all source copyrights
3567:
3568: =cut
3569:
3570: sub source_copyrightids {
3571: return sort(keys(%scprtag));
3572: }
3573:
3574: =pod
3575:
1.648 raeburn 3576: =item * &source_copyrightdescription()
1.192 taceyjo1 3577:
3578: returns description of a specified source copyright id
3579:
3580: =cut
3581:
3582: sub source_copyrightdescription {
3583: return &mt($scprtag{shift(@_)});
3584: }
1.112 bowersj2 3585:
3586: =pod
3587:
1.648 raeburn 3588: =item * &filecategories()
1.112 bowersj2 3589:
3590: returns list of all file categories
3591:
3592: =cut
3593:
3594: sub filecategories {
3595: return sort(keys(%category_extensions));
3596: }
3597:
3598: =pod
3599:
1.648 raeburn 3600: =item * &filecategorytypes()
1.112 bowersj2 3601:
3602: returns list of file types belonging to a given file
3603: category
3604:
3605: =cut
3606:
3607: sub filecategorytypes {
1.356 albertel 3608: my ($cat) = @_;
3609: return @{$category_extensions{lc($cat)}};
1.112 bowersj2 3610: }
3611:
3612: =pod
3613:
1.648 raeburn 3614: =item * &fileembstyle()
1.112 bowersj2 3615:
3616: returns embedding style for a specified file type
3617:
3618: =cut
3619:
3620: sub fileembstyle {
3621: return $fe{lc(shift(@_))};
1.169 www 3622: }
3623:
1.351 www 3624: sub filemimetype {
3625: return $fm{lc(shift(@_))};
3626: }
3627:
1.169 www 3628:
3629: sub filecategoryselect {
3630: my ($name,$value)=@_;
1.189 matthew 3631: return &select_form($value,$name,
1.970 raeburn 3632: {'' => &mt('Any category'), map { $_,$_ } sort(keys(%category_extensions))});
1.112 bowersj2 3633: }
3634:
3635: =pod
3636:
1.648 raeburn 3637: =item * &filedescription()
1.112 bowersj2 3638:
3639: returns description for a specified file type
3640:
3641: =cut
3642:
3643: sub filedescription {
1.188 matthew 3644: my $file_description = $fd{lc(shift())};
3645: $file_description =~ s:([\[\]]):~$1:g;
3646: return &mt($file_description);
1.112 bowersj2 3647: }
3648:
3649: =pod
3650:
1.648 raeburn 3651: =item * &filedescriptionex()
1.112 bowersj2 3652:
3653: returns description for a specified file type with
3654: extra formatting
3655:
3656: =cut
3657:
3658: sub filedescriptionex {
3659: my $ex=shift;
1.188 matthew 3660: my $file_description = $fd{lc($ex)};
3661: $file_description =~ s:([\[\]]):~$1:g;
3662: return '.'.$ex.' '.&mt($file_description);
1.112 bowersj2 3663: }
3664:
3665: # End of .tab access
3666: =pod
3667:
3668: =back
3669:
3670: =cut
3671:
3672: # ------------------------------------------------------------------ File Types
3673: sub fileextensions {
3674: return sort(keys(%fe));
3675: }
3676:
1.97 www 3677: # ----------------------------------------------------------- Display Languages
3678: # returns a hash with all desired display languages
3679: #
3680:
3681: sub display_languages {
3682: my %languages=();
1.695 raeburn 3683: foreach my $lang (&Apache::lonlocal::preferred_languages()) {
1.356 albertel 3684: $languages{$lang}=1;
1.97 www 3685: }
3686: &get_unprocessed_cgi($ENV{'QUERY_STRING'},['displaylanguage']);
1.258 albertel 3687: if ($env{'form.displaylanguage'}) {
1.356 albertel 3688: foreach my $lang (split(/\s*(\,|\;|\:)\s*/,$env{'form.displaylanguage'})) {
3689: $languages{$lang}=1;
1.97 www 3690: }
3691: }
3692: return %languages;
1.14 harris41 3693: }
3694:
1.582 albertel 3695: sub languages {
3696: my ($possible_langs) = @_;
1.695 raeburn 3697: my @preferred_langs = &Apache::lonlocal::preferred_languages();
1.582 albertel 3698: if (!ref($possible_langs)) {
3699: if( wantarray ) {
3700: return @preferred_langs;
3701: } else {
3702: return $preferred_langs[0];
3703: }
3704: }
3705: my %possibilities = map { $_ => 1 } (@$possible_langs);
3706: my @preferred_possibilities;
3707: foreach my $preferred_lang (@preferred_langs) {
3708: if (exists($possibilities{$preferred_lang})) {
3709: push(@preferred_possibilities, $preferred_lang);
3710: }
3711: }
3712: if( wantarray ) {
3713: return @preferred_possibilities;
3714: }
3715: return $preferred_possibilities[0];
3716: }
3717:
1.742 raeburn 3718: sub user_lang {
3719: my ($touname,$toudom,$fromcid) = @_;
3720: my @userlangs;
3721: if (($fromcid ne '') && ($env{'course.'.$fromcid.'.languages'} ne '')) {
3722: @userlangs=(@userlangs,split(/\s*(\,|\;|\:)\s*/,
3723: $env{'course.'.$fromcid.'.languages'}));
3724: } else {
3725: my %langhash = &getlangs($touname,$toudom);
3726: if ($langhash{'languages'} ne '') {
3727: @userlangs = split(/\s*(\,|\;|\:)\s*/,$langhash{'languages'});
3728: } else {
3729: my %domdefs = &Apache::lonnet::get_domain_defaults($toudom);
3730: if ($domdefs{'lang_def'} ne '') {
3731: @userlangs = ($domdefs{'lang_def'});
3732: }
3733: }
3734: }
3735: my @languages=&Apache::lonlocal::get_genlanguages(@userlangs);
3736: my $user_lh = Apache::localize->get_handle(@languages);
3737: return $user_lh;
3738: }
3739:
3740:
1.112 bowersj2 3741: ###############################################################
3742: ## Student Answer Attempts ##
3743: ###############################################################
3744:
3745: =pod
3746:
3747: =head1 Alternate Problem Views
3748:
3749: =over 4
3750:
1.648 raeburn 3751: =item * &get_previous_attempt($symb, $username, $domain, $course,
1.112 bowersj2 3752: $getattempt, $regexp, $gradesub)
3753:
3754: Return string with previous attempt on problem. Arguments:
3755:
3756: =over 4
3757:
3758: =item * $symb: Problem, including path
3759:
3760: =item * $username: username of the desired student
3761:
3762: =item * $domain: domain of the desired student
1.14 harris41 3763:
1.112 bowersj2 3764: =item * $course: Course ID
1.14 harris41 3765:
1.112 bowersj2 3766: =item * $getattempt: Leave blank for all attempts, otherwise put
3767: something
1.14 harris41 3768:
1.112 bowersj2 3769: =item * $regexp: if string matches this regexp, the string will be
3770: sent to $gradesub
1.14 harris41 3771:
1.112 bowersj2 3772: =item * $gradesub: routine that processes the string if it matches $regexp
1.14 harris41 3773:
1.112 bowersj2 3774: =back
1.14 harris41 3775:
1.112 bowersj2 3776: The output string is a table containing all desired attempts, if any.
1.16 harris41 3777:
1.112 bowersj2 3778: =cut
1.1 albertel 3779:
3780: sub get_previous_attempt {
1.43 ng 3781: my ($symb,$username,$domain,$course,$getattempt,$regexp,$gradesub)=@_;
1.1 albertel 3782: my $prevattempts='';
1.43 ng 3783: no strict 'refs';
1.1 albertel 3784: if ($symb) {
1.3 albertel 3785: my (%returnhash)=
3786: &Apache::lonnet::restore($symb,$course,$domain,$username);
1.1 albertel 3787: if ($returnhash{'version'}) {
3788: my %lasthash=();
3789: my $version;
3790: for ($version=1;$version<=$returnhash{'version'};$version++) {
1.356 albertel 3791: foreach my $key (sort(split(/\:/,$returnhash{$version.':keys'}))) {
3792: $lasthash{$key}=$returnhash{$version.':'.$key};
1.19 harris41 3793: }
1.1 albertel 3794: }
1.596 albertel 3795: $prevattempts=&start_data_table().&start_data_table_header_row();
3796: $prevattempts.='<th>'.&mt('History').'</th>';
1.978 raeburn 3797: my (%typeparts,%lasthidden);
1.945 raeburn 3798: my $showsurv=&Apache::lonnet::allowed('vas',$env{'request.course.id'});
1.356 albertel 3799: foreach my $key (sort(keys(%lasthash))) {
3800: my ($ign,@parts) = split(/\./,$key);
1.41 ng 3801: if ($#parts > 0) {
1.31 albertel 3802: my $data=$parts[-1];
1.989 raeburn 3803: next if ($data eq 'foilorder');
1.31 albertel 3804: pop(@parts);
1.1010 www 3805: $prevattempts.='<th>'.&mt('Part ').join('.',@parts).'<br />'.$data.' </th>';
1.945 raeburn 3806: if ($data eq 'type') {
3807: unless ($showsurv) {
3808: my $id = join(',',@parts);
3809: $typeparts{$ign.'.'.$id} = $lasthash{$key};
1.978 raeburn 3810: if (($lasthash{$key} eq 'anonsurvey') || ($lasthash{$key} eq 'anonsurveycred')) {
3811: $lasthidden{$ign.'.'.$id} = 1;
3812: }
1.945 raeburn 3813: }
1.1010 www 3814: }
1.31 albertel 3815: } else {
1.41 ng 3816: if ($#parts == 0) {
3817: $prevattempts.='<th>'.$parts[0].'</th>';
3818: } else {
3819: $prevattempts.='<th>'.$ign.'</th>';
3820: }
1.31 albertel 3821: }
1.16 harris41 3822: }
1.596 albertel 3823: $prevattempts.=&end_data_table_header_row();
1.40 ng 3824: if ($getattempt eq '') {
3825: for ($version=1;$version<=$returnhash{'version'};$version++) {
1.945 raeburn 3826: my @hidden;
3827: if (%typeparts) {
3828: foreach my $id (keys(%typeparts)) {
3829: if (($returnhash{$version.':'.$id.'.type'} eq 'anonsurvey') || ($returnhash{$version.':'.$id.'.type'} eq 'anonsurveycred')) {
3830: push(@hidden,$id);
3831: }
3832: }
3833: }
3834: $prevattempts.=&start_data_table_row().
3835: '<td>'.&mt('Transaction [_1]',$version).'</td>';
3836: if (@hidden) {
3837: foreach my $key (sort(keys(%lasthash))) {
1.989 raeburn 3838: next if ($key =~ /\.foilorder$/);
1.945 raeburn 3839: my $hide;
3840: foreach my $id (@hidden) {
3841: if ($key =~ /^\Q$id\E/) {
3842: $hide = 1;
3843: last;
3844: }
3845: }
3846: if ($hide) {
3847: my ($id,$data) = ($key =~ /^(.+)\.([^.]+)$/);
3848: if (($data eq 'award') || ($data eq 'awarddetail')) {
3849: my $value = &format_previous_attempt_value($key,
3850: $returnhash{$version.':'.$key});
1.1173 kruse 3851: $prevattempts.='<td>'.$value.' </td>';
1.945 raeburn 3852: } else {
3853: $prevattempts.='<td> </td>';
3854: }
3855: } else {
3856: if ($key =~ /\./) {
3857: my $value = &format_previous_attempt_value($key,
3858: $returnhash{$version.':'.$key});
1.1173 kruse 3859: $prevattempts.='<td>'.$value.' </td>';
1.945 raeburn 3860: } else {
3861: $prevattempts.='<td> </td>';
3862: }
3863: }
3864: }
3865: } else {
3866: foreach my $key (sort(keys(%lasthash))) {
1.989 raeburn 3867: next if ($key =~ /\.foilorder$/);
1.945 raeburn 3868: my $value = &format_previous_attempt_value($key,
3869: $returnhash{$version.':'.$key});
1.1173 kruse 3870: $prevattempts.='<td>'.$value.' </td>';
1.945 raeburn 3871: }
3872: }
3873: $prevattempts.=&end_data_table_row();
1.40 ng 3874: }
1.1 albertel 3875: }
1.945 raeburn 3876: my @currhidden = keys(%lasthidden);
1.596 albertel 3877: $prevattempts.=&start_data_table_row().'<td>'.&mt('Current').'</td>';
1.356 albertel 3878: foreach my $key (sort(keys(%lasthash))) {
1.989 raeburn 3879: next if ($key =~ /\.foilorder$/);
1.945 raeburn 3880: if (%typeparts) {
3881: my $hidden;
3882: foreach my $id (@currhidden) {
3883: if ($key =~ /^\Q$id\E/) {
3884: $hidden = 1;
3885: last;
3886: }
3887: }
3888: if ($hidden) {
3889: my ($id,$data) = ($key =~ /^(.+)\.([^.]+)$/);
3890: if (($data eq 'award') || ($data eq 'awarddetail')) {
3891: my $value = &format_previous_attempt_value($key,$lasthash{$key});
3892: if ($key =~/$regexp$/ && (defined &$gradesub)) {
3893: $value = &$gradesub($value);
3894: }
1.1173 kruse 3895: $prevattempts.='<td>'. $value.' </td>';
1.945 raeburn 3896: } else {
3897: $prevattempts.='<td> </td>';
3898: }
3899: } else {
3900: my $value = &format_previous_attempt_value($key,$lasthash{$key});
3901: if ($key =~/$regexp$/ && (defined &$gradesub)) {
3902: $value = &$gradesub($value);
3903: }
1.1173 kruse 3904: $prevattempts.='<td>'.$value.' </td>';
1.945 raeburn 3905: }
3906: } else {
3907: my $value = &format_previous_attempt_value($key,$lasthash{$key});
3908: if ($key =~/$regexp$/ && (defined &$gradesub)) {
3909: $value = &$gradesub($value);
3910: }
1.1173 kruse 3911: $prevattempts.='<td>'.$value.' </td>';
1.945 raeburn 3912: }
1.16 harris41 3913: }
1.596 albertel 3914: $prevattempts.= &end_data_table_row().&end_data_table();
1.1 albertel 3915: } else {
1.596 albertel 3916: $prevattempts=
3917: &start_data_table().&start_data_table_row().
3918: '<td>'.&mt('Nothing submitted - no attempts.').'</td>'.
3919: &end_data_table_row().&end_data_table();
1.1 albertel 3920: }
3921: } else {
1.596 albertel 3922: $prevattempts=
3923: &start_data_table().&start_data_table_row().
3924: '<td>'.&mt('No data.').'</td>'.
3925: &end_data_table_row().&end_data_table();
1.1 albertel 3926: }
1.10 albertel 3927: }
3928:
1.581 albertel 3929: sub format_previous_attempt_value {
3930: my ($key,$value) = @_;
1.1011 www 3931: if (($key =~ /timestamp/) || ($key=~/duedate/)) {
1.1173 kruse 3932: $value = &Apache::lonlocal::locallocaltime($value);
1.581 albertel 3933: } elsif (ref($value) eq 'ARRAY') {
1.1173 kruse 3934: $value = &HTML::Entities::encode('('.join(', ', @{ $value }).')','"<>&');
1.988 raeburn 3935: } elsif ($key =~ /answerstring$/) {
3936: my %answers = &Apache::lonnet::str2hash($value);
1.1173 kruse 3937: my @answer = %answers;
3938: %answers = map {&HTML::Entities::encode($_, '"<>&')} @answer;
1.988 raeburn 3939: my @anskeys = sort(keys(%answers));
3940: if (@anskeys == 1) {
3941: my $answer = $answers{$anskeys[0]};
1.1001 raeburn 3942: if ($answer =~ m{\0}) {
3943: $answer =~ s{\0}{,}g;
1.988 raeburn 3944: }
3945: my $tag_internal_answer_name = 'INTERNAL';
3946: if ($anskeys[0] eq $tag_internal_answer_name) {
3947: $value = $answer;
3948: } else {
3949: $value = $anskeys[0].'='.$answer;
3950: }
3951: } else {
3952: foreach my $ans (@anskeys) {
3953: my $answer = $answers{$ans};
1.1001 raeburn 3954: if ($answer =~ m{\0}) {
3955: $answer =~ s{\0}{,}g;
1.988 raeburn 3956: }
3957: $value .= $ans.'='.$answer.'<br />';;
3958: }
3959: }
1.581 albertel 3960: } else {
1.1173 kruse 3961: $value = &HTML::Entities::encode(&unescape($value), '"<>&');
1.581 albertel 3962: }
3963: return $value;
3964: }
3965:
3966:
1.107 albertel 3967: sub relative_to_absolute {
3968: my ($url,$output)=@_;
3969: my $parser=HTML::TokeParser->new(\$output);
3970: my $token;
3971: my $thisdir=$url;
3972: my @rlinks=();
3973: while ($token=$parser->get_token) {
3974: if ($token->[0] eq 'S') {
3975: if ($token->[1] eq 'a') {
3976: if ($token->[2]->{'href'}) {
3977: $rlinks[$#rlinks+1]=$token->[2]->{'href'};
3978: }
3979: } elsif ($token->[1] eq 'img' || $token->[1] eq 'embed' ) {
3980: $rlinks[$#rlinks+1]=$token->[2]->{'src'};
3981: } elsif ($token->[1] eq 'base') {
3982: $thisdir=$token->[2]->{'href'};
3983: }
3984: }
3985: }
3986: $thisdir=~s-/[^/]*$--;
1.356 albertel 3987: foreach my $link (@rlinks) {
1.726 raeburn 3988: unless (($link=~/^https?\:\/\//i) ||
1.356 albertel 3989: ($link=~/^\//) ||
3990: ($link=~/^javascript:/i) ||
3991: ($link=~/^mailto:/i) ||
3992: ($link=~/^\#/)) {
3993: my $newlocation=&Apache::lonnet::hreflocation($thisdir,$link);
3994: $output=~s/(\"|\'|\=\s*)\Q$link\E(\"|\'|\s|\>)/$1$newlocation$2/;
1.107 albertel 3995: }
3996: }
3997: # -------------------------------------------------- Deal with Applet codebases
3998: $output=~s/(\<applet[^\>]+)(codebase\=[^\S\>]+)*([^\>]*)\>/$1.($2?$2:' codebase="'.$thisdir.'"').$3.'>'/gei;
3999: return $output;
4000: }
4001:
1.112 bowersj2 4002: =pod
4003:
1.648 raeburn 4004: =item * &get_student_view()
1.112 bowersj2 4005:
4006: show a snapshot of what student was looking at
4007:
4008: =cut
4009:
1.10 albertel 4010: sub get_student_view {
1.186 albertel 4011: my ($symb,$username,$domain,$courseid,$target,$moreenv) = @_;
1.114 www 4012: my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb);
1.186 albertel 4013: my (%form);
1.10 albertel 4014: my @elements=('symb','courseid','domain','username');
4015: foreach my $element (@elements) {
1.186 albertel 4016: $form{'grade_'.$element}=eval '$'.$element #'
1.10 albertel 4017: }
1.186 albertel 4018: if (defined($moreenv)) {
4019: %form=(%form,%{$moreenv});
4020: }
1.236 albertel 4021: if (defined($target)) { $form{'grade_target'} = $target; }
1.107 albertel 4022: $feedurl=&Apache::lonnet::clutter($feedurl);
1.650 www 4023: my ($userview,$response)=&Apache::lonnet::ssi_body($feedurl,%form);
1.11 albertel 4024: $userview=~s/\<body[^\>]*\>//gi;
4025: $userview=~s/\<\/body\>//gi;
4026: $userview=~s/\<html\>//gi;
4027: $userview=~s/\<\/html\>//gi;
4028: $userview=~s/\<head\>//gi;
4029: $userview=~s/\<\/head\>//gi;
4030: $userview=~s/action\s*\=/would_be_action\=/gi;
1.107 albertel 4031: $userview=&relative_to_absolute($feedurl,$userview);
1.650 www 4032: if (wantarray) {
4033: return ($userview,$response);
4034: } else {
4035: return $userview;
4036: }
4037: }
4038:
4039: sub get_student_view_with_retries {
4040: my ($symb,$retries,$username,$domain,$courseid,$target,$moreenv) = @_;
4041:
4042: my $ok = 0; # True if we got a good response.
4043: my $content;
4044: my $response;
4045:
4046: # Try to get the student_view done. within the retries count:
4047:
4048: do {
4049: ($content, $response) = &get_student_view($symb,$username,$domain,$courseid,$target,$moreenv);
4050: $ok = $response->is_success;
4051: if (!$ok) {
4052: &Apache::lonnet::logthis("Failed get_student_view_with_retries on $symb: ".$response->is_success.', '.$response->code.', '.$response->message);
4053: }
4054: $retries--;
4055: } while (!$ok && ($retries > 0));
4056:
4057: if (!$ok) {
4058: $content = ''; # On error return an empty content.
4059: }
1.651 www 4060: if (wantarray) {
4061: return ($content, $response);
4062: } else {
4063: return $content;
4064: }
1.11 albertel 4065: }
4066:
1.112 bowersj2 4067: =pod
4068:
1.648 raeburn 4069: =item * &get_student_answers()
1.112 bowersj2 4070:
4071: show a snapshot of how student was answering problem
4072:
4073: =cut
4074:
1.11 albertel 4075: sub get_student_answers {
1.100 sakharuk 4076: my ($symb,$username,$domain,$courseid,%form) = @_;
1.114 www 4077: my ($map,$id,$feedurl) = &Apache::lonnet::decode_symb($symb);
1.186 albertel 4078: my (%moreenv);
1.11 albertel 4079: my @elements=('symb','courseid','domain','username');
4080: foreach my $element (@elements) {
1.186 albertel 4081: $moreenv{'grade_'.$element}=eval '$'.$element #'
1.10 albertel 4082: }
1.186 albertel 4083: $moreenv{'grade_target'}='answer';
4084: %moreenv=(%form,%moreenv);
1.497 raeburn 4085: $feedurl = &Apache::lonnet::clutter($feedurl);
4086: my $userview=&Apache::lonnet::ssi($feedurl,%moreenv);
1.10 albertel 4087: return $userview;
1.1 albertel 4088: }
1.116 albertel 4089:
4090: =pod
4091:
4092: =item * &submlink()
4093:
1.242 albertel 4094: Inputs: $text $uname $udom $symb $target
1.116 albertel 4095:
4096: Returns: A link to grades.pm such as to see the SUBM view of a student
4097:
4098: =cut
4099:
4100: ###############################################
4101: sub submlink {
1.242 albertel 4102: my ($text,$uname,$udom,$symb,$target)=@_;
1.116 albertel 4103: if (!($uname && $udom)) {
4104: (my $cursymb, my $courseid,$udom,$uname)=
1.463 albertel 4105: &Apache::lonnet::whichuser($symb);
1.116 albertel 4106: if (!$symb) { $symb=$cursymb; }
4107: }
1.254 matthew 4108: if (!$symb) { $symb=&Apache::lonnet::symbread(); }
1.369 www 4109: $symb=&escape($symb);
1.960 bisitz 4110: if ($target) { $target=" target=\"$target\""; }
4111: return
4112: '<a href="/adm/grades?command=submission'.
4113: '&symb='.$symb.
4114: '&student='.$uname.
4115: '&userdom='.$udom.'"'.
4116: $target.'>'.$text.'</a>';
1.242 albertel 4117: }
4118: ##############################################
4119:
4120: =pod
4121:
4122: =item * &pgrdlink()
4123:
4124: Inputs: $text $uname $udom $symb $target
4125:
4126: Returns: A link to grades.pm such as to see the PGRD view of a student
4127:
4128: =cut
4129:
4130: ###############################################
4131: sub pgrdlink {
4132: my $link=&submlink(@_);
4133: $link=~s/(&command=submission)/$1&showgrading=yes/;
4134: return $link;
4135: }
4136: ##############################################
4137:
4138: =pod
4139:
4140: =item * &pprmlink()
4141:
4142: Inputs: $text $uname $udom $symb $target
4143:
4144: Returns: A link to parmset.pm such as to see the PPRM view of a
1.283 albertel 4145: student and a specific resource
1.242 albertel 4146:
4147: =cut
4148:
4149: ###############################################
4150: sub pprmlink {
4151: my ($text,$uname,$udom,$symb,$target)=@_;
4152: if (!($uname && $udom)) {
4153: (my $cursymb, my $courseid,$udom,$uname)=
1.463 albertel 4154: &Apache::lonnet::whichuser($symb);
1.242 albertel 4155: if (!$symb) { $symb=$cursymb; }
4156: }
1.254 matthew 4157: if (!$symb) { $symb=&Apache::lonnet::symbread(); }
1.369 www 4158: $symb=&escape($symb);
1.242 albertel 4159: if ($target) { $target="target=\"$target\""; }
1.595 albertel 4160: return '<a href="/adm/parmset?command=set&'.
4161: 'symb='.$symb.'&uname='.$uname.
4162: '&udom='.$udom.'" '.$target.'>'.$text.'</a>';
1.116 albertel 4163: }
4164: ##############################################
1.37 matthew 4165:
1.112 bowersj2 4166: =pod
4167:
4168: =back
4169:
4170: =cut
4171:
1.37 matthew 4172: ###############################################
1.51 www 4173:
4174:
4175: sub timehash {
1.687 raeburn 4176: my ($thistime) = @_;
4177: my $timezone = &Apache::lonlocal::gettimezone();
4178: my $dt = DateTime->from_epoch(epoch => $thistime)
4179: ->set_time_zone($timezone);
4180: my $wday = $dt->day_of_week();
4181: if ($wday == 7) { $wday = 0; }
4182: return ( 'second' => $dt->second(),
4183: 'minute' => $dt->minute(),
4184: 'hour' => $dt->hour(),
4185: 'day' => $dt->day_of_month(),
4186: 'month' => $dt->month(),
4187: 'year' => $dt->year(),
4188: 'weekday' => $wday,
4189: 'dayyear' => $dt->day_of_year(),
4190: 'dlsav' => $dt->is_dst() );
1.51 www 4191: }
4192:
1.370 www 4193: sub utc_string {
4194: my ($date)=@_;
1.371 www 4195: return strftime("%Y%m%dT%H%M%SZ",gmtime($date));
1.370 www 4196: }
4197:
1.51 www 4198: sub maketime {
4199: my %th=@_;
1.687 raeburn 4200: my ($epoch_time,$timezone,$dt);
4201: $timezone = &Apache::lonlocal::gettimezone();
4202: eval {
4203: $dt = DateTime->new( year => $th{'year'},
4204: month => $th{'month'},
4205: day => $th{'day'},
4206: hour => $th{'hour'},
4207: minute => $th{'minute'},
4208: second => $th{'second'},
4209: time_zone => $timezone,
4210: );
4211: };
4212: if (!$@) {
4213: $epoch_time = $dt->epoch;
4214: if ($epoch_time) {
4215: return $epoch_time;
4216: }
4217: }
1.51 www 4218: return POSIX::mktime(
4219: ($th{'seconds'},$th{'minutes'},$th{'hours'},
1.210 www 4220: $th{'day'},$th{'month'}-1,$th{'year'}-1900,0,0,-1));
1.70 www 4221: }
4222:
4223: #########################################
1.51 www 4224:
4225: sub findallcourses {
1.482 raeburn 4226: my ($roles,$uname,$udom) = @_;
1.355 albertel 4227: my %roles;
4228: if (ref($roles)) { %roles = map { $_ => 1 } @{$roles}; }
1.348 albertel 4229: my %courses;
1.51 www 4230: my $now=time;
1.482 raeburn 4231: if (!defined($uname)) {
4232: $uname = $env{'user.name'};
4233: }
4234: if (!defined($udom)) {
4235: $udom = $env{'user.domain'};
4236: }
4237: if (($uname ne $env{'user.name'}) || ($udom ne $env{'user.domain'})) {
1.1073 raeburn 4238: my %roleshash = &Apache::lonnet::dump('roles',$udom,$uname);
1.482 raeburn 4239: if (!%roles) {
4240: %roles = (
4241: cc => 1,
1.907 raeburn 4242: co => 1,
1.482 raeburn 4243: in => 1,
4244: ep => 1,
4245: ta => 1,
4246: cr => 1,
4247: st => 1,
4248: );
4249: }
4250: foreach my $entry (keys(%roleshash)) {
4251: my ($trole,$tend,$tstart) = split(/_/,$roleshash{$entry});
4252: if ($trole =~ /^cr/) {
4253: next if (!exists($roles{$trole}) && !exists($roles{'cr'}));
4254: } else {
4255: next if (!exists($roles{$trole}));
4256: }
4257: if ($tend) {
4258: next if ($tend < $now);
4259: }
4260: if ($tstart) {
4261: next if ($tstart > $now);
4262: }
1.1058 raeburn 4263: my ($cdom,$cnum,$sec,$cnumpart,$secpart,$role);
1.482 raeburn 4264: (undef,$cdom,$cnumpart,$secpart) = split(/\//,$entry);
1.1058 raeburn 4265: my $value = $trole.'/'.$cdom.'/';
1.482 raeburn 4266: if ($secpart eq '') {
4267: ($cnum,$role) = split(/_/,$cnumpart);
4268: $sec = 'none';
1.1058 raeburn 4269: $value .= $cnum.'/';
1.482 raeburn 4270: } else {
4271: $cnum = $cnumpart;
4272: ($sec,$role) = split(/_/,$secpart);
1.1058 raeburn 4273: $value .= $cnum.'/'.$sec;
4274: }
4275: if (ref($courses{$cdom.'_'.$cnum}{$sec}) eq 'ARRAY') {
4276: unless (grep(/^\Q$value\E$/,@{$courses{$cdom.'_'.$cnum}{$sec}})) {
4277: push(@{$courses{$cdom.'_'.$cnum}{$sec}},$value);
4278: }
4279: } else {
4280: @{$courses{$cdom.'_'.$cnum}{$sec}} = ($value);
1.490 raeburn 4281: }
1.482 raeburn 4282: }
4283: } else {
4284: foreach my $key (keys(%env)) {
1.483 albertel 4285: if ( $key=~m{^user\.role\.(\w+)\./($match_domain)/($match_courseid)/?(\w*)$} ||
4286: $key=~m{^user\.role\.(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_courseid)/?(\w*)$}) {
1.482 raeburn 4287: my ($role,$cdom,$cnum,$sec) = ($1,$2,$3,$4);
4288: next if ($role eq 'ca' || $role eq 'aa');
4289: next if (%roles && !exists($roles{$role}));
4290: my ($starttime,$endtime)=split(/\./,$env{$key});
4291: my $active=1;
4292: if ($starttime) {
4293: if ($now<$starttime) { $active=0; }
4294: }
4295: if ($endtime) {
4296: if ($now>$endtime) { $active=0; }
4297: }
4298: if ($active) {
1.1058 raeburn 4299: my $value = $role.'/'.$cdom.'/'.$cnum.'/';
1.482 raeburn 4300: if ($sec eq '') {
4301: $sec = 'none';
1.1058 raeburn 4302: } else {
4303: $value .= $sec;
4304: }
4305: if (ref($courses{$cdom.'_'.$cnum}{$sec}) eq 'ARRAY') {
4306: unless (grep(/^\Q$value\E$/,@{$courses{$cdom.'_'.$cnum}{$sec}})) {
4307: push(@{$courses{$cdom.'_'.$cnum}{$sec}},$value);
4308: }
4309: } else {
4310: @{$courses{$cdom.'_'.$cnum}{$sec}} = ($value);
1.482 raeburn 4311: }
1.474 raeburn 4312: }
4313: }
1.51 www 4314: }
4315: }
1.474 raeburn 4316: return %courses;
1.51 www 4317: }
1.37 matthew 4318:
1.54 www 4319: ###############################################
1.474 raeburn 4320:
4321: sub blockcheck {
1.1062 raeburn 4322: my ($setters,$activity,$uname,$udom,$url) = @_;
1.490 raeburn 4323:
4324: if (!defined($udom)) {
4325: $udom = $env{'user.domain'};
4326: }
4327: if (!defined($uname)) {
4328: $uname = $env{'user.name'};
4329: }
4330:
4331: # If uname and udom are for a course, check for blocks in the course.
4332:
4333: if (&Apache::lonnet::is_course($udom,$uname)) {
1.1062 raeburn 4334: my ($startblock,$endblock,$triggerblock) =
4335: &get_blocks($setters,$activity,$udom,$uname,$url);
4336: return ($startblock,$endblock,$triggerblock);
1.490 raeburn 4337: }
1.474 raeburn 4338:
1.502 raeburn 4339: my $startblock = 0;
4340: my $endblock = 0;
1.1062 raeburn 4341: my $triggerblock = '';
1.482 raeburn 4342: my %live_courses = &findallcourses(undef,$uname,$udom);
1.474 raeburn 4343:
1.490 raeburn 4344: # If uname is for a user, and activity is course-specific, i.e.,
4345: # boards, chat or groups, check for blocking in current course only.
1.474 raeburn 4346:
1.490 raeburn 4347: if (($activity eq 'boards' || $activity eq 'chat' ||
4348: $activity eq 'groups') && ($env{'request.course.id'})) {
4349: foreach my $key (keys(%live_courses)) {
4350: if ($key ne $env{'request.course.id'}) {
4351: delete($live_courses{$key});
4352: }
4353: }
4354: }
4355:
4356: my $otheruser = 0;
4357: my %own_courses;
4358: if ((($uname ne $env{'user.name'})) || ($udom ne $env{'user.domain'})) {
4359: # Resource belongs to user other than current user.
4360: $otheruser = 1;
4361: # Gather courses for current user
4362: %own_courses =
4363: &findallcourses(undef,$env{'user.name'},$env{'user.domain'});
4364: }
4365:
4366: # Gather active course roles - course coordinator, instructor,
4367: # exam proctor, ta, student, or custom role.
1.474 raeburn 4368:
4369: foreach my $course (keys(%live_courses)) {
1.482 raeburn 4370: my ($cdom,$cnum);
4371: if ((defined($env{'course.'.$course.'.domain'})) && (defined($env{'course.'.$course.'.num'}))) {
4372: $cdom = $env{'course.'.$course.'.domain'};
4373: $cnum = $env{'course.'.$course.'.num'};
4374: } else {
1.490 raeburn 4375: ($cdom,$cnum) = split(/_/,$course);
1.482 raeburn 4376: }
4377: my $no_ownblock = 0;
4378: my $no_userblock = 0;
1.533 raeburn 4379: if ($otheruser && $activity ne 'com') {
1.490 raeburn 4380: # Check if current user has 'evb' priv for this
4381: if (defined($own_courses{$course})) {
4382: foreach my $sec (keys(%{$own_courses{$course}})) {
4383: my $checkrole = 'cm./'.$cdom.'/'.$cnum;
4384: if ($sec ne 'none') {
4385: $checkrole .= '/'.$sec;
4386: }
4387: if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
4388: $no_ownblock = 1;
4389: last;
4390: }
4391: }
4392: }
4393: # if they have 'evb' priv and are currently not playing student
4394: next if (($no_ownblock) &&
4395: ($env{'request.role'} !~ m{^st\./$cdom/$cnum}));
4396: }
1.474 raeburn 4397: foreach my $sec (keys(%{$live_courses{$course}})) {
1.482 raeburn 4398: my $checkrole = 'cm./'.$cdom.'/'.$cnum;
1.474 raeburn 4399: if ($sec ne 'none') {
1.482 raeburn 4400: $checkrole .= '/'.$sec;
1.474 raeburn 4401: }
1.490 raeburn 4402: if ($otheruser) {
4403: # Resource belongs to user other than current user.
4404: # Assemble privs for that user, and check for 'evb' priv.
1.1058 raeburn 4405: my (%allroles,%userroles);
4406: if (ref($live_courses{$course}{$sec}) eq 'ARRAY') {
4407: foreach my $entry (@{$live_courses{$course}{$sec}}) {
4408: my ($trole,$tdom,$tnum,$tsec);
4409: if ($entry =~ /^cr/) {
4410: ($trole,$tdom,$tnum,$tsec) =
4411: ($entry =~ m|^(cr/$match_domain/$match_username/\w+)\./($match_domain)/($match_username)/?(\w*)$|);
4412: } else {
4413: ($trole,$tdom,$tnum,$tsec) = split(/\//,$entry);
4414: }
4415: my ($spec,$area,$trest);
4416: $area = '/'.$tdom.'/'.$tnum;
4417: $trest = $tnum;
4418: if ($tsec ne '') {
4419: $area .= '/'.$tsec;
4420: $trest .= '/'.$tsec;
4421: }
4422: $spec = $trole.'.'.$area;
4423: if ($trole =~ /^cr/) {
4424: &Apache::lonnet::custom_roleprivs(\%allroles,$trole,
4425: $tdom,$spec,$trest,$area);
4426: } else {
4427: &Apache::lonnet::standard_roleprivs(\%allroles,$trole,
4428: $tdom,$spec,$trest,$area);
4429: }
4430: }
4431: my ($author,$adv) = &Apache::lonnet::set_userprivs(\%userroles,\%allroles);
4432: if ($userroles{'user.priv.'.$checkrole} =~ /evb\&([^\:]*)/) {
4433: if ($1) {
4434: $no_userblock = 1;
4435: last;
4436: }
1.486 raeburn 4437: }
4438: }
1.490 raeburn 4439: } else {
4440: # Resource belongs to current user
4441: # Check for 'evb' priv via lonnet::allowed().
1.482 raeburn 4442: if (&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) {
4443: $no_ownblock = 1;
4444: last;
4445: }
1.474 raeburn 4446: }
4447: }
4448: # if they have the evb priv and are currently not playing student
1.482 raeburn 4449: next if (($no_ownblock) &&
1.491 albertel 4450: ($env{'request.role'} !~ m{^st\./\Q$cdom\E/\Q$cnum\E}));
1.482 raeburn 4451: next if ($no_userblock);
1.474 raeburn 4452:
1.866 kalberla 4453: # Retrieve blocking times and identity of locker for course
1.490 raeburn 4454: # of specified user, unless user has 'evb' privilege.
1.502 raeburn 4455:
1.1062 raeburn 4456: my ($start,$end,$trigger) =
4457: &get_blocks($setters,$activity,$cdom,$cnum,$url);
1.502 raeburn 4458: if (($start != 0) &&
4459: (($startblock == 0) || ($startblock > $start))) {
4460: $startblock = $start;
1.1062 raeburn 4461: if ($trigger ne '') {
4462: $triggerblock = $trigger;
4463: }
1.502 raeburn 4464: }
4465: if (($end != 0) &&
4466: (($endblock == 0) || ($endblock < $end))) {
4467: $endblock = $end;
1.1062 raeburn 4468: if ($trigger ne '') {
4469: $triggerblock = $trigger;
4470: }
1.502 raeburn 4471: }
1.490 raeburn 4472: }
1.1062 raeburn 4473: return ($startblock,$endblock,$triggerblock);
1.490 raeburn 4474: }
4475:
4476: sub get_blocks {
1.1062 raeburn 4477: my ($setters,$activity,$cdom,$cnum,$url) = @_;
1.490 raeburn 4478: my $startblock = 0;
4479: my $endblock = 0;
1.1062 raeburn 4480: my $triggerblock = '';
1.490 raeburn 4481: my $course = $cdom.'_'.$cnum;
4482: $setters->{$course} = {};
4483: $setters->{$course}{'staff'} = [];
4484: $setters->{$course}{'times'} = [];
1.1062 raeburn 4485: $setters->{$course}{'triggers'} = [];
4486: my (@blockers,%triggered);
4487: my $now = time;
4488: my %commblocks = &Apache::lonnet::get_comm_blocks($cdom,$cnum);
4489: if ($activity eq 'docs') {
4490: @blockers = &Apache::lonnet::has_comm_blocking('bre',undef,$url,\%commblocks);
4491: foreach my $block (@blockers) {
4492: if ($block =~ /^firstaccess____(.+)$/) {
4493: my $item = $1;
4494: my $type = 'map';
4495: my $timersymb = $item;
4496: if ($item eq 'course') {
4497: $type = 'course';
4498: } elsif ($item =~ /___\d+___/) {
4499: $type = 'resource';
4500: } else {
4501: $timersymb = &Apache::lonnet::symbread($item);
4502: }
4503: my $start = $env{'course.'.$cdom.'_'.$cnum.'.firstaccess.'.$timersymb};
4504: my $end = $start + $env{'course.'.$cdom.'_'.$cnum.'.timerinterval.'.$timersymb};
4505: $triggered{$block} = {
4506: start => $start,
4507: end => $end,
4508: type => $type,
4509: };
4510: }
4511: }
4512: } else {
4513: foreach my $block (keys(%commblocks)) {
4514: if ($block =~ m/^(\d+)____(\d+)$/) {
4515: my ($start,$end) = ($1,$2);
4516: if ($start <= time && $end >= time) {
4517: if (ref($commblocks{$block}) eq 'HASH') {
4518: if (ref($commblocks{$block}{'blocks'}) eq 'HASH') {
4519: if ($commblocks{$block}{'blocks'}{$activity} eq 'on') {
4520: unless(grep(/^\Q$block\E$/,@blockers)) {
4521: push(@blockers,$block);
4522: }
4523: }
4524: }
4525: }
4526: }
4527: } elsif ($block =~ /^firstaccess____(.+)$/) {
4528: my $item = $1;
4529: my $timersymb = $item;
4530: my $type = 'map';
4531: if ($item eq 'course') {
4532: $type = 'course';
4533: } elsif ($item =~ /___\d+___/) {
4534: $type = 'resource';
4535: } else {
4536: $timersymb = &Apache::lonnet::symbread($item);
4537: }
4538: my $start = $env{'course.'.$cdom.'_'.$cnum.'.firstaccess.'.$timersymb};
4539: my $end = $start + $env{'course.'.$cdom.'_'.$cnum.'.timerinterval.'.$timersymb};
4540: if ($start && $end) {
4541: if (($start <= time) && ($end >= time)) {
4542: unless (grep(/^\Q$block\E$/,@blockers)) {
4543: push(@blockers,$block);
4544: $triggered{$block} = {
4545: start => $start,
4546: end => $end,
4547: type => $type,
4548: };
4549: }
4550: }
1.490 raeburn 4551: }
1.1062 raeburn 4552: }
4553: }
4554: }
4555: foreach my $blocker (@blockers) {
4556: my ($staff_name,$staff_dom,$title,$blocks) =
4557: &parse_block_record($commblocks{$blocker});
4558: push(@{$$setters{$course}{'staff'}},[$staff_name,$staff_dom]);
4559: my ($start,$end,$triggertype);
4560: if ($blocker =~ m/^(\d+)____(\d+)$/) {
4561: ($start,$end) = ($1,$2);
4562: } elsif (ref($triggered{$blocker}) eq 'HASH') {
4563: $start = $triggered{$blocker}{'start'};
4564: $end = $triggered{$blocker}{'end'};
4565: $triggertype = $triggered{$blocker}{'type'};
4566: }
4567: if ($start) {
4568: push(@{$$setters{$course}{'times'}}, [$start,$end]);
4569: if ($triggertype) {
4570: push(@{$$setters{$course}{'triggers'}},$triggertype);
4571: } else {
4572: push(@{$$setters{$course}{'triggers'}},0);
4573: }
4574: if ( ($startblock == 0) || ($startblock > $start) ) {
4575: $startblock = $start;
4576: if ($triggertype) {
4577: $triggerblock = $blocker;
1.474 raeburn 4578: }
4579: }
1.1062 raeburn 4580: if ( ($endblock == 0) || ($endblock < $end) ) {
4581: $endblock = $end;
4582: if ($triggertype) {
4583: $triggerblock = $blocker;
4584: }
4585: }
1.474 raeburn 4586: }
4587: }
1.1062 raeburn 4588: return ($startblock,$endblock,$triggerblock);
1.474 raeburn 4589: }
4590:
4591: sub parse_block_record {
4592: my ($record) = @_;
4593: my ($setuname,$setudom,$title,$blocks);
4594: if (ref($record) eq 'HASH') {
4595: ($setuname,$setudom) = split(/:/,$record->{'setter'});
4596: $title = &unescape($record->{'event'});
4597: $blocks = $record->{'blocks'};
4598: } else {
4599: my @data = split(/:/,$record,3);
4600: if (scalar(@data) eq 2) {
4601: $title = $data[1];
4602: ($setuname,$setudom) = split(/@/,$data[0]);
4603: } else {
4604: ($setuname,$setudom,$title) = @data;
4605: }
4606: $blocks = { 'com' => 'on' };
4607: }
4608: return ($setuname,$setudom,$title,$blocks);
4609: }
4610:
1.854 kalberla 4611: sub blocking_status {
1.1062 raeburn 4612: my ($activity,$uname,$udom,$url) = @_;
1.1061 raeburn 4613: my %setters;
1.890 droeschl 4614:
1.1061 raeburn 4615: # check for active blocking
1.1062 raeburn 4616: my ($startblock,$endblock,$triggerblock) =
4617: &blockcheck(\%setters,$activity,$uname,$udom,$url);
4618: my $blocked = 0;
4619: if ($startblock && $endblock) {
4620: $blocked = 1;
4621: }
1.890 droeschl 4622:
1.1061 raeburn 4623: # caller just wants to know whether a block is active
4624: if (!wantarray) { return $blocked; }
4625:
4626: # build a link to a popup window containing the details
4627: my $querystring = "?activity=$activity";
4628: # $uname and $udom decide whose portfolio the user is trying to look at
1.1062 raeburn 4629: if ($activity eq 'port') {
4630: $querystring .= "&udom=$udom" if $udom;
4631: $querystring .= "&uname=$uname" if $uname;
4632: } elsif ($activity eq 'docs') {
4633: $querystring .= '&url='.&HTML::Entities::encode($url,'&"');
4634: }
1.1061 raeburn 4635:
4636: my $output .= <<'END_MYBLOCK';
4637: function openWindow(url, wdwName, w, h, toolbar,scrollbar) {
4638: var options = "width=" + w + ",height=" + h + ",";
4639: options += "resizable=yes,scrollbars="+scrollbar+",status=no,";
4640: options += "menubar=no,toolbar="+toolbar+",location=no,directories=no";
4641: var newWin = window.open(url, wdwName, options);
4642: newWin.focus();
4643: }
1.890 droeschl 4644: END_MYBLOCK
1.854 kalberla 4645:
1.1061 raeburn 4646: $output = Apache::lonhtmlcommon::scripttag($output);
1.890 droeschl 4647:
1.1061 raeburn 4648: my $popupUrl = "/adm/blockingstatus/$querystring";
1.1062 raeburn 4649: my $text = &mt('Communication Blocked');
4650: if ($activity eq 'docs') {
4651: $text = &mt('Content Access Blocked');
1.1063 raeburn 4652: } elsif ($activity eq 'printout') {
4653: $text = &mt('Printing Blocked');
1.1062 raeburn 4654: }
1.1061 raeburn 4655: $output .= <<"END_BLOCK";
1.867 kalberla 4656: <div class='LC_comblock'>
1.869 kalberla 4657: <a onclick='openWindow("$popupUrl","Blocking Table",600,300,"no","no");return false;' href='/adm/blockingstatus/$querystring'
1.890 droeschl 4658: title='$text'>
4659: <img class='LC_noBorder LC_middle' title='$text' src='/res/adm/pages/comblock.png' alt='$text'/></a>
1.869 kalberla 4660: <a onclick='openWindow("$popupUrl","Blocking Table",600,300,"no","no");return false;' href='/adm/blockingstatus/$querystring'
1.890 droeschl 4661: title='$text'>$text</a>
1.867 kalberla 4662: </div>
4663:
4664: END_BLOCK
1.474 raeburn 4665:
1.1061 raeburn 4666: return ($blocked, $output);
1.854 kalberla 4667: }
1.490 raeburn 4668:
1.60 matthew 4669: ###############################################
4670:
1.682 raeburn 4671: sub check_ip_acc {
4672: my ($acc)=@_;
4673: &Apache::lonxml::debug("acc is $acc");
4674: if (!defined($acc) || $acc =~ /^\s*$/ || $acc =~/^\s*no\s*$/i) {
4675: return 1;
4676: }
4677: my $allowed=0;
4678: my $ip=$env{'request.host'} || $ENV{'REMOTE_ADDR'};
4679:
4680: my $name;
4681: foreach my $pattern (split(',',$acc)) {
4682: $pattern =~ s/^\s*//;
4683: $pattern =~ s/\s*$//;
4684: if ($pattern =~ /\*$/) {
4685: #35.8.*
4686: $pattern=~s/\*//;
4687: if ($ip =~ /^\Q$pattern\E/) { $allowed=1; }
4688: } elsif ($pattern =~ /(\d+\.\d+\.\d+)\.\[(\d+)-(\d+)\]$/) {
4689: #35.8.3.[34-56]
4690: my $low=$2;
4691: my $high=$3;
4692: $pattern=$1;
4693: if ($ip =~ /^\Q$pattern\E/) {
4694: my $last=(split(/\./,$ip))[3];
4695: if ($last <=$high && $last >=$low) { $allowed=1; }
4696: }
4697: } elsif ($pattern =~ /^\*/) {
4698: #*.msu.edu
4699: $pattern=~s/\*//;
4700: if (!defined($name)) {
4701: use Socket;
4702: my $netaddr=inet_aton($ip);
4703: ($name)=gethostbyaddr($netaddr,AF_INET);
4704: }
4705: if ($name =~ /\Q$pattern\E$/i) { $allowed=1; }
4706: } elsif ($pattern =~ /\d+\.\d+\.\d+\.\d+/) {
4707: #127.0.0.1
4708: if ($ip =~ /^\Q$pattern\E/) { $allowed=1; }
4709: } else {
4710: #some.name.com
4711: if (!defined($name)) {
4712: use Socket;
4713: my $netaddr=inet_aton($ip);
4714: ($name)=gethostbyaddr($netaddr,AF_INET);
4715: }
4716: if ($name =~ /\Q$pattern\E$/i) { $allowed=1; }
4717: }
4718: if ($allowed) { last; }
4719: }
4720: return $allowed;
4721: }
4722:
4723: ###############################################
4724:
1.60 matthew 4725: =pod
4726:
1.112 bowersj2 4727: =head1 Domain Template Functions
4728:
4729: =over 4
4730:
4731: =item * &determinedomain()
1.60 matthew 4732:
4733: Inputs: $domain (usually will be undef)
4734:
1.63 www 4735: Returns: Determines which domain should be used for designs
1.60 matthew 4736:
4737: =cut
1.54 www 4738:
1.60 matthew 4739: ###############################################
1.63 www 4740: sub determinedomain {
4741: my $domain=shift;
1.531 albertel 4742: if (! $domain) {
1.60 matthew 4743: # Determine domain if we have not been given one
1.893 raeburn 4744: $domain = &Apache::lonnet::default_login_domain();
1.258 albertel 4745: if ($env{'user.domain'}) { $domain=$env{'user.domain'}; }
4746: if ($env{'request.role.domain'}) {
4747: $domain=$env{'request.role.domain'};
1.60 matthew 4748: }
4749: }
1.63 www 4750: return $domain;
4751: }
4752: ###############################################
1.517 raeburn 4753:
1.518 albertel 4754: sub devalidate_domconfig_cache {
4755: my ($udom)=@_;
4756: &Apache::lonnet::devalidate_cache_new('domainconfig',$udom);
4757: }
4758:
4759: # ---------------------- Get domain configuration for a domain
4760: sub get_domainconf {
4761: my ($udom) = @_;
4762: my $cachetime=1800;
4763: my ($result,$cached)=&Apache::lonnet::is_cached_new('domainconfig',$udom);
4764: if (defined($cached)) { return %{$result}; }
4765:
4766: my %domconfig = &Apache::lonnet::get_dom('configuration',
1.948 raeburn 4767: ['login','rolecolors','autoenroll'],$udom);
1.632 raeburn 4768: my (%designhash,%legacy);
1.518 albertel 4769: if (keys(%domconfig) > 0) {
4770: if (ref($domconfig{'login'}) eq 'HASH') {
1.632 raeburn 4771: if (keys(%{$domconfig{'login'}})) {
4772: foreach my $key (keys(%{$domconfig{'login'}})) {
1.699 raeburn 4773: if (ref($domconfig{'login'}{$key}) eq 'HASH') {
1.946 raeburn 4774: if ($key eq 'loginvia') {
4775: if (ref($domconfig{'login'}{'loginvia'}) eq 'HASH') {
1.1013 raeburn 4776: foreach my $hostname (keys(%{$domconfig{'login'}{'loginvia'}})) {
1.948 raeburn 4777: if (ref($domconfig{'login'}{'loginvia'}{$hostname}) eq 'HASH') {
4778: if ($domconfig{'login'}{'loginvia'}{$hostname}{'server'}) {
4779: my $server = $domconfig{'login'}{'loginvia'}{$hostname}{'server'};
4780: $designhash{$udom.'.login.loginvia'} = $server;
4781: if ($domconfig{'login'}{'loginvia'}{$hostname}{'serverpath'} eq 'custom') {
4782:
4783: $designhash{$udom.'.login.loginvia_'.$hostname} = $server.':'.$domconfig{'login'}{'loginvia'}{$hostname}{'custompath'};
4784: } else {
1.1013 raeburn 4785: $designhash{$udom.'.login.loginvia_'.$hostname} = $server.':'.$domconfig{'login'}{'loginvia'}{$hostname}{'serverpath'};
1.948 raeburn 4786: }
4787: if ($domconfig{'login'}{'loginvia'}{$hostname}{'exempt'}) {
4788: $designhash{$udom.'.login.loginvia_exempt_'.$hostname} = $domconfig{'login'}{'loginvia'}{$hostname}{'exempt'};
4789: }
1.946 raeburn 4790: }
4791: }
4792: }
4793: }
4794: } else {
4795: foreach my $img (keys(%{$domconfig{'login'}{$key}})) {
4796: $designhash{$udom.'.login.'.$key.'_'.$img} =
4797: $domconfig{'login'}{$key}{$img};
4798: }
1.699 raeburn 4799: }
4800: } else {
4801: $designhash{$udom.'.login.'.$key}=$domconfig{'login'}{$key};
4802: }
1.632 raeburn 4803: }
4804: } else {
4805: $legacy{'login'} = 1;
1.518 albertel 4806: }
1.632 raeburn 4807: } else {
4808: $legacy{'login'} = 1;
1.518 albertel 4809: }
4810: if (ref($domconfig{'rolecolors'}) eq 'HASH') {
1.632 raeburn 4811: if (keys(%{$domconfig{'rolecolors'}})) {
4812: foreach my $role (keys(%{$domconfig{'rolecolors'}})) {
4813: if (ref($domconfig{'rolecolors'}{$role}) eq 'HASH') {
4814: foreach my $item (keys(%{$domconfig{'rolecolors'}{$role}})) {
4815: $designhash{$udom.'.'.$role.'.'.$item}=$domconfig{'rolecolors'}{$role}{$item};
4816: }
1.518 albertel 4817: }
4818: }
1.632 raeburn 4819: } else {
4820: $legacy{'rolecolors'} = 1;
1.518 albertel 4821: }
1.632 raeburn 4822: } else {
4823: $legacy{'rolecolors'} = 1;
1.518 albertel 4824: }
1.948 raeburn 4825: if (ref($domconfig{'autoenroll'}) eq 'HASH') {
4826: if ($domconfig{'autoenroll'}{'co-owners'}) {
4827: $designhash{$udom.'.autoassign.co-owners'}=$domconfig{'autoenroll'}{'co-owners'};
4828: }
4829: }
1.632 raeburn 4830: if (keys(%legacy) > 0) {
4831: my %legacyhash = &get_legacy_domconf($udom);
4832: foreach my $item (keys(%legacyhash)) {
4833: if ($item =~ /^\Q$udom\E\.login/) {
4834: if ($legacy{'login'}) {
4835: $designhash{$item} = $legacyhash{$item};
4836: }
4837: } else {
4838: if ($legacy{'rolecolors'}) {
4839: $designhash{$item} = $legacyhash{$item};
4840: }
1.518 albertel 4841: }
4842: }
4843: }
1.632 raeburn 4844: } else {
4845: %designhash = &get_legacy_domconf($udom);
1.518 albertel 4846: }
4847: &Apache::lonnet::do_cache_new('domainconfig',$udom,\%designhash,
4848: $cachetime);
4849: return %designhash;
4850: }
4851:
1.632 raeburn 4852: sub get_legacy_domconf {
4853: my ($udom) = @_;
4854: my %legacyhash;
4855: my $designdir=$Apache::lonnet::perlvar{'lonTabDir'}.'/lonDomColors';
4856: my $designfile = $designdir.'/'.$udom.'.tab';
4857: if (-e $designfile) {
4858: if ( open (my $fh,"<$designfile") ) {
4859: while (my $line = <$fh>) {
4860: next if ($line =~ /^\#/);
4861: chomp($line);
4862: my ($key,$val)=(split(/\=/,$line));
4863: if ($val) { $legacyhash{$udom.'.'.$key}=$val; }
4864: }
4865: close($fh);
4866: }
4867: }
1.1026 raeburn 4868: if (-e $Apache::lonnet::perlvar{'lonDocRoot'}.'/adm/lonDomLogos/'.$udom.'.gif') {
1.632 raeburn 4869: $legacyhash{$udom.'.login.domlogo'} = "/adm/lonDomLogos/$udom.gif";
4870: }
4871: return %legacyhash;
4872: }
4873:
1.63 www 4874: =pod
4875:
1.112 bowersj2 4876: =item * &domainlogo()
1.63 www 4877:
4878: Inputs: $domain (usually will be undef)
4879:
4880: Returns: A link to a domain logo, if the domain logo exists.
4881: If the domain logo does not exist, a description of the domain.
4882:
4883: =cut
1.112 bowersj2 4884:
1.63 www 4885: ###############################################
4886: sub domainlogo {
1.517 raeburn 4887: my $domain = &determinedomain(shift);
1.518 albertel 4888: my %designhash = &get_domainconf($domain);
1.517 raeburn 4889: # See if there is a logo
4890: if ($designhash{$domain.'.login.domlogo'} ne '') {
1.519 raeburn 4891: my $imgsrc = $designhash{$domain.'.login.domlogo'};
1.538 albertel 4892: if ($imgsrc =~ m{^/(adm|res)/}) {
4893: if ($imgsrc =~ m{^/res/}) {
4894: my $local_name = &Apache::lonnet::filelocation('',$imgsrc);
4895: &Apache::lonnet::repcopy($local_name);
4896: }
4897: $imgsrc = &lonhttpdurl($imgsrc);
1.519 raeburn 4898: }
4899: return '<img src="'.$imgsrc.'" alt="'.$domain.'" />';
1.514 albertel 4900: } elsif (defined(&Apache::lonnet::domain($domain,'description'))) {
4901: return &Apache::lonnet::domain($domain,'description');
1.59 www 4902: } else {
1.60 matthew 4903: return '';
1.59 www 4904: }
4905: }
1.63 www 4906: ##############################################
4907:
4908: =pod
4909:
1.112 bowersj2 4910: =item * &designparm()
1.63 www 4911:
4912: Inputs: $which parameter; $domain (usually will be undef)
4913:
4914: Returns: value of designparamter $which
4915:
4916: =cut
1.112 bowersj2 4917:
1.397 albertel 4918:
1.400 albertel 4919: ##############################################
1.397 albertel 4920: sub designparm {
4921: my ($which,$domain)=@_;
4922: if (exists($env{'environment.color.'.$which})) {
1.817 bisitz 4923: return $env{'environment.color.'.$which};
1.96 www 4924: }
1.63 www 4925: $domain=&determinedomain($domain);
1.1016 raeburn 4926: my %domdesign;
4927: unless ($domain eq 'public') {
4928: %domdesign = &get_domainconf($domain);
4929: }
1.520 raeburn 4930: my $output;
1.517 raeburn 4931: if ($domdesign{$domain.'.'.$which} ne '') {
1.817 bisitz 4932: $output = $domdesign{$domain.'.'.$which};
1.63 www 4933: } else {
1.520 raeburn 4934: $output = $defaultdesign{$which};
4935: }
4936: if (($which =~ /^(student|coordinator|author|admin)\.img$/) ||
1.635 raeburn 4937: ($which =~ /login\.(img|logo|domlogo|login)/)) {
1.538 albertel 4938: if ($output =~ m{^/(adm|res)/}) {
1.817 bisitz 4939: if ($output =~ m{^/res/}) {
4940: my $local_name = &Apache::lonnet::filelocation('',$output);
4941: &Apache::lonnet::repcopy($local_name);
4942: }
1.520 raeburn 4943: $output = &lonhttpdurl($output);
4944: }
1.63 www 4945: }
1.520 raeburn 4946: return $output;
1.63 www 4947: }
1.59 www 4948:
1.822 bisitz 4949: ##############################################
4950: =pod
4951:
1.832 bisitz 4952: =item * &authorspace()
4953:
1.1028 raeburn 4954: Inputs: $url (usually will be undef).
1.832 bisitz 4955:
1.1132 raeburn 4956: Returns: Path to Authoring Space containing the resource or
1.1028 raeburn 4957: directory being viewed (or for which action is being taken).
4958: If $url is provided, and begins /priv/<domain>/<uname>
4959: the path will be that portion of the $context argument.
4960: Otherwise the path will be for the author space of the current
4961: user when the current role is author, or for that of the
4962: co-author/assistant co-author space when the current role
4963: is co-author or assistant co-author.
1.832 bisitz 4964:
4965: =cut
4966:
4967: sub authorspace {
1.1028 raeburn 4968: my ($url) = @_;
4969: if ($url ne '') {
4970: if ($url =~ m{^(/priv/$match_domain/$match_username/)}) {
4971: return $1;
4972: }
4973: }
1.832 bisitz 4974: my $caname = '';
1.1024 www 4975: my $cadom = '';
1.1028 raeburn 4976: if ($env{'request.role'} =~ /^(?:ca|aa)/) {
1.1024 www 4977: ($cadom,$caname) =
1.832 bisitz 4978: ($env{'request.role'}=~/($match_domain)\/($match_username)$/);
1.1028 raeburn 4979: } elsif ($env{'request.role'} =~ m{^au\./($match_domain)/}) {
1.832 bisitz 4980: $caname = $env{'user.name'};
1.1024 www 4981: $cadom = $env{'user.domain'};
1.832 bisitz 4982: }
1.1028 raeburn 4983: if (($caname ne '') && ($cadom ne '')) {
4984: return "/priv/$cadom/$caname/";
4985: }
4986: return;
1.832 bisitz 4987: }
4988:
4989: ##############################################
4990: =pod
4991:
1.822 bisitz 4992: =item * &head_subbox()
4993:
4994: Inputs: $content (contains HTML code with page functions, etc.)
4995:
4996: Returns: HTML div with $content
4997: To be included in page header
4998:
4999: =cut
5000:
5001: sub head_subbox {
5002: my ($content)=@_;
5003: my $output =
1.993 raeburn 5004: '<div class="LC_head_subbox">'
1.822 bisitz 5005: .$content
5006: .'</div>'
5007: }
5008:
5009: ##############################################
5010: =pod
5011:
5012: =item * &CSTR_pageheader()
5013:
1.1026 raeburn 5014: Input: (optional) filename from which breadcrumb trail is built.
5015: In most cases no input as needed, as $env{'request.filename'}
5016: is appropriate for use in building the breadcrumb trail.
1.822 bisitz 5017:
5018: Returns: HTML div with CSTR path and recent box
1.1132 raeburn 5019: To be included on Authoring Space pages
1.822 bisitz 5020:
5021: =cut
5022:
5023: sub CSTR_pageheader {
1.1026 raeburn 5024: my ($trailfile) = @_;
5025: if ($trailfile eq '') {
5026: $trailfile = $env{'request.filename'};
5027: }
5028:
5029: # this is for resources; directories have customtitle, and crumbs
5030: # and select recent are created in lonpubdir.pm
5031:
5032: my $londocroot = $Apache::lonnet::perlvar{'lonDocRoot'};
1.1022 www 5033: my ($udom,$uname,$thisdisfn)=
1.1113 raeburn 5034: ($trailfile =~ m{^\Q$londocroot\E/priv/([^/]+)/([^/]+)(?:|/(.*))$});
1.1026 raeburn 5035: my $formaction = "/priv/$udom/$uname/$thisdisfn";
5036: $formaction =~ s{/+}{/}g;
1.822 bisitz 5037:
5038: my $parentpath = '';
5039: my $lastitem = '';
5040: if ($thisdisfn =~ m-(.+/)([^/]*)$-) {
5041: $parentpath = $1;
5042: $lastitem = $2;
5043: } else {
5044: $lastitem = $thisdisfn;
5045: }
1.921 bisitz 5046:
5047: my $output =
1.822 bisitz 5048: '<div>'
5049: .&Apache::loncommon::help_open_menu('','',3,'Authoring') #FIXME: Broken? Where is it?
1.1132 raeburn 5050: .'<b>'.&mt('Authoring Space:').'</b> '
1.822 bisitz 5051: .'<form name="dirs" method="post" action="'.$formaction
1.921 bisitz 5052: .'" target="_top">' #FIXME lonpubdir: target="_parent"
1.1024 www 5053: .&Apache::lonhtmlcommon::crumbs($uname.'/'.$parentpath,'_top','/priv/'.$udom,undef,undef);
1.921 bisitz 5054:
5055: if ($lastitem) {
5056: $output .=
5057: '<span class="LC_filename">'
5058: .$lastitem
5059: .'</span>';
5060: }
5061: $output .=
5062: '<br />'
1.822 bisitz 5063: #FIXME lonpubdir: &Apache::lonhtmlcommon::crumbs($uname.$thisdisfn.'/','_top','/priv','','+1',1)."</b></tt><br />"
5064: .&Apache::lonhtmlcommon::select_recent('construct','recent','this.form.action=this.form.recent.value;this.form.submit()')
5065: .'</form>'
5066: .&Apache::lonmenu::constspaceform()
5067: .'</div>';
1.921 bisitz 5068:
5069: return $output;
1.822 bisitz 5070: }
5071:
1.60 matthew 5072: ###############################################
5073: ###############################################
5074:
5075: =pod
5076:
1.112 bowersj2 5077: =back
5078:
1.549 albertel 5079: =head1 HTML Helpers
1.112 bowersj2 5080:
5081: =over 4
5082:
5083: =item * &bodytag()
1.60 matthew 5084:
5085: Returns a uniform header for LON-CAPA web pages.
5086:
5087: Inputs:
5088:
1.112 bowersj2 5089: =over 4
5090:
5091: =item * $title, A title to be displayed on the page.
5092:
5093: =item * $function, the current role (can be undef).
5094:
5095: =item * $addentries, extra parameters for the <body> tag.
5096:
5097: =item * $bodyonly, if defined, only return the <body> tag.
5098:
5099: =item * $domain, if defined, force a given domain.
5100:
5101: =item * $forcereg, if page should register as content page (relevant for
1.86 www 5102: text interface only)
1.60 matthew 5103:
1.814 bisitz 5104: =item * $no_nav_bar, if true, keep the 'what is this' info but remove the
5105: navigational links
1.317 albertel 5106:
1.338 albertel 5107: =item * $bgcolor, used to override the bgcolor on a webpage to a specific value
5108:
1.460 albertel 5109: =item * $args, optional argument valid values are
5110: no_auto_mt_title -> prevents &mt()ing the title arg
1.562 albertel 5111: inherit_jsmath -> when creating popup window in a page,
5112: should it have jsmath forced on by the
5113: current page
1.460 albertel 5114:
1.1096 raeburn 5115: =item * $advtoolsref, optional argument, ref to an array containing
5116: inlineremote items to be added in "Functions" menu below
5117: breadcrumbs.
5118:
1.112 bowersj2 5119: =back
5120:
1.60 matthew 5121: Returns: A uniform header for LON-CAPA web pages.
5122: If $bodyonly is nonzero, a string containing a <body> tag will be returned.
5123: If $bodyonly is undef or zero, an html string containing a <body> tag and
5124: other decorations will be returned.
5125:
5126: =cut
5127:
1.54 www 5128: sub bodytag {
1.831 bisitz 5129: my ($title,$function,$addentries,$bodyonly,$domain,$forcereg,
1.1096 raeburn 5130: $no_nav_bar,$bgcolor,$args,$advtoolsref)=@_;
1.339 albertel 5131:
1.954 raeburn 5132: my $public;
5133: if ((($env{'user.name'} eq 'public') && ($env{'user.domain'} eq 'public'))
5134: || ($env{'user.name'} eq '') && ($env{'user.domain'} eq '')) {
5135: $public = 1;
5136: }
1.460 albertel 5137: if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
1.1154 raeburn 5138: my $httphost = $args->{'use_absolute'};
1.339 albertel 5139:
1.183 matthew 5140: $function = &get_users_function() if (!$function);
1.339 albertel 5141: my $img = &designparm($function.'.img',$domain);
5142: my $font = &designparm($function.'.font',$domain);
5143: my $pgbg = $bgcolor || &designparm($function.'.pgbg',$domain);
5144:
1.803 bisitz 5145: my %design = ( 'style' => 'margin-top: 0',
1.535 albertel 5146: 'bgcolor' => $pgbg,
1.339 albertel 5147: 'text' => $font,
5148: 'alink' => &designparm($function.'.alink',$domain),
5149: 'vlink' => &designparm($function.'.vlink',$domain),
5150: 'link' => &designparm($function.'.link',$domain),);
1.438 albertel 5151: @design{keys(%$addentries)} = @$addentries{keys(%$addentries)};
1.339 albertel 5152:
1.63 www 5153: # role and realm
1.1178 ! raeburn 5154: my ($role,$realm) = split(m{\./},$env{'request.role'},2);
! 5155: if ($realm) {
! 5156: $realm = '/'.$realm;
! 5157: }
1.378 raeburn 5158: if ($role eq 'ca') {
1.479 albertel 5159: my ($rdom,$rname) = ($realm =~ m{^/($match_domain)/($match_username)$});
1.500 albertel 5160: $realm = &plainname($rname,$rdom);
1.378 raeburn 5161: }
1.55 www 5162: # realm
1.258 albertel 5163: if ($env{'request.course.id'}) {
1.378 raeburn 5164: if ($env{'request.role'} !~ /^cr/) {
5165: $role = &Apache::lonnet::plaintext($role,&course_type());
5166: }
1.898 raeburn 5167: if ($env{'request.course.sec'}) {
5168: $role .= (' 'x2).'- '.&mt('section:').' '.$env{'request.course.sec'};
5169: }
1.359 albertel 5170: $realm = $env{'course.'.$env{'request.course.id'}.'.description'};
1.378 raeburn 5171: } else {
5172: $role = &Apache::lonnet::plaintext($role);
1.54 www 5173: }
1.433 albertel 5174:
1.359 albertel 5175: if (!$realm) { $realm=' '; }
1.330 albertel 5176:
1.438 albertel 5177: my $extra_body_attr = &make_attr_string($forcereg,\%design);
1.329 albertel 5178:
1.101 www 5179: # construct main body tag
1.359 albertel 5180: my $bodytag = "<body $extra_body_attr>".
1.562 albertel 5181: &Apache::lontexconvert::init_math_support($args->{'inherit_jsmath'});
1.252 albertel 5182:
1.1131 raeburn 5183: &get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['inhibitmenu']);
5184:
1.1130 raeburn 5185: if (($bodyonly) || ($no_nav_bar) || ($env{'form.inhibitmenu'} eq 'yes')) {
1.60 matthew 5186: return $bodytag;
1.1130 raeburn 5187: }
1.359 albertel 5188:
1.954 raeburn 5189: if ($public) {
1.433 albertel 5190: undef($role);
5191: }
1.359 albertel 5192:
1.762 bisitz 5193: my $titleinfo = '<h1>'.$title.'</h1>';
1.359 albertel 5194: #
5195: # Extra info if you are the DC
5196: my $dc_info = '';
5197: if ($env{'user.adv'} && exists($env{'user.role.dc./'.
5198: $env{'course.'.$env{'request.course.id'}.
5199: '.domain'}.'/'})) {
5200: my $cid = $env{'request.course.id'};
1.917 raeburn 5201: $dc_info = $cid.' '.$env{'course.'.$cid.'.internal.coursecode'};
1.380 www 5202: $dc_info =~ s/\s+$//;
1.359 albertel 5203: }
5204:
1.898 raeburn 5205: $role = '<span class="LC_nobreak">('.$role.')</span>' if $role;
1.853 droeschl 5206:
1.903 droeschl 5207: if ($env{'request.state'} eq 'construct') { $forcereg=1; }
5208:
5209: # if ($env{'request.state'} eq 'construct') {
5210: # $titleinfo = &CSTR_pageheader(); #FIXME: Will be removed once all scripts have their own calls
5211: # }
5212:
1.1130 raeburn 5213: $bodytag .= Apache::lonhtmlcommon::scripttag(
1.1154 raeburn 5214: Apache::lonmenu::utilityfunctions($httphost), 'start');
1.359 albertel 5215:
1.1130 raeburn 5216: my ($left,$right) = Apache::lonmenu::primary_menu();
1.359 albertel 5217:
1.916 droeschl 5218: if ($env{'request.noversionuri'} =~ m{^/res/adm/pages/}) {
1.917 raeburn 5219: if ($dc_info) {
5220: $dc_info = qq|<span class="LC_cusr_subheading">$dc_info</span>|;
5221: }
1.1130 raeburn 5222: $bodytag .= qq|<div id="LC_nav_bar">$left $role<br />
1.916 droeschl 5223: <em>$realm</em> $dc_info</div>|;
1.903 droeschl 5224: return $bodytag;
5225: }
1.894 droeschl 5226:
1.927 raeburn 5227: unless ($env{'request.symb'} =~ m/\.page___\d+___/) {
1.1130 raeburn 5228: $bodytag .= qq|<div id="LC_nav_bar">$left $role</div>|;
1.927 raeburn 5229: }
1.916 droeschl 5230:
1.1130 raeburn 5231: $bodytag .= $right;
1.852 droeschl 5232:
1.917 raeburn 5233: if ($dc_info) {
5234: $dc_info = &dc_courseid_toggle($dc_info);
5235: }
5236: $bodytag .= qq|<div id="LC_realm">$realm $dc_info</div>|;
1.916 droeschl 5237:
1.1169 raeburn 5238: #if directed to not display the secondary menu, don't.
1.1168 raeburn 5239: if ($args->{'no_secondary_menu'}) {
5240: return $bodytag;
5241: }
1.1169 raeburn 5242: #don't show menus for public users
1.954 raeburn 5243: if (!$public){
1.1154 raeburn 5244: $bodytag .= Apache::lonmenu::secondary_menu($httphost);
1.903 droeschl 5245: $bodytag .= Apache::lonmenu::serverform();
1.920 raeburn 5246: $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');
5247: if ($env{'request.state'} eq 'construct') {
1.962 droeschl 5248: $bodytag .= &Apache::lonmenu::innerregister($forcereg,
1.920 raeburn 5249: $args->{'bread_crumbs'});
1.1096 raeburn 5250: } elsif ($forcereg) {
5251: $bodytag .= &Apache::lonmenu::innerregister($forcereg,undef,
5252: $args->{'group'});
5253: } else {
5254: $bodytag .=
5255: &Apache::lonmenu::prepare_functions($env{'request.noversionuri'},
5256: $forcereg,$args->{'group'},
5257: $args->{'bread_crumbs'},
5258: $advtoolsref);
1.920 raeburn 5259: }
1.903 droeschl 5260: }else{
5261: # this is to seperate menu from content when there's no secondary
5262: # menu. Especially needed for public accessible ressources.
5263: $bodytag .= '<hr style="clear:both" />';
5264: $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');
1.235 raeburn 5265: }
1.903 droeschl 5266:
1.235 raeburn 5267: return $bodytag;
1.182 matthew 5268: }
5269:
1.917 raeburn 5270: sub dc_courseid_toggle {
5271: my ($dc_info) = @_;
1.980 raeburn 5272: return ' <span id="dccidtext" class="LC_cusr_subheading LC_nobreak">'.
1.1069 raeburn 5273: '<a href="javascript:showCourseID();" class="LC_menubuttons_link">'.
1.917 raeburn 5274: &mt('(More ...)').'</a></span>'.
5275: '<div id="dccid" class="LC_dccid">'.$dc_info.'</div>';
5276: }
5277:
1.330 albertel 5278: sub make_attr_string {
5279: my ($register,$attr_ref) = @_;
5280:
5281: if ($attr_ref && !ref($attr_ref)) {
5282: die("addentries Must be a hash ref ".
5283: join(':',caller(1))." ".
5284: join(':',caller(0))." ");
5285: }
5286:
5287: if ($register) {
1.339 albertel 5288: my ($on_load,$on_unload);
5289: foreach my $key (keys(%{$attr_ref})) {
5290: if (lc($key) eq 'onload') {
5291: $on_load.=$attr_ref->{$key}.';';
5292: delete($attr_ref->{$key});
5293:
5294: } elsif (lc($key) eq 'onunload') {
5295: $on_unload.=$attr_ref->{$key}.';';
5296: delete($attr_ref->{$key});
5297: }
5298: }
1.953 droeschl 5299: $attr_ref->{'onload'} = $on_load;
5300: $attr_ref->{'onunload'}= $on_unload;
1.330 albertel 5301: }
1.339 albertel 5302:
1.330 albertel 5303: my $attr_string;
1.1159 raeburn 5304: foreach my $attr (sort(keys(%$attr_ref))) {
1.330 albertel 5305: $attr_string .= " $attr=\"".$attr_ref->{$attr}.'" ';
5306: }
5307: return $attr_string;
5308: }
5309:
5310:
1.182 matthew 5311: ###############################################
1.251 albertel 5312: ###############################################
5313:
5314: =pod
5315:
5316: =item * &endbodytag()
5317:
5318: Returns a uniform footer for LON-CAPA web pages.
5319:
1.635 raeburn 5320: Inputs: 1 - optional reference to an args hash
5321: If in the hash, key for noredirectlink has a value which evaluates to true,
5322: a 'Continue' link is not displayed if the page contains an
5323: internal redirect in the <head></head> section,
5324: i.e., $env{'internal.head.redirect'} exists
1.251 albertel 5325:
5326: =cut
5327:
5328: sub endbodytag {
1.635 raeburn 5329: my ($args) = @_;
1.1080 raeburn 5330: my $endbodytag;
5331: unless ((ref($args) eq 'HASH') && ($args->{'notbody'})) {
5332: $endbodytag='</body>';
5333: }
1.269 albertel 5334: $endbodytag=&Apache::lontexconvert::jsMath_process()."\n".$endbodytag;
1.315 albertel 5335: if ( exists( $env{'internal.head.redirect'} ) ) {
1.635 raeburn 5336: if (!(ref($args) eq 'HASH' && $args->{'noredirectlink'})) {
5337: $endbodytag=
5338: "<br /><a href=\"$env{'internal.head.redirect'}\">".
5339: &mt('Continue').'</a>'.
5340: $endbodytag;
5341: }
1.315 albertel 5342: }
1.251 albertel 5343: return $endbodytag;
5344: }
5345:
1.352 albertel 5346: =pod
5347:
5348: =item * &standard_css()
5349:
5350: Returns a style sheet
5351:
5352: Inputs: (all optional)
5353: domain -> force to color decorate a page for a specific
5354: domain
5355: function -> force usage of a specific rolish color scheme
5356: bgcolor -> override the default page bgcolor
5357:
5358: =cut
5359:
1.343 albertel 5360: sub standard_css {
1.345 albertel 5361: my ($function,$domain,$bgcolor) = @_;
1.352 albertel 5362: $function = &get_users_function() if (!$function);
5363: my $img = &designparm($function.'.img', $domain);
5364: my $tabbg = &designparm($function.'.tabbg', $domain);
5365: my $font = &designparm($function.'.font', $domain);
1.801 tempelho 5366: my $fontmenu = &designparm($function.'.fontmenu', $domain);
1.791 tempelho 5367: #second colour for later usage
1.345 albertel 5368: my $sidebg = &designparm($function.'.sidebg',$domain);
1.382 albertel 5369: my $pgbg_or_bgcolor =
5370: $bgcolor ||
1.352 albertel 5371: &designparm($function.'.pgbg', $domain);
1.382 albertel 5372: my $pgbg = &designparm($function.'.pgbg', $domain);
1.352 albertel 5373: my $alink = &designparm($function.'.alink', $domain);
5374: my $vlink = &designparm($function.'.vlink', $domain);
5375: my $link = &designparm($function.'.link', $domain);
5376:
1.602 albertel 5377: my $sans = 'Verdana,Arial,Helvetica,sans-serif';
1.395 albertel 5378: my $mono = 'monospace';
1.850 bisitz 5379: my $data_table_head = $sidebg;
5380: my $data_table_light = '#FAFAFA';
1.1060 bisitz 5381: my $data_table_dark = '#E0E0E0';
1.470 banghart 5382: my $data_table_darker = '#CCCCCC';
1.349 albertel 5383: my $data_table_highlight = '#FFFF00';
1.352 albertel 5384: my $mail_new = '#FFBB77';
5385: my $mail_new_hover = '#DD9955';
5386: my $mail_read = '#BBBB77';
5387: my $mail_read_hover = '#999944';
5388: my $mail_replied = '#AAAA88';
5389: my $mail_replied_hover = '#888855';
5390: my $mail_other = '#99BBBB';
5391: my $mail_other_hover = '#669999';
1.391 albertel 5392: my $table_header = '#DDDDDD';
1.489 raeburn 5393: my $feedback_link_bg = '#BBBBBB';
1.911 bisitz 5394: my $lg_border_color = '#C8C8C8';
1.952 onken 5395: my $button_hover = '#BF2317';
1.392 albertel 5396:
1.608 albertel 5397: my $border = ($env{'browser.type'} eq 'explorer' ||
1.911 bisitz 5398: $env{'browser.type'} eq 'safari' ) ? '0 2px 0 2px'
5399: : '0 3px 0 4px';
1.448 albertel 5400:
1.523 albertel 5401:
1.343 albertel 5402: return <<END;
1.947 droeschl 5403:
5404: /* needed for iframe to allow 100% height in FF */
5405: body, html {
5406: margin: 0;
5407: padding: 0 0.5%;
5408: height: 99%; /* to avoid scrollbars */
5409: }
5410:
1.795 www 5411: body {
1.911 bisitz 5412: font-family: $sans;
5413: line-height:130%;
5414: font-size:0.83em;
5415: color:$font;
1.795 www 5416: }
5417:
1.959 onken 5418: a:focus,
5419: a:focus img {
1.795 www 5420: color: red;
5421: }
1.698 harmsja 5422:
1.911 bisitz 5423: form, .inline {
5424: display: inline;
1.795 www 5425: }
1.721 harmsja 5426:
1.795 www 5427: .LC_right {
1.911 bisitz 5428: text-align:right;
1.795 www 5429: }
5430:
5431: .LC_middle {
1.911 bisitz 5432: vertical-align:middle;
1.795 www 5433: }
1.721 harmsja 5434:
1.1130 raeburn 5435: .LC_floatleft {
5436: float: left;
5437: }
5438:
5439: .LC_floatright {
5440: float: right;
5441: }
5442:
1.911 bisitz 5443: .LC_400Box {
5444: width:400px;
5445: }
1.721 harmsja 5446:
1.947 droeschl 5447: .LC_iframecontainer {
5448: width: 98%;
5449: margin: 0;
5450: position: fixed;
5451: top: 8.5em;
5452: bottom: 0;
5453: }
5454:
5455: .LC_iframecontainer iframe{
5456: border: none;
5457: width: 100%;
5458: height: 100%;
5459: }
5460:
1.778 bisitz 5461: .LC_filename {
5462: font-family: $mono;
5463: white-space:pre;
1.921 bisitz 5464: font-size: 120%;
1.778 bisitz 5465: }
5466:
5467: .LC_fileicon {
5468: border: none;
5469: height: 1.3em;
5470: vertical-align: text-bottom;
5471: margin-right: 0.3em;
5472: text-decoration:none;
5473: }
5474:
1.1008 www 5475: .LC_setting {
5476: text-decoration:underline;
5477: }
5478:
1.350 albertel 5479: .LC_error {
5480: color: red;
5481: }
1.795 www 5482:
1.1097 bisitz 5483: .LC_warning {
5484: color: darkorange;
5485: }
5486:
1.457 albertel 5487: .LC_diff_removed {
1.733 bisitz 5488: color: red;
1.394 albertel 5489: }
1.532 albertel 5490:
5491: .LC_info,
1.457 albertel 5492: .LC_success,
5493: .LC_diff_added {
1.350 albertel 5494: color: green;
5495: }
1.795 www 5496:
1.802 bisitz 5497: div.LC_confirm_box {
5498: background-color: #FAFAFA;
5499: border: 1px solid $lg_border_color;
5500: margin-right: 0;
5501: padding: 5px;
5502: }
5503:
5504: div.LC_confirm_box .LC_error img,
5505: div.LC_confirm_box .LC_success img {
5506: vertical-align: middle;
5507: }
5508:
1.440 albertel 5509: .LC_icon {
1.771 droeschl 5510: border: none;
1.790 droeschl 5511: vertical-align: middle;
1.771 droeschl 5512: }
5513:
1.543 albertel 5514: .LC_docs_spacer {
5515: width: 25px;
5516: height: 1px;
1.771 droeschl 5517: border: none;
1.543 albertel 5518: }
1.346 albertel 5519:
1.532 albertel 5520: .LC_internal_info {
1.735 bisitz 5521: color: #999999;
1.532 albertel 5522: }
5523:
1.794 www 5524: .LC_discussion {
1.1050 www 5525: background: $data_table_dark;
1.911 bisitz 5526: border: 1px solid black;
5527: margin: 2px;
1.794 www 5528: }
5529:
5530: .LC_disc_action_left {
1.1050 www 5531: background: $sidebg;
1.911 bisitz 5532: text-align: left;
1.1050 www 5533: padding: 4px;
5534: margin: 2px;
1.794 www 5535: }
5536:
5537: .LC_disc_action_right {
1.1050 www 5538: background: $sidebg;
1.911 bisitz 5539: text-align: right;
1.1050 www 5540: padding: 4px;
5541: margin: 2px;
1.794 www 5542: }
5543:
5544: .LC_disc_new_item {
1.911 bisitz 5545: background: white;
5546: border: 2px solid red;
1.1050 www 5547: margin: 4px;
5548: padding: 4px;
1.794 www 5549: }
5550:
5551: .LC_disc_old_item {
1.911 bisitz 5552: background: white;
1.1050 www 5553: margin: 4px;
5554: padding: 4px;
1.794 www 5555: }
5556:
1.458 albertel 5557: table.LC_pastsubmission {
5558: border: 1px solid black;
5559: margin: 2px;
5560: }
5561:
1.924 bisitz 5562: table#LC_menubuttons {
1.345 albertel 5563: width: 100%;
5564: background: $pgbg;
1.392 albertel 5565: border: 2px;
1.402 albertel 5566: border-collapse: separate;
1.803 bisitz 5567: padding: 0;
1.345 albertel 5568: }
1.392 albertel 5569:
1.801 tempelho 5570: table#LC_title_bar a {
5571: color: $fontmenu;
5572: }
1.836 bisitz 5573:
1.807 droeschl 5574: table#LC_title_bar {
1.819 tempelho 5575: clear: both;
1.836 bisitz 5576: display: none;
1.807 droeschl 5577: }
5578:
1.795 www 5579: table#LC_title_bar,
1.933 droeschl 5580: table.LC_breadcrumbs, /* obsolete? */
1.393 albertel 5581: table#LC_title_bar.LC_with_remote {
1.359 albertel 5582: width: 100%;
1.392 albertel 5583: border-color: $pgbg;
5584: border-style: solid;
5585: border-width: $border;
1.379 albertel 5586: background: $pgbg;
1.801 tempelho 5587: color: $fontmenu;
1.392 albertel 5588: border-collapse: collapse;
1.803 bisitz 5589: padding: 0;
1.819 tempelho 5590: margin: 0;
1.359 albertel 5591: }
1.795 www 5592:
1.933 droeschl 5593: ul.LC_breadcrumb_tools_outerlist {
1.913 droeschl 5594: margin: 0;
5595: padding: 0;
1.933 droeschl 5596: position: relative;
5597: list-style: none;
1.913 droeschl 5598: }
1.933 droeschl 5599: ul.LC_breadcrumb_tools_outerlist li {
1.913 droeschl 5600: display: inline;
5601: }
1.933 droeschl 5602:
5603: .LC_breadcrumb_tools_navigation {
1.913 droeschl 5604: padding: 0;
1.933 droeschl 5605: margin: 0;
5606: float: left;
1.913 droeschl 5607: }
1.933 droeschl 5608: .LC_breadcrumb_tools_tools {
5609: padding: 0;
5610: margin: 0;
1.913 droeschl 5611: float: right;
5612: }
5613:
1.359 albertel 5614: table#LC_title_bar td {
5615: background: $tabbg;
5616: }
1.795 www 5617:
1.911 bisitz 5618: table#LC_menubuttons img {
1.803 bisitz 5619: border: none;
1.346 albertel 5620: }
1.795 www 5621:
1.842 droeschl 5622: .LC_breadcrumbs_component {
1.911 bisitz 5623: float: right;
5624: margin: 0 1em;
1.357 albertel 5625: }
1.842 droeschl 5626: .LC_breadcrumbs_component img {
1.911 bisitz 5627: vertical-align: middle;
1.777 tempelho 5628: }
1.795 www 5629:
1.383 albertel 5630: td.LC_table_cell_checkbox {
5631: text-align: center;
5632: }
1.795 www 5633:
5634: .LC_fontsize_small {
1.911 bisitz 5635: font-size: 70%;
1.705 tempelho 5636: }
5637:
1.844 bisitz 5638: #LC_breadcrumbs {
1.911 bisitz 5639: clear:both;
5640: background: $sidebg;
5641: border-bottom: 1px solid $lg_border_color;
5642: line-height: 2.5em;
1.933 droeschl 5643: overflow: hidden;
1.911 bisitz 5644: margin: 0;
5645: padding: 0;
1.995 raeburn 5646: text-align: left;
1.819 tempelho 5647: }
1.862 bisitz 5648:
1.1098 bisitz 5649: .LC_head_subbox, .LC_actionbox {
1.911 bisitz 5650: clear:both;
5651: background: #F8F8F8; /* $sidebg; */
1.915 droeschl 5652: border: 1px solid $sidebg;
1.1098 bisitz 5653: margin: 0 0 10px 0;
1.966 bisitz 5654: padding: 3px;
1.995 raeburn 5655: text-align: left;
1.822 bisitz 5656: }
5657:
1.795 www 5658: .LC_fontsize_medium {
1.911 bisitz 5659: font-size: 85%;
1.705 tempelho 5660: }
5661:
1.795 www 5662: .LC_fontsize_large {
1.911 bisitz 5663: font-size: 120%;
1.705 tempelho 5664: }
5665:
1.346 albertel 5666: .LC_menubuttons_inline_text {
5667: color: $font;
1.698 harmsja 5668: font-size: 90%;
1.701 harmsja 5669: padding-left:3px;
1.346 albertel 5670: }
5671:
1.934 droeschl 5672: .LC_menubuttons_inline_text img{
5673: vertical-align: middle;
5674: }
5675:
1.1051 www 5676: li.LC_menubuttons_inline_text img {
1.951 onken 5677: cursor:pointer;
1.1002 droeschl 5678: text-decoration: none;
1.951 onken 5679: }
5680:
1.526 www 5681: .LC_menubuttons_link {
5682: text-decoration: none;
5683: }
1.795 www 5684:
1.522 albertel 5685: .LC_menubuttons_category {
1.521 www 5686: color: $font;
1.526 www 5687: background: $pgbg;
1.521 www 5688: font-size: larger;
5689: font-weight: bold;
5690: }
5691:
1.346 albertel 5692: td.LC_menubuttons_text {
1.911 bisitz 5693: color: $font;
1.346 albertel 5694: }
1.706 harmsja 5695:
1.346 albertel 5696: .LC_current_location {
5697: background: $tabbg;
5698: }
1.795 www 5699:
1.938 bisitz 5700: table.LC_data_table {
1.347 albertel 5701: border: 1px solid #000000;
1.402 albertel 5702: border-collapse: separate;
1.426 albertel 5703: border-spacing: 1px;
1.610 albertel 5704: background: $pgbg;
1.347 albertel 5705: }
1.795 www 5706:
1.422 albertel 5707: .LC_data_table_dense {
5708: font-size: small;
5709: }
1.795 www 5710:
1.507 raeburn 5711: table.LC_nested_outer {
5712: border: 1px solid #000000;
1.589 raeburn 5713: border-collapse: collapse;
1.803 bisitz 5714: border-spacing: 0;
1.507 raeburn 5715: width: 100%;
5716: }
1.795 www 5717:
1.879 raeburn 5718: table.LC_innerpickbox,
1.507 raeburn 5719: table.LC_nested {
1.803 bisitz 5720: border: none;
1.589 raeburn 5721: border-collapse: collapse;
1.803 bisitz 5722: border-spacing: 0;
1.507 raeburn 5723: width: 100%;
5724: }
1.795 www 5725:
1.911 bisitz 5726: table.LC_data_table tr th,
5727: table.LC_calendar tr th,
1.879 raeburn 5728: table.LC_prior_tries tr th,
5729: table.LC_innerpickbox tr th {
1.349 albertel 5730: font-weight: bold;
5731: background-color: $data_table_head;
1.801 tempelho 5732: color:$fontmenu;
1.701 harmsja 5733: font-size:90%;
1.347 albertel 5734: }
1.795 www 5735:
1.879 raeburn 5736: table.LC_innerpickbox tr th,
5737: table.LC_innerpickbox tr td {
5738: vertical-align: top;
5739: }
5740:
1.711 raeburn 5741: table.LC_data_table tr.LC_info_row > td {
1.735 bisitz 5742: background-color: #CCCCCC;
1.711 raeburn 5743: font-weight: bold;
5744: text-align: left;
5745: }
1.795 www 5746:
1.912 bisitz 5747: table.LC_data_table tr.LC_odd_row > td {
5748: background-color: $data_table_light;
5749: padding: 2px;
5750: vertical-align: top;
5751: }
5752:
1.809 bisitz 5753: table.LC_pick_box tr > td.LC_odd_row {
1.349 albertel 5754: background-color: $data_table_light;
1.912 bisitz 5755: vertical-align: top;
5756: }
5757:
5758: table.LC_data_table tr.LC_even_row > td {
5759: background-color: $data_table_dark;
1.425 albertel 5760: padding: 2px;
1.900 bisitz 5761: vertical-align: top;
1.347 albertel 5762: }
1.795 www 5763:
1.809 bisitz 5764: table.LC_pick_box tr > td.LC_even_row {
1.349 albertel 5765: background-color: $data_table_dark;
1.900 bisitz 5766: vertical-align: top;
1.347 albertel 5767: }
1.795 www 5768:
1.425 albertel 5769: table.LC_data_table tr.LC_data_table_highlight td {
5770: background-color: $data_table_darker;
5771: }
1.795 www 5772:
1.639 raeburn 5773: table.LC_data_table tr td.LC_leftcol_header {
5774: background-color: $data_table_head;
5775: font-weight: bold;
5776: }
1.795 www 5777:
1.451 albertel 5778: table.LC_data_table tr.LC_empty_row td,
1.507 raeburn 5779: table.LC_nested tr.LC_empty_row td {
1.421 albertel 5780: font-weight: bold;
5781: font-style: italic;
5782: text-align: center;
5783: padding: 8px;
1.347 albertel 5784: }
1.795 www 5785:
1.1114 raeburn 5786: table.LC_data_table tr.LC_empty_row td,
5787: table.LC_data_table tr.LC_footer_row td {
1.940 bisitz 5788: background-color: $sidebg;
5789: }
5790:
5791: table.LC_nested tr.LC_empty_row td {
5792: background-color: #FFFFFF;
5793: }
5794:
1.890 droeschl 5795: table.LC_caption {
5796: }
5797:
1.507 raeburn 5798: table.LC_nested tr.LC_empty_row td {
1.465 albertel 5799: padding: 4ex
5800: }
1.795 www 5801:
1.507 raeburn 5802: table.LC_nested_outer tr th {
5803: font-weight: bold;
1.801 tempelho 5804: color:$fontmenu;
1.507 raeburn 5805: background-color: $data_table_head;
1.701 harmsja 5806: font-size: small;
1.507 raeburn 5807: border-bottom: 1px solid #000000;
5808: }
1.795 www 5809:
1.507 raeburn 5810: table.LC_nested_outer tr td.LC_subheader {
5811: background-color: $data_table_head;
5812: font-weight: bold;
5813: font-size: small;
5814: border-bottom: 1px solid #000000;
5815: text-align: right;
1.451 albertel 5816: }
1.795 www 5817:
1.507 raeburn 5818: table.LC_nested tr.LC_info_row td {
1.735 bisitz 5819: background-color: #CCCCCC;
1.451 albertel 5820: font-weight: bold;
5821: font-size: small;
1.507 raeburn 5822: text-align: center;
5823: }
1.795 www 5824:
1.589 raeburn 5825: table.LC_nested tr.LC_info_row td.LC_left_item,
5826: table.LC_nested_outer tr th.LC_left_item {
1.507 raeburn 5827: text-align: left;
1.451 albertel 5828: }
1.795 www 5829:
1.507 raeburn 5830: table.LC_nested td {
1.735 bisitz 5831: background-color: #FFFFFF;
1.451 albertel 5832: font-size: small;
1.507 raeburn 5833: }
1.795 www 5834:
1.507 raeburn 5835: table.LC_nested_outer tr th.LC_right_item,
5836: table.LC_nested tr.LC_info_row td.LC_right_item,
5837: table.LC_nested tr.LC_odd_row td.LC_right_item,
5838: table.LC_nested tr td.LC_right_item {
1.451 albertel 5839: text-align: right;
5840: }
5841:
1.507 raeburn 5842: table.LC_nested tr.LC_odd_row td {
1.735 bisitz 5843: background-color: #EEEEEE;
1.451 albertel 5844: }
5845:
1.473 raeburn 5846: table.LC_createuser {
5847: }
5848:
5849: table.LC_createuser tr.LC_section_row td {
1.701 harmsja 5850: font-size: small;
1.473 raeburn 5851: }
5852:
5853: table.LC_createuser tr.LC_info_row td {
1.735 bisitz 5854: background-color: #CCCCCC;
1.473 raeburn 5855: font-weight: bold;
5856: text-align: center;
5857: }
5858:
1.349 albertel 5859: table.LC_calendar {
5860: border: 1px solid #000000;
5861: border-collapse: collapse;
1.917 raeburn 5862: width: 98%;
1.349 albertel 5863: }
1.795 www 5864:
1.349 albertel 5865: table.LC_calendar_pickdate {
5866: font-size: xx-small;
5867: }
1.795 www 5868:
1.349 albertel 5869: table.LC_calendar tr td {
5870: border: 1px solid #000000;
5871: vertical-align: top;
1.917 raeburn 5872: width: 14%;
1.349 albertel 5873: }
1.795 www 5874:
1.349 albertel 5875: table.LC_calendar tr td.LC_calendar_day_empty {
5876: background-color: $data_table_dark;
5877: }
1.795 www 5878:
1.779 bisitz 5879: table.LC_calendar tr td.LC_calendar_day_current {
5880: background-color: $data_table_highlight;
1.777 tempelho 5881: }
1.795 www 5882:
1.938 bisitz 5883: table.LC_data_table tr td.LC_mail_new {
1.349 albertel 5884: background-color: $mail_new;
5885: }
1.795 www 5886:
1.938 bisitz 5887: table.LC_data_table tr.LC_mail_new:hover {
1.349 albertel 5888: background-color: $mail_new_hover;
5889: }
1.795 www 5890:
1.938 bisitz 5891: table.LC_data_table tr td.LC_mail_read {
1.349 albertel 5892: background-color: $mail_read;
5893: }
1.795 www 5894:
1.938 bisitz 5895: /*
5896: table.LC_data_table tr.LC_mail_read:hover {
1.349 albertel 5897: background-color: $mail_read_hover;
5898: }
1.938 bisitz 5899: */
1.795 www 5900:
1.938 bisitz 5901: table.LC_data_table tr td.LC_mail_replied {
1.349 albertel 5902: background-color: $mail_replied;
5903: }
1.795 www 5904:
1.938 bisitz 5905: /*
5906: table.LC_data_table tr.LC_mail_replied:hover {
1.349 albertel 5907: background-color: $mail_replied_hover;
5908: }
1.938 bisitz 5909: */
1.795 www 5910:
1.938 bisitz 5911: table.LC_data_table tr td.LC_mail_other {
1.349 albertel 5912: background-color: $mail_other;
5913: }
1.795 www 5914:
1.938 bisitz 5915: /*
5916: table.LC_data_table tr.LC_mail_other:hover {
1.349 albertel 5917: background-color: $mail_other_hover;
5918: }
1.938 bisitz 5919: */
1.494 raeburn 5920:
1.777 tempelho 5921: table.LC_data_table tr > td.LC_browser_file,
5922: table.LC_data_table tr > td.LC_browser_file_published {
1.899 bisitz 5923: background: #AAEE77;
1.389 albertel 5924: }
1.795 www 5925:
1.777 tempelho 5926: table.LC_data_table tr > td.LC_browser_file_locked,
5927: table.LC_data_table tr > td.LC_browser_file_unpublished {
1.389 albertel 5928: background: #FFAA99;
1.387 albertel 5929: }
1.795 www 5930:
1.777 tempelho 5931: table.LC_data_table tr > td.LC_browser_file_obsolete {
1.899 bisitz 5932: background: #888888;
1.779 bisitz 5933: }
1.795 www 5934:
1.777 tempelho 5935: table.LC_data_table tr > td.LC_browser_file_modified,
1.779 bisitz 5936: table.LC_data_table tr > td.LC_browser_file_metamodified {
1.899 bisitz 5937: background: #F8F866;
1.777 tempelho 5938: }
1.795 www 5939:
1.696 bisitz 5940: table.LC_data_table tr.LC_browser_folder > td {
1.899 bisitz 5941: background: #E0E8FF;
1.387 albertel 5942: }
1.696 bisitz 5943:
1.707 bisitz 5944: table.LC_data_table tr > td.LC_roles_is {
1.911 bisitz 5945: /* background: #77FF77; */
1.707 bisitz 5946: }
1.795 www 5947:
1.707 bisitz 5948: table.LC_data_table tr > td.LC_roles_future {
1.939 bisitz 5949: border-right: 8px solid #FFFF77;
1.707 bisitz 5950: }
1.795 www 5951:
1.707 bisitz 5952: table.LC_data_table tr > td.LC_roles_will {
1.939 bisitz 5953: border-right: 8px solid #FFAA77;
1.707 bisitz 5954: }
1.795 www 5955:
1.707 bisitz 5956: table.LC_data_table tr > td.LC_roles_expired {
1.939 bisitz 5957: border-right: 8px solid #FF7777;
1.707 bisitz 5958: }
1.795 www 5959:
1.707 bisitz 5960: table.LC_data_table tr > td.LC_roles_will_not {
1.939 bisitz 5961: border-right: 8px solid #AAFF77;
1.707 bisitz 5962: }
1.795 www 5963:
1.707 bisitz 5964: table.LC_data_table tr > td.LC_roles_selected {
1.939 bisitz 5965: border-right: 8px solid #11CC55;
1.707 bisitz 5966: }
5967:
1.388 albertel 5968: span.LC_current_location {
1.701 harmsja 5969: font-size:larger;
1.388 albertel 5970: background: $pgbg;
5971: }
1.387 albertel 5972:
1.1029 www 5973: span.LC_current_nav_location {
5974: font-weight:bold;
5975: background: $sidebg;
5976: }
5977:
1.395 albertel 5978: span.LC_parm_menu_item {
5979: font-size: larger;
5980: }
1.795 www 5981:
1.395 albertel 5982: span.LC_parm_scope_all {
5983: color: red;
5984: }
1.795 www 5985:
1.395 albertel 5986: span.LC_parm_scope_folder {
5987: color: green;
5988: }
1.795 www 5989:
1.395 albertel 5990: span.LC_parm_scope_resource {
5991: color: orange;
5992: }
1.795 www 5993:
1.395 albertel 5994: span.LC_parm_part {
5995: color: blue;
5996: }
1.795 www 5997:
1.911 bisitz 5998: span.LC_parm_folder,
5999: span.LC_parm_symb {
1.395 albertel 6000: font-size: x-small;
6001: font-family: $mono;
6002: color: #AAAAAA;
6003: }
6004:
1.977 bisitz 6005: ul.LC_parm_parmlist li {
6006: display: inline-block;
6007: padding: 0.3em 0.8em;
6008: vertical-align: top;
6009: width: 150px;
6010: border-top:1px solid $lg_border_color;
6011: }
6012:
1.795 www 6013: td.LC_parm_overview_level_menu,
6014: td.LC_parm_overview_map_menu,
6015: td.LC_parm_overview_parm_selectors,
6016: td.LC_parm_overview_restrictions {
1.396 albertel 6017: border: 1px solid black;
6018: border-collapse: collapse;
6019: }
1.795 www 6020:
1.396 albertel 6021: table.LC_parm_overview_restrictions td {
6022: border-width: 1px 4px 1px 4px;
6023: border-style: solid;
6024: border-color: $pgbg;
6025: text-align: center;
6026: }
1.795 www 6027:
1.396 albertel 6028: table.LC_parm_overview_restrictions th {
6029: background: $tabbg;
6030: border-width: 1px 4px 1px 4px;
6031: border-style: solid;
6032: border-color: $pgbg;
6033: }
1.795 www 6034:
1.398 albertel 6035: table#LC_helpmenu {
1.803 bisitz 6036: border: none;
1.398 albertel 6037: height: 55px;
1.803 bisitz 6038: border-spacing: 0;
1.398 albertel 6039: }
6040:
6041: table#LC_helpmenu fieldset legend {
6042: font-size: larger;
6043: }
1.795 www 6044:
1.397 albertel 6045: table#LC_helpmenu_links {
6046: width: 100%;
6047: border: 1px solid black;
6048: background: $pgbg;
1.803 bisitz 6049: padding: 0;
1.397 albertel 6050: border-spacing: 1px;
6051: }
1.795 www 6052:
1.397 albertel 6053: table#LC_helpmenu_links tr td {
6054: padding: 1px;
6055: background: $tabbg;
1.399 albertel 6056: text-align: center;
6057: font-weight: bold;
1.397 albertel 6058: }
1.396 albertel 6059:
1.795 www 6060: table#LC_helpmenu_links a:link,
6061: table#LC_helpmenu_links a:visited,
1.397 albertel 6062: table#LC_helpmenu_links a:active {
6063: text-decoration: none;
6064: color: $font;
6065: }
1.795 www 6066:
1.397 albertel 6067: table#LC_helpmenu_links a:hover {
6068: text-decoration: underline;
6069: color: $vlink;
6070: }
1.396 albertel 6071:
1.417 albertel 6072: .LC_chrt_popup_exists {
6073: border: 1px solid #339933;
6074: margin: -1px;
6075: }
1.795 www 6076:
1.417 albertel 6077: .LC_chrt_popup_up {
6078: border: 1px solid yellow;
6079: margin: -1px;
6080: }
1.795 www 6081:
1.417 albertel 6082: .LC_chrt_popup {
6083: border: 1px solid #8888FF;
6084: background: #CCCCFF;
6085: }
1.795 www 6086:
1.421 albertel 6087: table.LC_pick_box {
6088: border-collapse: separate;
6089: background: white;
6090: border: 1px solid black;
6091: border-spacing: 1px;
6092: }
1.795 www 6093:
1.421 albertel 6094: table.LC_pick_box td.LC_pick_box_title {
1.850 bisitz 6095: background: $sidebg;
1.421 albertel 6096: font-weight: bold;
1.900 bisitz 6097: text-align: left;
1.740 bisitz 6098: vertical-align: top;
1.421 albertel 6099: width: 184px;
6100: padding: 8px;
6101: }
1.795 www 6102:
1.579 raeburn 6103: table.LC_pick_box td.LC_pick_box_value {
6104: text-align: left;
6105: padding: 8px;
6106: }
1.795 www 6107:
1.579 raeburn 6108: table.LC_pick_box td.LC_pick_box_select {
6109: text-align: left;
6110: padding: 8px;
6111: }
1.795 www 6112:
1.424 albertel 6113: table.LC_pick_box td.LC_pick_box_separator {
1.803 bisitz 6114: padding: 0;
1.421 albertel 6115: height: 1px;
6116: background: black;
6117: }
1.795 www 6118:
1.421 albertel 6119: table.LC_pick_box td.LC_pick_box_submit {
6120: text-align: right;
6121: }
1.795 www 6122:
1.579 raeburn 6123: table.LC_pick_box td.LC_evenrow_value {
6124: text-align: left;
6125: padding: 8px;
6126: background-color: $data_table_light;
6127: }
1.795 www 6128:
1.579 raeburn 6129: table.LC_pick_box td.LC_oddrow_value {
6130: text-align: left;
6131: padding: 8px;
6132: background-color: $data_table_light;
6133: }
1.795 www 6134:
1.579 raeburn 6135: span.LC_helpform_receipt_cat {
6136: font-weight: bold;
6137: }
1.795 www 6138:
1.424 albertel 6139: table.LC_group_priv_box {
6140: background: white;
6141: border: 1px solid black;
6142: border-spacing: 1px;
6143: }
1.795 www 6144:
1.424 albertel 6145: table.LC_group_priv_box td.LC_pick_box_title {
6146: background: $tabbg;
6147: font-weight: bold;
6148: text-align: right;
6149: width: 184px;
6150: }
1.795 www 6151:
1.424 albertel 6152: table.LC_group_priv_box td.LC_groups_fixed {
6153: background: $data_table_light;
6154: text-align: center;
6155: }
1.795 www 6156:
1.424 albertel 6157: table.LC_group_priv_box td.LC_groups_optional {
6158: background: $data_table_dark;
6159: text-align: center;
6160: }
1.795 www 6161:
1.424 albertel 6162: table.LC_group_priv_box td.LC_groups_functionality {
6163: background: $data_table_darker;
6164: text-align: center;
6165: font-weight: bold;
6166: }
1.795 www 6167:
1.424 albertel 6168: table.LC_group_priv td {
6169: text-align: left;
1.803 bisitz 6170: padding: 0;
1.424 albertel 6171: }
6172:
6173: .LC_navbuttons {
6174: margin: 2ex 0ex 2ex 0ex;
6175: }
1.795 www 6176:
1.423 albertel 6177: .LC_topic_bar {
6178: font-weight: bold;
6179: background: $tabbg;
1.918 wenzelju 6180: margin: 1em 0em 1em 2em;
1.805 bisitz 6181: padding: 3px;
1.918 wenzelju 6182: font-size: 1.2em;
1.423 albertel 6183: }
1.795 www 6184:
1.423 albertel 6185: .LC_topic_bar span {
1.918 wenzelju 6186: left: 0.5em;
6187: position: absolute;
1.423 albertel 6188: vertical-align: middle;
1.918 wenzelju 6189: font-size: 1.2em;
1.423 albertel 6190: }
1.795 www 6191:
1.423 albertel 6192: table.LC_course_group_status {
6193: margin: 20px;
6194: }
1.795 www 6195:
1.423 albertel 6196: table.LC_status_selector td {
6197: vertical-align: top;
6198: text-align: center;
1.424 albertel 6199: padding: 4px;
6200: }
1.795 www 6201:
1.599 albertel 6202: div.LC_feedback_link {
1.616 albertel 6203: clear: both;
1.829 kalberla 6204: background: $sidebg;
1.779 bisitz 6205: width: 100%;
1.829 kalberla 6206: padding-bottom: 10px;
6207: border: 1px $tabbg solid;
1.833 kalberla 6208: height: 22px;
6209: line-height: 22px;
6210: padding-top: 5px;
6211: }
6212:
6213: div.LC_feedback_link img {
6214: height: 22px;
1.867 kalberla 6215: vertical-align:middle;
1.829 kalberla 6216: }
6217:
1.911 bisitz 6218: div.LC_feedback_link a {
1.829 kalberla 6219: text-decoration: none;
1.489 raeburn 6220: }
1.795 www 6221:
1.867 kalberla 6222: div.LC_comblock {
1.911 bisitz 6223: display:inline;
1.867 kalberla 6224: color:$font;
6225: font-size:90%;
6226: }
6227:
6228: div.LC_feedback_link div.LC_comblock {
6229: padding-left:5px;
6230: }
6231:
6232: div.LC_feedback_link div.LC_comblock a {
6233: color:$font;
6234: }
6235:
1.489 raeburn 6236: span.LC_feedback_link {
1.858 bisitz 6237: /* background: $feedback_link_bg; */
1.599 albertel 6238: font-size: larger;
6239: }
1.795 www 6240:
1.599 albertel 6241: span.LC_message_link {
1.858 bisitz 6242: /* background: $feedback_link_bg; */
1.599 albertel 6243: font-size: larger;
6244: position: absolute;
6245: right: 1em;
1.489 raeburn 6246: }
1.421 albertel 6247:
1.515 albertel 6248: table.LC_prior_tries {
1.524 albertel 6249: border: 1px solid #000000;
6250: border-collapse: separate;
6251: border-spacing: 1px;
1.515 albertel 6252: }
1.523 albertel 6253:
1.515 albertel 6254: table.LC_prior_tries td {
1.524 albertel 6255: padding: 2px;
1.515 albertel 6256: }
1.523 albertel 6257:
6258: .LC_answer_correct {
1.795 www 6259: background: lightgreen;
6260: color: darkgreen;
6261: padding: 6px;
1.523 albertel 6262: }
1.795 www 6263:
1.523 albertel 6264: .LC_answer_charged_try {
1.797 www 6265: background: #FFAAAA;
1.795 www 6266: color: darkred;
6267: padding: 6px;
1.523 albertel 6268: }
1.795 www 6269:
1.779 bisitz 6270: .LC_answer_not_charged_try,
1.523 albertel 6271: .LC_answer_no_grade,
6272: .LC_answer_late {
1.795 www 6273: background: lightyellow;
1.523 albertel 6274: color: black;
1.795 www 6275: padding: 6px;
1.523 albertel 6276: }
1.795 www 6277:
1.523 albertel 6278: .LC_answer_previous {
1.795 www 6279: background: lightblue;
6280: color: darkblue;
6281: padding: 6px;
1.523 albertel 6282: }
1.795 www 6283:
1.779 bisitz 6284: .LC_answer_no_message {
1.777 tempelho 6285: background: #FFFFFF;
6286: color: black;
1.795 www 6287: padding: 6px;
1.779 bisitz 6288: }
1.795 www 6289:
1.779 bisitz 6290: .LC_answer_unknown {
6291: background: orange;
6292: color: black;
1.795 www 6293: padding: 6px;
1.777 tempelho 6294: }
1.795 www 6295:
1.529 albertel 6296: span.LC_prior_numerical,
6297: span.LC_prior_string,
6298: span.LC_prior_custom,
6299: span.LC_prior_reaction,
6300: span.LC_prior_math {
1.925 bisitz 6301: font-family: $mono;
1.523 albertel 6302: white-space: pre;
6303: }
6304:
1.525 albertel 6305: span.LC_prior_string {
1.925 bisitz 6306: font-family: $mono;
1.525 albertel 6307: white-space: pre;
6308: }
6309:
1.523 albertel 6310: table.LC_prior_option {
6311: width: 100%;
6312: border-collapse: collapse;
6313: }
1.795 www 6314:
1.911 bisitz 6315: table.LC_prior_rank,
1.795 www 6316: table.LC_prior_match {
1.528 albertel 6317: border-collapse: collapse;
6318: }
1.795 www 6319:
1.528 albertel 6320: table.LC_prior_option tr td,
6321: table.LC_prior_rank tr td,
6322: table.LC_prior_match tr td {
1.524 albertel 6323: border: 1px solid #000000;
1.515 albertel 6324: }
6325:
1.855 bisitz 6326: .LC_nobreak {
1.544 albertel 6327: white-space: nowrap;
1.519 raeburn 6328: }
6329:
1.576 raeburn 6330: span.LC_cusr_emph {
6331: font-style: italic;
6332: }
6333:
1.633 raeburn 6334: span.LC_cusr_subheading {
6335: font-weight: normal;
6336: font-size: 85%;
6337: }
6338:
1.861 bisitz 6339: div.LC_docs_entry_move {
1.859 bisitz 6340: border: 1px solid #BBBBBB;
1.545 albertel 6341: background: #DDDDDD;
1.861 bisitz 6342: width: 22px;
1.859 bisitz 6343: padding: 1px;
6344: margin: 0;
1.545 albertel 6345: }
6346:
1.861 bisitz 6347: table.LC_data_table tr > td.LC_docs_entry_commands,
6348: table.LC_data_table tr > td.LC_docs_entry_parameter {
1.545 albertel 6349: font-size: x-small;
6350: }
1.795 www 6351:
1.861 bisitz 6352: .LC_docs_entry_parameter {
6353: white-space: nowrap;
6354: }
6355:
1.544 albertel 6356: .LC_docs_copy {
1.545 albertel 6357: color: #000099;
1.544 albertel 6358: }
1.795 www 6359:
1.544 albertel 6360: .LC_docs_cut {
1.545 albertel 6361: color: #550044;
1.544 albertel 6362: }
1.795 www 6363:
1.544 albertel 6364: .LC_docs_rename {
1.545 albertel 6365: color: #009900;
1.544 albertel 6366: }
1.795 www 6367:
1.544 albertel 6368: .LC_docs_remove {
1.545 albertel 6369: color: #990000;
6370: }
6371:
1.547 albertel 6372: .LC_docs_reinit_warn,
6373: .LC_docs_ext_edit {
6374: font-size: x-small;
6375: }
6376:
1.545 albertel 6377: table.LC_docs_adddocs td,
6378: table.LC_docs_adddocs th {
6379: border: 1px solid #BBBBBB;
6380: padding: 4px;
6381: background: #DDDDDD;
1.543 albertel 6382: }
6383:
1.584 albertel 6384: table.LC_sty_begin {
6385: background: #BBFFBB;
6386: }
1.795 www 6387:
1.584 albertel 6388: table.LC_sty_end {
6389: background: #FFBBBB;
6390: }
6391:
1.589 raeburn 6392: table.LC_double_column {
1.803 bisitz 6393: border-width: 0;
1.589 raeburn 6394: border-collapse: collapse;
6395: width: 100%;
6396: padding: 2px;
6397: }
6398:
6399: table.LC_double_column tr td.LC_left_col {
1.590 raeburn 6400: top: 2px;
1.589 raeburn 6401: left: 2px;
6402: width: 47%;
6403: vertical-align: top;
6404: }
6405:
6406: table.LC_double_column tr td.LC_right_col {
6407: top: 2px;
1.779 bisitz 6408: right: 2px;
1.589 raeburn 6409: width: 47%;
6410: vertical-align: top;
6411: }
6412:
1.591 raeburn 6413: div.LC_left_float {
6414: float: left;
6415: padding-right: 5%;
1.597 albertel 6416: padding-bottom: 4px;
1.591 raeburn 6417: }
6418:
6419: div.LC_clear_float_header {
1.597 albertel 6420: padding-bottom: 2px;
1.591 raeburn 6421: }
6422:
6423: div.LC_clear_float_footer {
1.597 albertel 6424: padding-top: 10px;
1.591 raeburn 6425: clear: both;
6426: }
6427:
1.597 albertel 6428: div.LC_grade_show_user {
1.941 bisitz 6429: /* border-left: 5px solid $sidebg; */
6430: border-top: 5px solid #000000;
6431: margin: 50px 0 0 0;
1.936 bisitz 6432: padding: 15px 0 5px 10px;
1.597 albertel 6433: }
1.795 www 6434:
1.936 bisitz 6435: div.LC_grade_show_user_odd_row {
1.941 bisitz 6436: /* border-left: 5px solid #000000; */
6437: }
6438:
6439: div.LC_grade_show_user div.LC_Box {
6440: margin-right: 50px;
1.597 albertel 6441: }
6442:
6443: div.LC_grade_submissions,
6444: div.LC_grade_message_center,
1.936 bisitz 6445: div.LC_grade_info_links {
1.597 albertel 6446: margin: 5px;
6447: width: 99%;
6448: background: #FFFFFF;
6449: }
1.795 www 6450:
1.597 albertel 6451: div.LC_grade_submissions_header,
1.936 bisitz 6452: div.LC_grade_message_center_header {
1.705 tempelho 6453: font-weight: bold;
6454: font-size: large;
1.597 albertel 6455: }
1.795 www 6456:
1.597 albertel 6457: div.LC_grade_submissions_body,
1.936 bisitz 6458: div.LC_grade_message_center_body {
1.597 albertel 6459: border: 1px solid black;
6460: width: 99%;
6461: background: #FFFFFF;
6462: }
1.795 www 6463:
1.613 albertel 6464: table.LC_scantron_action {
6465: width: 100%;
6466: }
1.795 www 6467:
1.613 albertel 6468: table.LC_scantron_action tr th {
1.698 harmsja 6469: font-weight:bold;
6470: font-style:normal;
1.613 albertel 6471: }
1.795 www 6472:
1.779 bisitz 6473: .LC_edit_problem_header,
1.614 albertel 6474: div.LC_edit_problem_footer {
1.705 tempelho 6475: font-weight: normal;
6476: font-size: medium;
1.602 albertel 6477: margin: 2px;
1.1060 bisitz 6478: background-color: $sidebg;
1.600 albertel 6479: }
1.795 www 6480:
1.600 albertel 6481: div.LC_edit_problem_header,
1.602 albertel 6482: div.LC_edit_problem_header div,
1.614 albertel 6483: div.LC_edit_problem_footer,
6484: div.LC_edit_problem_footer div,
1.602 albertel 6485: div.LC_edit_problem_editxml_header,
6486: div.LC_edit_problem_editxml_header div {
1.600 albertel 6487: margin-top: 5px;
6488: }
1.795 www 6489:
1.600 albertel 6490: div.LC_edit_problem_header_title {
1.705 tempelho 6491: font-weight: bold;
6492: font-size: larger;
1.602 albertel 6493: background: $tabbg;
6494: padding: 3px;
1.1060 bisitz 6495: margin: 0 0 5px 0;
1.602 albertel 6496: }
1.795 www 6497:
1.602 albertel 6498: table.LC_edit_problem_header_title {
6499: width: 100%;
1.600 albertel 6500: background: $tabbg;
1.602 albertel 6501: }
6502:
6503: div.LC_edit_problem_discards {
6504: float: left;
6505: padding-bottom: 5px;
6506: }
1.795 www 6507:
1.602 albertel 6508: div.LC_edit_problem_saves {
6509: float: right;
6510: padding-bottom: 5px;
1.600 albertel 6511: }
1.795 www 6512:
1.1124 bisitz 6513: .LC_edit_opt {
6514: padding-left: 1em;
6515: white-space: nowrap;
6516: }
6517:
1.1152 golterma 6518: .LC_edit_problem_latexhelper{
6519: text-align: right;
6520: }
6521:
6522: #LC_edit_problem_colorful div{
6523: margin-left: 40px;
6524: }
6525:
1.911 bisitz 6526: img.stift {
1.803 bisitz 6527: border-width: 0;
6528: vertical-align: middle;
1.677 riegler 6529: }
1.680 riegler 6530:
1.923 bisitz 6531: table td.LC_mainmenu_col_fieldset {
1.680 riegler 6532: vertical-align: top;
1.777 tempelho 6533: }
1.795 www 6534:
1.716 raeburn 6535: div.LC_createcourse {
1.911 bisitz 6536: margin: 10px 10px 10px 10px;
1.716 raeburn 6537: }
6538:
1.917 raeburn 6539: .LC_dccid {
1.1130 raeburn 6540: float: right;
1.917 raeburn 6541: margin: 0.2em 0 0 0;
6542: padding: 0;
6543: font-size: 90%;
6544: display:none;
6545: }
6546:
1.897 wenzelju 6547: ol.LC_primary_menu a:hover,
1.721 harmsja 6548: ol#LC_MenuBreadcrumbs a:hover,
6549: ol#LC_PathBreadcrumbs a:hover,
1.897 wenzelju 6550: ul#LC_secondary_menu a:hover,
1.721 harmsja 6551: .LC_FormSectionClearButton input:hover
1.795 www 6552: ul.LC_TabContent li:hover a {
1.952 onken 6553: color:$button_hover;
1.911 bisitz 6554: text-decoration:none;
1.693 droeschl 6555: }
6556:
1.779 bisitz 6557: h1 {
1.911 bisitz 6558: padding: 0;
6559: line-height:130%;
1.693 droeschl 6560: }
1.698 harmsja 6561:
1.911 bisitz 6562: h2,
6563: h3,
6564: h4,
6565: h5,
6566: h6 {
6567: margin: 5px 0 5px 0;
6568: padding: 0;
6569: line-height:130%;
1.693 droeschl 6570: }
1.795 www 6571:
6572: .LC_hcell {
1.911 bisitz 6573: padding:3px 15px 3px 15px;
6574: margin: 0;
6575: background-color:$tabbg;
6576: color:$fontmenu;
6577: border-bottom:solid 1px $lg_border_color;
1.693 droeschl 6578: }
1.795 www 6579:
1.840 bisitz 6580: .LC_Box > .LC_hcell {
1.911 bisitz 6581: margin: 0 -10px 10px -10px;
1.835 bisitz 6582: }
6583:
1.721 harmsja 6584: .LC_noBorder {
1.911 bisitz 6585: border: 0;
1.698 harmsja 6586: }
1.693 droeschl 6587:
1.721 harmsja 6588: .LC_FormSectionClearButton input {
1.911 bisitz 6589: background-color:transparent;
6590: border: none;
6591: cursor:pointer;
6592: text-decoration:underline;
1.693 droeschl 6593: }
1.763 bisitz 6594:
6595: .LC_help_open_topic {
1.911 bisitz 6596: color: #FFFFFF;
6597: background-color: #EEEEFF;
6598: margin: 1px;
6599: padding: 4px;
6600: border: 1px solid #000033;
6601: white-space: nowrap;
6602: /* vertical-align: middle; */
1.759 neumanie 6603: }
1.693 droeschl 6604:
1.911 bisitz 6605: dl,
6606: ul,
6607: div,
6608: fieldset {
6609: margin: 10px 10px 10px 0;
6610: /* overflow: hidden; */
1.693 droeschl 6611: }
1.795 www 6612:
1.838 bisitz 6613: fieldset > legend {
1.911 bisitz 6614: font-weight: bold;
6615: padding: 0 5px 0 5px;
1.838 bisitz 6616: }
6617:
1.813 bisitz 6618: #LC_nav_bar {
1.911 bisitz 6619: float: left;
1.995 raeburn 6620: background-color: $pgbg_or_bgcolor;
1.966 bisitz 6621: margin: 0 0 2px 0;
1.807 droeschl 6622: }
6623:
1.916 droeschl 6624: #LC_realm {
6625: margin: 0.2em 0 0 0;
6626: padding: 0;
6627: font-weight: bold;
6628: text-align: center;
1.995 raeburn 6629: background-color: $pgbg_or_bgcolor;
1.916 droeschl 6630: }
6631:
1.911 bisitz 6632: #LC_nav_bar em {
6633: font-weight: bold;
6634: font-style: normal;
1.807 droeschl 6635: }
6636:
1.897 wenzelju 6637: ol.LC_primary_menu {
1.934 droeschl 6638: margin: 0;
1.1076 raeburn 6639: padding: 0;
1.995 raeburn 6640: background-color: $pgbg_or_bgcolor;
1.807 droeschl 6641: }
6642:
1.852 droeschl 6643: ol#LC_PathBreadcrumbs {
1.911 bisitz 6644: margin: 0;
1.693 droeschl 6645: }
6646:
1.897 wenzelju 6647: ol.LC_primary_menu li {
1.1076 raeburn 6648: color: RGB(80, 80, 80);
6649: vertical-align: middle;
6650: text-align: left;
6651: list-style: none;
6652: float: left;
6653: }
6654:
6655: ol.LC_primary_menu li a {
6656: display: block;
6657: margin: 0;
6658: padding: 0 5px 0 10px;
6659: text-decoration: none;
6660: }
6661:
6662: ol.LC_primary_menu li ul {
6663: display: none;
6664: width: 10em;
6665: background-color: $data_table_light;
6666: }
6667:
6668: ol.LC_primary_menu li:hover ul, ol.LC_primary_menu li.hover ul {
6669: display: block;
6670: position: absolute;
6671: margin: 0;
6672: padding: 0;
1.1078 raeburn 6673: z-index: 2;
1.1076 raeburn 6674: }
6675:
6676: ol.LC_primary_menu li:hover li, ol.LC_primary_menu li.hover li {
6677: font-size: 90%;
1.911 bisitz 6678: vertical-align: top;
1.1076 raeburn 6679: float: none;
1.1079 raeburn 6680: border-left: 1px solid black;
6681: border-right: 1px solid black;
1.1076 raeburn 6682: }
6683:
6684: ol.LC_primary_menu li:hover li a, ol.LC_primary_menu li.hover li a {
1.1078 raeburn 6685: background-color:$data_table_light;
1.1076 raeburn 6686: }
6687:
6688: ol.LC_primary_menu li li a:hover {
6689: color:$button_hover;
6690: background-color:$data_table_dark;
1.693 droeschl 6691: }
6692:
1.897 wenzelju 6693: ol.LC_primary_menu li img {
1.911 bisitz 6694: vertical-align: bottom;
1.934 droeschl 6695: height: 1.1em;
1.1077 raeburn 6696: margin: 0.2em 0 0 0;
1.693 droeschl 6697: }
6698:
1.897 wenzelju 6699: ol.LC_primary_menu a {
1.911 bisitz 6700: color: RGB(80, 80, 80);
6701: text-decoration: none;
1.693 droeschl 6702: }
1.795 www 6703:
1.949 droeschl 6704: ol.LC_primary_menu a.LC_new_message {
6705: font-weight:bold;
6706: color: darkred;
6707: }
6708:
1.975 raeburn 6709: ol.LC_docs_parameters {
6710: margin-left: 0;
6711: padding: 0;
6712: list-style: none;
6713: }
6714:
6715: ol.LC_docs_parameters li {
6716: margin: 0;
6717: padding-right: 20px;
6718: display: inline;
6719: }
6720:
1.976 raeburn 6721: ol.LC_docs_parameters li:before {
6722: content: "\\002022 \\0020";
6723: }
6724:
6725: li.LC_docs_parameters_title {
6726: font-weight: bold;
6727: }
6728:
6729: ol.LC_docs_parameters li.LC_docs_parameters_title:before {
6730: content: "";
6731: }
6732:
1.897 wenzelju 6733: ul#LC_secondary_menu {
1.1107 raeburn 6734: clear: right;
1.911 bisitz 6735: color: $fontmenu;
6736: background: $tabbg;
6737: list-style: none;
6738: padding: 0;
6739: margin: 0;
6740: width: 100%;
1.995 raeburn 6741: text-align: left;
1.1107 raeburn 6742: float: left;
1.808 droeschl 6743: }
6744:
1.897 wenzelju 6745: ul#LC_secondary_menu li {
1.911 bisitz 6746: font-weight: bold;
6747: line-height: 1.8em;
1.1107 raeburn 6748: border-right: 1px solid black;
6749: float: left;
6750: }
6751:
6752: ul#LC_secondary_menu li.LC_hoverable:hover, ul#LC_secondary_menu li.hover {
6753: background-color: $data_table_light;
6754: }
6755:
6756: ul#LC_secondary_menu li a {
1.911 bisitz 6757: padding: 0 0.8em;
1.1107 raeburn 6758: }
6759:
6760: ul#LC_secondary_menu li ul {
6761: display: none;
6762: }
6763:
6764: ul#LC_secondary_menu li:hover ul, ul#LC_secondary_menu li.hover ul {
6765: display: block;
6766: position: absolute;
6767: margin: 0;
6768: padding: 0;
6769: list-style:none;
6770: float: none;
6771: background-color: $data_table_light;
6772: z-index: 2;
6773: margin-left: -1px;
6774: }
6775:
6776: ul#LC_secondary_menu li ul li {
6777: font-size: 90%;
6778: vertical-align: top;
6779: border-left: 1px solid black;
1.911 bisitz 6780: border-right: 1px solid black;
1.1119 raeburn 6781: background-color: $data_table_light;
1.1107 raeburn 6782: list-style:none;
6783: float: none;
6784: }
6785:
6786: ul#LC_secondary_menu li ul li:hover, ul#LC_secondary_menu li ul li.hover {
6787: background-color: $data_table_dark;
1.807 droeschl 6788: }
6789:
1.847 tempelho 6790: ul.LC_TabContent {
1.911 bisitz 6791: display:block;
6792: background: $sidebg;
6793: border-bottom: solid 1px $lg_border_color;
6794: list-style:none;
1.1020 raeburn 6795: margin: -1px -10px 0 -10px;
1.911 bisitz 6796: padding: 0;
1.693 droeschl 6797: }
6798:
1.795 www 6799: ul.LC_TabContent li,
6800: ul.LC_TabContentBigger li {
1.911 bisitz 6801: float:left;
1.741 harmsja 6802: }
1.795 www 6803:
1.897 wenzelju 6804: ul#LC_secondary_menu li a {
1.911 bisitz 6805: color: $fontmenu;
6806: text-decoration: none;
1.693 droeschl 6807: }
1.795 www 6808:
1.721 harmsja 6809: ul.LC_TabContent {
1.952 onken 6810: min-height:20px;
1.721 harmsja 6811: }
1.795 www 6812:
6813: ul.LC_TabContent li {
1.911 bisitz 6814: vertical-align:middle;
1.959 onken 6815: padding: 0 16px 0 10px;
1.911 bisitz 6816: background-color:$tabbg;
6817: border-bottom:solid 1px $lg_border_color;
1.1020 raeburn 6818: border-left: solid 1px $font;
1.721 harmsja 6819: }
1.795 www 6820:
1.847 tempelho 6821: ul.LC_TabContent .right {
1.911 bisitz 6822: float:right;
1.847 tempelho 6823: }
6824:
1.911 bisitz 6825: ul.LC_TabContent li a,
6826: ul.LC_TabContent li {
6827: color:rgb(47,47,47);
6828: text-decoration:none;
6829: font-size:95%;
6830: font-weight:bold;
1.952 onken 6831: min-height:20px;
6832: }
6833:
1.959 onken 6834: ul.LC_TabContent li a:hover,
6835: ul.LC_TabContent li a:focus {
1.952 onken 6836: color: $button_hover;
1.959 onken 6837: background:none;
6838: outline:none;
1.952 onken 6839: }
6840:
6841: ul.LC_TabContent li:hover {
6842: color: $button_hover;
6843: cursor:pointer;
1.721 harmsja 6844: }
1.795 www 6845:
1.911 bisitz 6846: ul.LC_TabContent li.active {
1.952 onken 6847: color: $font;
1.911 bisitz 6848: background:#FFFFFF url(/adm/lonIcons/open.gif) no-repeat scroll right center;
1.952 onken 6849: border-bottom:solid 1px #FFFFFF;
6850: cursor: default;
1.744 ehlerst 6851: }
1.795 www 6852:
1.959 onken 6853: ul.LC_TabContent li.active a {
6854: color:$font;
6855: background:#FFFFFF;
6856: outline: none;
6857: }
1.1047 raeburn 6858:
6859: ul.LC_TabContent li.goback {
6860: float: left;
6861: border-left: none;
6862: }
6863:
1.870 tempelho 6864: #maincoursedoc {
1.911 bisitz 6865: clear:both;
1.870 tempelho 6866: }
6867:
6868: ul.LC_TabContentBigger {
1.911 bisitz 6869: display:block;
6870: list-style:none;
6871: padding: 0;
1.870 tempelho 6872: }
6873:
1.795 www 6874: ul.LC_TabContentBigger li {
1.911 bisitz 6875: vertical-align:bottom;
6876: height: 30px;
6877: font-size:110%;
6878: font-weight:bold;
6879: color: #737373;
1.841 tempelho 6880: }
6881:
1.957 onken 6882: ul.LC_TabContentBigger li.active {
6883: position: relative;
6884: top: 1px;
6885: }
6886:
1.870 tempelho 6887: ul.LC_TabContentBigger li a {
1.911 bisitz 6888: background:url('/adm/lonIcons/tabbgleft.gif') left bottom no-repeat;
6889: height: 30px;
6890: line-height: 30px;
6891: text-align: center;
6892: display: block;
6893: text-decoration: none;
1.958 onken 6894: outline: none;
1.741 harmsja 6895: }
1.795 www 6896:
1.870 tempelho 6897: ul.LC_TabContentBigger li.active a {
1.911 bisitz 6898: background:url('/adm/lonIcons/tabbgleft.gif') left top no-repeat;
6899: color:$font;
1.744 ehlerst 6900: }
1.795 www 6901:
1.870 tempelho 6902: ul.LC_TabContentBigger li b {
1.911 bisitz 6903: background: url('/adm/lonIcons/tabbgright.gif') no-repeat right bottom;
6904: display: block;
6905: float: left;
6906: padding: 0 30px;
1.957 onken 6907: border-bottom: 1px solid $lg_border_color;
1.870 tempelho 6908: }
6909:
1.956 onken 6910: ul.LC_TabContentBigger li:hover b {
6911: color:$button_hover;
6912: }
6913:
1.870 tempelho 6914: ul.LC_TabContentBigger li.active b {
1.911 bisitz 6915: background:url('/adm/lonIcons/tabbgright.gif') right top no-repeat;
6916: color:$font;
1.957 onken 6917: border: 0;
1.741 harmsja 6918: }
1.693 droeschl 6919:
1.870 tempelho 6920:
1.862 bisitz 6921: ul.LC_CourseBreadcrumbs {
6922: background: $sidebg;
1.1020 raeburn 6923: height: 2em;
1.862 bisitz 6924: padding-left: 10px;
1.1020 raeburn 6925: margin: 0;
1.862 bisitz 6926: list-style-position: inside;
6927: }
6928:
1.911 bisitz 6929: ol#LC_MenuBreadcrumbs,
1.862 bisitz 6930: ol#LC_PathBreadcrumbs {
1.911 bisitz 6931: padding-left: 10px;
6932: margin: 0;
1.933 droeschl 6933: height: 2.5em; /* equal to #LC_breadcrumbs line-height */
1.693 droeschl 6934: }
6935:
1.911 bisitz 6936: ol#LC_MenuBreadcrumbs li,
6937: ol#LC_PathBreadcrumbs li,
1.862 bisitz 6938: ul.LC_CourseBreadcrumbs li {
1.911 bisitz 6939: display: inline;
1.933 droeschl 6940: white-space: normal;
1.693 droeschl 6941: }
6942:
1.823 bisitz 6943: ol#LC_MenuBreadcrumbs li a,
1.862 bisitz 6944: ul.LC_CourseBreadcrumbs li a {
1.911 bisitz 6945: text-decoration: none;
6946: font-size:90%;
1.693 droeschl 6947: }
1.795 www 6948:
1.969 droeschl 6949: ol#LC_MenuBreadcrumbs h1 {
6950: display: inline;
6951: font-size: 90%;
6952: line-height: 2.5em;
6953: margin: 0;
6954: padding: 0;
6955: }
6956:
1.795 www 6957: ol#LC_PathBreadcrumbs li a {
1.911 bisitz 6958: text-decoration:none;
6959: font-size:100%;
6960: font-weight:bold;
1.693 droeschl 6961: }
1.795 www 6962:
1.840 bisitz 6963: .LC_Box {
1.911 bisitz 6964: border: solid 1px $lg_border_color;
6965: padding: 0 10px 10px 10px;
1.746 neumanie 6966: }
1.795 www 6967:
1.1020 raeburn 6968: .LC_DocsBox {
6969: border: solid 1px $lg_border_color;
6970: padding: 0 0 10px 10px;
6971: }
6972:
1.795 www 6973: .LC_AboutMe_Image {
1.911 bisitz 6974: float:left;
6975: margin-right:10px;
1.747 neumanie 6976: }
1.795 www 6977:
6978: .LC_Clear_AboutMe_Image {
1.911 bisitz 6979: clear:left;
1.747 neumanie 6980: }
1.795 www 6981:
1.721 harmsja 6982: dl.LC_ListStyleClean dt {
1.911 bisitz 6983: padding-right: 5px;
6984: display: table-header-group;
1.693 droeschl 6985: }
6986:
1.721 harmsja 6987: dl.LC_ListStyleClean dd {
1.911 bisitz 6988: display: table-row;
1.693 droeschl 6989: }
6990:
1.721 harmsja 6991: .LC_ListStyleClean,
6992: .LC_ListStyleSimple,
6993: .LC_ListStyleNormal,
1.795 www 6994: .LC_ListStyleSpecial {
1.911 bisitz 6995: /* display:block; */
6996: list-style-position: inside;
6997: list-style-type: none;
6998: overflow: hidden;
6999: padding: 0;
1.693 droeschl 7000: }
7001:
1.721 harmsja 7002: .LC_ListStyleSimple li,
7003: .LC_ListStyleSimple dd,
7004: .LC_ListStyleNormal li,
7005: .LC_ListStyleNormal dd,
7006: .LC_ListStyleSpecial li,
1.795 www 7007: .LC_ListStyleSpecial dd {
1.911 bisitz 7008: margin: 0;
7009: padding: 5px 5px 5px 10px;
7010: clear: both;
1.693 droeschl 7011: }
7012:
1.721 harmsja 7013: .LC_ListStyleClean li,
7014: .LC_ListStyleClean dd {
1.911 bisitz 7015: padding-top: 0;
7016: padding-bottom: 0;
1.693 droeschl 7017: }
7018:
1.721 harmsja 7019: .LC_ListStyleSimple dd,
1.795 www 7020: .LC_ListStyleSimple li {
1.911 bisitz 7021: border-bottom: solid 1px $lg_border_color;
1.693 droeschl 7022: }
7023:
1.721 harmsja 7024: .LC_ListStyleSpecial li,
7025: .LC_ListStyleSpecial dd {
1.911 bisitz 7026: list-style-type: none;
7027: background-color: RGB(220, 220, 220);
7028: margin-bottom: 4px;
1.693 droeschl 7029: }
7030:
1.721 harmsja 7031: table.LC_SimpleTable {
1.911 bisitz 7032: margin:5px;
7033: border:solid 1px $lg_border_color;
1.795 www 7034: }
1.693 droeschl 7035:
1.721 harmsja 7036: table.LC_SimpleTable tr {
1.911 bisitz 7037: padding: 0;
7038: border:solid 1px $lg_border_color;
1.693 droeschl 7039: }
1.795 www 7040:
7041: table.LC_SimpleTable thead {
1.911 bisitz 7042: background:rgb(220,220,220);
1.693 droeschl 7043: }
7044:
1.721 harmsja 7045: div.LC_columnSection {
1.911 bisitz 7046: display: block;
7047: clear: both;
7048: overflow: hidden;
7049: margin: 0;
1.693 droeschl 7050: }
7051:
1.721 harmsja 7052: div.LC_columnSection>* {
1.911 bisitz 7053: float: left;
7054: margin: 10px 20px 10px 0;
7055: overflow:hidden;
1.693 droeschl 7056: }
1.721 harmsja 7057:
1.795 www 7058: table em {
1.911 bisitz 7059: font-weight: bold;
7060: font-style: normal;
1.748 schulted 7061: }
1.795 www 7062:
1.779 bisitz 7063: table.LC_tableBrowseRes,
1.795 www 7064: table.LC_tableOfContent {
1.911 bisitz 7065: border:none;
7066: border-spacing: 1px;
7067: padding: 3px;
7068: background-color: #FFFFFF;
7069: font-size: 90%;
1.753 droeschl 7070: }
1.789 droeschl 7071:
1.911 bisitz 7072: table.LC_tableOfContent {
7073: border-collapse: collapse;
1.789 droeschl 7074: }
7075:
1.771 droeschl 7076: table.LC_tableBrowseRes a,
1.768 schulted 7077: table.LC_tableOfContent a {
1.911 bisitz 7078: background-color: transparent;
7079: text-decoration: none;
1.753 droeschl 7080: }
7081:
1.795 www 7082: table.LC_tableOfContent img {
1.911 bisitz 7083: border: none;
7084: height: 1.3em;
7085: vertical-align: text-bottom;
7086: margin-right: 0.3em;
1.753 droeschl 7087: }
1.757 schulted 7088:
1.795 www 7089: a#LC_content_toolbar_firsthomework {
1.911 bisitz 7090: background-image:url(/res/adm/pages/open-first-problem.gif);
1.774 ehlerst 7091: }
7092:
1.795 www 7093: a#LC_content_toolbar_everything {
1.911 bisitz 7094: background-image:url(/res/adm/pages/show-all.gif);
1.774 ehlerst 7095: }
7096:
1.795 www 7097: a#LC_content_toolbar_uncompleted {
1.911 bisitz 7098: background-image:url(/res/adm/pages/show-incomplete-problems.gif);
1.774 ehlerst 7099: }
7100:
1.795 www 7101: #LC_content_toolbar_clearbubbles {
1.911 bisitz 7102: background-image:url(/res/adm/pages/mark-discussionentries-read.gif);
1.774 ehlerst 7103: }
7104:
1.795 www 7105: a#LC_content_toolbar_changefolder {
1.911 bisitz 7106: background : url(/res/adm/pages/close-all-folders.gif) top center ;
1.757 schulted 7107: }
7108:
1.795 www 7109: a#LC_content_toolbar_changefolder_toggled {
1.911 bisitz 7110: background-image:url(/res/adm/pages/open-all-folders.gif);
1.757 schulted 7111: }
7112:
1.1043 raeburn 7113: a#LC_content_toolbar_edittoplevel {
7114: background-image:url(/res/adm/pages/edittoplevel.gif);
7115: }
7116:
1.795 www 7117: ul#LC_toolbar li a:hover {
1.911 bisitz 7118: background-position: bottom center;
1.757 schulted 7119: }
7120:
1.795 www 7121: ul#LC_toolbar {
1.911 bisitz 7122: padding: 0;
7123: margin: 2px;
7124: list-style:none;
7125: position:relative;
7126: background-color:white;
1.1082 raeburn 7127: overflow: auto;
1.757 schulted 7128: }
7129:
1.795 www 7130: ul#LC_toolbar li {
1.911 bisitz 7131: border:1px solid white;
7132: padding: 0;
7133: margin: 0;
7134: float: left;
7135: display:inline;
7136: vertical-align:middle;
1.1082 raeburn 7137: white-space: nowrap;
1.911 bisitz 7138: }
1.757 schulted 7139:
1.783 amueller 7140:
1.795 www 7141: a.LC_toolbarItem {
1.911 bisitz 7142: display:block;
7143: padding: 0;
7144: margin: 0;
7145: height: 32px;
7146: width: 32px;
7147: color:white;
7148: border: none;
7149: background-repeat:no-repeat;
7150: background-color:transparent;
1.757 schulted 7151: }
7152:
1.915 droeschl 7153: ul.LC_funclist {
7154: margin: 0;
7155: padding: 0.5em 1em 0.5em 0;
7156: }
7157:
1.933 droeschl 7158: ul.LC_funclist > li:first-child {
7159: font-weight:bold;
7160: margin-left:0.8em;
7161: }
7162:
1.915 droeschl 7163: ul.LC_funclist + ul.LC_funclist {
7164: /*
7165: left border as a seperator if we have more than
7166: one list
7167: */
7168: border-left: 1px solid $sidebg;
7169: /*
7170: this hides the left border behind the border of the
7171: outer box if element is wrapped to the next 'line'
7172: */
7173: margin-left: -1px;
7174: }
7175:
1.843 bisitz 7176: ul.LC_funclist li {
1.915 droeschl 7177: display: inline;
1.782 bisitz 7178: white-space: nowrap;
1.915 droeschl 7179: margin: 0 0 0 25px;
7180: line-height: 150%;
1.782 bisitz 7181: }
7182:
1.974 wenzelju 7183: .LC_hidden {
7184: display: none;
7185: }
7186:
1.1030 www 7187: .LCmodal-overlay {
7188: position:fixed;
7189: top:0;
7190: right:0;
7191: bottom:0;
7192: left:0;
7193: height:100%;
7194: width:100%;
7195: margin:0;
7196: padding:0;
7197: background:#999;
7198: opacity:.75;
7199: filter: alpha(opacity=75);
7200: -moz-opacity: 0.75;
7201: z-index:101;
7202: }
7203:
7204: * html .LCmodal-overlay {
7205: position: absolute;
7206: height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');
7207: }
7208:
7209: .LCmodal-window {
7210: position:fixed;
7211: top:50%;
7212: left:50%;
7213: margin:0;
7214: padding:0;
7215: z-index:102;
7216: }
7217:
7218: * html .LCmodal-window {
7219: position:absolute;
7220: }
7221:
7222: .LCclose-window {
7223: position:absolute;
7224: width:32px;
7225: height:32px;
7226: right:8px;
7227: top:8px;
7228: background:transparent url('/res/adm/pages/process-stop.png') no-repeat scroll right top;
7229: text-indent:-99999px;
7230: overflow:hidden;
7231: cursor:pointer;
7232: }
7233:
1.1100 raeburn 7234: /*
7235: styles used by TTH when "Default set of options to pass to tth/m
7236: when converting TeX" in course settings has been set
7237:
7238: option passed: -t
7239:
7240: */
7241:
7242: td div.comp { margin-top: -0.6ex; margin-bottom: -1ex;}
7243: td div.comb { margin-top: -0.6ex; margin-bottom: -.6ex;}
7244: td div.hrcomp { line-height: 0.9; margin-top: -0.8ex; margin-bottom: -1ex;}
7245: td div.norm {line-height:normal;}
7246:
7247: /*
7248: option passed -y3
7249: */
7250:
7251: span.roman {font-family: serif; font-style: normal; font-weight: normal;}
7252: span.overacc2 {position: relative; left: .8em; top: -1.2ex;}
7253: span.overacc1 {position: relative; left: .6em; top: -1.2ex;}
7254:
1.343 albertel 7255: END
7256: }
7257:
1.306 albertel 7258: =pod
7259:
7260: =item * &headtag()
7261:
7262: Returns a uniform footer for LON-CAPA web pages.
7263:
1.307 albertel 7264: Inputs: $title - optional title for the head
7265: $head_extra - optional extra HTML to put inside the <head>
1.315 albertel 7266: $args - optional arguments
1.319 albertel 7267: force_register - if is true call registerurl so the remote is
7268: informed
1.415 albertel 7269: redirect -> array ref of
7270: 1- seconds before redirect occurs
7271: 2- url to redirect to
7272: 3- whether the side effect should occur
1.315 albertel 7273: (side effect of setting
7274: $env{'internal.head.redirect'} to the url
7275: redirected too)
1.352 albertel 7276: domain -> force to color decorate a page for a specific
7277: domain
7278: function -> force usage of a specific rolish color scheme
7279: bgcolor -> override the default page bgcolor
1.460 albertel 7280: no_auto_mt_title
7281: -> prevent &mt()ing the title arg
1.464 albertel 7282:
1.306 albertel 7283: =cut
7284:
7285: sub headtag {
1.313 albertel 7286: my ($title,$head_extra,$args) = @_;
1.306 albertel 7287:
1.363 albertel 7288: my $function = $args->{'function'} || &get_users_function();
7289: my $domain = $args->{'domain'} || &determinedomain();
7290: my $bgcolor = $args->{'bgcolor'} || &designparm($function.'.pgbg',$domain);
1.1154 raeburn 7291: my $httphost = $args->{'use_absolute'};
1.418 albertel 7292: my $url = join(':',$env{'user.name'},$env{'user.domain'},
1.458 albertel 7293: $Apache::lonnet::perlvar{'lonVersion'},
1.531 albertel 7294: #time(),
1.418 albertel 7295: $env{'environment.color.timestamp'},
1.363 albertel 7296: $function,$domain,$bgcolor);
7297:
1.369 www 7298: $url = '/adm/css/'.&escape($url).'.css';
1.363 albertel 7299:
1.308 albertel 7300: my $result =
7301: '<head>'.
1.1160 raeburn 7302: &font_settings($args);
1.319 albertel 7303:
1.1064 raeburn 7304: my $inhibitprint = &print_suppression();
7305:
1.461 albertel 7306: if (!$args->{'frameset'}) {
7307: $result .= &Apache::lonhtmlcommon::htmlareaheaders();
7308: }
1.962 droeschl 7309: if ($args->{'force_register'} && $env{'request.noversionuri'} !~ m{^/res/adm/pages/}) {
7310: $result .= Apache::lonxml::display_title();
1.319 albertel 7311: }
1.436 albertel 7312: if (!$args->{'no_nav_bar'}
7313: && !$args->{'only_body'}
7314: && !$args->{'frameset'}) {
1.1154 raeburn 7315: $result .= &help_menu_js($httphost);
1.1032 www 7316: $result.=&modal_window();
1.1038 www 7317: $result.=&togglebox_script();
1.1034 www 7318: $result.=&wishlist_window();
1.1041 www 7319: $result.=&LCprogressbarUpdate_script();
1.1034 www 7320: } else {
7321: if ($args->{'add_modal'}) {
7322: $result.=&modal_window();
7323: }
7324: if ($args->{'add_wishlist'}) {
7325: $result.=&wishlist_window();
7326: }
1.1038 www 7327: if ($args->{'add_togglebox'}) {
7328: $result.=&togglebox_script();
7329: }
1.1041 www 7330: if ($args->{'add_progressbar'}) {
7331: $result.=&LCprogressbarUpdate_script();
7332: }
1.436 albertel 7333: }
1.314 albertel 7334: if (ref($args->{'redirect'})) {
1.414 albertel 7335: my ($time,$url,$inhibit_continue) = @{$args->{'redirect'}};
1.315 albertel 7336: $url = &Apache::lonenc::check_encrypt($url);
1.414 albertel 7337: if (!$inhibit_continue) {
7338: $env{'internal.head.redirect'} = $url;
7339: }
1.313 albertel 7340: $result.=<<ADDMETA
7341: <meta http-equiv="pragma" content="no-cache" />
1.344 albertel 7342: <meta http-equiv="Refresh" content="$time; url=$url" />
1.313 albertel 7343: ADDMETA
7344: }
1.306 albertel 7345: if (!defined($title)) {
7346: $title = 'The LearningOnline Network with CAPA';
7347: }
1.460 albertel 7348: if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
7349: $result .= '<title> LON-CAPA '.$title.'</title>'
1.1168 raeburn 7350: .'<link rel="stylesheet" type="text/css" href="'.$url.'"';
7351: if (!$args->{'frameset'}) {
7352: $result .= ' /';
7353: }
7354: $result .= '>'
1.1064 raeburn 7355: .$inhibitprint
1.414 albertel 7356: .$head_extra;
1.1137 raeburn 7357: if ($env{'browser.mobile'}) {
7358: $result .= '
7359: <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0">
7360: <meta name="apple-mobile-web-app-capable" content="yes" />';
7361: }
1.962 droeschl 7362: return $result.'</head>';
1.306 albertel 7363: }
7364:
7365: =pod
7366:
1.340 albertel 7367: =item * &font_settings()
7368:
7369: Returns neccessary <meta> to set the proper encoding
7370:
1.1160 raeburn 7371: Inputs: optional reference to HASH -- $args passed to &headtag()
1.340 albertel 7372:
7373: =cut
7374:
7375: sub font_settings {
1.1160 raeburn 7376: my ($args) = @_;
1.340 albertel 7377: my $headerstring='';
1.1160 raeburn 7378: if ((!$env{'browser.mathml'} && $env{'browser.unicode'}) ||
7379: ((ref($args) eq 'HASH') && ($args->{'browser.unicode'}))) {
1.1168 raeburn 7380: $headerstring.=
7381: '<meta http-equiv="Content-Type" content="text/html; charset=utf-8"';
7382: if (!$args->{'frameset'}) {
7383: $headerstring.= ' /';
7384: }
7385: $headerstring .= '>'."\n";
1.340 albertel 7386: }
7387: return $headerstring;
7388: }
7389:
1.341 albertel 7390: =pod
7391:
1.1064 raeburn 7392: =item * &print_suppression()
7393:
7394: In course context returns css which causes the body to be blank when media="print",
7395: if printout generation is unavailable for the current resource.
7396:
7397: This could be because:
7398:
7399: (a) printstartdate is in the future
7400:
7401: (b) printenddate is in the past
7402:
7403: (c) there is an active exam block with "printout"
7404: functionality blocked
7405:
7406: Users with pav, pfo or evb privileges are exempt.
7407:
7408: Inputs: none
7409:
7410: =cut
7411:
7412:
7413: sub print_suppression {
7414: my $noprint;
7415: if ($env{'request.course.id'}) {
7416: my $scope = $env{'request.course.id'};
7417: if ((&Apache::lonnet::allowed('pav',$scope)) ||
7418: (&Apache::lonnet::allowed('pfo',$scope))) {
7419: return;
7420: }
7421: if ($env{'request.course.sec'} ne '') {
7422: $scope .= "/$env{'request.course.sec'}";
7423: if ((&Apache::lonnet::allowed('pav',$scope)) ||
7424: (&Apache::lonnet::allowed('pfo',$scope))) {
1.1065 raeburn 7425: return;
1.1064 raeburn 7426: }
7427: }
7428: my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
7429: my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
1.1065 raeburn 7430: my $blocked = &blocking_status('printout',$cnum,$cdom);
1.1064 raeburn 7431: if ($blocked) {
7432: my $checkrole = "cm./$cdom/$cnum";
7433: if ($env{'request.course.sec'} ne '') {
7434: $checkrole .= "/$env{'request.course.sec'}";
7435: }
7436: unless ((&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) &&
7437: ($env{'request.role'} !~ m{^st\./$cdom/$cnum})) {
7438: $noprint = 1;
7439: }
7440: }
7441: unless ($noprint) {
7442: my $symb = &Apache::lonnet::symbread();
7443: if ($symb ne '') {
7444: my $navmap = Apache::lonnavmaps::navmap->new();
7445: if (ref($navmap)) {
7446: my $res = $navmap->getBySymb($symb);
7447: if (ref($res)) {
7448: if (!$res->resprintable()) {
7449: $noprint = 1;
7450: }
7451: }
7452: }
7453: }
7454: }
7455: if ($noprint) {
7456: return <<"ENDSTYLE";
7457: <style type="text/css" media="print">
7458: body { display:none }
7459: </style>
7460: ENDSTYLE
7461: }
7462: }
7463: return;
7464: }
7465:
7466: =pod
7467:
1.341 albertel 7468: =item * &xml_begin()
7469:
7470: Returns the needed doctype and <html>
7471:
7472: Inputs: none
7473:
7474: =cut
7475:
7476: sub xml_begin {
1.1168 raeburn 7477: my ($is_frameset) = @_;
1.341 albertel 7478: my $output='';
7479:
7480: if ($env{'browser.mathml'}) {
7481: $output='<?xml version="1.0"?>'
7482: #.'<?xml-stylesheet type="text/css" href="/adm/MathML/mathml.css"?>'."\n"
7483: # .'<!DOCTYPE html SYSTEM "/adm/MathML/mathml.dtd" '
7484:
7485: # .'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" [<!ENTITY mathns "http://www.w3.org/1998/Math/MathML">] >'
7486: .'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN" "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd">'
7487: .'<html xmlns:math="http://www.w3.org/1998/Math/MathML" '
7488: .'xmlns="http://www.w3.org/1999/xhtml">';
1.1168 raeburn 7489: } elsif ($is_frameset) {
7490: $output='<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">'."\n".
7491: '<html>'."\n";
1.341 albertel 7492: } else {
1.1168 raeburn 7493: $output='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'."\n".
7494: '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">'."\n";
1.341 albertel 7495: }
7496: return $output;
7497: }
1.340 albertel 7498:
7499: =pod
7500:
1.306 albertel 7501: =item * &start_page()
7502:
7503: Returns a complete <html> .. <body> section for LON-CAPA web pages.
7504:
1.648 raeburn 7505: Inputs:
7506:
7507: =over 4
7508:
7509: $title - optional title for the page
7510:
7511: $head_extra - optional extra HTML to incude inside the <head>
7512:
7513: $args - additional optional args supported are:
7514:
7515: =over 8
7516:
7517: only_body -> is true will set &bodytag() onlybodytag
1.317 albertel 7518: arg on
1.814 bisitz 7519: no_nav_bar -> is true will set &bodytag() no_nav_bar arg on
1.648 raeburn 7520: add_entries -> additional attributes to add to the <body>
7521: domain -> force to color decorate a page for a
1.317 albertel 7522: specific domain
1.648 raeburn 7523: function -> force usage of a specific rolish color
1.317 albertel 7524: scheme
1.648 raeburn 7525: redirect -> see &headtag()
7526: bgcolor -> override the default page bg color
7527: js_ready -> return a string ready for being used in
1.317 albertel 7528: a javascript writeln
1.648 raeburn 7529: html_encode -> return a string ready for being used in
1.320 albertel 7530: a html attribute
1.648 raeburn 7531: force_register -> if is true will turn on the &bodytag()
1.317 albertel 7532: $forcereg arg
1.648 raeburn 7533: frameset -> if true will start with a <frameset>
1.330 albertel 7534: rather than <body>
1.648 raeburn 7535: skip_phases -> hash ref of
1.338 albertel 7536: head -> skip the <html><head> generation
7537: body -> skip all <body> generation
1.648 raeburn 7538: no_auto_mt_title -> prevent &mt()ing the title arg
7539: inherit_jsmath -> when creating popup window in a page,
7540: should it have jsmath forced on by the
7541: current page
1.867 kalberla 7542: bread_crumbs -> Array containing breadcrumbs
1.983 raeburn 7543: bread_crumbs_component -> if exists show it as headline else show only the breadcrumbs
1.1096 raeburn 7544: group -> includes the current group, if page is for a
7545: specific group
1.361 albertel 7546:
1.648 raeburn 7547: =back
1.460 albertel 7548:
1.648 raeburn 7549: =back
1.562 albertel 7550:
1.306 albertel 7551: =cut
7552:
7553: sub start_page {
1.309 albertel 7554: my ($title,$head_extra,$args) = @_;
1.318 albertel 7555: #&Apache::lonnet::logthis("start_page ".join(':',caller(0)));
1.319 albertel 7556:
1.315 albertel 7557: $env{'internal.start_page'}++;
1.1096 raeburn 7558: my ($result,@advtools);
1.964 droeschl 7559:
1.338 albertel 7560: if (! exists($args->{'skip_phases'}{'head'}) ) {
1.1168 raeburn 7561: $result .= &xml_begin($args->{'frameset'}) . &headtag($title, $head_extra, $args);
1.338 albertel 7562: }
7563:
7564: if (! exists($args->{'skip_phases'}{'body'}) ) {
7565: if ($args->{'frameset'}) {
7566: my $attr_string = &make_attr_string($args->{'force_register'},
7567: $args->{'add_entries'});
7568: $result .= "\n<frameset $attr_string>\n";
1.831 bisitz 7569: } else {
7570: $result .=
7571: &bodytag($title,
7572: $args->{'function'}, $args->{'add_entries'},
7573: $args->{'only_body'}, $args->{'domain'},
7574: $args->{'force_register'}, $args->{'no_nav_bar'},
1.1096 raeburn 7575: $args->{'bgcolor'}, $args,
7576: \@advtools);
1.831 bisitz 7577: }
1.330 albertel 7578: }
1.338 albertel 7579:
1.315 albertel 7580: if ($args->{'js_ready'}) {
1.713 kaisler 7581: $result = &js_ready($result);
1.315 albertel 7582: }
1.320 albertel 7583: if ($args->{'html_encode'}) {
1.713 kaisler 7584: $result = &html_encode($result);
7585: }
7586:
1.813 bisitz 7587: # Preparation for new and consistent functionlist at top of screen
7588: # if ($args->{'functionlist'}) {
7589: # $result .= &build_functionlist();
7590: #}
7591:
1.964 droeschl 7592: # Don't add anything more if only_body wanted or in const space
7593: return $result if $args->{'only_body'}
7594: || $env{'request.state'} eq 'construct';
1.813 bisitz 7595:
7596: #Breadcrumbs
1.758 kaisler 7597: if (exists($args->{'bread_crumbs'}) or exists($args->{'bread_crumbs_component'})) {
7598: &Apache::lonhtmlcommon::clear_breadcrumbs();
7599: #if any br links exists, add them to the breadcrumbs
7600: if (exists($args->{'bread_crumbs'}) and ref($args->{'bread_crumbs'}) eq 'ARRAY') {
7601: foreach my $crumb (@{$args->{'bread_crumbs'}}){
7602: &Apache::lonhtmlcommon::add_breadcrumb($crumb);
7603: }
7604: }
1.1096 raeburn 7605: # if @advtools array contains items add then to the breadcrumbs
7606: if (@advtools > 0) {
7607: &Apache::lonmenu::advtools_crumbs(@advtools);
7608: }
1.758 kaisler 7609:
7610: #if bread_crumbs_component exists show it as headline else show only the breadcrumbs
7611: if(exists($args->{'bread_crumbs_component'})){
7612: $result .= &Apache::lonhtmlcommon::breadcrumbs($args->{'bread_crumbs_component'});
7613: }else{
7614: $result .= &Apache::lonhtmlcommon::breadcrumbs();
7615: }
1.320 albertel 7616: }
1.315 albertel 7617: return $result;
1.306 albertel 7618: }
7619:
7620: sub end_page {
1.315 albertel 7621: my ($args) = @_;
7622: $env{'internal.end_page'}++;
1.330 albertel 7623: my $result;
1.335 albertel 7624: if ($args->{'discussion'}) {
7625: my ($target,$parser);
7626: if (ref($args->{'discussion'})) {
7627: ($target,$parser) =($args->{'discussion'}{'target'},
7628: $args->{'discussion'}{'parser'});
7629: }
7630: $result .= &Apache::lonxml::xmlend($target,$parser);
7631: }
1.330 albertel 7632: if ($args->{'frameset'}) {
7633: $result .= '</frameset>';
7634: } else {
1.635 raeburn 7635: $result .= &endbodytag($args);
1.330 albertel 7636: }
1.1080 raeburn 7637: unless ($args->{'notbody'}) {
7638: $result .= "\n</html>";
7639: }
1.330 albertel 7640:
1.315 albertel 7641: if ($args->{'js_ready'}) {
1.317 albertel 7642: $result = &js_ready($result);
1.315 albertel 7643: }
1.335 albertel 7644:
1.320 albertel 7645: if ($args->{'html_encode'}) {
7646: $result = &html_encode($result);
7647: }
1.335 albertel 7648:
1.315 albertel 7649: return $result;
7650: }
7651:
1.1034 www 7652: sub wishlist_window {
7653: return(<<'ENDWISHLIST');
1.1046 raeburn 7654: <script type="text/javascript">
1.1034 www 7655: // <![CDATA[
7656: // <!-- BEGIN LON-CAPA Internal
7657: function set_wishlistlink(title, path) {
7658: if (!title) {
7659: title = document.title;
7660: title = title.replace(/^LON-CAPA /,'');
7661: }
1.1175 raeburn 7662: title = encodeURIComponent(title);
1.1034 www 7663: if (!path) {
7664: path = location.pathname;
7665: }
1.1175 raeburn 7666: path = encodeURIComponent(path);
1.1034 www 7667: Win = window.open('/adm/wishlist?mode=newLink&setTitle='+title+'&setPath='+path,
7668: 'wishlistNewLink','width=560,height=350,scrollbars=0');
7669: }
7670: // END LON-CAPA Internal -->
7671: // ]]>
7672: </script>
7673: ENDWISHLIST
7674: }
7675:
1.1030 www 7676: sub modal_window {
7677: return(<<'ENDMODAL');
1.1046 raeburn 7678: <script type="text/javascript">
1.1030 www 7679: // <![CDATA[
7680: // <!-- BEGIN LON-CAPA Internal
7681: var modalWindow = {
7682: parent:"body",
7683: windowId:null,
7684: content:null,
7685: width:null,
7686: height:null,
7687: close:function()
7688: {
7689: $(".LCmodal-window").remove();
7690: $(".LCmodal-overlay").remove();
7691: },
7692: open:function()
7693: {
7694: var modal = "";
7695: modal += "<div class=\"LCmodal-overlay\"></div>";
7696: modal += "<div id=\"" + this.windowId + "\" class=\"LCmodal-window\" style=\"width:" + this.width + "px; height:" + this.height + "px; margin-top:-" + (this.height / 2) + "px; margin-left:-" + (this.width / 2) + "px;\">";
7697: modal += this.content;
7698: modal += "</div>";
7699:
7700: $(this.parent).append(modal);
7701:
7702: $(".LCmodal-window").append("<a class=\"LCclose-window\"></a>");
7703: $(".LCclose-window").click(function(){modalWindow.close();});
7704: $(".LCmodal-overlay").click(function(){modalWindow.close();});
7705: }
7706: };
1.1140 raeburn 7707: var openMyModal = function(source,width,height,scrolling,transparency,style)
1.1030 www 7708: {
7709: modalWindow.windowId = "myModal";
7710: modalWindow.width = width;
7711: modalWindow.height = height;
1.1140 raeburn 7712: modalWindow.content = "<iframe width='"+width+"' height='"+height+"' frameborder='0' scrolling='"+scrolling+"' allowtransparency='"+transparency+"' src='" + source + "' style='"+style+"'></iframe>";
1.1030 www 7713: modalWindow.open();
7714: };
7715: // END LON-CAPA Internal -->
7716: // ]]>
7717: </script>
7718: ENDMODAL
7719: }
7720:
7721: sub modal_link {
1.1140 raeburn 7722: my ($link,$linktext,$width,$height,$target,$scrolling,$title,$transparency,$style)=@_;
1.1030 www 7723: unless ($width) { $width=480; }
7724: unless ($height) { $height=400; }
1.1031 www 7725: unless ($scrolling) { $scrolling='yes'; }
1.1140 raeburn 7726: unless ($transparency) { $transparency='true'; }
7727:
1.1074 raeburn 7728: my $target_attr;
7729: if (defined($target)) {
7730: $target_attr = 'target="'.$target.'"';
7731: }
7732: return <<"ENDLINK";
1.1140 raeburn 7733: <a href="$link" $target_attr title="$title" onclick="javascript:openMyModal('$link',$width,$height,'$scrolling','$transparency','$style'); return false;">
1.1074 raeburn 7734: $linktext</a>
7735: ENDLINK
1.1030 www 7736: }
7737:
1.1032 www 7738: sub modal_adhoc_script {
7739: my ($funcname,$width,$height,$content)=@_;
7740: return (<<ENDADHOC);
1.1046 raeburn 7741: <script type="text/javascript">
1.1032 www 7742: // <![CDATA[
7743: var $funcname = function()
7744: {
7745: modalWindow.windowId = "myModal";
7746: modalWindow.width = $width;
7747: modalWindow.height = $height;
7748: modalWindow.content = '$content';
7749: modalWindow.open();
7750: };
7751: // ]]>
7752: </script>
7753: ENDADHOC
7754: }
7755:
1.1041 www 7756: sub modal_adhoc_inner {
7757: my ($funcname,$width,$height,$content)=@_;
7758: my $innerwidth=$width-20;
7759: $content=&js_ready(
1.1140 raeburn 7760: &start_page('Dialog',undef,{'only_body'=>1,'bgcolor'=>'#FFFFFF'}).
7761: &start_scrollbox($width.'px',$innerwidth.'px',$height.'px','myModal','#FFFFFF',undef,1).
7762: $content.
1.1041 www 7763: &end_scrollbox().
1.1140 raeburn 7764: &end_page()
1.1041 www 7765: );
7766: return &modal_adhoc_script($funcname,$width,$height,$content);
7767: }
7768:
7769: sub modal_adhoc_window {
7770: my ($funcname,$width,$height,$content,$linktext)=@_;
7771: return &modal_adhoc_inner($funcname,$width,$height,$content).
7772: "<a href=\"javascript:$funcname();void(0);\">".$linktext."</a>";
7773: }
7774:
7775: sub modal_adhoc_launch {
7776: my ($funcname,$width,$height,$content)=@_;
7777: return &modal_adhoc_inner($funcname,$width,$height,$content).(<<ENDLAUNCH);
7778: <script type="text/javascript">
7779: // <![CDATA[
7780: $funcname();
7781: // ]]>
7782: </script>
7783: ENDLAUNCH
7784: }
7785:
7786: sub modal_adhoc_close {
7787: return (<<ENDCLOSE);
7788: <script type="text/javascript">
7789: // <![CDATA[
7790: modalWindow.close();
7791: // ]]>
7792: </script>
7793: ENDCLOSE
7794: }
7795:
1.1038 www 7796: sub togglebox_script {
7797: return(<<ENDTOGGLE);
7798: <script type="text/javascript">
7799: // <![CDATA[
7800: function LCtoggleDisplay(id,hidetext,showtext) {
7801: link = document.getElementById(id + "link").childNodes[0];
7802: with (document.getElementById(id).style) {
7803: if (display == "none" ) {
7804: display = "inline";
7805: link.nodeValue = hidetext;
7806: } else {
7807: display = "none";
7808: link.nodeValue = showtext;
7809: }
7810: }
7811: }
7812: // ]]>
7813: </script>
7814: ENDTOGGLE
7815: }
7816:
1.1039 www 7817: sub start_togglebox {
7818: my ($id,$heading,$headerbg,$hidetext,$showtext)=@_;
7819: unless ($heading) { $heading=''; } else { $heading.=' '; }
7820: unless ($showtext) { $showtext=&mt('show'); }
7821: unless ($hidetext) { $hidetext=&mt('hide'); }
7822: unless ($headerbg) { $headerbg='#FFFFFF'; }
7823: return &start_data_table().
7824: &start_data_table_header_row().
7825: '<td bgcolor="'.$headerbg.'">'.$heading.
7826: '[<a id="'.$id.'link" href="javascript:LCtoggleDisplay(\''.$id.'\',\''.$hidetext.'\',\''.
7827: $showtext.'\')">'.$showtext.'</a>]</td>'.
7828: &end_data_table_header_row().
7829: '<tr id="'.$id.'" style="display:none""><td>';
7830: }
7831:
7832: sub end_togglebox {
7833: return '</td></tr>'.&end_data_table();
7834: }
7835:
1.1041 www 7836: sub LCprogressbar_script {
1.1045 www 7837: my ($id)=@_;
1.1041 www 7838: return(<<ENDPROGRESS);
7839: <script type="text/javascript">
7840: // <![CDATA[
1.1045 www 7841: \$('#progressbar$id').progressbar({
1.1041 www 7842: value: 0,
7843: change: function(event, ui) {
7844: var newVal = \$(this).progressbar('option', 'value');
7845: \$('.pblabel', this).text(LCprogressTxt);
7846: }
7847: });
7848: // ]]>
7849: </script>
7850: ENDPROGRESS
7851: }
7852:
7853: sub LCprogressbarUpdate_script {
7854: return(<<ENDPROGRESSUPDATE);
7855: <style type="text/css">
7856: .ui-progressbar { position:relative; }
7857: .pblabel { position: absolute; width: 100%; text-align: center; line-height: 1.9em; }
7858: </style>
7859: <script type="text/javascript">
7860: // <![CDATA[
1.1045 www 7861: var LCprogressTxt='---';
7862:
7863: function LCupdateProgress(percent,progresstext,id) {
1.1041 www 7864: LCprogressTxt=progresstext;
1.1045 www 7865: \$('#progressbar'+id).progressbar('value',percent);
1.1041 www 7866: }
7867: // ]]>
7868: </script>
7869: ENDPROGRESSUPDATE
7870: }
7871:
1.1042 www 7872: my $LClastpercent;
1.1045 www 7873: my $LCidcnt;
7874: my $LCcurrentid;
1.1042 www 7875:
1.1041 www 7876: sub LCprogressbar {
1.1042 www 7877: my ($r)=(@_);
7878: $LClastpercent=0;
1.1045 www 7879: $LCidcnt++;
7880: $LCcurrentid=$$.'_'.$LCidcnt;
1.1041 www 7881: my $starting=&mt('Starting');
7882: my $content=(<<ENDPROGBAR);
1.1045 www 7883: <div id="progressbar$LCcurrentid">
1.1041 www 7884: <span class="pblabel">$starting</span>
7885: </div>
7886: ENDPROGBAR
1.1045 www 7887: &r_print($r,$content.&LCprogressbar_script($LCcurrentid));
1.1041 www 7888: }
7889:
7890: sub LCprogressbarUpdate {
1.1042 www 7891: my ($r,$val,$text)=@_;
7892: unless ($val) {
7893: if ($LClastpercent) {
7894: $val=$LClastpercent;
7895: } else {
7896: $val=0;
7897: }
7898: }
1.1041 www 7899: if ($val<0) { $val=0; }
7900: if ($val>100) { $val=0; }
1.1042 www 7901: $LClastpercent=$val;
1.1041 www 7902: unless ($text) { $text=$val.'%'; }
7903: $text=&js_ready($text);
1.1044 www 7904: &r_print($r,<<ENDUPDATE);
1.1041 www 7905: <script type="text/javascript">
7906: // <![CDATA[
1.1045 www 7907: LCupdateProgress($val,'$text','$LCcurrentid');
1.1041 www 7908: // ]]>
7909: </script>
7910: ENDUPDATE
1.1035 www 7911: }
7912:
1.1042 www 7913: sub LCprogressbarClose {
7914: my ($r)=@_;
7915: $LClastpercent=0;
1.1044 www 7916: &r_print($r,<<ENDCLOSE);
1.1042 www 7917: <script type="text/javascript">
7918: // <![CDATA[
1.1045 www 7919: \$("#progressbar$LCcurrentid").hide('slow');
1.1042 www 7920: // ]]>
7921: </script>
7922: ENDCLOSE
1.1044 www 7923: }
7924:
7925: sub r_print {
7926: my ($r,$to_print)=@_;
7927: if ($r) {
7928: $r->print($to_print);
7929: $r->rflush();
7930: } else {
7931: print($to_print);
7932: }
1.1042 www 7933: }
7934:
1.320 albertel 7935: sub html_encode {
7936: my ($result) = @_;
7937:
1.322 albertel 7938: $result = &HTML::Entities::encode($result,'<>&"');
1.320 albertel 7939:
7940: return $result;
7941: }
1.1044 www 7942:
1.317 albertel 7943: sub js_ready {
7944: my ($result) = @_;
7945:
1.323 albertel 7946: $result =~ s/[\n\r]/ /xmsg;
7947: $result =~ s/\\/\\\\/xmsg;
7948: $result =~ s/'/\\'/xmsg;
1.372 albertel 7949: $result =~ s{</}{<\\/}xmsg;
1.317 albertel 7950:
7951: return $result;
7952: }
7953:
1.315 albertel 7954: sub validate_page {
7955: if ( exists($env{'internal.start_page'})
1.316 albertel 7956: && $env{'internal.start_page'} > 1) {
7957: &Apache::lonnet::logthis('start_page called multiple times '.
1.318 albertel 7958: $env{'internal.start_page'}.' '.
1.316 albertel 7959: $ENV{'request.filename'});
1.315 albertel 7960: }
7961: if ( exists($env{'internal.end_page'})
1.316 albertel 7962: && $env{'internal.end_page'} > 1) {
7963: &Apache::lonnet::logthis('end_page called multiple times '.
1.318 albertel 7964: $env{'internal.end_page'}.' '.
1.316 albertel 7965: $env{'request.filename'});
1.315 albertel 7966: }
7967: if ( exists($env{'internal.start_page'})
7968: && ! exists($env{'internal.end_page'})) {
1.316 albertel 7969: &Apache::lonnet::logthis('start_page called without end_page '.
7970: $env{'request.filename'});
1.315 albertel 7971: }
7972: if ( ! exists($env{'internal.start_page'})
7973: && exists($env{'internal.end_page'})) {
1.316 albertel 7974: &Apache::lonnet::logthis('end_page called without start_page'.
7975: $env{'request.filename'});
1.315 albertel 7976: }
1.306 albertel 7977: }
1.315 albertel 7978:
1.996 www 7979:
7980: sub start_scrollbox {
1.1140 raeburn 7981: my ($outerwidth,$width,$height,$id,$bgcolor,$cursor,$needjsready) = @_;
1.998 raeburn 7982: unless ($outerwidth) { $outerwidth='520px'; }
7983: unless ($width) { $width='500px'; }
7984: unless ($height) { $height='200px'; }
1.1075 raeburn 7985: my ($table_id,$div_id,$tdcol);
1.1018 raeburn 7986: if ($id ne '') {
1.1140 raeburn 7987: $table_id = ' id="table_'.$id.'"';
1.1137 raeburn 7988: $div_id = ' id="div_'.$id.'"';
1.1018 raeburn 7989: }
1.1075 raeburn 7990: if ($bgcolor ne '') {
7991: $tdcol = "background-color: $bgcolor;";
7992: }
1.1137 raeburn 7993: my $nicescroll_js;
7994: if ($env{'browser.mobile'}) {
1.1140 raeburn 7995: $nicescroll_js = &nicescroll_javascript('div_'.$id,$cursor,$needjsready);
7996: }
7997: return <<"END";
7998: $nicescroll_js
7999:
8000: <table style="width: $outerwidth; border: 1px solid none;"$table_id><tr><td style="width: $width;$tdcol">
8001: <div style="overflow:auto; width:$width; height:$height;"$div_id>
8002: END
8003: }
8004:
8005: sub end_scrollbox {
8006: return '</div></td></tr></table>';
8007: }
8008:
8009: sub nicescroll_javascript {
8010: my ($id,$cursor,$needjsready,$framecheck,$location) = @_;
8011: my %options;
8012: if (ref($cursor) eq 'HASH') {
8013: %options = %{$cursor};
8014: }
8015: unless ($options{'railalign'} =~ /^left|right$/) {
8016: $options{'railalign'} = 'left';
8017: }
8018: unless ($options{'cursorcolor'} =~ /^\#\w+$/) {
8019: my $function = &get_users_function();
8020: $options{'cursorcolor'} = &designparm($function.'.sidebg',$env{'request.role.domain'});
1.1138 raeburn 8021: unless ($options{'cursorcolor'} =~ /^\#\w+$/) {
1.1140 raeburn 8022: $options{'cursorcolor'} = '#00F';
1.1138 raeburn 8023: }
1.1140 raeburn 8024: }
8025: if ($options{'cursoropacity'} =~ /^[\d.]+$/) {
8026: unless ($options{'cursoropacity'} >= 0.0 && $options{'cursoropacity'} <=1.0) {
1.1138 raeburn 8027: $options{'cursoropacity'}='1.0';
8028: }
1.1140 raeburn 8029: } else {
8030: $options{'cursoropacity'}='1.0';
8031: }
8032: if ($options{'cursorfixedheight'} eq 'none') {
8033: delete($options{'cursorfixedheight'});
8034: } else {
8035: unless ($options{'cursorfixedheight'} =~ /^\d+$/) { $options{'cursorfixedheight'}='50'; }
8036: }
8037: unless ($options{'railoffset'} =~ /^{[\w\:\d\-,]+}$/) {
8038: delete($options{'railoffset'});
8039: }
8040: my @niceoptions;
8041: while (my($key,$value) = each(%options)) {
8042: if ($value =~ /^\{.+\}$/) {
8043: push(@niceoptions,$key.':'.$value);
1.1138 raeburn 8044: } else {
1.1140 raeburn 8045: push(@niceoptions,$key.':"'.$value.'"');
1.1138 raeburn 8046: }
1.1140 raeburn 8047: }
8048: my $nicescroll_js = '
1.1137 raeburn 8049: $(document).ready(
1.1140 raeburn 8050: function() {
8051: $("#'.$id.'").niceScroll({'.join(',',@niceoptions).'});
8052: }
1.1137 raeburn 8053: );
8054: ';
1.1140 raeburn 8055: if ($framecheck) {
8056: $nicescroll_js .= '
8057: function expand_div(caller) {
8058: if (top === self) {
8059: document.getElementById("'.$id.'").style.width = "auto";
8060: document.getElementById("'.$id.'").style.height = "auto";
8061: } else {
8062: try {
8063: if (parent.frames) {
8064: if (parent.frames.length > 1) {
8065: var framesrc = parent.frames[1].location.href;
8066: var currsrc = framesrc.replace(/\#.*$/,"");
8067: if ((caller == "search") || (currsrc == "'.$location.'")) {
8068: document.getElementById("'.$id.'").style.width = "auto";
8069: document.getElementById("'.$id.'").style.height = "auto";
8070: }
8071: }
8072: }
8073: } catch (e) {
8074: return;
8075: }
1.1137 raeburn 8076: }
1.1140 raeburn 8077: return;
1.996 www 8078: }
1.1140 raeburn 8079: ';
8080: }
8081: if ($needjsready) {
8082: $nicescroll_js = '
8083: <script type="text/javascript">'."\n".$nicescroll_js."\n</script>\n";
8084: } else {
8085: $nicescroll_js = &Apache::lonhtmlcommon::scripttag($nicescroll_js);
8086: }
8087: return $nicescroll_js;
1.996 www 8088: }
8089:
1.318 albertel 8090: sub simple_error_page {
1.1150 bisitz 8091: my ($r,$title,$msg,$args) = @_;
1.1151 raeburn 8092: if (ref($args) eq 'HASH') {
8093: if (!$args->{'no_auto_mt_msg'}) { $msg = &mt($msg); }
8094: } else {
8095: $msg = &mt($msg);
8096: }
1.1150 bisitz 8097:
1.318 albertel 8098: my $page =
8099: &Apache::loncommon::start_page($title).
1.1150 bisitz 8100: '<p class="LC_error">'.$msg.'</p>'.
1.318 albertel 8101: &Apache::loncommon::end_page();
8102: if (ref($r)) {
8103: $r->print($page);
1.327 albertel 8104: return;
1.318 albertel 8105: }
8106: return $page;
8107: }
1.347 albertel 8108:
8109: {
1.610 albertel 8110: my @row_count;
1.961 onken 8111:
8112: sub start_data_table_count {
8113: unshift(@row_count, 0);
8114: return;
8115: }
8116:
8117: sub end_data_table_count {
8118: shift(@row_count);
8119: return;
8120: }
8121:
1.347 albertel 8122: sub start_data_table {
1.1018 raeburn 8123: my ($add_class,$id) = @_;
1.422 albertel 8124: my $css_class = (join(' ','LC_data_table',$add_class));
1.1018 raeburn 8125: my $table_id;
8126: if (defined($id)) {
8127: $table_id = ' id="'.$id.'"';
8128: }
1.961 onken 8129: &start_data_table_count();
1.1018 raeburn 8130: return '<table class="'.$css_class.'"'.$table_id.'>'."\n";
1.347 albertel 8131: }
8132:
8133: sub end_data_table {
1.961 onken 8134: &end_data_table_count();
1.389 albertel 8135: return '</table>'."\n";;
1.347 albertel 8136: }
8137:
8138: sub start_data_table_row {
1.974 wenzelju 8139: my ($add_class, $id) = @_;
1.610 albertel 8140: $row_count[0]++;
8141: my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row';
1.900 bisitz 8142: $css_class = (join(' ',$css_class,$add_class)) unless ($add_class eq '');
1.974 wenzelju 8143: $id = (' id="'.$id.'"') unless ($id eq '');
8144: return '<tr class="'.$css_class.'"'.$id.'>'."\n";
1.347 albertel 8145: }
1.471 banghart 8146:
8147: sub continue_data_table_row {
1.974 wenzelju 8148: my ($add_class, $id) = @_;
1.610 albertel 8149: my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row';
1.974 wenzelju 8150: $css_class = (join(' ',$css_class,$add_class)) unless ($add_class eq '');
8151: $id = (' id="'.$id.'"') unless ($id eq '');
8152: return '<tr class="'.$css_class.'"'.$id.'>'."\n";
1.471 banghart 8153: }
1.347 albertel 8154:
8155: sub end_data_table_row {
1.389 albertel 8156: return '</tr>'."\n";;
1.347 albertel 8157: }
1.367 www 8158:
1.421 albertel 8159: sub start_data_table_empty_row {
1.707 bisitz 8160: # $row_count[0]++;
1.421 albertel 8161: return '<tr class="LC_empty_row" >'."\n";;
8162: }
8163:
8164: sub end_data_table_empty_row {
8165: return '</tr>'."\n";;
8166: }
8167:
1.367 www 8168: sub start_data_table_header_row {
1.389 albertel 8169: return '<tr class="LC_header_row">'."\n";;
1.367 www 8170: }
8171:
8172: sub end_data_table_header_row {
1.389 albertel 8173: return '</tr>'."\n";;
1.367 www 8174: }
1.890 droeschl 8175:
8176: sub data_table_caption {
8177: my $caption = shift;
8178: return "<caption class=\"LC_caption\">$caption</caption>";
8179: }
1.347 albertel 8180: }
8181:
1.548 albertel 8182: =pod
8183:
8184: =item * &inhibit_menu_check($arg)
8185:
8186: Checks for a inhibitmenu state and generates output to preserve it
8187:
8188: Inputs: $arg - can be any of
8189: - undef - in which case the return value is a string
8190: to add into arguments list of a uri
8191: - 'input' - in which case the return value is a HTML
8192: <form> <input> field of type hidden to
8193: preserve the value
8194: - a url - in which case the return value is the url with
8195: the neccesary cgi args added to preserve the
8196: inhibitmenu state
8197: - a ref to a url - no return value, but the string is
8198: updated to include the neccessary cgi
8199: args to preserve the inhibitmenu state
8200:
8201: =cut
8202:
8203: sub inhibit_menu_check {
8204: my ($arg) = @_;
8205: &get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['inhibitmenu']);
8206: if ($arg eq 'input') {
8207: if ($env{'form.inhibitmenu'}) {
8208: return '<input type="hidden" name="inhibitmenu" value="'.$env{'form.inhibitmenu'}.'" />';
8209: } else {
8210: return
8211: }
8212: }
8213: if ($env{'form.inhibitmenu'}) {
8214: if (ref($arg)) {
8215: $$arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
8216: } elsif ($arg eq '') {
8217: $arg .= 'inhibitmenu='.$env{'form.inhibitmenu'};
8218: } else {
8219: $arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
8220: }
8221: }
8222: if (!ref($arg)) {
8223: return $arg;
8224: }
8225: }
8226:
1.251 albertel 8227: ###############################################
1.182 matthew 8228:
8229: =pod
8230:
1.549 albertel 8231: =back
8232:
8233: =head1 User Information Routines
8234:
8235: =over 4
8236:
1.405 albertel 8237: =item * &get_users_function()
1.182 matthew 8238:
8239: Used by &bodytag to determine the current users primary role.
8240: Returns either 'student','coordinator','admin', or 'author'.
8241:
8242: =cut
8243:
8244: ###############################################
8245: sub get_users_function {
1.815 tempelho 8246: my $function = 'norole';
1.818 tempelho 8247: if ($env{'request.role'}=~/^(st)/) {
8248: $function='student';
8249: }
1.907 raeburn 8250: if ($env{'request.role'}=~/^(cc|co|in|ta|ep)/) {
1.182 matthew 8251: $function='coordinator';
8252: }
1.258 albertel 8253: if ($env{'request.role'}=~/^(su|dc|ad|li)/) {
1.182 matthew 8254: $function='admin';
8255: }
1.826 bisitz 8256: if (($env{'request.role'}=~/^(au|ca|aa)/) ||
1.1025 raeburn 8257: ($ENV{'REQUEST_URI'}=~ m{/^(/priv)})) {
1.182 matthew 8258: $function='author';
8259: }
8260: return $function;
1.54 www 8261: }
1.99 www 8262:
8263: ###############################################
8264:
1.233 raeburn 8265: =pod
8266:
1.821 raeburn 8267: =item * &show_course()
8268:
8269: Used by lonmenu.pm and lonroles.pm to determine whether to use the word
8270: 'Courses' or 'Roles' in inline navigation and on screen displaying user's roles.
8271:
8272: Inputs:
8273: None
8274:
8275: Outputs:
8276: Scalar: 1 if 'Course' to be used, 0 otherwise.
8277:
8278: =cut
8279:
8280: ###############################################
8281: sub show_course {
8282: my $course = !$env{'user.adv'};
8283: if (!$env{'user.adv'}) {
8284: foreach my $env (keys(%env)) {
8285: next if ($env !~ m/^user\.priv\./);
8286: if ($env !~ m/^user\.priv\.(?:st|cm)/) {
8287: $course = 0;
8288: last;
8289: }
8290: }
8291: }
8292: return $course;
8293: }
8294:
8295: ###############################################
8296:
8297: =pod
8298:
1.542 raeburn 8299: =item * &check_user_status()
1.274 raeburn 8300:
8301: Determines current status of supplied role for a
8302: specific user. Roles can be active, previous or future.
8303:
8304: Inputs:
8305: user's domain, user's username, course's domain,
1.375 raeburn 8306: course's number, optional section ID.
1.274 raeburn 8307:
8308: Outputs:
8309: role status: active, previous or future.
8310:
8311: =cut
8312:
8313: sub check_user_status {
1.412 raeburn 8314: my ($udom,$uname,$cdom,$crs,$role,$sec) = @_;
1.1073 raeburn 8315: my %userinfo = &Apache::lonnet::dump('roles',$udom,$uname);
1.274 raeburn 8316: my @uroles = keys %userinfo;
8317: my $srchstr;
8318: my $active_chk = 'none';
1.412 raeburn 8319: my $now = time;
1.274 raeburn 8320: if (@uroles > 0) {
1.908 raeburn 8321: if (($role eq 'cc') || ($role eq 'co') || ($sec eq '') || (!defined($sec))) {
1.274 raeburn 8322: $srchstr = '/'.$cdom.'/'.$crs.'_'.$role;
8323: } else {
1.412 raeburn 8324: $srchstr = '/'.$cdom.'/'.$crs.'/'.$sec.'_'.$role;
8325: }
8326: if (grep/^\Q$srchstr\E$/,@uroles) {
1.274 raeburn 8327: my $role_end = 0;
8328: my $role_start = 0;
8329: $active_chk = 'active';
1.412 raeburn 8330: if ($userinfo{$srchstr} =~ m/^\Q$role\E_(\d+)/) {
8331: $role_end = $1;
8332: if ($userinfo{$srchstr} =~ m/^\Q$role\E_\Q$role_end\E_(\d+)$/) {
8333: $role_start = $1;
1.274 raeburn 8334: }
8335: }
8336: if ($role_start > 0) {
1.412 raeburn 8337: if ($now < $role_start) {
1.274 raeburn 8338: $active_chk = 'future';
8339: }
8340: }
8341: if ($role_end > 0) {
1.412 raeburn 8342: if ($now > $role_end) {
1.274 raeburn 8343: $active_chk = 'previous';
8344: }
8345: }
8346: }
8347: }
8348: return $active_chk;
8349: }
8350:
8351: ###############################################
8352:
8353: =pod
8354:
1.405 albertel 8355: =item * &get_sections()
1.233 raeburn 8356:
8357: Determines all the sections for a course including
8358: sections with students and sections containing other roles.
1.419 raeburn 8359: Incoming parameters:
8360:
8361: 1. domain
8362: 2. course number
8363: 3. reference to array containing roles for which sections should
8364: be gathered (optional).
8365: 4. reference to array containing status types for which sections
8366: should be gathered (optional).
8367:
8368: If the third argument is undefined, sections are gathered for any role.
8369: If the fourth argument is undefined, sections are gathered for any status.
8370: Permissible values are 'active' or 'future' or 'previous'.
1.233 raeburn 8371:
1.374 raeburn 8372: Returns section hash (keys are section IDs, values are
8373: number of users in each section), subject to the
1.419 raeburn 8374: optional roles filter, optional status filter
1.233 raeburn 8375:
8376: =cut
8377:
8378: ###############################################
8379: sub get_sections {
1.419 raeburn 8380: my ($cdom,$cnum,$possible_roles,$possible_status) = @_;
1.366 albertel 8381: if (!defined($cdom) || !defined($cnum)) {
8382: my $cid = $env{'request.course.id'};
8383:
8384: return if (!defined($cid));
8385:
8386: $cdom = $env{'course.'.$cid.'.domain'};
8387: $cnum = $env{'course.'.$cid.'.num'};
8388: }
8389:
8390: my %sectioncount;
1.419 raeburn 8391: my $now = time;
1.240 albertel 8392:
1.1118 raeburn 8393: my $check_students = 1;
8394: my $only_students = 0;
8395: if (ref($possible_roles) eq 'ARRAY') {
8396: if (grep(/^st$/,@{$possible_roles})) {
8397: if (@{$possible_roles} == 1) {
8398: $only_students = 1;
8399: }
8400: } else {
8401: $check_students = 0;
8402: }
8403: }
8404:
8405: if ($check_students) {
1.276 albertel 8406: my ($classlist) = &Apache::loncoursedata::get_classlist($cdom,$cnum);
1.240 albertel 8407: my $sec_index = &Apache::loncoursedata::CL_SECTION();
8408: my $status_index = &Apache::loncoursedata::CL_STATUS();
1.419 raeburn 8409: my $start_index = &Apache::loncoursedata::CL_START();
8410: my $end_index = &Apache::loncoursedata::CL_END();
8411: my $status;
1.366 albertel 8412: while (my ($student,$data) = each(%$classlist)) {
1.419 raeburn 8413: my ($section,$stu_status,$start,$end) = ($data->[$sec_index],
8414: $data->[$status_index],
8415: $data->[$start_index],
8416: $data->[$end_index]);
8417: if ($stu_status eq 'Active') {
8418: $status = 'active';
8419: } elsif ($end < $now) {
8420: $status = 'previous';
8421: } elsif ($start > $now) {
8422: $status = 'future';
8423: }
8424: if ($section ne '-1' && $section !~ /^\s*$/) {
8425: if ((!defined($possible_status)) || (($status ne '') &&
8426: (grep/^\Q$status\E$/,@{$possible_status}))) {
8427: $sectioncount{$section}++;
8428: }
1.240 albertel 8429: }
8430: }
8431: }
1.1118 raeburn 8432: if ($only_students) {
8433: return %sectioncount;
8434: }
1.240 albertel 8435: my %courseroles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
8436: foreach my $user (sort(keys(%courseroles))) {
8437: if ($user !~ /^(\w{2})/) { next; }
8438: my ($role) = ($user =~ /^(\w{2})/);
8439: if ($possible_roles && !(grep(/^$role$/,@$possible_roles))) { next; }
1.419 raeburn 8440: my ($section,$status);
1.240 albertel 8441: if ($role eq 'cr' &&
8442: $user =~ m-^$role/[^/]*/[^/]*/[^/]*:[^:]*:[^:]*:(\w+)-) {
8443: $section=$1;
8444: }
8445: if ($user =~ /^$role:[^:]*:[^:]*:(\w+)/) { $section=$1; }
8446: if (!defined($section) || $section eq '-1') { next; }
1.419 raeburn 8447: my ($end,$start) = ($courseroles{$user} =~ /^([^:]*):([^:]*)$/);
8448: if ($end == -1 && $start == -1) {
8449: next; #deleted role
8450: }
8451: if (!defined($possible_status)) {
8452: $sectioncount{$section}++;
8453: } else {
8454: if ((!$end || $end >= $now) && (!$start || $start <= $now)) {
8455: $status = 'active';
8456: } elsif ($end < $now) {
8457: $status = 'future';
8458: } elsif ($start > $now) {
8459: $status = 'previous';
8460: }
8461: if (($status ne '') && (grep/^\Q$status\E$/,@{$possible_status})) {
8462: $sectioncount{$section}++;
8463: }
8464: }
1.233 raeburn 8465: }
1.366 albertel 8466: return %sectioncount;
1.233 raeburn 8467: }
8468:
1.274 raeburn 8469: ###############################################
1.294 raeburn 8470:
8471: =pod
1.405 albertel 8472:
8473: =item * &get_course_users()
8474:
1.275 raeburn 8475: Retrieves usernames:domains for users in the specified course
8476: with specific role(s), and access status.
8477:
8478: Incoming parameters:
1.277 albertel 8479: 1. course domain
8480: 2. course number
8481: 3. access status: users must have - either active,
1.275 raeburn 8482: previous, future, or all.
1.277 albertel 8483: 4. reference to array of permissible roles
1.288 raeburn 8484: 5. reference to array of section restrictions (optional)
8485: 6. reference to results object (hash of hashes).
8486: 7. reference to optional userdata hash
1.609 raeburn 8487: 8. reference to optional statushash
1.630 raeburn 8488: 9. flag if privileged users (except those set to unhide in
8489: course settings) should be excluded
1.609 raeburn 8490: Keys of top level results hash are roles.
1.275 raeburn 8491: Keys of inner hashes are username:domain, with
8492: values set to access type.
1.288 raeburn 8493: Optional userdata hash returns an array with arguments in the
8494: same order as loncoursedata::get_classlist() for student data.
8495:
1.609 raeburn 8496: Optional statushash returns
8497:
1.288 raeburn 8498: Entries for end, start, section and status are blank because
8499: of the possibility of multiple values for non-student roles.
8500:
1.275 raeburn 8501: =cut
1.405 albertel 8502:
1.275 raeburn 8503: ###############################################
1.405 albertel 8504:
1.275 raeburn 8505: sub get_course_users {
1.630 raeburn 8506: my ($cdom,$cnum,$types,$roles,$sections,$users,$userdata,$statushash,$hidepriv) = @_;
1.288 raeburn 8507: my %idx = ();
1.419 raeburn 8508: my %seclists;
1.288 raeburn 8509:
8510: $idx{udom} = &Apache::loncoursedata::CL_SDOM();
8511: $idx{uname} = &Apache::loncoursedata::CL_SNAME();
8512: $idx{end} = &Apache::loncoursedata::CL_END();
8513: $idx{start} = &Apache::loncoursedata::CL_START();
8514: $idx{id} = &Apache::loncoursedata::CL_ID();
8515: $idx{section} = &Apache::loncoursedata::CL_SECTION();
8516: $idx{fullname} = &Apache::loncoursedata::CL_FULLNAME();
8517: $idx{status} = &Apache::loncoursedata::CL_STATUS();
8518:
1.290 albertel 8519: if (grep(/^st$/,@{$roles})) {
1.276 albertel 8520: my ($classlist,$keylist)=&Apache::loncoursedata::get_classlist($cdom,$cnum);
1.278 raeburn 8521: my $now = time;
1.277 albertel 8522: foreach my $student (keys(%{$classlist})) {
1.288 raeburn 8523: my $match = 0;
1.412 raeburn 8524: my $secmatch = 0;
1.419 raeburn 8525: my $section = $$classlist{$student}[$idx{section}];
1.609 raeburn 8526: my $status = $$classlist{$student}[$idx{status}];
1.419 raeburn 8527: if ($section eq '') {
8528: $section = 'none';
8529: }
1.291 albertel 8530: if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420 albertel 8531: if (grep(/^all$/,@{$sections})) {
1.412 raeburn 8532: $secmatch = 1;
8533: } elsif ($$classlist{$student}[$idx{section}] eq '') {
1.420 albertel 8534: if (grep(/^none$/,@{$sections})) {
1.412 raeburn 8535: $secmatch = 1;
8536: }
8537: } else {
1.419 raeburn 8538: if (grep(/^\Q$section\E$/,@{$sections})) {
1.412 raeburn 8539: $secmatch = 1;
8540: }
1.290 albertel 8541: }
1.412 raeburn 8542: if (!$secmatch) {
8543: next;
8544: }
1.419 raeburn 8545: }
1.275 raeburn 8546: if (defined($$types{'active'})) {
1.288 raeburn 8547: if ($$classlist{$student}[$idx{status}] eq 'Active') {
1.275 raeburn 8548: push(@{$$users{st}{$student}},'active');
1.288 raeburn 8549: $match = 1;
1.275 raeburn 8550: }
8551: }
8552: if (defined($$types{'previous'})) {
1.609 raeburn 8553: if ($$classlist{$student}[$idx{status}] eq 'Expired') {
1.275 raeburn 8554: push(@{$$users{st}{$student}},'previous');
1.288 raeburn 8555: $match = 1;
1.275 raeburn 8556: }
8557: }
8558: if (defined($$types{'future'})) {
1.609 raeburn 8559: if ($$classlist{$student}[$idx{status}] eq 'Future') {
1.275 raeburn 8560: push(@{$$users{st}{$student}},'future');
1.288 raeburn 8561: $match = 1;
1.275 raeburn 8562: }
8563: }
1.609 raeburn 8564: if ($match) {
8565: push(@{$seclists{$student}},$section);
8566: if (ref($userdata) eq 'HASH') {
8567: $$userdata{$student} = $$classlist{$student};
8568: }
8569: if (ref($statushash) eq 'HASH') {
8570: $statushash->{$student}{'st'}{$section} = $status;
8571: }
1.288 raeburn 8572: }
1.275 raeburn 8573: }
8574: }
1.412 raeburn 8575: if ((@{$roles} > 1) || ((@{$roles} == 1) && ($$roles[0] ne "st"))) {
1.439 raeburn 8576: my %coursepersonnel = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
8577: my $now = time;
1.609 raeburn 8578: my %displaystatus = ( previous => 'Expired',
8579: active => 'Active',
8580: future => 'Future',
8581: );
1.1121 raeburn 8582: my (%nothide,@possdoms);
1.630 raeburn 8583: if ($hidepriv) {
8584: my %coursehash=&Apache::lonnet::coursedescription($cdom.'_'.$cnum);
8585: foreach my $user (split(/\s*\,\s*/,$coursehash{'nothideprivileged'})) {
8586: if ($user !~ /:/) {
8587: $nothide{join(':',split(/[\@]/,$user))}=1;
8588: } else {
8589: $nothide{$user} = 1;
8590: }
8591: }
1.1121 raeburn 8592: my @possdoms = ($cdom);
8593: if ($coursehash{'checkforpriv'}) {
8594: push(@possdoms,split(/,/,$coursehash{'checkforpriv'}));
8595: }
1.630 raeburn 8596: }
1.439 raeburn 8597: foreach my $person (sort(keys(%coursepersonnel))) {
1.288 raeburn 8598: my $match = 0;
1.412 raeburn 8599: my $secmatch = 0;
1.439 raeburn 8600: my $status;
1.412 raeburn 8601: my ($role,$user,$usec) = ($person =~ /^([^:]*):([^:]+:[^:]+):([^:]*)/);
1.275 raeburn 8602: $user =~ s/:$//;
1.439 raeburn 8603: my ($end,$start) = split(/:/,$coursepersonnel{$person});
8604: if ($end == -1 || $start == -1) {
8605: next;
8606: }
8607: if (($role) && ((grep(/^\Q$role\E$/,@{$roles})) ||
8608: (grep(/^cr$/,@{$roles}) && $role =~ /^cr\//))) {
1.412 raeburn 8609: my ($uname,$udom) = split(/:/,$user);
8610: if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420 albertel 8611: if (grep(/^all$/,@{$sections})) {
1.412 raeburn 8612: $secmatch = 1;
8613: } elsif ($usec eq '') {
1.420 albertel 8614: if (grep(/^none$/,@{$sections})) {
1.412 raeburn 8615: $secmatch = 1;
8616: }
8617: } else {
8618: if (grep(/^\Q$usec\E$/,@{$sections})) {
8619: $secmatch = 1;
8620: }
8621: }
8622: if (!$secmatch) {
8623: next;
8624: }
1.288 raeburn 8625: }
1.419 raeburn 8626: if ($usec eq '') {
8627: $usec = 'none';
8628: }
1.275 raeburn 8629: if ($uname ne '' && $udom ne '') {
1.630 raeburn 8630: if ($hidepriv) {
1.1121 raeburn 8631: if ((&Apache::lonnet::privileged($uname,$udom,\@possdoms)) &&
1.630 raeburn 8632: (!$nothide{$uname.':'.$udom})) {
8633: next;
8634: }
8635: }
1.503 raeburn 8636: if ($end > 0 && $end < $now) {
1.439 raeburn 8637: $status = 'previous';
8638: } elsif ($start > $now) {
8639: $status = 'future';
8640: } else {
8641: $status = 'active';
8642: }
1.277 albertel 8643: foreach my $type (keys(%{$types})) {
1.275 raeburn 8644: if ($status eq $type) {
1.420 albertel 8645: if (!grep(/^\Q$type\E$/,@{$$users{$role}{$user}})) {
1.419 raeburn 8646: push(@{$$users{$role}{$user}},$type);
8647: }
1.288 raeburn 8648: $match = 1;
8649: }
8650: }
1.419 raeburn 8651: if (($match) && (ref($userdata) eq 'HASH')) {
8652: if (!exists($$userdata{$uname.':'.$udom})) {
8653: &get_user_info($udom,$uname,\%idx,$userdata);
8654: }
1.420 albertel 8655: if (!grep(/^\Q$usec\E$/,@{$seclists{$uname.':'.$udom}})) {
1.419 raeburn 8656: push(@{$seclists{$uname.':'.$udom}},$usec);
8657: }
1.609 raeburn 8658: if (ref($statushash) eq 'HASH') {
8659: $statushash->{$uname.':'.$udom}{$role}{$usec} = $displaystatus{$status};
8660: }
1.275 raeburn 8661: }
8662: }
8663: }
8664: }
1.290 albertel 8665: if (grep(/^ow$/,@{$roles})) {
1.279 raeburn 8666: if ((defined($cdom)) && (defined($cnum))) {
8667: my %csettings = &Apache::lonnet::get('environment',['internal.courseowner'],$cdom,$cnum);
8668: if ( defined($csettings{'internal.courseowner'}) ) {
8669: my $owner = $csettings{'internal.courseowner'};
1.609 raeburn 8670: next if ($owner eq '');
8671: my ($ownername,$ownerdom);
8672: if ($owner =~ /^([^:]+):([^:]+)$/) {
8673: $ownername = $1;
8674: $ownerdom = $2;
8675: } else {
8676: $ownername = $owner;
8677: $ownerdom = $cdom;
8678: $owner = $ownername.':'.$ownerdom;
1.439 raeburn 8679: }
8680: @{$$users{'ow'}{$owner}} = 'any';
1.290 albertel 8681: if (defined($userdata) &&
1.609 raeburn 8682: !exists($$userdata{$owner})) {
8683: &get_user_info($ownerdom,$ownername,\%idx,$userdata);
8684: if (!grep(/^none$/,@{$seclists{$owner}})) {
8685: push(@{$seclists{$owner}},'none');
8686: }
8687: if (ref($statushash) eq 'HASH') {
8688: $statushash->{$owner}{'ow'}{'none'} = 'Any';
1.419 raeburn 8689: }
1.290 albertel 8690: }
1.279 raeburn 8691: }
8692: }
8693: }
1.419 raeburn 8694: foreach my $user (keys(%seclists)) {
8695: @{$seclists{$user}} = (sort {$a <=> $b} @{$seclists{$user}});
8696: $$userdata{$user}[$idx{section}] = join(',',@{$seclists{$user}});
8697: }
1.275 raeburn 8698: }
8699: return;
8700: }
8701:
1.288 raeburn 8702: sub get_user_info {
8703: my ($udom,$uname,$idx,$userdata) = @_;
1.289 albertel 8704: $$userdata{$uname.':'.$udom}[$$idx{fullname}] =
8705: &plainname($uname,$udom,'lastname');
1.291 albertel 8706: $$userdata{$uname.':'.$udom}[$$idx{uname}] = $uname;
1.297 raeburn 8707: $$userdata{$uname.':'.$udom}[$$idx{udom}] = $udom;
1.609 raeburn 8708: my %idhash = &Apache::lonnet::idrget($udom,($uname));
8709: $$userdata{$uname.':'.$udom}[$$idx{id}] = $idhash{$uname};
1.288 raeburn 8710: return;
8711: }
1.275 raeburn 8712:
1.472 raeburn 8713: ###############################################
8714:
8715: =pod
8716:
8717: =item * &get_user_quota()
8718:
1.1134 raeburn 8719: Retrieves quota assigned for storage of user files.
8720: Default is to report quota for portfolio files.
1.472 raeburn 8721:
8722: Incoming parameters:
8723: 1. user's username
8724: 2. user's domain
1.1134 raeburn 8725: 3. quota name - portfolio, author, or course
1.1136 raeburn 8726: (if no quota name provided, defaults to portfolio).
1.1165 raeburn 8727: 4. crstype - official, unofficial, textbook or community, if quota name is
1.1136 raeburn 8728: course
1.472 raeburn 8729:
8730: Returns:
1.1163 raeburn 8731: 1. Disk quota (in MB) assigned to student.
1.536 raeburn 8732: 2. (Optional) Type of setting: custom or default
8733: (individually assigned or default for user's
8734: institutional status).
8735: 3. (Optional) - User's institutional status (e.g., faculty, staff
8736: or student - types as defined in localenroll::inst_usertypes
8737: for user's domain, which determines default quota for user.
8738: 4. (Optional) - Default quota which would apply to the user.
1.472 raeburn 8739:
8740: If a value has been stored in the user's environment,
1.536 raeburn 8741: it will return that, otherwise it returns the maximal default
1.1134 raeburn 8742: defined for the user's institutional status(es) in the domain.
1.472 raeburn 8743:
8744: =cut
8745:
8746: ###############################################
8747:
8748:
8749: sub get_user_quota {
1.1136 raeburn 8750: my ($uname,$udom,$quotaname,$crstype) = @_;
1.536 raeburn 8751: my ($quota,$quotatype,$settingstatus,$defquota);
1.472 raeburn 8752: if (!defined($udom)) {
8753: $udom = $env{'user.domain'};
8754: }
8755: if (!defined($uname)) {
8756: $uname = $env{'user.name'};
8757: }
8758: if (($udom eq '' || $uname eq '') ||
8759: ($udom eq 'public') && ($uname eq 'public')) {
8760: $quota = 0;
1.536 raeburn 8761: $quotatype = 'default';
8762: $defquota = 0;
1.472 raeburn 8763: } else {
1.536 raeburn 8764: my $inststatus;
1.1134 raeburn 8765: if ($quotaname eq 'course') {
8766: if (($env{'course.'.$udom.'_'.$uname.'.num'} eq $uname) &&
8767: ($env{'course.'.$udom.'_'.$uname.'.domain'} eq $udom)) {
8768: $quota = $env{'course.'.$udom.'_'.$uname.'.internal.uploadquota'};
8769: } else {
8770: my %cenv = &Apache::lonnet::coursedescription("$udom/$uname");
8771: $quota = $cenv{'internal.uploadquota'};
8772: }
1.536 raeburn 8773: } else {
1.1134 raeburn 8774: if ($udom eq $env{'user.domain'} && $uname eq $env{'user.name'}) {
8775: if ($quotaname eq 'author') {
8776: $quota = $env{'environment.authorquota'};
8777: } else {
8778: $quota = $env{'environment.portfolioquota'};
8779: }
8780: $inststatus = $env{'environment.inststatus'};
8781: } else {
8782: my %userenv =
8783: &Apache::lonnet::get('environment',['portfolioquota',
8784: 'authorquota','inststatus'],$udom,$uname);
8785: my ($tmp) = keys(%userenv);
8786: if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
8787: if ($quotaname eq 'author') {
8788: $quota = $userenv{'authorquota'};
8789: } else {
8790: $quota = $userenv{'portfolioquota'};
8791: }
8792: $inststatus = $userenv{'inststatus'};
8793: } else {
8794: undef(%userenv);
8795: }
8796: }
8797: }
8798: if ($quota eq '' || wantarray) {
8799: if ($quotaname eq 'course') {
8800: my %domdefs = &Apache::lonnet::get_domain_defaults($udom);
1.1165 raeburn 8801: if (($crstype eq 'official') || ($crstype eq 'unofficial') ||
8802: ($crstype eq 'community') || ($crstype eq 'textbook')) {
1.1136 raeburn 8803: $defquota = $domdefs{$crstype.'quota'};
8804: }
8805: if ($defquota eq '') {
8806: $defquota = 500;
8807: }
1.1134 raeburn 8808: } else {
8809: ($defquota,$settingstatus) = &default_quota($udom,$inststatus,$quotaname);
8810: }
8811: if ($quota eq '') {
8812: $quota = $defquota;
8813: $quotatype = 'default';
8814: } else {
8815: $quotatype = 'custom';
8816: }
1.472 raeburn 8817: }
8818: }
1.536 raeburn 8819: if (wantarray) {
8820: return ($quota,$quotatype,$settingstatus,$defquota);
8821: } else {
8822: return $quota;
8823: }
1.472 raeburn 8824: }
8825:
8826: ###############################################
8827:
8828: =pod
8829:
8830: =item * &default_quota()
8831:
1.536 raeburn 8832: Retrieves default quota assigned for storage of user portfolio files,
8833: given an (optional) user's institutional status.
1.472 raeburn 8834:
8835: Incoming parameters:
1.1142 raeburn 8836:
1.472 raeburn 8837: 1. domain
1.536 raeburn 8838: 2. (Optional) institutional status(es). This is a : separated list of
8839: status types (e.g., faculty, staff, student etc.)
8840: which apply to the user for whom the default is being retrieved.
8841: If the institutional status string in undefined, the domain
1.1134 raeburn 8842: default quota will be returned.
8843: 3. quota name - portfolio, author, or course
8844: (if no quota name provided, defaults to portfolio).
1.472 raeburn 8845:
8846: Returns:
1.1142 raeburn 8847:
1.1163 raeburn 8848: 1. Default disk quota (in MB) for user portfolios in the domain.
1.536 raeburn 8849: 2. (Optional) institutional type which determined the value of the
8850: default quota.
1.472 raeburn 8851:
8852: If a value has been stored in the domain's configuration db,
8853: it will return that, otherwise it returns 20 (for backwards
8854: compatibility with domains which have not set up a configuration
1.1163 raeburn 8855: db file; the original statically defined portfolio quota was 20 MB).
1.472 raeburn 8856:
1.536 raeburn 8857: If the user's status includes multiple types (e.g., staff and student),
8858: the largest default quota which applies to the user determines the
8859: default quota returned.
8860:
1.472 raeburn 8861: =cut
8862:
8863: ###############################################
8864:
8865:
8866: sub default_quota {
1.1134 raeburn 8867: my ($udom,$inststatus,$quotaname) = @_;
1.536 raeburn 8868: my ($defquota,$settingstatus);
8869: my %quotahash = &Apache::lonnet::get_dom('configuration',
1.622 raeburn 8870: ['quotas'],$udom);
1.1134 raeburn 8871: my $key = 'defaultquota';
8872: if ($quotaname eq 'author') {
8873: $key = 'authorquota';
8874: }
1.622 raeburn 8875: if (ref($quotahash{'quotas'}) eq 'HASH') {
1.536 raeburn 8876: if ($inststatus ne '') {
1.765 raeburn 8877: my @statuses = map { &unescape($_); } split(/:/,$inststatus);
1.536 raeburn 8878: foreach my $item (@statuses) {
1.1134 raeburn 8879: if (ref($quotahash{'quotas'}{$key}) eq 'HASH') {
8880: if ($quotahash{'quotas'}{$key}{$item} ne '') {
1.711 raeburn 8881: if ($defquota eq '') {
1.1134 raeburn 8882: $defquota = $quotahash{'quotas'}{$key}{$item};
1.711 raeburn 8883: $settingstatus = $item;
1.1134 raeburn 8884: } elsif ($quotahash{'quotas'}{$key}{$item} > $defquota) {
8885: $defquota = $quotahash{'quotas'}{$key}{$item};
1.711 raeburn 8886: $settingstatus = $item;
8887: }
8888: }
1.1134 raeburn 8889: } elsif ($key eq 'defaultquota') {
1.711 raeburn 8890: if ($quotahash{'quotas'}{$item} ne '') {
8891: if ($defquota eq '') {
8892: $defquota = $quotahash{'quotas'}{$item};
8893: $settingstatus = $item;
8894: } elsif ($quotahash{'quotas'}{$item} > $defquota) {
8895: $defquota = $quotahash{'quotas'}{$item};
8896: $settingstatus = $item;
8897: }
1.536 raeburn 8898: }
8899: }
8900: }
8901: }
8902: if ($defquota eq '') {
1.1134 raeburn 8903: if (ref($quotahash{'quotas'}{$key}) eq 'HASH') {
8904: $defquota = $quotahash{'quotas'}{$key}{'default'};
8905: } elsif ($key eq 'defaultquota') {
1.711 raeburn 8906: $defquota = $quotahash{'quotas'}{'default'};
8907: }
1.536 raeburn 8908: $settingstatus = 'default';
1.1139 raeburn 8909: if ($defquota eq '') {
8910: if ($quotaname eq 'author') {
8911: $defquota = 500;
8912: }
8913: }
1.536 raeburn 8914: }
8915: } else {
8916: $settingstatus = 'default';
1.1134 raeburn 8917: if ($quotaname eq 'author') {
8918: $defquota = 500;
8919: } else {
8920: $defquota = 20;
8921: }
1.536 raeburn 8922: }
8923: if (wantarray) {
8924: return ($defquota,$settingstatus);
1.472 raeburn 8925: } else {
1.536 raeburn 8926: return $defquota;
1.472 raeburn 8927: }
8928: }
8929:
1.1135 raeburn 8930: ###############################################
8931:
8932: =pod
8933:
1.1136 raeburn 8934: =item * &excess_filesize_warning()
1.1135 raeburn 8935:
8936: Returns warning message if upload of file to authoring space, or copying
1.1136 raeburn 8937: of existing file within authoring space will cause quota for the authoring
1.1146 raeburn 8938: space to be exceeded.
1.1136 raeburn 8939:
8940: Same, if upload of a file directly to a course/community via Course Editor
1.1137 raeburn 8941: will cause quota for uploaded content for the course to be exceeded.
1.1135 raeburn 8942:
1.1165 raeburn 8943: Inputs: 7
1.1136 raeburn 8944: 1. username or coursenum
1.1135 raeburn 8945: 2. domain
1.1136 raeburn 8946: 3. context ('author' or 'course')
1.1135 raeburn 8947: 4. filename of file for which action is being requested
8948: 5. filesize (kB) of file
8949: 6. action being taken: copy or upload.
1.1165 raeburn 8950: 7. quotatype (in course context -- official, unofficial, community or textbook).
1.1135 raeburn 8951:
8952: Returns: 1 scalar: HTML to display containing warning if quota would be exceeded,
1.1142 raeburn 8953: otherwise return null.
8954:
8955: =back
1.1135 raeburn 8956:
8957: =cut
8958:
1.1136 raeburn 8959: sub excess_filesize_warning {
1.1165 raeburn 8960: my ($uname,$udom,$context,$filename,$filesize,$action,$quotatype) = @_;
1.1136 raeburn 8961: my $current_disk_usage = 0;
1.1165 raeburn 8962: my $disk_quota = &get_user_quota($uname,$udom,$context,$quotatype); #expressed in MB
1.1136 raeburn 8963: if ($context eq 'author') {
8964: my $authorspace = $Apache::lonnet::perlvar{'lonDocRoot'}."/priv/$udom/$uname";
8965: $current_disk_usage = &Apache::lonnet::diskusage($udom,$uname,$authorspace);
8966: } else {
8967: foreach my $subdir ('docs','supplemental') {
8968: $current_disk_usage += &Apache::lonnet::diskusage($udom,$uname,"userfiles/$subdir",1);
8969: }
8970: }
1.1135 raeburn 8971: $disk_quota = int($disk_quota * 1000);
8972: if (($current_disk_usage + $filesize) > $disk_quota) {
8973: return '<p><span class="LC_warning">'.
8974: &mt("Unable to $action [_1]. (size = [_2] kilobytes). Disk quota will be exceeded.",
8975: '<span class="LC_filename">'.$filename.'</span>',$filesize).'</span>'.
8976: '<br />'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',
8977: $disk_quota,$current_disk_usage).
8978: '</p>';
8979: }
8980: return;
8981: }
8982:
8983: ###############################################
8984:
8985:
1.1136 raeburn 8986:
8987:
1.384 raeburn 8988: sub get_secgrprole_info {
8989: my ($cdom,$cnum,$needroles,$type) = @_;
8990: my %sections_count = &get_sections($cdom,$cnum);
8991: my @sections = (sort {$a <=> $b} keys(%sections_count));
8992: my %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum);
8993: my @groups = sort(keys(%curr_groups));
8994: my $allroles = [];
8995: my $rolehash;
8996: my $accesshash = {
8997: active => 'Currently has access',
8998: future => 'Will have future access',
8999: previous => 'Previously had access',
9000: };
9001: if ($needroles) {
9002: $rolehash = {'all' => 'all'};
1.385 albertel 9003: my %user_roles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
9004: if (&Apache::lonnet::error(%user_roles)) {
9005: undef(%user_roles);
9006: }
9007: foreach my $item (keys(%user_roles)) {
1.384 raeburn 9008: my ($role)=split(/\:/,$item,2);
9009: if ($role eq 'cr') { next; }
9010: if ($role =~ /^cr/) {
9011: $$rolehash{$role} = (split('/',$role))[3];
9012: } else {
9013: $$rolehash{$role} = &Apache::lonnet::plaintext($role,$type);
9014: }
9015: }
9016: foreach my $key (sort(keys(%{$rolehash}))) {
9017: push(@{$allroles},$key);
9018: }
9019: push (@{$allroles},'st');
9020: $$rolehash{'st'} = &Apache::lonnet::plaintext('st',$type);
9021: }
9022: return (\@sections,\@groups,$allroles,$rolehash,$accesshash);
9023: }
9024:
1.555 raeburn 9025: sub user_picker {
1.994 raeburn 9026: my ($dom,$srch,$forcenewuser,$caller,$cancreate,$usertype,$context) = @_;
1.555 raeburn 9027: my $currdom = $dom;
9028: my %curr_selected = (
9029: srchin => 'dom',
1.580 raeburn 9030: srchby => 'lastname',
1.555 raeburn 9031: );
9032: my $srchterm;
1.625 raeburn 9033: if ((ref($srch) eq 'HASH') && ($env{'form.origform'} ne 'crtusername')) {
1.555 raeburn 9034: if ($srch->{'srchby'} ne '') {
9035: $curr_selected{'srchby'} = $srch->{'srchby'};
9036: }
9037: if ($srch->{'srchin'} ne '') {
9038: $curr_selected{'srchin'} = $srch->{'srchin'};
9039: }
9040: if ($srch->{'srchtype'} ne '') {
9041: $curr_selected{'srchtype'} = $srch->{'srchtype'};
9042: }
9043: if ($srch->{'srchdomain'} ne '') {
9044: $currdom = $srch->{'srchdomain'};
9045: }
9046: $srchterm = $srch->{'srchterm'};
9047: }
9048: my %lt=&Apache::lonlocal::texthash(
1.573 raeburn 9049: 'usr' => 'Search criteria',
1.563 raeburn 9050: 'doma' => 'Domain/institution to search',
1.558 albertel 9051: 'uname' => 'username',
9052: 'lastname' => 'last name',
1.555 raeburn 9053: 'lastfirst' => 'last name, first name',
1.558 albertel 9054: 'crs' => 'in this course',
1.576 raeburn 9055: 'dom' => 'in selected LON-CAPA domain',
1.558 albertel 9056: 'alc' => 'all LON-CAPA',
1.573 raeburn 9057: 'instd' => 'in institutional directory for selected domain',
1.558 albertel 9058: 'exact' => 'is',
9059: 'contains' => 'contains',
1.569 raeburn 9060: 'begins' => 'begins with',
1.571 raeburn 9061: 'youm' => "You must include some text to search for.",
9062: 'thte' => "The text you are searching for must contain at least two characters when using a 'begins' type search.",
9063: 'thet' => "The text you are searching for must contain at least three characters when using a 'contains' type search.",
9064: 'yomc' => "You must choose a domain when using an institutional directory search.",
9065: 'ymcd' => "You must choose a domain when using a domain search.",
9066: 'whus' => "When using searching by last,first you must include a comma as separator between last name and first name.",
9067: 'whse' => "When searching by last,first you must include at least one character in the first name.",
9068: 'thfo' => "The following need to be corrected before the search can be run:",
1.555 raeburn 9069: );
1.563 raeburn 9070: my $domform = &select_dom_form($currdom,'srchdomain',1,1);
9071: my $srchinsel = ' <select name="srchin">';
1.555 raeburn 9072:
9073: my @srchins = ('crs','dom','alc','instd');
9074:
9075: foreach my $option (@srchins) {
9076: # FIXME 'alc' option unavailable until
9077: # loncreateuser::print_user_query_page()
9078: # has been completed.
9079: next if ($option eq 'alc');
1.880 raeburn 9080: next if (($option eq 'crs') && ($env{'form.form'} eq 'requestcrs'));
1.555 raeburn 9081: next if ($option eq 'crs' && !$env{'request.course.id'});
1.563 raeburn 9082: if ($curr_selected{'srchin'} eq $option) {
9083: $srchinsel .= '
9084: <option value="'.$option.'" selected="selected">'.$lt{$option}.'</option>';
9085: } else {
9086: $srchinsel .= '
9087: <option value="'.$option.'">'.$lt{$option}.'</option>';
9088: }
1.555 raeburn 9089: }
1.563 raeburn 9090: $srchinsel .= "\n </select>\n";
1.555 raeburn 9091:
9092: my $srchbysel = ' <select name="srchby">';
1.580 raeburn 9093: foreach my $option ('lastname','lastfirst','uname') {
1.555 raeburn 9094: if ($curr_selected{'srchby'} eq $option) {
9095: $srchbysel .= '
9096: <option value="'.$option.'" selected="selected">'.$lt{$option}.'</option>';
9097: } else {
9098: $srchbysel .= '
9099: <option value="'.$option.'">'.$lt{$option}.'</option>';
9100: }
9101: }
9102: $srchbysel .= "\n </select>\n";
9103:
9104: my $srchtypesel = ' <select name="srchtype">';
1.580 raeburn 9105: foreach my $option ('begins','contains','exact') {
1.555 raeburn 9106: if ($curr_selected{'srchtype'} eq $option) {
9107: $srchtypesel .= '
9108: <option value="'.$option.'" selected="selected">'.$lt{$option}.'</option>';
9109: } else {
9110: $srchtypesel .= '
9111: <option value="'.$option.'">'.$lt{$option}.'</option>';
9112: }
9113: }
9114: $srchtypesel .= "\n </select>\n";
9115:
1.558 albertel 9116: my ($newuserscript,$new_user_create);
1.994 raeburn 9117: my $context_dom = $env{'request.role.domain'};
9118: if ($context eq 'requestcrs') {
9119: if ($env{'form.coursedom'} ne '') {
9120: $context_dom = $env{'form.coursedom'};
9121: }
9122: }
1.556 raeburn 9123: if ($forcenewuser) {
1.576 raeburn 9124: if (ref($srch) eq 'HASH') {
1.994 raeburn 9125: if ($srch->{'srchby'} eq 'uname' && $srch->{'srchtype'} eq 'exact' && $srch->{'srchin'} eq 'dom' && $srch->{'srchdomain'} eq $context_dom) {
1.627 raeburn 9126: if ($cancreate) {
9127: $new_user_create = '<p> <input type="submit" name="forcenew" value="'.&HTML::Entities::encode(&mt('Make new user "[_1]"',$srchterm),'<>&"').'" onclick="javascript:setSearch(\'1\','.$caller.');" /> </p>';
9128: } else {
1.799 bisitz 9129: my $helplink = 'javascript:helpMenu('."'display'".')';
1.627 raeburn 9130: my %usertypetext = (
9131: official => 'institutional',
9132: unofficial => 'non-institutional',
9133: );
1.799 bisitz 9134: $new_user_create = '<p class="LC_warning">'
9135: .&mt("You are not authorized to create new $usertypetext{$usertype} users in this domain.")
9136: .' '
9137: .&mt('Please contact the [_1]helpdesk[_2] for assistance.'
9138: ,'<a href="'.$helplink.'">','</a>')
9139: .'</p><br />';
1.627 raeburn 9140: }
1.576 raeburn 9141: }
9142: }
9143:
1.556 raeburn 9144: $newuserscript = <<"ENDSCRIPT";
9145:
1.570 raeburn 9146: function setSearch(createnew,callingForm) {
1.556 raeburn 9147: if (createnew == 1) {
1.570 raeburn 9148: for (var i=0; i<callingForm.srchby.length; i++) {
9149: if (callingForm.srchby.options[i].value == 'uname') {
9150: callingForm.srchby.selectedIndex = i;
1.556 raeburn 9151: }
9152: }
1.570 raeburn 9153: for (var i=0; i<callingForm.srchin.length; i++) {
9154: if ( callingForm.srchin.options[i].value == 'dom') {
9155: callingForm.srchin.selectedIndex = i;
1.556 raeburn 9156: }
9157: }
1.570 raeburn 9158: for (var i=0; i<callingForm.srchtype.length; i++) {
9159: if (callingForm.srchtype.options[i].value == 'exact') {
9160: callingForm.srchtype.selectedIndex = i;
1.556 raeburn 9161: }
9162: }
1.570 raeburn 9163: for (var i=0; i<callingForm.srchdomain.length; i++) {
1.994 raeburn 9164: if (callingForm.srchdomain.options[i].value == '$context_dom') {
1.570 raeburn 9165: callingForm.srchdomain.selectedIndex = i;
1.556 raeburn 9166: }
9167: }
9168: }
9169: }
9170: ENDSCRIPT
1.558 albertel 9171:
1.556 raeburn 9172: }
9173:
1.555 raeburn 9174: my $output = <<"END_BLOCK";
1.556 raeburn 9175: <script type="text/javascript">
1.824 bisitz 9176: // <![CDATA[
1.570 raeburn 9177: function validateEntry(callingForm) {
1.558 albertel 9178:
1.556 raeburn 9179: var checkok = 1;
1.558 albertel 9180: var srchin;
1.570 raeburn 9181: for (var i=0; i<callingForm.srchin.length; i++) {
9182: if ( callingForm.srchin[i].checked ) {
9183: srchin = callingForm.srchin[i].value;
1.558 albertel 9184: }
9185: }
9186:
1.570 raeburn 9187: var srchtype = callingForm.srchtype.options[callingForm.srchtype.selectedIndex].value;
9188: var srchby = callingForm.srchby.options[callingForm.srchby.selectedIndex].value;
9189: var srchdomain = callingForm.srchdomain.options[callingForm.srchdomain.selectedIndex].value;
9190: var srchterm = callingForm.srchterm.value;
9191: var srchin = callingForm.srchin.options[callingForm.srchin.selectedIndex].value;
1.556 raeburn 9192: var msg = "";
9193:
9194: if (srchterm == "") {
9195: checkok = 0;
1.571 raeburn 9196: msg += "$lt{'youm'}\\n";
1.556 raeburn 9197: }
9198:
1.569 raeburn 9199: if (srchtype== 'begins') {
9200: if (srchterm.length < 2) {
9201: checkok = 0;
1.571 raeburn 9202: msg += "$lt{'thte'}\\n";
1.569 raeburn 9203: }
9204: }
9205:
1.556 raeburn 9206: if (srchtype== 'contains') {
9207: if (srchterm.length < 3) {
9208: checkok = 0;
1.571 raeburn 9209: msg += "$lt{'thet'}\\n";
1.556 raeburn 9210: }
9211: }
9212: if (srchin == 'instd') {
9213: if (srchdomain == '') {
9214: checkok = 0;
1.571 raeburn 9215: msg += "$lt{'yomc'}\\n";
1.556 raeburn 9216: }
9217: }
9218: if (srchin == 'dom') {
9219: if (srchdomain == '') {
9220: checkok = 0;
1.571 raeburn 9221: msg += "$lt{'ymcd'}\\n";
1.556 raeburn 9222: }
9223: }
9224: if (srchby == 'lastfirst') {
9225: if (srchterm.indexOf(",") == -1) {
9226: checkok = 0;
1.571 raeburn 9227: msg += "$lt{'whus'}\\n";
1.556 raeburn 9228: }
9229: if (srchterm.indexOf(",") == srchterm.length -1) {
9230: checkok = 0;
1.571 raeburn 9231: msg += "$lt{'whse'}\\n";
1.556 raeburn 9232: }
9233: }
9234: if (checkok == 0) {
1.571 raeburn 9235: alert("$lt{'thfo'}\\n"+msg);
1.556 raeburn 9236: return;
9237: }
9238: if (checkok == 1) {
1.570 raeburn 9239: callingForm.submit();
1.556 raeburn 9240: }
9241: }
9242:
9243: $newuserscript
9244:
1.824 bisitz 9245: // ]]>
1.556 raeburn 9246: </script>
1.558 albertel 9247:
9248: $new_user_create
9249:
1.555 raeburn 9250: END_BLOCK
1.558 albertel 9251:
1.876 raeburn 9252: $output .= &Apache::lonhtmlcommon::start_pick_box().
9253: &Apache::lonhtmlcommon::row_title($lt{'doma'}).
9254: $domform.
9255: &Apache::lonhtmlcommon::row_closure().
9256: &Apache::lonhtmlcommon::row_title($lt{'usr'}).
9257: $srchbysel.
9258: $srchtypesel.
9259: '<input type="text" size="15" name="srchterm" value="'.$srchterm.'" />'.
9260: $srchinsel.
9261: &Apache::lonhtmlcommon::row_closure(1).
9262: &Apache::lonhtmlcommon::end_pick_box().
9263: '<br />';
1.555 raeburn 9264: return $output;
9265: }
9266:
1.612 raeburn 9267: sub user_rule_check {
1.615 raeburn 9268: my ($usershash,$checks,$alerts,$rulematch,$inst_results,$curr_rules,$got_rules) = @_;
1.612 raeburn 9269: my $response;
9270: if (ref($usershash) eq 'HASH') {
9271: foreach my $user (keys(%{$usershash})) {
9272: my ($uname,$udom) = split(/:/,$user);
9273: next if ($udom eq '' || $uname eq '');
1.615 raeburn 9274: my ($id,$newuser);
1.612 raeburn 9275: if (ref($usershash->{$user}) eq 'HASH') {
1.615 raeburn 9276: $newuser = $usershash->{$user}->{'newuser'};
1.612 raeburn 9277: $id = $usershash->{$user}->{'id'};
9278: }
9279: my $inst_response;
9280: if (ref($checks) eq 'HASH') {
9281: if (defined($checks->{'username'})) {
1.615 raeburn 9282: ($inst_response,%{$inst_results->{$user}}) =
1.612 raeburn 9283: &Apache::lonnet::get_instuser($udom,$uname);
9284: } elsif (defined($checks->{'id'})) {
1.615 raeburn 9285: ($inst_response,%{$inst_results->{$user}}) =
1.612 raeburn 9286: &Apache::lonnet::get_instuser($udom,undef,$id);
9287: }
1.615 raeburn 9288: } else {
9289: ($inst_response,%{$inst_results->{$user}}) =
9290: &Apache::lonnet::get_instuser($udom,$uname);
9291: return;
1.612 raeburn 9292: }
1.615 raeburn 9293: if (!$got_rules->{$udom}) {
1.612 raeburn 9294: my %domconfig = &Apache::lonnet::get_dom('configuration',
9295: ['usercreation'],$udom);
9296: if (ref($domconfig{'usercreation'}) eq 'HASH') {
1.615 raeburn 9297: foreach my $item ('username','id') {
1.612 raeburn 9298: if (ref($domconfig{'usercreation'}{$item.'_rule'}) eq 'ARRAY') {
9299: $$curr_rules{$udom}{$item} =
9300: $domconfig{'usercreation'}{$item.'_rule'};
1.585 raeburn 9301: }
9302: }
9303: }
1.615 raeburn 9304: $got_rules->{$udom} = 1;
1.585 raeburn 9305: }
1.612 raeburn 9306: foreach my $item (keys(%{$checks})) {
9307: if (ref($$curr_rules{$udom}) eq 'HASH') {
9308: if (ref($$curr_rules{$udom}{$item}) eq 'ARRAY') {
9309: if (@{$$curr_rules{$udom}{$item}} > 0) {
9310: my %rule_check = &Apache::lonnet::inst_rulecheck($udom,$uname,$id,$item,$$curr_rules{$udom}{$item});
9311: foreach my $rule (@{$$curr_rules{$udom}{$item}}) {
9312: if ($rule_check{$rule}) {
9313: $$rulematch{$user}{$item} = $rule;
9314: if ($inst_response eq 'ok') {
1.615 raeburn 9315: if (ref($inst_results) eq 'HASH') {
9316: if (ref($inst_results->{$user}) eq 'HASH') {
9317: if (keys(%{$inst_results->{$user}}) == 0) {
9318: $$alerts{$item}{$udom}{$uname} = 1;
9319: }
1.612 raeburn 9320: }
9321: }
1.615 raeburn 9322: }
9323: last;
1.585 raeburn 9324: }
9325: }
9326: }
9327: }
9328: }
9329: }
9330: }
9331: }
1.612 raeburn 9332: return;
9333: }
9334:
9335: sub user_rule_formats {
9336: my ($domain,$domdesc,$curr_rules,$check) = @_;
9337: my %text = (
9338: 'username' => 'Usernames',
9339: 'id' => 'IDs',
9340: );
9341: my $output;
9342: my ($rules,$ruleorder) = &Apache::lonnet::inst_userrules($domain,$check);
9343: if ((ref($rules) eq 'HASH') && (ref($ruleorder) eq 'ARRAY')) {
9344: if (@{$ruleorder} > 0) {
1.1102 raeburn 9345: $output = '<br />'.
9346: &mt($text{$check}.' with the following format(s) may [_1]only[_2] be used for verified users at [_3]:',
9347: '<span class="LC_cusr_emph">','</span>',$domdesc).
9348: ' <ul>';
1.612 raeburn 9349: foreach my $rule (@{$ruleorder}) {
9350: if (ref($curr_rules) eq 'ARRAY') {
9351: if (grep(/^\Q$rule\E$/,@{$curr_rules})) {
9352: if (ref($rules->{$rule}) eq 'HASH') {
9353: $output .= '<li>'.$rules->{$rule}{'name'}.': '.
9354: $rules->{$rule}{'desc'}.'</li>';
9355: }
9356: }
9357: }
9358: }
9359: $output .= '</ul>';
9360: }
9361: }
9362: return $output;
9363: }
9364:
9365: sub instrule_disallow_msg {
1.615 raeburn 9366: my ($checkitem,$domdesc,$count,$mode) = @_;
1.612 raeburn 9367: my $response;
9368: my %text = (
9369: item => 'username',
9370: items => 'usernames',
9371: match => 'matches',
9372: do => 'does',
9373: action => 'a username',
9374: one => 'one',
9375: );
9376: if ($count > 1) {
9377: $text{'item'} = 'usernames';
9378: $text{'match'} ='match';
9379: $text{'do'} = 'do';
9380: $text{'action'} = 'usernames',
9381: $text{'one'} = 'ones';
9382: }
9383: if ($checkitem eq 'id') {
9384: $text{'items'} = 'IDs';
9385: $text{'item'} = 'ID';
9386: $text{'action'} = 'an ID';
1.615 raeburn 9387: if ($count > 1) {
9388: $text{'item'} = 'IDs';
9389: $text{'action'} = 'IDs';
9390: }
1.612 raeburn 9391: }
1.674 bisitz 9392: $response = &mt("The $text{'item'} you chose $text{'match'} the format of $text{'items'} defined for [_1], but the $text{'item'} $text{'do'} not exist in the institutional directory.",'<span class="LC_cusr_emph">'.$domdesc.'</span>').'<br />';
1.615 raeburn 9393: if ($mode eq 'upload') {
9394: if ($checkitem eq 'username') {
9395: $response .= &mt("You will need to modify your upload file so it will include $text{'action'} with a different format -- $text{'one'} that will not conflict with 'official' institutional $text{'items'}.");
9396: } elsif ($checkitem eq 'id') {
1.674 bisitz 9397: $response .= &mt("Either upload a file which includes $text{'action'} with a different format -- $text{'one'} that will not conflict with 'official' institutional $text{'items'}, or when associating fields with data columns, omit an association for the Student/Employee ID field.");
1.615 raeburn 9398: }
1.669 raeburn 9399: } elsif ($mode eq 'selfcreate') {
9400: if ($checkitem eq 'id') {
9401: $response .= &mt("You must either choose $text{'action'} with a different format -- $text{'one'} that will not conflict with 'official' institutional $text{'items'}, or leave the ID field blank.");
9402: }
1.615 raeburn 9403: } else {
9404: if ($checkitem eq 'username') {
9405: $response .= &mt("You must choose $text{'action'} with a different format -- $text{'one'} that will not conflict with 'official' institutional $text{'items'}.");
9406: } elsif ($checkitem eq 'id') {
9407: $response .= &mt("You must either choose $text{'action'} with a different format -- $text{'one'} that will not conflict with 'official' institutional $text{'items'}, or leave the ID field blank.");
9408: }
1.612 raeburn 9409: }
9410: return $response;
1.585 raeburn 9411: }
9412:
1.624 raeburn 9413: sub personal_data_fieldtitles {
9414: my %fieldtitles = &Apache::lonlocal::texthash (
9415: id => 'Student/Employee ID',
9416: permanentemail => 'E-mail address',
9417: lastname => 'Last Name',
9418: firstname => 'First Name',
9419: middlename => 'Middle Name',
9420: generation => 'Generation',
9421: gen => 'Generation',
1.765 raeburn 9422: inststatus => 'Affiliation',
1.624 raeburn 9423: );
9424: return %fieldtitles;
9425: }
9426:
1.642 raeburn 9427: sub sorted_inst_types {
9428: my ($dom) = @_;
9429: my ($usertypes,$order) = &Apache::lonnet::retrieve_inst_usertypes($dom);
9430: my $othertitle = &mt('All users');
9431: if ($env{'request.course.id'}) {
1.668 raeburn 9432: $othertitle = &mt('Any users');
1.642 raeburn 9433: }
9434: my @types;
9435: if (ref($order) eq 'ARRAY') {
9436: @types = @{$order};
9437: }
9438: if (@types == 0) {
9439: if (ref($usertypes) eq 'HASH') {
9440: @types = sort(keys(%{$usertypes}));
9441: }
9442: }
9443: if (keys(%{$usertypes}) > 0) {
9444: $othertitle = &mt('Other users');
9445: }
9446: return ($othertitle,$usertypes,\@types);
9447: }
9448:
1.645 raeburn 9449: sub get_institutional_codes {
9450: my ($settings,$allcourses,$LC_code) = @_;
9451: # Get complete list of course sections to update
9452: my @currsections = ();
9453: my @currxlists = ();
9454: my $coursecode = $$settings{'internal.coursecode'};
9455:
9456: if ($$settings{'internal.sectionnums'} ne '') {
9457: @currsections = split(/,/,$$settings{'internal.sectionnums'});
9458: }
9459:
9460: if ($$settings{'internal.crosslistings'} ne '') {
9461: @currxlists = split(/,/,$$settings{'internal.crosslistings'});
9462: }
9463:
9464: if (@currxlists > 0) {
9465: foreach (@currxlists) {
9466: if (m/^([^:]+):(\w*)$/) {
9467: unless (grep/^$1$/,@{$allcourses}) {
9468: push @{$allcourses},$1;
9469: $$LC_code{$1} = $2;
9470: }
9471: }
9472: }
9473: }
9474:
9475: if (@currsections > 0) {
9476: foreach (@currsections) {
9477: if (m/^(\w+):(\w*)$/) {
9478: my $sec = $coursecode.$1;
9479: my $lc_sec = $2;
9480: unless (grep/^$sec$/,@{$allcourses}) {
9481: push @{$allcourses},$sec;
9482: $$LC_code{$sec} = $lc_sec;
9483: }
9484: }
9485: }
9486: }
9487: return;
9488: }
9489:
1.971 raeburn 9490: sub get_standard_codeitems {
9491: return ('Year','Semester','Department','Number','Section');
9492: }
9493:
1.112 bowersj2 9494: =pod
9495:
1.780 raeburn 9496: =head1 Slot Helpers
9497:
9498: =over 4
9499:
9500: =item * sorted_slots()
9501:
1.1040 raeburn 9502: Sorts an array of slot names in order of an optional sort key,
9503: default sort is by slot start time (earliest first).
1.780 raeburn 9504:
9505: Inputs:
9506:
9507: =over 4
9508:
9509: slotsarr - Reference to array of unsorted slot names.
9510:
9511: slots - Reference to hash of hash, where outer hash keys are slot names.
9512:
1.1040 raeburn 9513: sortkey - Name of key in inner hash to be sorted on (e.g., starttime).
9514:
1.549 albertel 9515: =back
9516:
1.780 raeburn 9517: Returns:
9518:
9519: =over 4
9520:
1.1040 raeburn 9521: sorted - An array of slot names sorted by a specified sort key
9522: (default sort key is start time of the slot).
1.780 raeburn 9523:
9524: =back
9525:
9526: =cut
9527:
9528:
9529: sub sorted_slots {
1.1040 raeburn 9530: my ($slotsarr,$slots,$sortkey) = @_;
9531: if ($sortkey eq '') {
9532: $sortkey = 'starttime';
9533: }
1.780 raeburn 9534: my @sorted;
9535: if ((ref($slotsarr) eq 'ARRAY') && (ref($slots) eq 'HASH')) {
9536: @sorted =
9537: sort {
9538: if (ref($slots->{$a}) && ref($slots->{$b})) {
1.1040 raeburn 9539: return $slots->{$a}{$sortkey} <=> $slots->{$b}{$sortkey}
1.780 raeburn 9540: }
9541: if (ref($slots->{$a})) { return -1;}
9542: if (ref($slots->{$b})) { return 1;}
9543: return 0;
9544: } @{$slotsarr};
9545: }
9546: return @sorted;
9547: }
9548:
1.1040 raeburn 9549: =pod
9550:
9551: =item * get_future_slots()
9552:
9553: Inputs:
9554:
9555: =over 4
9556:
9557: cnum - course number
9558:
9559: cdom - course domain
9560:
9561: now - current UNIX time
9562:
9563: symb - optional symb
9564:
9565: =back
9566:
9567: Returns:
9568:
9569: =over 4
9570:
9571: sorted_reservable - ref to array of student_schedulable slots currently
9572: reservable, ordered by end date of reservation period.
9573:
9574: reservable_now - ref to hash of student_schedulable slots currently
9575: reservable.
9576:
9577: Keys in inner hash are:
9578: (a) symb: either blank or symb to which slot use is restricted.
9579: (b) endreserve: end date of reservation period.
9580:
9581: sorted_future - ref to array of student_schedulable slots reservable in
9582: the future, ordered by start date of reservation period.
9583:
9584: future_reservable - ref to hash of student_schedulable slots reservable
9585: in the future.
9586:
9587: Keys in inner hash are:
9588: (a) symb: either blank or symb to which slot use is restricted.
9589: (b) startreserve: start date of reservation period.
9590:
9591: =back
9592:
9593: =cut
9594:
9595: sub get_future_slots {
9596: my ($cnum,$cdom,$now,$symb) = @_;
9597: my (%reservable_now,%future_reservable,@sorted_reservable,@sorted_future);
9598: my %slots = &Apache::lonnet::get_course_slots($cnum,$cdom);
9599: foreach my $slot (keys(%slots)) {
9600: next unless($slots{$slot}->{'type'} eq 'schedulable_student');
9601: if ($symb) {
9602: next if (($slots{$slot}->{'symb'} ne '') &&
9603: ($slots{$slot}->{'symb'} ne $symb));
9604: }
9605: if (($slots{$slot}->{'starttime'} > $now) &&
9606: ($slots{$slot}->{'endtime'} > $now)) {
9607: if (($slots{$slot}->{'allowedsections'}) || ($slots{$slot}->{'allowedusers'})) {
9608: my $userallowed = 0;
9609: if ($slots{$slot}->{'allowedsections'}) {
9610: my @allowed_sec = split(',',$slots{$slot}->{'allowedsections'});
9611: if (!defined($env{'request.role.sec'})
9612: && grep(/^No section assigned$/,@allowed_sec)) {
9613: $userallowed=1;
9614: } else {
9615: if (grep(/^\Q$env{'request.role.sec'}\E$/,@allowed_sec)) {
9616: $userallowed=1;
9617: }
9618: }
9619: unless ($userallowed) {
9620: if (defined($env{'request.course.groups'})) {
9621: my @groups = split(/:/,$env{'request.course.groups'});
9622: foreach my $group (@groups) {
9623: if (grep(/^\Q$group\E$/,@allowed_sec)) {
9624: $userallowed=1;
9625: last;
9626: }
9627: }
9628: }
9629: }
9630: }
9631: if ($slots{$slot}->{'allowedusers'}) {
9632: my @allowed_users = split(',',$slots{$slot}->{'allowedusers'});
9633: my $user = $env{'user.name'}.':'.$env{'user.domain'};
9634: if (grep(/^\Q$user\E$/,@allowed_users)) {
9635: $userallowed = 1;
9636: }
9637: }
9638: next unless($userallowed);
9639: }
9640: my $startreserve = $slots{$slot}->{'startreserve'};
9641: my $endreserve = $slots{$slot}->{'endreserve'};
9642: my $symb = $slots{$slot}->{'symb'};
9643: if (($startreserve < $now) &&
9644: (!$endreserve || $endreserve > $now)) {
9645: my $lastres = $endreserve;
9646: if (!$lastres) {
9647: $lastres = $slots{$slot}->{'starttime'};
9648: }
9649: $reservable_now{$slot} = {
9650: symb => $symb,
9651: endreserve => $lastres
9652: };
9653: } elsif (($startreserve > $now) &&
9654: (!$endreserve || $endreserve > $startreserve)) {
9655: $future_reservable{$slot} = {
9656: symb => $symb,
9657: startreserve => $startreserve
9658: };
9659: }
9660: }
9661: }
9662: my @unsorted_reservable = keys(%reservable_now);
9663: if (@unsorted_reservable > 0) {
9664: @sorted_reservable =
9665: &sorted_slots(\@unsorted_reservable,\%reservable_now,'endreserve');
9666: }
9667: my @unsorted_future = keys(%future_reservable);
9668: if (@unsorted_future > 0) {
9669: @sorted_future =
9670: &sorted_slots(\@unsorted_future,\%future_reservable,'startreserve');
9671: }
9672: return (\@sorted_reservable,\%reservable_now,\@sorted_future,\%future_reservable);
9673: }
1.780 raeburn 9674:
9675: =pod
9676:
1.1057 foxr 9677: =back
9678:
1.549 albertel 9679: =head1 HTTP Helpers
9680:
9681: =over 4
9682:
1.648 raeburn 9683: =item * &get_unprocessed_cgi($query,$possible_names)
1.112 bowersj2 9684:
1.258 albertel 9685: Modify the %env hash to contain unprocessed CGI form parameters held in
1.112 bowersj2 9686: $query. The parameters listed in $possible_names (an array reference),
1.258 albertel 9687: will be set in $env{'form.name'} if they do not already exist.
1.112 bowersj2 9688:
9689: Typically called with $ENV{'QUERY_STRING'} as the first parameter.
9690: $possible_names is an ref to an array of form element names. As an example:
9691: get_unprocessed_cgi($ENV{'QUERY_STRING'},['uname','udom']);
1.258 albertel 9692: will result in $env{'form.uname'} and $env{'form.udom'} being set.
1.112 bowersj2 9693:
9694: =cut
1.1 albertel 9695:
1.6 albertel 9696: sub get_unprocessed_cgi {
1.25 albertel 9697: my ($query,$possible_names)= @_;
1.26 matthew 9698: # $Apache::lonxml::debug=1;
1.356 albertel 9699: foreach my $pair (split(/&/,$query)) {
9700: my ($name, $value) = split(/=/,$pair);
1.369 www 9701: $name = &unescape($name);
1.25 albertel 9702: if (!defined($possible_names) || (grep {$_ eq $name} @$possible_names)) {
9703: $value =~ tr/+/ /;
9704: $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
1.258 albertel 9705: unless (defined($env{'form.'.$name})) { &add_to_env('form.'.$name,$value) };
1.25 albertel 9706: }
1.16 harris41 9707: }
1.6 albertel 9708: }
9709:
1.112 bowersj2 9710: =pod
9711:
1.648 raeburn 9712: =item * &cacheheader()
1.112 bowersj2 9713:
9714: returns cache-controlling header code
9715:
9716: =cut
9717:
1.7 albertel 9718: sub cacheheader {
1.258 albertel 9719: unless ($env{'request.method'} eq 'GET') { return ''; }
1.216 albertel 9720: my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime);
9721: my $output .='<meta HTTP-EQUIV="Expires" CONTENT="'.$date.'" />
1.7 albertel 9722: <meta HTTP-EQUIV="Cache-control" CONTENT="no-cache" />
9723: <meta HTTP-EQUIV="Pragma" CONTENT="no-cache" />';
1.216 albertel 9724: return $output;
1.7 albertel 9725: }
9726:
1.112 bowersj2 9727: =pod
9728:
1.648 raeburn 9729: =item * &no_cache($r)
1.112 bowersj2 9730:
9731: specifies header code to not have cache
9732:
9733: =cut
9734:
1.9 albertel 9735: sub no_cache {
1.216 albertel 9736: my ($r) = @_;
9737: if ($ENV{'REQUEST_METHOD'} ne 'GET' &&
1.258 albertel 9738: $env{'request.method'} ne 'GET') { return ''; }
1.216 albertel 9739: my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime(time));
9740: $r->no_cache(1);
9741: $r->header_out("Expires" => $date);
9742: $r->header_out("Pragma" => "no-cache");
1.123 www 9743: }
9744:
9745: sub content_type {
1.181 albertel 9746: my ($r,$type,$charset) = @_;
1.299 foxr 9747: if ($r) {
9748: # Note that printout.pl calls this with undef for $r.
9749: &no_cache($r);
9750: }
1.258 albertel 9751: if ($env{'browser.mathml'} && $type eq 'text/html') { $type='text/xml'; }
1.181 albertel 9752: unless ($charset) {
9753: $charset=&Apache::lonlocal::current_encoding;
9754: }
9755: if ($charset) { $type.='; charset='.$charset; }
9756: if ($r) {
9757: $r->content_type($type);
9758: } else {
9759: print("Content-type: $type\n\n");
9760: }
1.9 albertel 9761: }
1.25 albertel 9762:
1.112 bowersj2 9763: =pod
9764:
1.648 raeburn 9765: =item * &add_to_env($name,$value)
1.112 bowersj2 9766:
1.258 albertel 9767: adds $name to the %env hash with value
1.112 bowersj2 9768: $value, if $name already exists, the entry is converted to an array
9769: reference and $value is added to the array.
9770:
9771: =cut
9772:
1.25 albertel 9773: sub add_to_env {
9774: my ($name,$value)=@_;
1.258 albertel 9775: if (defined($env{$name})) {
9776: if (ref($env{$name})) {
1.25 albertel 9777: #already have multiple values
1.258 albertel 9778: push(@{ $env{$name} },$value);
1.25 albertel 9779: } else {
9780: #first time seeing multiple values, convert hash entry to an arrayref
1.258 albertel 9781: my $first=$env{$name};
9782: undef($env{$name});
9783: push(@{ $env{$name} },$first,$value);
1.25 albertel 9784: }
9785: } else {
1.258 albertel 9786: $env{$name}=$value;
1.25 albertel 9787: }
1.31 albertel 9788: }
1.149 albertel 9789:
9790: =pod
9791:
1.648 raeburn 9792: =item * &get_env_multiple($name)
1.149 albertel 9793:
1.258 albertel 9794: gets $name from the %env hash, it seemlessly handles the cases where multiple
1.149 albertel 9795: values may be defined and end up as an array ref.
9796:
9797: returns an array of values
9798:
9799: =cut
9800:
9801: sub get_env_multiple {
9802: my ($name) = @_;
9803: my @values;
1.258 albertel 9804: if (defined($env{$name})) {
1.149 albertel 9805: # exists is it an array
1.258 albertel 9806: if (ref($env{$name})) {
9807: @values=@{ $env{$name} };
1.149 albertel 9808: } else {
1.258 albertel 9809: $values[0]=$env{$name};
1.149 albertel 9810: }
9811: }
9812: return(@values);
9813: }
9814:
1.660 raeburn 9815: sub ask_for_embedded_content {
9816: my ($actionurl,$state,$allfiles,$codebase,$args)=@_;
1.1071 raeburn 9817: my (%subdependencies,%dependencies,%mapping,%existing,%newfiles,%pathchanges,
1.1085 raeburn 9818: %currsubfile,%unused,$rem);
1.1071 raeburn 9819: my $counter = 0;
9820: my $numnew = 0;
1.987 raeburn 9821: my $numremref = 0;
9822: my $numinvalid = 0;
9823: my $numpathchg = 0;
9824: my $numexisting = 0;
1.1071 raeburn 9825: my $numunused = 0;
9826: my ($output,$upload_output,$toplevel,$url,$udom,$uname,$getpropath,$cdom,$cnum,
1.1156 raeburn 9827: $fileloc,$filename,$delete_output,$modify_output,$title,$symb,$path,$navmap);
1.1071 raeburn 9828: my $heading = &mt('Upload embedded files');
9829: my $buttontext = &mt('Upload');
9830:
1.1085 raeburn 9831: if ($env{'request.course.id'}) {
1.1123 raeburn 9832: if ($actionurl eq '/adm/dependencies') {
9833: $navmap = Apache::lonnavmaps::navmap->new();
9834: }
9835: $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
9836: $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
1.1085 raeburn 9837: }
1.1123 raeburn 9838: if (($actionurl eq '/adm/portfolio') ||
9839: ($actionurl eq '/adm/coursegrp_portfolio')) {
1.984 raeburn 9840: my $current_path='/';
9841: if ($env{'form.currentpath'}) {
9842: $current_path = $env{'form.currentpath'};
9843: }
9844: if ($actionurl eq '/adm/coursegrp_portfolio') {
1.1123 raeburn 9845: $udom = $cdom;
9846: $uname = $cnum;
1.984 raeburn 9847: $url = '/userfiles/groups/'.$env{'form.group'}.'/portfolio';
9848: } else {
9849: $udom = $env{'user.domain'};
9850: $uname = $env{'user.name'};
9851: $url = '/userfiles/portfolio';
9852: }
1.987 raeburn 9853: $toplevel = $url.'/';
1.984 raeburn 9854: $url .= $current_path;
9855: $getpropath = 1;
1.987 raeburn 9856: } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank') ||
9857: ($actionurl eq '/adm/imsimport')) {
1.1022 www 9858: my ($udom,$uname,$rest) = ($args->{'current_path'} =~ m{/priv/($match_domain)/($match_username)/?(.*)$});
1.1026 raeburn 9859: $url = $Apache::lonnet::perlvar{'lonDocRoot'}."/priv/$udom/$uname/";
1.987 raeburn 9860: $toplevel = $url;
1.984 raeburn 9861: if ($rest ne '') {
1.987 raeburn 9862: $url .= $rest;
9863: }
9864: } elsif ($actionurl eq '/adm/coursedocs') {
9865: if (ref($args) eq 'HASH') {
1.1071 raeburn 9866: $url = $args->{'docs_url'};
9867: $toplevel = $url;
1.1084 raeburn 9868: if ($args->{'context'} eq 'paste') {
9869: ($cdom,$cnum) = ($url =~ m{^\Q/uploaded/\E($match_domain)/($match_courseid)/});
9870: ($path) =
9871: ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/\E(?:docs|supplemental)/(?:default|\d+)/\d+)/});
9872: $fileloc = &Apache::lonnet::filelocation('',$toplevel);
9873: $fileloc =~ s{^/}{};
9874: }
1.1071 raeburn 9875: }
1.1084 raeburn 9876: } elsif ($actionurl eq '/adm/dependencies') {
1.1071 raeburn 9877: if ($env{'request.course.id'} ne '') {
9878: if (ref($args) eq 'HASH') {
9879: $url = $args->{'docs_url'};
9880: $title = $args->{'docs_title'};
1.1126 raeburn 9881: $toplevel = $url;
9882: unless ($toplevel =~ m{^/}) {
9883: $toplevel = "/$url";
9884: }
1.1085 raeburn 9885: ($rem) = ($toplevel =~ m{^(.+/)[^/]+$});
1.1126 raeburn 9886: if ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/portfolio/syllabus\E)}) {
9887: $path = $1;
9888: } else {
9889: ($path) =
9890: ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/\E(?:docs|supplemental)/(?:default|\d+)/\d+)/});
9891: }
1.1071 raeburn 9892: $fileloc = &Apache::lonnet::filelocation('',$toplevel);
9893: $fileloc =~ s{^/}{};
9894: ($filename) = ($fileloc =~ m{.+/([^/]+)$});
9895: $heading = &mt('Status of dependencies in [_1]',"$title ($filename)");
9896: }
1.987 raeburn 9897: }
1.1123 raeburn 9898: } elsif ($actionurl eq "/public/$cdom/$cnum/syllabus") {
9899: $udom = $cdom;
9900: $uname = $cnum;
9901: $url = "/uploaded/$cdom/$cnum/portfolio/syllabus";
9902: $toplevel = $url;
9903: $path = $url;
9904: $fileloc = &Apache::lonnet::filelocation('',$toplevel).'/';
9905: $fileloc =~ s{^/}{};
1.987 raeburn 9906: }
1.1126 raeburn 9907: foreach my $file (keys(%{$allfiles})) {
9908: my $embed_file;
9909: if (($path eq "/uploaded/$cdom/$cnum/portfolio/syllabus") && ($file =~ m{^\Q$path/\E(.+)$})) {
9910: $embed_file = $1;
9911: } else {
9912: $embed_file = $file;
9913: }
1.1158 raeburn 9914: my ($absolutepath,$cleaned_file);
9915: if ($embed_file =~ m{^\w+://}) {
9916: $cleaned_file = $embed_file;
1.1147 raeburn 9917: $newfiles{$cleaned_file} = 1;
9918: $mapping{$cleaned_file} = $embed_file;
1.987 raeburn 9919: } else {
1.1158 raeburn 9920: $cleaned_file = &clean_path($embed_file);
1.987 raeburn 9921: if ($embed_file =~ m{^/}) {
9922: $absolutepath = $embed_file;
9923: }
1.1147 raeburn 9924: if ($cleaned_file =~ m{/}) {
9925: my ($path,$fname) = ($cleaned_file =~ m{^(.+)/([^/]*)$});
1.987 raeburn 9926: $path = &check_for_traversal($path,$url,$toplevel);
9927: my $item = $fname;
9928: if ($path ne '') {
9929: $item = $path.'/'.$fname;
9930: $subdependencies{$path}{$fname} = 1;
9931: } else {
9932: $dependencies{$item} = 1;
9933: }
9934: if ($absolutepath) {
9935: $mapping{$item} = $absolutepath;
9936: } else {
9937: $mapping{$item} = $embed_file;
9938: }
9939: } else {
9940: $dependencies{$embed_file} = 1;
9941: if ($absolutepath) {
1.1147 raeburn 9942: $mapping{$cleaned_file} = $absolutepath;
1.987 raeburn 9943: } else {
1.1147 raeburn 9944: $mapping{$cleaned_file} = $embed_file;
1.987 raeburn 9945: }
9946: }
1.984 raeburn 9947: }
9948: }
1.1071 raeburn 9949: my $dirptr = 16384;
1.984 raeburn 9950: foreach my $path (keys(%subdependencies)) {
1.1071 raeburn 9951: $currsubfile{$path} = {};
1.1123 raeburn 9952: if (($actionurl eq '/adm/portfolio') ||
9953: ($actionurl eq '/adm/coursegrp_portfolio')) {
1.1021 raeburn 9954: my ($sublistref,$listerror) =
9955: &Apache::lonnet::dirlist($url.$path,$udom,$uname,$getpropath);
9956: if (ref($sublistref) eq 'ARRAY') {
9957: foreach my $line (@{$sublistref}) {
9958: my ($file_name,$rest) = split(/\&/,$line,2);
1.1071 raeburn 9959: $currsubfile{$path}{$file_name} = 1;
1.1021 raeburn 9960: }
1.984 raeburn 9961: }
1.987 raeburn 9962: } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
1.984 raeburn 9963: if (opendir(my $dir,$url.'/'.$path)) {
9964: my @subdir_list = grep(!/^\./,readdir($dir));
1.1071 raeburn 9965: map {$currsubfile{$path}{$_} = 1;} @subdir_list;
9966: }
1.1084 raeburn 9967: } elsif (($actionurl eq '/adm/dependencies') ||
9968: (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
1.1123 raeburn 9969: ($args->{'context'} eq 'paste')) ||
9970: ($actionurl eq "/public/$cdom/$cnum/syllabus")) {
1.1071 raeburn 9971: if ($env{'request.course.id'} ne '') {
1.1123 raeburn 9972: my $dir;
9973: if ($actionurl eq "/public/$cdom/$cnum/syllabus") {
9974: $dir = $fileloc;
9975: } else {
9976: ($dir) = ($fileloc =~ m{^(.+/)[^/]+$});
9977: }
1.1071 raeburn 9978: if ($dir ne '') {
9979: my ($sublistref,$listerror) =
9980: &Apache::lonnet::dirlist($dir.$path,$cdom,$cnum,$getpropath,undef,'/');
9981: if (ref($sublistref) eq 'ARRAY') {
9982: foreach my $line (@{$sublistref}) {
9983: my ($file_name,$dom,undef,$testdir,undef,undef,undef,undef,$size,
9984: undef,$mtime)=split(/\&/,$line,12);
9985: unless (($testdir&$dirptr) ||
9986: ($file_name =~ /^\.\.?$/)) {
9987: $currsubfile{$path}{$file_name} = [$size,$mtime];
9988: }
9989: }
9990: }
9991: }
1.984 raeburn 9992: }
9993: }
9994: foreach my $file (keys(%{$subdependencies{$path}})) {
1.1071 raeburn 9995: if (exists($currsubfile{$path}{$file})) {
1.987 raeburn 9996: my $item = $path.'/'.$file;
9997: unless ($mapping{$item} eq $item) {
9998: $pathchanges{$item} = 1;
9999: }
10000: $existing{$item} = 1;
10001: $numexisting ++;
10002: } else {
10003: $newfiles{$path.'/'.$file} = 1;
1.984 raeburn 10004: }
10005: }
1.1071 raeburn 10006: if ($actionurl eq '/adm/dependencies') {
10007: foreach my $path (keys(%currsubfile)) {
10008: if (ref($currsubfile{$path}) eq 'HASH') {
10009: foreach my $file (keys(%{$currsubfile{$path}})) {
10010: unless ($subdependencies{$path}{$file}) {
1.1085 raeburn 10011: next if (($rem ne '') &&
10012: (($env{"httpref.$rem"."$path/$file"} ne '') ||
10013: (ref($navmap) &&
10014: (($navmap->getResourceByUrl($rem."$path/$file") ne '') ||
10015: (($file =~ /^(.*\.s?html?)\.bak$/i) &&
10016: ($navmap->getResourceByUrl($rem."$path/$1")))))));
1.1071 raeburn 10017: $unused{$path.'/'.$file} = 1;
10018: }
10019: }
10020: }
10021: }
10022: }
1.984 raeburn 10023: }
1.987 raeburn 10024: my %currfile;
1.1123 raeburn 10025: if (($actionurl eq '/adm/portfolio') ||
10026: ($actionurl eq '/adm/coursegrp_portfolio')) {
1.1021 raeburn 10027: my ($dirlistref,$listerror) =
10028: &Apache::lonnet::dirlist($url,$udom,$uname,$getpropath);
10029: if (ref($dirlistref) eq 'ARRAY') {
10030: foreach my $line (@{$dirlistref}) {
10031: my ($file_name,$rest) = split(/\&/,$line,2);
10032: $currfile{$file_name} = 1;
10033: }
1.984 raeburn 10034: }
1.987 raeburn 10035: } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
1.984 raeburn 10036: if (opendir(my $dir,$url)) {
1.987 raeburn 10037: my @dir_list = grep(!/^\./,readdir($dir));
1.984 raeburn 10038: map {$currfile{$_} = 1;} @dir_list;
10039: }
1.1084 raeburn 10040: } elsif (($actionurl eq '/adm/dependencies') ||
10041: (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
1.1123 raeburn 10042: ($args->{'context'} eq 'paste')) ||
10043: ($actionurl eq "/public/$cdom/$cnum/syllabus")) {
1.1071 raeburn 10044: if ($env{'request.course.id'} ne '') {
10045: my ($dir) = ($fileloc =~ m{^(.+/)[^/]+$});
10046: if ($dir ne '') {
10047: my ($dirlistref,$listerror) =
10048: &Apache::lonnet::dirlist($dir,$cdom,$cnum,$getpropath,undef,'/');
10049: if (ref($dirlistref) eq 'ARRAY') {
10050: foreach my $line (@{$dirlistref}) {
10051: my ($file_name,$dom,undef,$testdir,undef,undef,undef,undef,
10052: $size,undef,$mtime)=split(/\&/,$line,12);
10053: unless (($testdir&$dirptr) ||
10054: ($file_name =~ /^\.\.?$/)) {
10055: $currfile{$file_name} = [$size,$mtime];
10056: }
10057: }
10058: }
10059: }
10060: }
1.984 raeburn 10061: }
10062: foreach my $file (keys(%dependencies)) {
1.1071 raeburn 10063: if (exists($currfile{$file})) {
1.987 raeburn 10064: unless ($mapping{$file} eq $file) {
10065: $pathchanges{$file} = 1;
10066: }
10067: $existing{$file} = 1;
10068: $numexisting ++;
10069: } else {
1.984 raeburn 10070: $newfiles{$file} = 1;
10071: }
10072: }
1.1071 raeburn 10073: foreach my $file (keys(%currfile)) {
10074: unless (($file eq $filename) ||
10075: ($file eq $filename.'.bak') ||
10076: ($dependencies{$file})) {
1.1085 raeburn 10077: if ($actionurl eq '/adm/dependencies') {
1.1126 raeburn 10078: unless ($toplevel =~ m{^\Q/uploaded/$cdom/$cnum/portfolio/syllabus\E}) {
10079: next if (($rem ne '') &&
10080: (($env{"httpref.$rem".$file} ne '') ||
10081: (ref($navmap) &&
10082: (($navmap->getResourceByUrl($rem.$file) ne '') ||
10083: (($file =~ /^(.*\.s?html?)\.bak$/i) &&
10084: ($navmap->getResourceByUrl($rem.$1)))))));
10085: }
1.1085 raeburn 10086: }
1.1071 raeburn 10087: $unused{$file} = 1;
10088: }
10089: }
1.1084 raeburn 10090: if (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
10091: ($args->{'context'} eq 'paste')) {
10092: $counter = scalar(keys(%existing));
10093: $numpathchg = scalar(keys(%pathchanges));
1.1123 raeburn 10094: return ($output,$counter,$numpathchg,\%existing);
10095: } elsif (($actionurl eq "/public/$cdom/$cnum/syllabus") &&
10096: (ref($args) eq 'HASH') && ($args->{'context'} eq 'rewrites')) {
10097: $counter = scalar(keys(%existing));
10098: $numpathchg = scalar(keys(%pathchanges));
10099: return ($output,$counter,$numpathchg,\%existing,\%mapping);
1.1084 raeburn 10100: }
1.984 raeburn 10101: foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%newfiles)) {
1.1071 raeburn 10102: if ($actionurl eq '/adm/dependencies') {
10103: next if ($embed_file =~ m{^\w+://});
10104: }
1.660 raeburn 10105: $upload_output .= &start_data_table_row().
1.1123 raeburn 10106: '<td valign="top"><img src="'.&icon($embed_file).'" /> '.
1.1071 raeburn 10107: '<span class="LC_filename">'.$embed_file.'</span>';
1.987 raeburn 10108: unless ($mapping{$embed_file} eq $embed_file) {
1.1123 raeburn 10109: $upload_output .= '<br /><span class="LC_info" style="font-size:smaller;">'.
10110: &mt('changed from: [_1]',$mapping{$embed_file}).'</span>';
1.987 raeburn 10111: }
1.1123 raeburn 10112: $upload_output .= '</td>';
1.1071 raeburn 10113: if ($args->{'ignore_remote_references'} && $embed_file =~ m{^\w+://}) {
1.1123 raeburn 10114: $upload_output.='<td align="right">'.
10115: '<span class="LC_info LC_fontsize_medium">'.
10116: &mt("URL points to web address").'</span>';
1.987 raeburn 10117: $numremref++;
1.660 raeburn 10118: } elsif ($args->{'error_on_invalid_names'}
10119: && $embed_file ne &Apache::lonnet::clean_filename($embed_file,{'keep_path' => 1,})) {
1.1123 raeburn 10120: $upload_output.='<td align="right"><span class="LC_warning">'.
10121: &mt('Invalid characters').'</span>';
1.987 raeburn 10122: $numinvalid++;
1.660 raeburn 10123: } else {
1.1123 raeburn 10124: $upload_output .= '<td>'.
10125: &embedded_file_element('upload_embedded',$counter,
1.987 raeburn 10126: $embed_file,\%mapping,
1.1071 raeburn 10127: $allfiles,$codebase,'upload');
10128: $counter ++;
10129: $numnew ++;
1.987 raeburn 10130: }
10131: $upload_output .= '</td>'.&Apache::loncommon::end_data_table_row()."\n";
10132: }
10133: foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%existing)) {
1.1071 raeburn 10134: if ($actionurl eq '/adm/dependencies') {
10135: my ($size,$mtime) = &get_dependency_details(\%currfile,\%currsubfile,$embed_file);
10136: $modify_output .= &start_data_table_row().
10137: '<td><a href="'.$path.'/'.$embed_file.'" style="text-decoration:none;">'.
10138: '<img src="'.&icon($embed_file).'" border="0" />'.
10139: ' <span class="LC_filename">'.$embed_file.'</span></a></td>'.
10140: '<td>'.$size.'</td>'.
10141: '<td>'.$mtime.'</td>'.
10142: '<td><label><input type="checkbox" name="mod_upload_dep" '.
10143: 'onclick="toggleBrowse('."'$counter'".')" id="mod_upload_dep_'.
10144: $counter.'" value="'.$counter.'" />'.&mt('Yes').'</label>'.
10145: '<div id="moduploaddep_'.$counter.'" style="display:none;">'.
10146: &embedded_file_element('upload_embedded',$counter,
10147: $embed_file,\%mapping,
10148: $allfiles,$codebase,'modify').
10149: '</div></td>'.
10150: &end_data_table_row()."\n";
10151: $counter ++;
10152: } else {
10153: $upload_output .= &start_data_table_row().
1.1123 raeburn 10154: '<td valign="top"><img src="'.&icon($embed_file).'" /> '.
10155: '<span class="LC_filename">'.$embed_file.'</span></td>'.
10156: '<td align="right"><span class="LC_info LC_fontsize_medium">'.&mt('Already exists').'</span></td>'.
1.1071 raeburn 10157: &Apache::loncommon::end_data_table_row()."\n";
10158: }
10159: }
10160: my $delidx = $counter;
10161: foreach my $oldfile (sort {lc($a) cmp lc($b)} keys(%unused)) {
10162: my ($size,$mtime) = &get_dependency_details(\%currfile,\%currsubfile,$oldfile);
10163: $delete_output .= &start_data_table_row().
10164: '<td><img src="'.&icon($oldfile).'" />'.
10165: ' <span class="LC_filename">'.$oldfile.'</span></td>'.
10166: '<td>'.$size.'</td>'.
10167: '<td>'.$mtime.'</td>'.
10168: '<td><label><input type="checkbox" name="del_upload_dep" '.
10169: ' value="'.$delidx.'" />'.&mt('Yes').'</label>'.
10170: &embedded_file_element('upload_embedded',$delidx,
10171: $oldfile,\%mapping,$allfiles,
10172: $codebase,'delete').'</td>'.
10173: &end_data_table_row()."\n";
10174: $numunused ++;
10175: $delidx ++;
1.987 raeburn 10176: }
10177: if ($upload_output) {
10178: $upload_output = &start_data_table().
10179: $upload_output.
10180: &end_data_table()."\n";
10181: }
1.1071 raeburn 10182: if ($modify_output) {
10183: $modify_output = &start_data_table().
10184: &start_data_table_header_row().
10185: '<th>'.&mt('File').'</th>'.
10186: '<th>'.&mt('Size (KB)').'</th>'.
10187: '<th>'.&mt('Modified').'</th>'.
10188: '<th>'.&mt('Upload replacement?').'</th>'.
10189: &end_data_table_header_row().
10190: $modify_output.
10191: &end_data_table()."\n";
10192: }
10193: if ($delete_output) {
10194: $delete_output = &start_data_table().
10195: &start_data_table_header_row().
10196: '<th>'.&mt('File').'</th>'.
10197: '<th>'.&mt('Size (KB)').'</th>'.
10198: '<th>'.&mt('Modified').'</th>'.
10199: '<th>'.&mt('Delete?').'</th>'.
10200: &end_data_table_header_row().
10201: $delete_output.
10202: &end_data_table()."\n";
10203: }
1.987 raeburn 10204: my $applies = 0;
10205: if ($numremref) {
10206: $applies ++;
10207: }
10208: if ($numinvalid) {
10209: $applies ++;
10210: }
10211: if ($numexisting) {
10212: $applies ++;
10213: }
1.1071 raeburn 10214: if ($counter || $numunused) {
1.987 raeburn 10215: $output = '<form name="upload_embedded" action="'.$actionurl.'"'.
10216: ' method="post" enctype="multipart/form-data">'."\n".
1.1071 raeburn 10217: $state.'<h3>'.$heading.'</h3>';
10218: if ($actionurl eq '/adm/dependencies') {
10219: if ($numnew) {
10220: $output .= '<h4>'.&mt('Missing dependencies').'</h4>'.
10221: '<p>'.&mt('The following files need to be uploaded.').'</p>'."\n".
10222: $upload_output.'<br />'."\n";
10223: }
10224: if ($numexisting) {
10225: $output .= '<h4>'.&mt('Uploaded dependencies (in use)').'</h4>'.
10226: '<p>'.&mt('Upload a new file to replace the one currently in use.').'</p>'."\n".
10227: $modify_output.'<br />'."\n";
10228: $buttontext = &mt('Save changes');
10229: }
10230: if ($numunused) {
10231: $output .= '<h4>'.&mt('Unused files').'</h4>'.
10232: '<p>'.&mt('The following uploaded files are no longer used.').'</p>'."\n".
10233: $delete_output.'<br />'."\n";
10234: $buttontext = &mt('Save changes');
10235: }
10236: } else {
10237: $output .= $upload_output.'<br />'."\n";
10238: }
10239: $output .= '<input type ="hidden" name="number_embedded_items" value="'.
10240: $counter.'" />'."\n";
10241: if ($actionurl eq '/adm/dependencies') {
10242: $output .= '<input type ="hidden" name="number_newemb_items" value="'.
10243: $numnew.'" />'."\n";
10244: } elsif ($actionurl eq '') {
1.987 raeburn 10245: $output .= '<input type="hidden" name="phase" value="three" />';
10246: }
10247: } elsif ($applies) {
10248: $output = '<b>'.&mt('Referenced files').'</b>:<br />';
10249: if ($applies > 1) {
10250: $output .=
1.1123 raeburn 10251: &mt('No dependencies need to be uploaded, as one of the following applies to each reference:').'<ul>';
1.987 raeburn 10252: if ($numremref) {
10253: $output .= '<li>'.&mt('reference is to a URL which points to another server').'</li>'."\n";
10254: }
10255: if ($numinvalid) {
10256: $output .= '<li>'.&mt('reference is to file with a name containing invalid characters').'</li>'."\n";
10257: }
10258: if ($numexisting) {
10259: $output .= '<li>'.&mt('reference is to an existing file at the specified location').'</li>'."\n";
10260: }
10261: $output .= '</ul><br />';
10262: } elsif ($numremref) {
10263: $output .= '<p>'.&mt('None to upload, as all references are to URLs pointing to another server.').'</p>';
10264: } elsif ($numinvalid) {
10265: $output .= '<p>'.&mt('None to upload, as all references are to files with names containing invalid characters.').'</p>';
10266: } elsif ($numexisting) {
10267: $output .= '<p>'.&mt('None to upload, as all references are to existing files.').'</p>';
10268: }
10269: $output .= $upload_output.'<br />';
10270: }
10271: my ($pathchange_output,$chgcount);
1.1071 raeburn 10272: $chgcount = $counter;
1.987 raeburn 10273: if (keys(%pathchanges) > 0) {
10274: foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%pathchanges)) {
1.1071 raeburn 10275: if ($counter) {
1.987 raeburn 10276: $output .= &embedded_file_element('pathchange',$chgcount,
10277: $embed_file,\%mapping,
1.1071 raeburn 10278: $allfiles,$codebase,'change');
1.987 raeburn 10279: } else {
10280: $pathchange_output .=
10281: &start_data_table_row().
10282: '<td><input type ="checkbox" name="namechange" value="'.
10283: $chgcount.'" checked="checked" /></td>'.
10284: '<td>'.$mapping{$embed_file}.'</td>'.
10285: '<td>'.$embed_file.
10286: &embedded_file_element('pathchange',$numpathchg,$embed_file,
1.1071 raeburn 10287: \%mapping,$allfiles,$codebase,'change').
1.987 raeburn 10288: '</td>'.&end_data_table_row();
1.660 raeburn 10289: }
1.987 raeburn 10290: $numpathchg ++;
10291: $chgcount ++;
1.660 raeburn 10292: }
10293: }
1.1127 raeburn 10294: if (($counter) || ($numunused)) {
1.987 raeburn 10295: if ($numpathchg) {
10296: $output .= '<input type ="hidden" name="number_pathchange_items" value="'.
10297: $numpathchg.'" />'."\n";
10298: }
10299: if (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank') ||
10300: ($actionurl eq '/adm/imsimport')) {
10301: $output .= '<input type="hidden" name="phase" value="three" />'."\n";
10302: } elsif ($actionurl eq '/adm/portfolio' || $actionurl eq '/adm/coursegrp_portfolio') {
10303: $output .= '<input type="hidden" name="action" value="upload_embedded" />';
1.1071 raeburn 10304: } elsif ($actionurl eq '/adm/dependencies') {
10305: $output .= '<input type="hidden" name="action" value="process_changes" />';
1.987 raeburn 10306: }
1.1123 raeburn 10307: $output .= '<input type ="submit" value="'.$buttontext.'" />'."\n".'</form>'."\n";
1.987 raeburn 10308: } elsif ($numpathchg) {
10309: my %pathchange = ();
10310: $output .= &modify_html_form('pathchange',$actionurl,$state,\%pathchange,$pathchange_output);
10311: if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
10312: $output .= '<p>'.&mt('or').'</p>';
1.1123 raeburn 10313: }
1.987 raeburn 10314: }
1.1071 raeburn 10315: return ($output,$counter,$numpathchg);
1.987 raeburn 10316: }
10317:
1.1147 raeburn 10318: =pod
10319:
10320: =item * clean_path($name)
10321:
10322: Performs clean-up of directories, subdirectories and filename in an
10323: embedded object, referenced in an HTML file which is being uploaded
10324: to a course or portfolio, where
10325: "Upload embedded images/multimedia files if HTML file" checkbox was
10326: checked.
10327:
10328: Clean-up is similar to replacements in lonnet::clean_filename()
10329: except each / between sub-directory and next level is preserved.
10330:
10331: =cut
10332:
10333: sub clean_path {
10334: my ($embed_file) = @_;
10335: $embed_file =~s{^/+}{};
10336: my @contents;
10337: if ($embed_file =~ m{/}) {
10338: @contents = split(/\//,$embed_file);
10339: } else {
10340: @contents = ($embed_file);
10341: }
10342: my $lastidx = scalar(@contents)-1;
10343: for (my $i=0; $i<=$lastidx; $i++) {
10344: $contents[$i]=~s{\\}{/}g;
10345: $contents[$i]=~s/\s+/\_/g;
10346: $contents[$i]=~s{[^/\w\.\-]}{}g;
10347: if ($i == $lastidx) {
10348: $contents[$i]=~s/\.(\d+)(?=\.)/_$1/g;
10349: }
10350: }
10351: if ($lastidx > 0) {
10352: return join('/',@contents);
10353: } else {
10354: return $contents[0];
10355: }
10356: }
10357:
1.987 raeburn 10358: sub embedded_file_element {
1.1071 raeburn 10359: my ($context,$num,$embed_file,$mapping,$allfiles,$codebase,$type) = @_;
1.987 raeburn 10360: return unless ((ref($mapping) eq 'HASH') && (ref($allfiles) eq 'HASH') &&
10361: (ref($codebase) eq 'HASH'));
10362: my $output;
1.1071 raeburn 10363: if (($context eq 'upload_embedded') && ($type ne 'delete')) {
1.987 raeburn 10364: $output = '<input name="embedded_item_'.$num.'" type="file" value="" />'."\n";
10365: }
10366: $output .= '<input name="embedded_orig_'.$num.'" type="hidden" value="'.
10367: &escape($embed_file).'" />';
10368: unless (($context eq 'upload_embedded') &&
10369: ($mapping->{$embed_file} eq $embed_file)) {
10370: $output .='
10371: <input name="embedded_ref_'.$num.'" type="hidden" value="'.&escape($mapping->{$embed_file}).'" />';
10372: }
10373: my $attrib;
10374: if (ref($allfiles->{$mapping->{$embed_file}}) eq 'ARRAY') {
10375: $attrib = &escape(join(':',@{$allfiles->{$mapping->{$embed_file}}}));
10376: }
10377: $output .=
10378: "\n\t\t".
10379: '<input name="embedded_attrib_'.$num.'" type="hidden" value="'.
10380: $attrib.'" />';
10381: if (exists($codebase->{$mapping->{$embed_file}})) {
10382: $output .=
10383: "\n\t\t".
10384: '<input name="codebase_'.$num.'" type="hidden" value="'.
10385: &escape($codebase->{$mapping->{$embed_file}}).'" />';
1.984 raeburn 10386: }
1.987 raeburn 10387: return $output;
1.660 raeburn 10388: }
10389:
1.1071 raeburn 10390: sub get_dependency_details {
10391: my ($currfile,$currsubfile,$embed_file) = @_;
10392: my ($size,$mtime,$showsize,$showmtime);
10393: if ((ref($currfile) eq 'HASH') && (ref($currsubfile))) {
10394: if ($embed_file =~ m{/}) {
10395: my ($path,$fname) = split(/\//,$embed_file);
10396: if (ref($currsubfile->{$path}{$fname}) eq 'ARRAY') {
10397: ($size,$mtime) = @{$currsubfile->{$path}{$fname}};
10398: }
10399: } else {
10400: if (ref($currfile->{$embed_file}) eq 'ARRAY') {
10401: ($size,$mtime) = @{$currfile->{$embed_file}};
10402: }
10403: }
10404: $showsize = $size/1024.0;
10405: $showsize = sprintf("%.1f",$showsize);
10406: if ($mtime > 0) {
10407: $showmtime = &Apache::lonlocal::locallocaltime($mtime);
10408: }
10409: }
10410: return ($showsize,$showmtime);
10411: }
10412:
10413: sub ask_embedded_js {
10414: return <<"END";
10415: <script type="text/javascript"">
10416: // <![CDATA[
10417: function toggleBrowse(counter) {
10418: var chkboxid = document.getElementById('mod_upload_dep_'+counter);
10419: var fileid = document.getElementById('embedded_item_'+counter);
10420: var uploaddivid = document.getElementById('moduploaddep_'+counter);
10421: if (chkboxid.checked == true) {
10422: uploaddivid.style.display='block';
10423: } else {
10424: uploaddivid.style.display='none';
10425: fileid.value = '';
10426: }
10427: }
10428: // ]]>
10429: </script>
10430:
10431: END
10432: }
10433:
1.661 raeburn 10434: sub upload_embedded {
10435: my ($context,$dirpath,$uname,$udom,$dir_root,$url_root,$group,$disk_quota,
1.987 raeburn 10436: $current_disk_usage,$hiddenstate,$actionurl) = @_;
10437: my (%pathchange,$output,$modifyform,$footer,$returnflag);
1.661 raeburn 10438: for (my $i=0; $i<$env{'form.number_embedded_items'}; $i++) {
10439: next if (!exists($env{'form.embedded_item_'.$i.'.filename'}));
10440: my $orig_uploaded_filename =
10441: $env{'form.embedded_item_'.$i.'.filename'};
1.987 raeburn 10442: foreach my $type ('orig','ref','attrib','codebase') {
10443: if ($env{'form.embedded_'.$type.'_'.$i} ne '') {
10444: $env{'form.embedded_'.$type.'_'.$i} =
10445: &unescape($env{'form.embedded_'.$type.'_'.$i});
10446: }
10447: }
1.661 raeburn 10448: my ($path,$fname) =
10449: ($env{'form.embedded_orig_'.$i} =~ m{(.*/)([^/]*)});
10450: # no path, whole string is fname
10451: if (!$fname) { $fname = $env{'form.embedded_orig_'.$i} };
10452: $fname = &Apache::lonnet::clean_filename($fname);
10453: # See if there is anything left
10454: next if ($fname eq '');
10455:
10456: # Check if file already exists as a file or directory.
10457: my ($state,$msg);
10458: if ($context eq 'portfolio') {
10459: my $port_path = $dirpath;
10460: if ($group ne '') {
10461: $port_path = "groups/$group/$port_path";
10462: }
1.987 raeburn 10463: ($state,$msg) = &check_for_upload($env{'form.currentpath'}.$path,
10464: $fname,$group,'embedded_item_'.$i,
1.661 raeburn 10465: $dir_root,$port_path,$disk_quota,
10466: $current_disk_usage,$uname,$udom);
10467: if ($state eq 'will_exceed_quota'
1.984 raeburn 10468: || $state eq 'file_locked') {
1.661 raeburn 10469: $output .= $msg;
10470: next;
10471: }
10472: } elsif (($context eq 'author') || ($context eq 'testbank')) {
10473: ($state,$msg) = &check_for_existing($path,$fname,'embedded_item_'.$i);
10474: if ($state eq 'exists') {
10475: $output .= $msg;
10476: next;
10477: }
10478: }
10479: # Check if extension is valid
10480: if (($fname =~ /\.(\w+)$/) &&
10481: (&Apache::loncommon::fileembstyle($1) eq 'hdn')) {
1.1155 bisitz 10482: $output .= &mt('Invalid file extension ([_1]) - reserved for internal use.',$1)
10483: .' '.&mt('Rename the file with a different extension and re-upload.').'<br />';
1.661 raeburn 10484: next;
10485: } elsif (($fname =~ /\.(\w+)$/) &&
10486: (!defined(&Apache::loncommon::fileembstyle($1)))) {
1.987 raeburn 10487: $output .= &mt('Unrecognized file extension ([_1]) - rename the file with a proper extension and re-upload.',$1).'<br />';
1.661 raeburn 10488: next;
10489: } elsif ($fname=~/\.(\d+)\.(\w+)$/) {
1.1120 bisitz 10490: $output .= &mt('Filename not allowed - rename the file to remove the number immediately before the file extension([_1]) and re-upload.',$2).'<br />';
1.661 raeburn 10491: next;
10492: }
10493: $env{'form.embedded_item_'.$i.'.filename'}=$fname;
1.1123 raeburn 10494: my $subdir = $path;
10495: $subdir =~ s{/+$}{};
1.661 raeburn 10496: if ($context eq 'portfolio') {
1.984 raeburn 10497: my $result;
10498: if ($state eq 'existingfile') {
10499: $result=
10500: &Apache::lonnet::userfileupload('embedded_item_'.$i,'existingfile',
1.1123 raeburn 10501: $dirpath.$env{'form.currentpath'}.$subdir);
1.661 raeburn 10502: } else {
1.984 raeburn 10503: $result=
10504: &Apache::lonnet::userfileupload('embedded_item_'.$i,'',
1.987 raeburn 10505: $dirpath.
1.1123 raeburn 10506: $env{'form.currentpath'}.$subdir);
1.984 raeburn 10507: if ($result !~ m|^/uploaded/|) {
10508: $output .= '<span class="LC_error">'
10509: .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].'
10510: ,$result,$orig_uploaded_filename,$env{'form.embedded_orig_'.$i})
10511: .'</span><br />';
10512: next;
10513: } else {
1.987 raeburn 10514: $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
10515: $path.$fname.'</span>').'<br />';
1.984 raeburn 10516: }
1.661 raeburn 10517: }
1.1123 raeburn 10518: } elsif (($context eq 'coursedoc') || ($context eq 'syllabus')) {
1.1126 raeburn 10519: my $extendedsubdir = $dirpath.'/'.$subdir;
10520: $extendedsubdir =~ s{/+$}{};
1.987 raeburn 10521: my $result =
1.1126 raeburn 10522: &Apache::lonnet::userfileupload('embedded_item_'.$i,$context,$extendedsubdir);
1.987 raeburn 10523: if ($result !~ m|^/uploaded/|) {
10524: $output .= '<span class="LC_error">'
10525: .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].'
10526: ,$result,$orig_uploaded_filename,$env{'form.embedded_orig_'.$i})
10527: .'</span><br />';
10528: next;
10529: } else {
10530: $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
10531: $path.$fname.'</span>').'<br />';
1.1125 raeburn 10532: if ($context eq 'syllabus') {
10533: &Apache::lonnet::make_public_indefinitely($result);
10534: }
1.987 raeburn 10535: }
1.661 raeburn 10536: } else {
10537: # Save the file
10538: my $target = $env{'form.embedded_item_'.$i};
10539: my $fullpath = $dir_root.$dirpath.'/'.$path;
10540: my $dest = $fullpath.$fname;
10541: my $url = $url_root.$dirpath.'/'.$path.$fname;
1.1027 raeburn 10542: my @parts=split(/\//,"$dirpath/$path");
1.661 raeburn 10543: my $count;
10544: my $filepath = $dir_root;
1.1027 raeburn 10545: foreach my $subdir (@parts) {
10546: $filepath .= "/$subdir";
10547: if (!-e $filepath) {
1.661 raeburn 10548: mkdir($filepath,0770);
10549: }
10550: }
10551: my $fh;
10552: if (!open($fh,'>'.$dest)) {
10553: &Apache::lonnet::logthis('Failed to create '.$dest);
10554: $output .= '<span class="LC_error">'.
1.1071 raeburn 10555: &mt('An error occurred while trying to upload [_1] for embedded element [_2].',
10556: $orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
1.661 raeburn 10557: '</span><br />';
10558: } else {
10559: if (!print $fh $env{'form.embedded_item_'.$i}) {
10560: &Apache::lonnet::logthis('Failed to write to '.$dest);
10561: $output .= '<span class="LC_error">'.
1.1071 raeburn 10562: &mt('An error occurred while writing the file [_1] for embedded element [_2].',
10563: $orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
1.661 raeburn 10564: '</span><br />';
10565: } else {
1.987 raeburn 10566: $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
10567: $url.'</span>').'<br />';
10568: unless ($context eq 'testbank') {
10569: $footer .= &mt('View embedded file: [_1]',
10570: '<a href="'.$url.'">'.$fname.'</a>').'<br />';
10571: }
10572: }
10573: close($fh);
10574: }
10575: }
10576: if ($env{'form.embedded_ref_'.$i}) {
10577: $pathchange{$i} = 1;
10578: }
10579: }
10580: if ($output) {
10581: $output = '<p>'.$output.'</p>';
10582: }
10583: $output .= &modify_html_form('upload_embedded',$actionurl,$hiddenstate,\%pathchange);
10584: $returnflag = 'ok';
1.1071 raeburn 10585: my $numpathchgs = scalar(keys(%pathchange));
10586: if ($numpathchgs > 0) {
1.987 raeburn 10587: if ($context eq 'portfolio') {
10588: $output .= '<p>'.&mt('or').'</p>';
10589: } elsif ($context eq 'testbank') {
1.1071 raeburn 10590: $output .= '<p>'.&mt('Or [_1]continue[_2] the testbank import without modifying the reference(s).',
10591: '<a href="javascript:document.testbankForm.submit();">','</a>').'</p>';
1.987 raeburn 10592: $returnflag = 'modify_orightml';
10593: }
10594: }
1.1071 raeburn 10595: return ($output.$footer,$returnflag,$numpathchgs);
1.987 raeburn 10596: }
10597:
10598: sub modify_html_form {
10599: my ($context,$actionurl,$hiddenstate,$pathchange,$pathchgtable) = @_;
10600: my $end = 0;
10601: my $modifyform;
10602: if ($context eq 'upload_embedded') {
10603: return unless (ref($pathchange) eq 'HASH');
10604: if ($env{'form.number_embedded_items'}) {
10605: $end += $env{'form.number_embedded_items'};
10606: }
10607: if ($env{'form.number_pathchange_items'}) {
10608: $end += $env{'form.number_pathchange_items'};
10609: }
10610: if ($end) {
10611: for (my $i=0; $i<$end; $i++) {
10612: if ($i < $env{'form.number_embedded_items'}) {
10613: next unless($pathchange->{$i});
10614: }
10615: $modifyform .=
10616: &start_data_table_row().
10617: '<td><input type ="checkbox" name="namechange" value="'.$i.'" '.
10618: 'checked="checked" /></td>'.
10619: '<td>'.$env{'form.embedded_ref_'.$i}.
10620: '<input type="hidden" name="embedded_ref_'.$i.'" value="'.
10621: &escape($env{'form.embedded_ref_'.$i}).'" />'.
10622: '<input type="hidden" name="embedded_codebase_'.$i.'" value="'.
10623: &escape($env{'form.embedded_codebase_'.$i}).'" />'.
10624: '<input type="hidden" name="embedded_attrib_'.$i.'" value="'.
10625: &escape($env{'form.embedded_attrib_'.$i}).'" /></td>'.
10626: '<td>'.$env{'form.embedded_orig_'.$i}.
10627: '<input type="hidden" name="embedded_orig_'.$i.'" value="'.
10628: &escape($env{'form.embedded_orig_'.$i}).'" /></td>'.
10629: &end_data_table_row();
1.1071 raeburn 10630: }
1.987 raeburn 10631: }
10632: } else {
10633: $modifyform = $pathchgtable;
10634: if (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
10635: $hiddenstate .= '<input type="hidden" name="phase" value="four" />';
10636: } elsif (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
10637: $hiddenstate .= '<input type="hidden" name="action" value="modify_orightml" />';
10638: }
10639: }
10640: if ($modifyform) {
1.1071 raeburn 10641: if ($actionurl eq '/adm/dependencies') {
10642: $hiddenstate .= '<input type="hidden" name="action" value="modifyhrefs" />';
10643: }
1.987 raeburn 10644: return '<h3>'.&mt('Changes in content of HTML file required').'</h3>'."\n".
10645: '<p>'.&mt('Changes need to be made to the reference(s) used for one or more of the dependencies, if your HTML file is to work correctly:').'<ol>'."\n".
10646: '<li>'.&mt('For consistency between the reference(s) and the location of the corresponding stored file within LON-CAPA.').'</li>'."\n".
10647: '<li>'.&mt('To change absolute paths to relative paths, or replace directory traversal via "../" within the original reference.').'</li>'."\n".
10648: '</ol></p>'."\n".'<p>'.
10649: &mt('LON-CAPA can make the required changes to your HTML file.').'</p>'."\n".
10650: '<form method="post" name="refchanger" action="'.$actionurl.'">'.
10651: &start_data_table()."\n".
10652: &start_data_table_header_row().
10653: '<th>'.&mt('Change?').'</th>'.
10654: '<th>'.&mt('Current reference').'</th>'.
10655: '<th>'.&mt('Required reference').'</th>'.
10656: &end_data_table_header_row()."\n".
10657: $modifyform.
10658: &end_data_table().'<br />'."\n".$hiddenstate.
10659: '<input type="submit" name="pathchanges" value="'.&mt('Modify HTML file').'" />'.
10660: '</form>'."\n";
10661: }
10662: return;
10663: }
10664:
10665: sub modify_html_refs {
1.1123 raeburn 10666: my ($context,$dirpath,$uname,$udom,$dir_root,$url) = @_;
1.987 raeburn 10667: my $container;
10668: if ($context eq 'portfolio') {
10669: $container = $env{'form.container'};
10670: } elsif ($context eq 'coursedoc') {
10671: $container = $env{'form.primaryurl'};
1.1071 raeburn 10672: } elsif ($context eq 'manage_dependencies') {
10673: (undef,undef,$container) = &Apache::lonnet::decode_symb($env{'form.symb'});
10674: $container = "/$container";
1.1123 raeburn 10675: } elsif ($context eq 'syllabus') {
10676: $container = $url;
1.987 raeburn 10677: } else {
1.1027 raeburn 10678: $container = $Apache::lonnet::perlvar{'lonDocRoot'}.$env{'form.filename'};
1.987 raeburn 10679: }
10680: my (%allfiles,%codebase,$output,$content);
10681: my @changes = &get_env_multiple('form.namechange');
1.1126 raeburn 10682: unless ((@changes > 0) || ($context eq 'syllabus')) {
1.1071 raeburn 10683: if (wantarray) {
10684: return ('',0,0);
10685: } else {
10686: return;
10687: }
10688: }
10689: if (($context eq 'portfolio') || ($context eq 'coursedoc') ||
1.1123 raeburn 10690: ($context eq 'manage_dependencies') || ($context eq 'syllabus')) {
1.1071 raeburn 10691: unless ($container =~ m{^/uploaded/\Q$udom\E/\Q$uname\E/}) {
10692: if (wantarray) {
10693: return ('',0,0);
10694: } else {
10695: return;
10696: }
10697: }
1.987 raeburn 10698: $content = &Apache::lonnet::getfile($container);
1.1071 raeburn 10699: if ($content eq '-1') {
10700: if (wantarray) {
10701: return ('',0,0);
10702: } else {
10703: return;
10704: }
10705: }
1.987 raeburn 10706: } else {
1.1071 raeburn 10707: unless ($container =~ /^\Q$dir_root\E/) {
10708: if (wantarray) {
10709: return ('',0,0);
10710: } else {
10711: return;
10712: }
10713: }
1.987 raeburn 10714: if (open(my $fh,"<$container")) {
10715: $content = join('', <$fh>);
10716: close($fh);
10717: } else {
1.1071 raeburn 10718: if (wantarray) {
10719: return ('',0,0);
10720: } else {
10721: return;
10722: }
1.987 raeburn 10723: }
10724: }
10725: my ($count,$codebasecount) = (0,0);
10726: my $mm = new File::MMagic;
10727: my $mime_type = $mm->checktype_contents($content);
10728: if ($mime_type eq 'text/html') {
10729: my $parse_result =
10730: &Apache::lonnet::extract_embedded_items($container,\%allfiles,
10731: \%codebase,\$content);
10732: if ($parse_result eq 'ok') {
10733: foreach my $i (@changes) {
10734: my $orig = &unescape($env{'form.embedded_orig_'.$i});
10735: my $ref = &unescape($env{'form.embedded_ref_'.$i});
10736: if ($allfiles{$ref}) {
10737: my $newname = $orig;
10738: my ($attrib_regexp,$codebase);
1.1006 raeburn 10739: $attrib_regexp = &unescape($env{'form.embedded_attrib_'.$i});
1.987 raeburn 10740: if ($attrib_regexp =~ /:/) {
10741: $attrib_regexp =~ s/\:/|/g;
10742: }
10743: if ($content =~ m{($attrib_regexp\s*=\s*['"]?)\Q$ref\E(['"]?)}) {
10744: my $numchg = ($content =~ s{($attrib_regexp\s*=\s*['"]?)\Q$ref\E(['"]?)}{$1$newname$2}gi);
10745: $count += $numchg;
1.1123 raeburn 10746: $allfiles{$newname} = $allfiles{$ref};
1.1148 raeburn 10747: delete($allfiles{$ref});
1.987 raeburn 10748: }
10749: if ($env{'form.embedded_codebase_'.$i} ne '') {
1.1006 raeburn 10750: $codebase = &unescape($env{'form.embedded_codebase_'.$i});
1.987 raeburn 10751: my $numchg = ($content =~ s/(codebase\s*=\s*["']?)\Q$codebase\E(["']?)/$1.$2/i); #' stupid emacs
10752: $codebasecount ++;
10753: }
10754: }
10755: }
1.1123 raeburn 10756: my $skiprewrites;
1.987 raeburn 10757: if ($count || $codebasecount) {
10758: my $saveresult;
1.1071 raeburn 10759: if (($context eq 'portfolio') || ($context eq 'coursedoc') ||
1.1123 raeburn 10760: ($context eq 'manage_dependencies') || ($context eq 'syllabus')) {
1.987 raeburn 10761: my $url = &Apache::lonnet::store_edited_file($container,$content,$udom,$uname,\$saveresult);
10762: if ($url eq $container) {
10763: my ($fname) = ($container =~ m{/([^/]+)$});
10764: $output = '<p>'.&mt('Updated [quant,_1,reference] in [_2].',
10765: $count,'<span class="LC_filename">'.
1.1071 raeburn 10766: $fname.'</span>').'</p>';
1.987 raeburn 10767: } else {
10768: $output = '<p class="LC_error">'.
10769: &mt('Error: update failed for: [_1].',
10770: '<span class="LC_filename">'.
10771: $container.'</span>').'</p>';
10772: }
1.1123 raeburn 10773: if ($context eq 'syllabus') {
10774: unless ($saveresult eq 'ok') {
10775: $skiprewrites = 1;
10776: }
10777: }
1.987 raeburn 10778: } else {
10779: if (open(my $fh,">$container")) {
10780: print $fh $content;
10781: close($fh);
10782: $output = '<p>'.&mt('Updated [quant,_1,reference] in [_2].',
10783: $count,'<span class="LC_filename">'.
10784: $container.'</span>').'</p>';
1.661 raeburn 10785: } else {
1.987 raeburn 10786: $output = '<p class="LC_error">'.
10787: &mt('Error: could not update [_1].',
10788: '<span class="LC_filename">'.
10789: $container.'</span>').'</p>';
1.661 raeburn 10790: }
10791: }
10792: }
1.1123 raeburn 10793: if (($context eq 'syllabus') && (!$skiprewrites)) {
10794: my ($actionurl,$state);
10795: $actionurl = "/public/$udom/$uname/syllabus";
10796: my ($ignore,$num,$numpathchanges,$existing,$mapping) =
10797: &ask_for_embedded_content($actionurl,$state,\%allfiles,
10798: \%codebase,
10799: {'context' => 'rewrites',
10800: 'ignore_remote_references' => 1,});
10801: if (ref($mapping) eq 'HASH') {
10802: my $rewrites = 0;
10803: foreach my $key (keys(%{$mapping})) {
10804: next if ($key =~ m{^https?://});
10805: my $ref = $mapping->{$key};
10806: my $newname = "/uploaded/$udom/$uname/portfolio/syllabus/$key";
10807: my $attrib;
10808: if (ref($allfiles{$mapping->{$key}}) eq 'ARRAY') {
10809: $attrib = join('|',@{$allfiles{$mapping->{$key}}});
10810: }
10811: if ($content =~ m{($attrib\s*=\s*['"]?)\Q$ref\E(['"]?)}) {
10812: my $numchg = ($content =~ s{($attrib\s*=\s*['"]?)\Q$ref\E(['"]?)}{$1$newname$2}gi);
10813: $rewrites += $numchg;
10814: }
10815: }
10816: if ($rewrites) {
10817: my $saveresult;
10818: my $url = &Apache::lonnet::store_edited_file($container,$content,$udom,$uname,\$saveresult);
10819: if ($url eq $container) {
10820: my ($fname) = ($container =~ m{/([^/]+)$});
10821: $output .= '<p>'.&mt('Rewrote [quant,_1,link] as [quant,_1,absolute link] in [_2].',
10822: $count,'<span class="LC_filename">'.
10823: $fname.'</span>').'</p>';
10824: } else {
10825: $output .= '<p class="LC_error">'.
10826: &mt('Error: could not update links in [_1].',
10827: '<span class="LC_filename">'.
10828: $container.'</span>').'</p>';
10829:
10830: }
10831: }
10832: }
10833: }
1.987 raeburn 10834: } else {
10835: &logthis('Failed to parse '.$container.
10836: ' to modify references: '.$parse_result);
1.661 raeburn 10837: }
10838: }
1.1071 raeburn 10839: if (wantarray) {
10840: return ($output,$count,$codebasecount);
10841: } else {
10842: return $output;
10843: }
1.661 raeburn 10844: }
10845:
10846: sub check_for_existing {
10847: my ($path,$fname,$element) = @_;
10848: my ($state,$msg);
10849: if (-d $path.'/'.$fname) {
10850: $state = 'exists';
10851: $msg = &mt('Unable to upload [_1]. A directory by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$path);
10852: } elsif (-e $path.'/'.$fname) {
10853: $state = 'exists';
10854: $msg = &mt('Unable to upload [_1]. A file by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$path);
10855: }
10856: if ($state eq 'exists') {
10857: $msg = '<span class="LC_error">'.$msg.'</span><br />';
10858: }
10859: return ($state,$msg);
10860: }
10861:
10862: sub check_for_upload {
10863: my ($path,$fname,$group,$element,$portfolio_root,$port_path,
10864: $disk_quota,$current_disk_usage,$uname,$udom) = @_;
1.985 raeburn 10865: my $filesize = length($env{'form.'.$element});
10866: if (!$filesize) {
10867: my $msg = '<span class="LC_error">'.
10868: &mt('Unable to upload [_1]. (size = [_2] bytes)',
10869: '<span class="LC_filename">'.$fname.'</span>',
10870: $filesize).'<br />'.
1.1007 raeburn 10871: &mt('Either the file you attempted to upload was empty, or your web browser was unable to read its contents.').'<br />'.
1.985 raeburn 10872: '</span>';
10873: return ('zero_bytes',$msg);
10874: }
10875: $filesize = $filesize/1000; #express in k (1024?)
1.661 raeburn 10876: my $getpropath = 1;
1.1021 raeburn 10877: my ($dirlistref,$listerror) =
10878: &Apache::lonnet::dirlist($portfolio_root.$path,$udom,$uname,$getpropath);
1.661 raeburn 10879: my $found_file = 0;
10880: my $locked_file = 0;
1.991 raeburn 10881: my @lockers;
10882: my $navmap;
10883: if ($env{'request.course.id'}) {
10884: $navmap = Apache::lonnavmaps::navmap->new();
10885: }
1.1021 raeburn 10886: if (ref($dirlistref) eq 'ARRAY') {
10887: foreach my $line (@{$dirlistref}) {
10888: my ($file_name,$rest)=split(/\&/,$line,2);
10889: if ($file_name eq $fname){
10890: $file_name = $path.$file_name;
10891: if ($group ne '') {
10892: $file_name = $group.$file_name;
10893: }
10894: $found_file = 1;
10895: if (&Apache::lonnet::is_locked($file_name,$udom,$uname,\@lockers) eq 'true') {
10896: foreach my $lock (@lockers) {
10897: if (ref($lock) eq 'ARRAY') {
10898: my ($symb,$crsid) = @{$lock};
10899: if ($crsid eq $env{'request.course.id'}) {
10900: if (ref($navmap)) {
10901: my $res = $navmap->getBySymb($symb);
10902: foreach my $part (@{$res->parts()}) {
10903: my ($slot_status,$slot_time,$slot_name)=$res->check_for_slot($part);
10904: unless (($slot_status == $res->RESERVED) ||
10905: ($slot_status == $res->RESERVED_LOCATION)) {
10906: $locked_file = 1;
10907: }
1.991 raeburn 10908: }
1.1021 raeburn 10909: } else {
10910: $locked_file = 1;
1.991 raeburn 10911: }
10912: } else {
10913: $locked_file = 1;
10914: }
10915: }
1.1021 raeburn 10916: }
10917: } else {
10918: my @info = split(/\&/,$rest);
10919: my $currsize = $info[6]/1000;
10920: if ($currsize < $filesize) {
10921: my $extra = $filesize - $currsize;
10922: if (($current_disk_usage + $extra) > $disk_quota) {
10923: my $msg = '<span class="LC_error">'.
10924: &mt('Unable to upload [_1]. (size = [_2] kilobytes). Disk quota will be exceeded if existing (smaller) file with same name (size = [_3] kilobytes) is replaced.',
10925: '<span class="LC_filename">'.$fname.'</span>',$filesize,$currsize).'</span>'.
10926: '<br />'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',
10927: $disk_quota,$current_disk_usage);
10928: return ('will_exceed_quota',$msg);
10929: }
1.984 raeburn 10930: }
10931: }
1.661 raeburn 10932: }
10933: }
10934: }
10935: if (($current_disk_usage + $filesize) > $disk_quota){
10936: my $msg = '<span class="LC_error">'.
10937: &mt('Unable to upload [_1]. (size = [_2] kilobytes). Disk quota will be exceeded.','<span class="LC_filename">'.$fname.'</span>',$filesize).'</span>'.
10938: '<br />'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',$disk_quota,$current_disk_usage);
10939: return ('will_exceed_quota',$msg);
10940: } elsif ($found_file) {
10941: if ($locked_file) {
10942: my $msg = '<span class="LC_error">';
10943: $msg .= &mt('Unable to upload [_1]. A locked file by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>','<span class="LC_filename">'.$port_path.$env{'form.currentpath'}.'</span>');
10944: $msg .= '</span><br />';
10945: $msg .= &mt('You will be able to rename or delete existing [_1] after a grade has been assigned.','<span class="LC_filename">'.$fname.'</span>');
10946: return ('file_locked',$msg);
10947: } else {
10948: my $msg = '<span class="LC_error">';
1.984 raeburn 10949: $msg .= &mt(' A file by that name: [_1] was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$port_path.$env{'form.currentpath'});
1.661 raeburn 10950: $msg .= '</span>';
1.984 raeburn 10951: return ('existingfile',$msg);
1.661 raeburn 10952: }
10953: }
10954: }
10955:
1.987 raeburn 10956: sub check_for_traversal {
10957: my ($path,$url,$toplevel) = @_;
10958: my @parts=split(/\//,$path);
10959: my $cleanpath;
10960: my $fullpath = $url;
10961: for (my $i=0;$i<@parts;$i++) {
10962: next if ($parts[$i] eq '.');
10963: if ($parts[$i] eq '..') {
10964: $fullpath =~ s{([^/]+/)$}{};
10965: } else {
10966: $fullpath .= $parts[$i].'/';
10967: }
10968: }
10969: if ($fullpath =~ /^\Q$url\E(.*)$/) {
10970: $cleanpath = $1;
10971: } elsif ($fullpath =~ /^\Q$toplevel\E(.*)$/) {
10972: my $curr_toprel = $1;
10973: my @parts = split(/\//,$curr_toprel);
10974: my ($url_toprel) = ($url =~ /^\Q$toplevel\E(.*)$/);
10975: my @urlparts = split(/\//,$url_toprel);
10976: my $doubledots;
10977: my $startdiff = -1;
10978: for (my $i=0; $i<@urlparts; $i++) {
10979: if ($startdiff == -1) {
10980: unless ($urlparts[$i] eq $parts[$i]) {
10981: $startdiff = $i;
10982: $doubledots .= '../';
10983: }
10984: } else {
10985: $doubledots .= '../';
10986: }
10987: }
10988: if ($startdiff > -1) {
10989: $cleanpath = $doubledots;
10990: for (my $i=$startdiff; $i<@parts; $i++) {
10991: $cleanpath .= $parts[$i].'/';
10992: }
10993: }
10994: }
10995: $cleanpath =~ s{(/)$}{};
10996: return $cleanpath;
10997: }
1.31 albertel 10998:
1.1053 raeburn 10999: sub is_archive_file {
11000: my ($mimetype) = @_;
11001: if (($mimetype eq 'application/octet-stream') ||
11002: ($mimetype eq 'application/x-stuffit') ||
11003: ($mimetype =~ m{^application/(x\-)?(compressed|tar|zip|tgz|gz|gtar|gzip|gunzip|bz|bz2|bzip2)})) {
11004: return 1;
11005: }
11006: return;
11007: }
11008:
11009: sub decompress_form {
1.1065 raeburn 11010: my ($mimetype,$archiveurl,$action,$noextract,$hiddenelements,$dirlist) = @_;
1.1053 raeburn 11011: my %lt = &Apache::lonlocal::texthash (
11012: this => 'This file is an archive file.',
1.1067 raeburn 11013: camt => 'This file is a Camtasia archive file.',
1.1065 raeburn 11014: itsc => 'Its contents are as follows:',
1.1053 raeburn 11015: youm => 'You may wish to extract its contents.',
11016: extr => 'Extract contents',
1.1067 raeburn 11017: auto => 'LON-CAPA can process the files automatically, or you can decide how each should be handled.',
11018: proa => 'Process automatically?',
1.1053 raeburn 11019: yes => 'Yes',
11020: no => 'No',
1.1067 raeburn 11021: fold => 'Title for folder containing movie',
11022: movi => 'Title for page containing embedded movie',
1.1053 raeburn 11023: );
1.1065 raeburn 11024: my $fileloc = &Apache::lonnet::filelocation(undef,$archiveurl);
1.1067 raeburn 11025: my ($is_camtasia,$topdir,%toplevel,@paths);
1.1065 raeburn 11026: my $info = &list_archive_contents($fileloc,\@paths);
11027: if (@paths) {
11028: foreach my $path (@paths) {
11029: $path =~ s{^/}{};
1.1067 raeburn 11030: if ($path =~ m{^([^/]+)/$}) {
11031: $topdir = $1;
11032: }
1.1065 raeburn 11033: if ($path =~ m{^([^/]+)/}) {
11034: $toplevel{$1} = $path;
11035: } else {
11036: $toplevel{$path} = $path;
11037: }
11038: }
11039: }
1.1067 raeburn 11040: if ($mimetype =~ m{^application/(x\-)?(compressed|zip)}) {
1.1164 raeburn 11041: my @camtasia6 = ("$topdir/","$topdir/index.html",
1.1067 raeburn 11042: "$topdir/media/",
11043: "$topdir/media/$topdir.mp4",
11044: "$topdir/media/FirstFrame.png",
11045: "$topdir/media/player.swf",
11046: "$topdir/media/swfobject.js",
11047: "$topdir/media/expressInstall.swf");
1.1164 raeburn 11048: my @camtasia8 = ("$topdir/","$topdir/$topdir.html",
11049: "$topdir/$topdir.mp4",
11050: "$topdir/$topdir\_config.xml",
11051: "$topdir/$topdir\_controller.swf",
11052: "$topdir/$topdir\_embed.css",
11053: "$topdir/$topdir\_First_Frame.png",
11054: "$topdir/$topdir\_player.html",
11055: "$topdir/$topdir\_Thumbnails.png",
11056: "$topdir/playerProductInstall.swf",
11057: "$topdir/scripts/",
11058: "$topdir/scripts/config_xml.js",
11059: "$topdir/scripts/handlebars.js",
11060: "$topdir/scripts/jquery-1.7.1.min.js",
11061: "$topdir/scripts/jquery-ui-1.8.15.custom.min.js",
11062: "$topdir/scripts/modernizr.js",
11063: "$topdir/scripts/player-min.js",
11064: "$topdir/scripts/swfobject.js",
11065: "$topdir/skins/",
11066: "$topdir/skins/configuration_express.xml",
11067: "$topdir/skins/express_show/",
11068: "$topdir/skins/express_show/player-min.css",
11069: "$topdir/skins/express_show/spritesheet.png");
11070: my @diffs = &compare_arrays(\@paths,\@camtasia6);
1.1067 raeburn 11071: if (@diffs == 0) {
1.1164 raeburn 11072: $is_camtasia = 6;
11073: } else {
11074: @diffs = &compare_arrays(\@paths,\@camtasia8);
11075: if (@diffs == 0) {
11076: $is_camtasia = 8;
11077: }
1.1067 raeburn 11078: }
11079: }
11080: my $output;
11081: if ($is_camtasia) {
11082: $output = <<"ENDCAM";
11083: <script type="text/javascript" language="Javascript">
11084: // <![CDATA[
11085:
11086: function camtasiaToggle() {
11087: for (var i=0; i<document.uploaded_decompress.autoextract_camtasia.length; i++) {
11088: if (document.uploaded_decompress.autoextract_camtasia[i].checked) {
1.1164 raeburn 11089: if (document.uploaded_decompress.autoextract_camtasia[i].value == $is_camtasia) {
1.1067 raeburn 11090:
11091: document.getElementById('camtasia_titles').style.display='block';
11092: } else {
11093: document.getElementById('camtasia_titles').style.display='none';
11094: }
11095: }
11096: }
11097: return;
11098: }
11099:
11100: // ]]>
11101: </script>
11102: <p>$lt{'camt'}</p>
11103: ENDCAM
1.1065 raeburn 11104: } else {
1.1067 raeburn 11105: $output = '<p>'.$lt{'this'};
11106: if ($info eq '') {
11107: $output .= ' '.$lt{'youm'}.'</p>'."\n";
11108: } else {
11109: $output .= ' '.$lt{'itsc'}.'</p>'."\n".
11110: '<div><pre>'.$info.'</pre></div>';
11111: }
1.1065 raeburn 11112: }
1.1067 raeburn 11113: $output .= '<form name="uploaded_decompress" action="'.$action.'" method="post">'."\n";
1.1065 raeburn 11114: my $duplicates;
11115: my $num = 0;
11116: if (ref($dirlist) eq 'ARRAY') {
11117: foreach my $item (@{$dirlist}) {
11118: if (ref($item) eq 'ARRAY') {
11119: if (exists($toplevel{$item->[0]})) {
11120: $duplicates .=
11121: &start_data_table_row().
11122: '<td><label><input type="radio" name="archive_overwrite_'.$num.'" '.
11123: 'value="0" checked="checked" />'.&mt('No').'</label>'.
11124: ' <label><input type="radio" name="archive_overwrite_'.$num.'" '.
11125: 'value="1" />'.&mt('Yes').'</label>'.
11126: '<input type="hidden" name="archive_overwrite_name_'.$num.'" value="'.$item->[0].'" /></td>'."\n".
11127: '<td>'.$item->[0].'</td>';
11128: if ($item->[2]) {
11129: $duplicates .= '<td>'.&mt('Directory').'</td>';
11130: } else {
11131: $duplicates .= '<td>'.&mt('File').'</td>';
11132: }
11133: $duplicates .= '<td>'.$item->[3].'</td>'.
11134: '<td>'.
11135: &Apache::lonlocal::locallocaltime($item->[4]).
11136: '</td>'.
11137: &end_data_table_row();
11138: $num ++;
11139: }
11140: }
11141: }
11142: }
11143: my $itemcount;
11144: if (@paths > 0) {
11145: $itemcount = scalar(@paths);
11146: } else {
11147: $itemcount = 1;
11148: }
1.1067 raeburn 11149: if ($is_camtasia) {
11150: $output .= $lt{'auto'}.'<br />'.
11151: '<span class="LC_nobreak">'.$lt{'proa'}.'<label>'.
1.1164 raeburn 11152: '<input type="radio" name="autoextract_camtasia" value="'.$is_camtasia.'" onclick="javascript:camtasiaToggle();" checked="checked" />'.
1.1067 raeburn 11153: $lt{'yes'}.'</label> <label>'.
11154: '<input type="radio" name="autoextract_camtasia" value="0" onclick="javascript:camtasiaToggle();" />'.
11155: $lt{'no'}.'</label></span><br />'.
11156: '<div id="camtasia_titles" style="display:block">'.
11157: &Apache::lonhtmlcommon::start_pick_box().
11158: &Apache::lonhtmlcommon::row_title($lt{'fold'}).
11159: '<input type="textbox" name="camtasia_foldername" value="'.$env{'form.comment'}.'" />'."\n".
11160: &Apache::lonhtmlcommon::row_closure().
11161: &Apache::lonhtmlcommon::row_title($lt{'movi'}).
11162: '<input type="textbox" name="camtasia_moviename" value="" />'."\n".
11163: &Apache::lonhtmlcommon::row_closure(1).
11164: &Apache::lonhtmlcommon::end_pick_box().
11165: '</div>';
11166: }
1.1065 raeburn 11167: $output .=
11168: '<input type="hidden" name="archive_overwrite_total" value="'.$num.'" />'.
1.1067 raeburn 11169: '<input type="hidden" name="archive_itemcount" value="'.$itemcount.'" />'.
11170: "\n";
1.1065 raeburn 11171: if ($duplicates ne '') {
11172: $output .= '<p><span class="LC_warning">'.
11173: &mt('Warning: decompression of the archive will overwrite the following items which already exist:').'</span><br />'.
11174: &start_data_table().
11175: &start_data_table_header_row().
11176: '<th>'.&mt('Overwrite?').'</th>'.
11177: '<th>'.&mt('Name').'</th>'.
11178: '<th>'.&mt('Type').'</th>'.
11179: '<th>'.&mt('Size').'</th>'.
11180: '<th>'.&mt('Last modified').'</th>'.
11181: &end_data_table_header_row().
11182: $duplicates.
11183: &end_data_table().
11184: '</p>';
11185: }
1.1067 raeburn 11186: $output .= '<input type="hidden" name="archiveurl" value="'.$archiveurl.'" />'."\n";
1.1053 raeburn 11187: if (ref($hiddenelements) eq 'HASH') {
11188: foreach my $hidden (sort(keys(%{$hiddenelements}))) {
11189: $output .= '<input type="hidden" name="'.$hidden.'" value="'.$hiddenelements->{$hidden}.'" />'."\n";
11190: }
11191: }
11192: $output .= <<"END";
1.1067 raeburn 11193: <br />
1.1053 raeburn 11194: <input type="submit" name="decompress" value="$lt{'extr'}" />
11195: </form>
11196: $noextract
11197: END
11198: return $output;
11199: }
11200:
1.1065 raeburn 11201: sub decompression_utility {
11202: my ($program) = @_;
11203: my @utilities = ('tar','gunzip','bunzip2','unzip');
11204: my $location;
11205: if (grep(/^\Q$program\E$/,@utilities)) {
11206: foreach my $dir ('/bin/','/usr/bin/','/usr/local/bin/','/sbin/',
11207: '/usr/sbin/') {
11208: if (-x $dir.$program) {
11209: $location = $dir.$program;
11210: last;
11211: }
11212: }
11213: }
11214: return $location;
11215: }
11216:
11217: sub list_archive_contents {
11218: my ($file,$pathsref) = @_;
11219: my (@cmd,$output);
11220: my $needsregexp;
11221: if ($file =~ /\.zip$/) {
11222: @cmd = (&decompression_utility('unzip'),"-l");
11223: $needsregexp = 1;
11224: } elsif (($file =~ m/\.tar\.gz$/) ||
11225: ($file =~ /\.tgz$/)) {
11226: @cmd = (&decompression_utility('tar'),"-ztf");
11227: } elsif ($file =~ /\.tar\.bz2$/) {
11228: @cmd = (&decompression_utility('tar'),"-jtf");
11229: } elsif ($file =~ m|\.tar$|) {
11230: @cmd = (&decompression_utility('tar'),"-tf");
11231: }
11232: if (@cmd) {
11233: undef($!);
11234: undef($@);
11235: if (open(my $fh,"-|", @cmd, $file)) {
11236: while (my $line = <$fh>) {
11237: $output .= $line;
11238: chomp($line);
11239: my $item;
11240: if ($needsregexp) {
11241: ($item) = ($line =~ /^\s*\d+\s+[\d\-]+\s+[\d:]+\s*(.+)$/);
11242: } else {
11243: $item = $line;
11244: }
11245: if ($item ne '') {
11246: unless (grep(/^\Q$item\E$/,@{$pathsref})) {
11247: push(@{$pathsref},$item);
11248: }
11249: }
11250: }
11251: close($fh);
11252: }
11253: }
11254: return $output;
11255: }
11256:
1.1053 raeburn 11257: sub decompress_uploaded_file {
11258: my ($file,$dir) = @_;
11259: &Apache::lonnet::appenv({'cgi.file' => $file});
11260: &Apache::lonnet::appenv({'cgi.dir' => $dir});
11261: my $result = &Apache::lonnet::ssi_body('/cgi-bin/decompress.pl');
11262: my ($handle) = ($env{'user.environment'} =~m{/([^/]+)\.id$});
11263: my $lonidsdir = $Apache::lonnet::perlvar{'lonIDsDir'};
11264: &Apache::lonnet::transfer_profile_to_env($lonidsdir,$handle,1);
11265: my $decompressed = $env{'cgi.decompressed'};
11266: &Apache::lonnet::delenv('cgi.file');
11267: &Apache::lonnet::delenv('cgi.dir');
11268: &Apache::lonnet::delenv('cgi.decompressed');
11269: return ($decompressed,$result);
11270: }
11271:
1.1055 raeburn 11272: sub process_decompression {
11273: my ($docudom,$docuname,$file,$destination,$dir_root,$hiddenelem) = @_;
11274: my ($dir,$error,$warning,$output);
11275: if ($file !~ /\.(zip|tar|bz2|gz|tar.gz|tar.bz2|tgz)$/) {
1.1120 bisitz 11276: $error = &mt('Filename not a supported archive file type.').
11277: '<br />'.&mt('Filename should end with one of: [_1].',
1.1055 raeburn 11278: '.zip, .tar, .bz2, .gz, .tar.gz, .tar.bz2, .tgz');
11279: } else {
11280: my $docuhome = &Apache::lonnet::homeserver($docuname,$docudom);
11281: if ($docuhome eq 'no_host') {
11282: $error = &mt('Could not determine home server for course.');
11283: } else {
11284: my @ids=&Apache::lonnet::current_machine_ids();
11285: my $currdir = "$dir_root/$destination";
11286: if (grep(/^\Q$docuhome\E$/,@ids)) {
11287: $dir = &LONCAPA::propath($docudom,$docuname).
11288: "$dir_root/$destination";
11289: } else {
11290: $dir = $Apache::lonnet::perlvar{'lonDocRoot'}.
11291: "$dir_root/$docudom/$docuname/$destination";
11292: unless (&Apache::lonnet::repcopy_userfile("$dir/$file") eq 'ok') {
11293: $error = &mt('Archive file not found.');
11294: }
11295: }
1.1065 raeburn 11296: my (@to_overwrite,@to_skip);
11297: if ($env{'form.archive_overwrite_total'} > 0) {
11298: my $total = $env{'form.archive_overwrite_total'};
11299: for (my $i=0; $i<$total; $i++) {
11300: if ($env{'form.archive_overwrite_'.$i} == 1) {
11301: push(@to_overwrite,$env{'form.archive_overwrite_name_'.$i});
11302: } elsif ($env{'form.archive_overwrite_'.$i} == 0) {
11303: push(@to_skip,$env{'form.archive_overwrite_name_'.$i});
11304: }
11305: }
11306: }
11307: my $numskip = scalar(@to_skip);
11308: if (($numskip > 0) &&
11309: ($numskip == $env{'form.archive_itemcount'})) {
11310: $warning = &mt('All items in the archive file already exist, and no overwriting of existing files has been requested.');
11311: } elsif ($dir eq '') {
1.1055 raeburn 11312: $error = &mt('Directory containing archive file unavailable.');
11313: } elsif (!$error) {
1.1065 raeburn 11314: my ($decompressed,$display);
11315: if ($numskip > 0) {
11316: my $tempdir = time.'_'.$$.int(rand(10000));
11317: mkdir("$dir/$tempdir",0755);
11318: system("mv $dir/$file $dir/$tempdir/$file");
11319: ($decompressed,$display) =
11320: &decompress_uploaded_file($file,"$dir/$tempdir");
11321: foreach my $item (@to_skip) {
11322: if (($item ne '') && ($item !~ /\.\./)) {
11323: if (-f "$dir/$tempdir/$item") {
11324: unlink("$dir/$tempdir/$item");
11325: } elsif (-d "$dir/$tempdir/$item") {
11326: system("rm -rf $dir/$tempdir/$item");
11327: }
11328: }
11329: }
11330: system("mv $dir/$tempdir/* $dir");
11331: rmdir("$dir/$tempdir");
11332: } else {
11333: ($decompressed,$display) =
11334: &decompress_uploaded_file($file,$dir);
11335: }
1.1055 raeburn 11336: if ($decompressed eq 'ok') {
1.1065 raeburn 11337: $output = '<p class="LC_info">'.
11338: &mt('Files extracted successfully from archive.').
11339: '</p>'."\n";
1.1055 raeburn 11340: my ($warning,$result,@contents);
11341: my ($newdirlistref,$newlisterror) =
11342: &Apache::lonnet::dirlist($currdir,$docudom,
11343: $docuname,1);
11344: my (%is_dir,%changes,@newitems);
11345: my $dirptr = 16384;
1.1065 raeburn 11346: if (ref($newdirlistref) eq 'ARRAY') {
1.1055 raeburn 11347: foreach my $dir_line (@{$newdirlistref}) {
11348: my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,5);
1.1065 raeburn 11349: unless (($item =~ /^\.+$/) || ($item eq $file) ||
11350: ((@to_skip > 0) && (grep(/^\Q$item\E$/,@to_skip)))) {
1.1055 raeburn 11351: push(@newitems,$item);
11352: if ($dirptr&$testdir) {
11353: $is_dir{$item} = 1;
11354: }
11355: $changes{$item} = 1;
11356: }
11357: }
11358: }
11359: if (keys(%changes) > 0) {
11360: foreach my $item (sort(@newitems)) {
11361: if ($changes{$item}) {
11362: push(@contents,$item);
11363: }
11364: }
11365: }
11366: if (@contents > 0) {
1.1067 raeburn 11367: my $wantform;
11368: unless ($env{'form.autoextract_camtasia'}) {
11369: $wantform = 1;
11370: }
1.1056 raeburn 11371: my (%children,%parent,%dirorder,%titles);
1.1055 raeburn 11372: my ($count,$datatable) = &get_extracted($docudom,$docuname,
11373: $currdir,\%is_dir,
11374: \%children,\%parent,
1.1056 raeburn 11375: \@contents,\%dirorder,
11376: \%titles,$wantform);
1.1055 raeburn 11377: if ($datatable ne '') {
11378: $output .= &archive_options_form('decompressed',$datatable,
11379: $count,$hiddenelem);
1.1065 raeburn 11380: my $startcount = 6;
1.1055 raeburn 11381: $output .= &archive_javascript($startcount,$count,
1.1056 raeburn 11382: \%titles,\%children);
1.1055 raeburn 11383: }
1.1067 raeburn 11384: if ($env{'form.autoextract_camtasia'}) {
1.1164 raeburn 11385: my $version = $env{'form.autoextract_camtasia'};
1.1067 raeburn 11386: my %displayed;
11387: my $total = 1;
11388: $env{'form.archive_directory'} = [];
11389: foreach my $i (sort { $a <=> $b } keys(%dirorder)) {
11390: my $path = join('/',map { $titles{$_}; } @{$dirorder{$i}});
11391: $path =~ s{/$}{};
11392: my $item;
11393: if ($path ne '') {
11394: $item = "$path/$titles{$i}";
11395: } else {
11396: $item = $titles{$i};
11397: }
11398: $env{'form.archive_content_'.$i} = "$dir_root/$destination/$item";
11399: if ($item eq $contents[0]) {
11400: push(@{$env{'form.archive_directory'}},$i);
11401: $env{'form.archive_'.$i} = 'display';
11402: $env{'form.archive_title_'.$i} = $env{'form.camtasia_foldername'};
11403: $displayed{'folder'} = $i;
1.1164 raeburn 11404: } elsif ((($item eq "$contents[0]/index.html") && ($version == 6)) ||
11405: (($item eq "$contents[0]/$contents[0]".'.html') && ($version == 8))) {
1.1067 raeburn 11406: $env{'form.archive_'.$i} = 'display';
11407: $env{'form.archive_title_'.$i} = $env{'form.camtasia_moviename'};
11408: $displayed{'web'} = $i;
11409: } else {
1.1164 raeburn 11410: if ((($item eq "$contents[0]/media") && ($version == 6)) ||
11411: ((($item eq "$contents[0]/scripts") || ($item eq "$contents[0]/skins") ||
11412: ($item eq "$contents[0]/skins/express_show")) && ($version == 8))) {
1.1067 raeburn 11413: push(@{$env{'form.archive_directory'}},$i);
11414: }
11415: $env{'form.archive_'.$i} = 'dependency';
11416: }
11417: $total ++;
11418: }
11419: for (my $i=1; $i<$total; $i++) {
11420: next if ($i == $displayed{'web'});
11421: next if ($i == $displayed{'folder'});
11422: $env{'form.archive_dependent_on_'.$i} = $displayed{'web'};
11423: }
11424: $env{'form.phase'} = 'decompress_cleanup';
11425: $env{'form.archivedelete'} = 1;
11426: $env{'form.archive_count'} = $total-1;
11427: $output .=
11428: &process_extracted_files('coursedocs',$docudom,
11429: $docuname,$destination,
11430: $dir_root,$hiddenelem);
11431: }
1.1055 raeburn 11432: } else {
11433: $warning = &mt('No new items extracted from archive file.');
11434: }
11435: } else {
11436: $output = $display;
11437: $error = &mt('An error occurred during extraction from the archive file.');
11438: }
11439: }
11440: }
11441: }
11442: if ($error) {
11443: $output .= '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
11444: $error.'</p>'."\n";
11445: }
11446: if ($warning) {
11447: $output .= '<p class="LC_warning">'.$warning.'</p>'."\n";
11448: }
11449: return $output;
11450: }
11451:
11452: sub get_extracted {
1.1056 raeburn 11453: my ($docudom,$docuname,$currdir,$is_dir,$children,$parent,$contents,$dirorder,
11454: $titles,$wantform) = @_;
1.1055 raeburn 11455: my $count = 0;
11456: my $depth = 0;
11457: my $datatable;
1.1056 raeburn 11458: my @hierarchy;
1.1055 raeburn 11459: return unless ((ref($is_dir) eq 'HASH') && (ref($children) eq 'HASH') &&
1.1056 raeburn 11460: (ref($parent) eq 'HASH') && (ref($contents) eq 'ARRAY') &&
11461: (ref($dirorder) eq 'HASH') && (ref($titles) eq 'HASH'));
1.1055 raeburn 11462: foreach my $item (@{$contents}) {
11463: $count ++;
1.1056 raeburn 11464: @{$dirorder->{$count}} = @hierarchy;
11465: $titles->{$count} = $item;
1.1055 raeburn 11466: &archive_hierarchy($depth,$count,$parent,$children);
11467: if ($wantform) {
11468: $datatable .= &archive_row($is_dir->{$item},$item,
11469: $currdir,$depth,$count);
11470: }
11471: if ($is_dir->{$item}) {
11472: $depth ++;
1.1056 raeburn 11473: push(@hierarchy,$count);
11474: $parent->{$depth} = $count;
1.1055 raeburn 11475: $datatable .=
11476: &recurse_extracted_archive("$currdir/$item",$docudom,$docuname,
1.1056 raeburn 11477: \$depth,\$count,\@hierarchy,$dirorder,
11478: $children,$parent,$titles,$wantform);
1.1055 raeburn 11479: $depth --;
1.1056 raeburn 11480: pop(@hierarchy);
1.1055 raeburn 11481: }
11482: }
11483: return ($count,$datatable);
11484: }
11485:
11486: sub recurse_extracted_archive {
1.1056 raeburn 11487: my ($currdir,$docudom,$docuname,$depth,$count,$hierarchy,$dirorder,
11488: $children,$parent,$titles,$wantform) = @_;
1.1055 raeburn 11489: my $result='';
1.1056 raeburn 11490: unless ((ref($depth)) && (ref($count)) && (ref($hierarchy) eq 'ARRAY') &&
11491: (ref($children) eq 'HASH') && (ref($parent) eq 'HASH') &&
11492: (ref($dirorder) eq 'HASH')) {
1.1055 raeburn 11493: return $result;
11494: }
11495: my $dirptr = 16384;
11496: my ($newdirlistref,$newlisterror) =
11497: &Apache::lonnet::dirlist($currdir,$docudom,$docuname,1);
11498: if (ref($newdirlistref) eq 'ARRAY') {
11499: foreach my $dir_line (@{$newdirlistref}) {
11500: my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,5);
11501: unless ($item =~ /^\.+$/) {
11502: $$count ++;
1.1056 raeburn 11503: @{$dirorder->{$$count}} = @{$hierarchy};
11504: $titles->{$$count} = $item;
1.1055 raeburn 11505: &archive_hierarchy($$depth,$$count,$parent,$children);
1.1056 raeburn 11506:
1.1055 raeburn 11507: my $is_dir;
11508: if ($dirptr&$testdir) {
11509: $is_dir = 1;
11510: }
11511: if ($wantform) {
11512: $result .= &archive_row($is_dir,$item,$currdir,$$depth,$$count);
11513: }
11514: if ($is_dir) {
11515: $$depth ++;
1.1056 raeburn 11516: push(@{$hierarchy},$$count);
11517: $parent->{$$depth} = $$count;
1.1055 raeburn 11518: $result .=
11519: &recurse_extracted_archive("$currdir/$item",$docudom,
11520: $docuname,$depth,$count,
1.1056 raeburn 11521: $hierarchy,$dirorder,$children,
11522: $parent,$titles,$wantform);
1.1055 raeburn 11523: $$depth --;
1.1056 raeburn 11524: pop(@{$hierarchy});
1.1055 raeburn 11525: }
11526: }
11527: }
11528: }
11529: return $result;
11530: }
11531:
11532: sub archive_hierarchy {
11533: my ($depth,$count,$parent,$children) =@_;
11534: if ((ref($parent) eq 'HASH') && (ref($children) eq 'HASH')) {
11535: if (exists($parent->{$depth})) {
11536: $children->{$parent->{$depth}} .= $count.':';
11537: }
11538: }
11539: return;
11540: }
11541:
11542: sub archive_row {
11543: my ($is_dir,$item,$currdir,$depth,$count) = @_;
11544: my ($name) = ($item =~ m{([^/]+)$});
11545: my %choices = &Apache::lonlocal::texthash (
1.1059 raeburn 11546: 'display' => 'Add as file',
1.1055 raeburn 11547: 'dependency' => 'Include as dependency',
11548: 'discard' => 'Discard',
11549: );
11550: if ($is_dir) {
1.1059 raeburn 11551: $choices{'display'} = &mt('Add as folder');
1.1055 raeburn 11552: }
1.1056 raeburn 11553: my $output = &start_data_table_row().'<td align="right">'.$count.'</td>'."\n";
11554: my $offset = 0;
1.1055 raeburn 11555: foreach my $action ('display','dependency','discard') {
1.1056 raeburn 11556: $offset ++;
1.1065 raeburn 11557: if ($action ne 'display') {
11558: $offset ++;
11559: }
1.1055 raeburn 11560: $output .= '<td><span class="LC_nobreak">'.
11561: '<label><input type="radio" name="archive_'.$count.
11562: '" id="archive_'.$action.'_'.$count.'" value="'.$action.'"';
11563: my $text = $choices{$action};
11564: if ($is_dir) {
11565: $output .= ' onclick="javascript:propagateCheck(this.form,'."'$count'".');"';
11566: if ($action eq 'display') {
1.1059 raeburn 11567: $text = &mt('Add as folder');
1.1055 raeburn 11568: }
1.1056 raeburn 11569: } else {
11570: $output .= ' onclick="javascript:dependencyCheck(this.form,'."$count,$offset".');"';
11571:
11572: }
11573: $output .= ' /> '.$choices{$action}.'</label></span>';
11574: if ($action eq 'dependency') {
11575: $output .= '<div id="arc_depon_'.$count.'" style="display:none;">'."\n".
11576: &mt('Used by:').' <select name="archive_dependent_on_'.$count.'" '.
11577: 'onchange="propagateSelect(this.form,'."$count,$offset".')">'."\n".
11578: '<option value=""></option>'."\n".
11579: '</select>'."\n".
11580: '</div>';
1.1059 raeburn 11581: } elsif ($action eq 'display') {
11582: $output .= '<div id="arc_title_'.$count.'" style="display:none;">'."\n".
11583: &mt('Title:').' <input type="text" name="archive_title_'.$count.'" id="archive_title_'.$count.'" />'."\n".
11584: '</div>';
1.1055 raeburn 11585: }
1.1056 raeburn 11586: $output .= '</td>';
1.1055 raeburn 11587: }
11588: $output .= '<td><input type="hidden" name="archive_content_'.$count.'" value="'.
11589: &HTML::Entities::encode("$currdir/$item",'"<>&').'" />'.(' ' x 2);
11590: for (my $i=0; $i<$depth; $i++) {
11591: $output .= ('<img src="/adm/lonIcons/whitespace1.gif" class="LC_docs_spacer" alt="" />' x2)."\n";
11592: }
11593: if ($is_dir) {
11594: $output .= '<img src="/adm/lonIcons/navmap.folder.open.gif" alt="" /> '."\n".
11595: '<input type="hidden" name="archive_directory" value="'.$count.'" />'."\n";
11596: } else {
11597: $output .= '<input type="hidden" name="archive_file" value="'.$count.'" />'."\n";
11598: }
11599: $output .= ' '.$name.'</td>'."\n".
11600: &end_data_table_row();
11601: return $output;
11602: }
11603:
11604: sub archive_options_form {
1.1065 raeburn 11605: my ($form,$display,$count,$hiddenelem) = @_;
11606: my %lt = &Apache::lonlocal::texthash(
11607: perm => 'Permanently remove archive file?',
11608: hows => 'How should each extracted item be incorporated in the course?',
11609: cont => 'Content actions for all',
11610: addf => 'Add as folder/file',
11611: incd => 'Include as dependency for a displayed file',
11612: disc => 'Discard',
11613: no => 'No',
11614: yes => 'Yes',
11615: save => 'Save',
11616: );
11617: my $output = <<"END";
11618: <form name="$form" method="post" action="">
11619: <p><span class="LC_nobreak">$lt{'perm'}
11620: <label>
11621: <input type="radio" name="archivedelete" value="0" checked="checked" />$lt{'no'}
11622: </label>
11623:
11624: <label>
11625: <input type="radio" name="archivedelete" value="1" />$lt{'yes'}</label>
11626: </span>
11627: </p>
11628: <input type="hidden" name="phase" value="decompress_cleanup" />
11629: <br />$lt{'hows'}
11630: <div class="LC_columnSection">
11631: <fieldset>
11632: <legend>$lt{'cont'}</legend>
11633: <input type="button" value="$lt{'addf'}" onclick="javascript:checkAll(document.$form,'display');" />
11634: <input type="button" value="$lt{'incd'}" onclick="javascript:checkAll(document.$form,'dependency');" />
11635: <input type="button" value="$lt{'disc'}" onclick="javascript:checkAll(document.$form,'discard');" />
11636: </fieldset>
11637: </div>
11638: END
11639: return $output.
1.1055 raeburn 11640: &start_data_table()."\n".
1.1065 raeburn 11641: $display."\n".
1.1055 raeburn 11642: &end_data_table()."\n".
11643: '<input type="hidden" name="archive_count" value="'.$count.'" />'.
11644: $hiddenelem.
1.1065 raeburn 11645: '<br /><input type="submit" name="archive_submit" value="'.$lt{'save'}.'" />'.
1.1055 raeburn 11646: '</form>';
11647: }
11648:
11649: sub archive_javascript {
1.1056 raeburn 11650: my ($startcount,$numitems,$titles,$children) = @_;
11651: return unless ((ref($titles) eq 'HASH') && (ref($children) eq 'HASH'));
1.1059 raeburn 11652: my $maintitle = $env{'form.comment'};
1.1055 raeburn 11653: my $scripttag = <<START;
11654: <script type="text/javascript">
11655: // <![CDATA[
11656:
11657: function checkAll(form,prefix) {
11658: var idstr = new RegExp("^archive_"+prefix+"_\\\\d+\$");
11659: for (var i=0; i < form.elements.length; i++) {
11660: var id = form.elements[i].id;
11661: if ((id != '') && (id != undefined)) {
11662: if (idstr.test(id)) {
11663: if (form.elements[i].type == 'radio') {
11664: form.elements[i].checked = true;
1.1056 raeburn 11665: var nostart = i-$startcount;
1.1059 raeburn 11666: var offset = nostart%7;
11667: var count = (nostart-offset)/7;
1.1056 raeburn 11668: dependencyCheck(form,count,offset);
1.1055 raeburn 11669: }
11670: }
11671: }
11672: }
11673: }
11674:
11675: function propagateCheck(form,count) {
11676: if (count > 0) {
1.1059 raeburn 11677: var startelement = $startcount + ((count-1) * 7);
11678: for (var j=1; j<6; j++) {
11679: if ((j != 2) && (j != 4)) {
1.1056 raeburn 11680: var item = startelement + j;
11681: if (form.elements[item].type == 'radio') {
11682: if (form.elements[item].checked) {
11683: containerCheck(form,count,j);
11684: break;
11685: }
1.1055 raeburn 11686: }
11687: }
11688: }
11689: }
11690: }
11691:
11692: numitems = $numitems
1.1056 raeburn 11693: var titles = new Array(numitems);
11694: var parents = new Array(numitems);
1.1055 raeburn 11695: for (var i=0; i<numitems; i++) {
1.1056 raeburn 11696: parents[i] = new Array;
1.1055 raeburn 11697: }
1.1059 raeburn 11698: var maintitle = '$maintitle';
1.1055 raeburn 11699:
11700: START
11701:
1.1056 raeburn 11702: foreach my $container (sort { $a <=> $b } (keys(%{$children}))) {
11703: my @contents = split(/:/,$children->{$container});
1.1055 raeburn 11704: for (my $i=0; $i<@contents; $i ++) {
11705: $scripttag .= 'parents['.$container.']['.$i.'] = '.$contents[$i]."\n";
11706: }
11707: }
11708:
1.1056 raeburn 11709: foreach my $key (sort { $a <=> $b } (keys(%{$titles}))) {
11710: $scripttag .= "titles[$key] = '".$titles->{$key}."';\n";
11711: }
11712:
1.1055 raeburn 11713: $scripttag .= <<END;
11714:
11715: function containerCheck(form,count,offset) {
11716: if (count > 0) {
1.1056 raeburn 11717: dependencyCheck(form,count,offset);
1.1059 raeburn 11718: var item = (offset+$startcount)+7*(count-1);
1.1055 raeburn 11719: form.elements[item].checked = true;
11720: if(Object.prototype.toString.call(parents[count]) === '[object Array]') {
11721: if (parents[count].length > 0) {
11722: for (var j=0; j<parents[count].length; j++) {
1.1056 raeburn 11723: containerCheck(form,parents[count][j],offset);
11724: }
11725: }
11726: }
11727: }
11728: }
11729:
11730: function dependencyCheck(form,count,offset) {
11731: if (count > 0) {
1.1059 raeburn 11732: var chosen = (offset+$startcount)+7*(count-1);
11733: var depitem = $startcount + ((count-1) * 7) + 4;
1.1056 raeburn 11734: var currtype = form.elements[depitem].type;
11735: if (form.elements[chosen].value == 'dependency') {
11736: document.getElementById('arc_depon_'+count).style.display='block';
11737: form.elements[depitem].options.length = 0;
11738: form.elements[depitem].options[0] = new Option('Select','',true,true);
1.1085 raeburn 11739: for (var i=1; i<=numitems; i++) {
11740: if (i == count) {
11741: continue;
11742: }
1.1059 raeburn 11743: var startelement = $startcount + (i-1) * 7;
11744: for (var j=1; j<6; j++) {
11745: if ((j != 2) && (j!= 4)) {
1.1056 raeburn 11746: var item = startelement + j;
11747: if (form.elements[item].type == 'radio') {
11748: if (form.elements[item].checked) {
11749: if (form.elements[item].value == 'display') {
11750: var n = form.elements[depitem].options.length;
11751: form.elements[depitem].options[n] = new Option(titles[i],i,false,false);
11752: }
11753: }
11754: }
11755: }
11756: }
11757: }
11758: } else {
11759: document.getElementById('arc_depon_'+count).style.display='none';
11760: form.elements[depitem].options.length = 0;
11761: form.elements[depitem].options[0] = new Option('Select','',true,true);
11762: }
1.1059 raeburn 11763: titleCheck(form,count,offset);
1.1056 raeburn 11764: }
11765: }
11766:
11767: function propagateSelect(form,count,offset) {
11768: if (count > 0) {
1.1065 raeburn 11769: var item = (1+offset+$startcount)+7*(count-1);
1.1056 raeburn 11770: var picked = form.elements[item].options[form.elements[item].selectedIndex].value;
11771: if (Object.prototype.toString.call(parents[count]) === '[object Array]') {
11772: if (parents[count].length > 0) {
11773: for (var j=0; j<parents[count].length; j++) {
11774: containerSelect(form,parents[count][j],offset,picked);
1.1055 raeburn 11775: }
11776: }
11777: }
11778: }
11779: }
1.1056 raeburn 11780:
11781: function containerSelect(form,count,offset,picked) {
11782: if (count > 0) {
1.1065 raeburn 11783: var item = (offset+$startcount)+7*(count-1);
1.1056 raeburn 11784: if (form.elements[item].type == 'radio') {
11785: if (form.elements[item].value == 'dependency') {
11786: if (form.elements[item+1].type == 'select-one') {
11787: for (var i=0; i<form.elements[item+1].options.length; i++) {
11788: if (form.elements[item+1].options[i].value == picked) {
11789: form.elements[item+1].selectedIndex = i;
11790: break;
11791: }
11792: }
11793: }
11794: if (Object.prototype.toString.call(parents[count]) === '[object Array]') {
11795: if (parents[count].length > 0) {
11796: for (var j=0; j<parents[count].length; j++) {
11797: containerSelect(form,parents[count][j],offset,picked);
11798: }
11799: }
11800: }
11801: }
11802: }
11803: }
11804: }
11805:
1.1059 raeburn 11806: function titleCheck(form,count,offset) {
11807: if (count > 0) {
11808: var chosen = (offset+$startcount)+7*(count-1);
11809: var depitem = $startcount + ((count-1) * 7) + 2;
11810: var currtype = form.elements[depitem].type;
11811: if (form.elements[chosen].value == 'display') {
11812: document.getElementById('arc_title_'+count).style.display='block';
11813: if ((count==1) && ((parents[count].length > 0) || (numitems == 1))) {
11814: document.getElementById('archive_title_'+count).value=maintitle;
11815: }
11816: } else {
11817: document.getElementById('arc_title_'+count).style.display='none';
11818: if (currtype == 'text') {
11819: document.getElementById('archive_title_'+count).value='';
11820: }
11821: }
11822: }
11823: return;
11824: }
11825:
1.1055 raeburn 11826: // ]]>
11827: </script>
11828: END
11829: return $scripttag;
11830: }
11831:
11832: sub process_extracted_files {
1.1067 raeburn 11833: my ($context,$docudom,$docuname,$destination,$dir_root,$hiddenelem) = @_;
1.1055 raeburn 11834: my $numitems = $env{'form.archive_count'};
11835: return unless ($numitems);
11836: my @ids=&Apache::lonnet::current_machine_ids();
11837: my ($prefix,$pathtocheck,$dir,$ishome,$error,$warning,%toplevelitems,%is_dir,
1.1067 raeburn 11838: %folders,%containers,%mapinner,%prompttofetch);
1.1055 raeburn 11839: my $docuhome = &Apache::lonnet::homeserver($docuname,$docudom);
11840: if (grep(/^\Q$docuhome\E$/,@ids)) {
11841: $prefix = &LONCAPA::propath($docudom,$docuname);
11842: $pathtocheck = "$dir_root/$destination";
11843: $dir = $dir_root;
11844: $ishome = 1;
11845: } else {
11846: $prefix = $Apache::lonnet::perlvar{'lonDocRoot'};
11847: $pathtocheck = "$dir_root/$docudom/$docuname/$destination";
11848: $dir = "$dir_root/$docudom/$docuname";
11849: }
11850: my $currdir = "$dir_root/$destination";
11851: (my $docstype,$mapinner{'0'}) = ($destination =~ m{^(docs|supplemental)/(\w+)/});
11852: if ($env{'form.folderpath'}) {
11853: my @items = split('&',$env{'form.folderpath'});
11854: $folders{'0'} = $items[-2];
1.1099 raeburn 11855: if ($env{'form.folderpath'} =~ /\:1$/) {
11856: $containers{'0'}='page';
11857: } else {
11858: $containers{'0'}='sequence';
11859: }
1.1055 raeburn 11860: }
11861: my @archdirs = &get_env_multiple('form.archive_directory');
11862: if ($numitems) {
11863: for (my $i=1; $i<=$numitems; $i++) {
11864: my $path = $env{'form.archive_content_'.$i};
11865: if ($path =~ m{^\Q$pathtocheck\E/([^/]+)$}) {
11866: my $item = $1;
11867: $toplevelitems{$item} = $i;
11868: if (grep(/^\Q$i\E$/,@archdirs)) {
11869: $is_dir{$item} = 1;
11870: }
11871: }
11872: }
11873: }
1.1067 raeburn 11874: my ($output,%children,%parent,%titles,%dirorder,$result);
1.1055 raeburn 11875: if (keys(%toplevelitems) > 0) {
11876: my @contents = sort(keys(%toplevelitems));
1.1056 raeburn 11877: (my $count,undef) = &get_extracted($docudom,$docuname,$currdir,\%is_dir,\%children,
11878: \%parent,\@contents,\%dirorder,\%titles);
1.1055 raeburn 11879: }
1.1066 raeburn 11880: my (%referrer,%orphaned,%todelete,%todeletedir,%newdest,%newseqid);
1.1055 raeburn 11881: if ($numitems) {
11882: for (my $i=1; $i<=$numitems; $i++) {
1.1086 raeburn 11883: next if ($env{'form.archive_'.$i} eq 'dependency');
1.1055 raeburn 11884: my $path = $env{'form.archive_content_'.$i};
11885: if ($path =~ /^\Q$pathtocheck\E/) {
11886: if ($env{'form.archive_'.$i} eq 'discard') {
11887: if ($prefix ne '' && $path ne '') {
11888: if (-e $prefix.$path) {
1.1066 raeburn 11889: if ((@archdirs > 0) &&
11890: (grep(/^\Q$i\E$/,@archdirs))) {
11891: $todeletedir{$prefix.$path} = 1;
11892: } else {
11893: $todelete{$prefix.$path} = 1;
11894: }
1.1055 raeburn 11895: }
11896: }
11897: } elsif ($env{'form.archive_'.$i} eq 'display') {
1.1059 raeburn 11898: my ($docstitle,$title,$url,$outer);
1.1055 raeburn 11899: ($title) = ($path =~ m{/([^/]+)$});
1.1059 raeburn 11900: $docstitle = $env{'form.archive_title_'.$i};
11901: if ($docstitle eq '') {
11902: $docstitle = $title;
11903: }
1.1055 raeburn 11904: $outer = 0;
1.1056 raeburn 11905: if (ref($dirorder{$i}) eq 'ARRAY') {
11906: if (@{$dirorder{$i}} > 0) {
11907: foreach my $item (reverse(@{$dirorder{$i}})) {
1.1055 raeburn 11908: if ($env{'form.archive_'.$item} eq 'display') {
11909: $outer = $item;
11910: last;
11911: }
11912: }
11913: }
11914: }
11915: my ($errtext,$fatal) =
11916: &LONCAPA::map::mapread('/uploaded/'.$docudom.'/'.$docuname.
11917: '/'.$folders{$outer}.'.'.
11918: $containers{$outer});
11919: next if ($fatal);
11920: if ((@archdirs > 0) && (grep(/^\Q$i\E$/,@archdirs))) {
11921: if ($context eq 'coursedocs') {
1.1056 raeburn 11922: $mapinner{$i} = time;
1.1055 raeburn 11923: $folders{$i} = 'default_'.$mapinner{$i};
11924: $containers{$i} = 'sequence';
11925: my $url = '/uploaded/'.$docudom.'/'.$docuname.'/'.
11926: $folders{$i}.'.'.$containers{$i};
11927: my $newidx = &LONCAPA::map::getresidx();
11928: $LONCAPA::map::resources[$newidx]=
1.1059 raeburn 11929: $docstitle.':'.$url.':false:normal:res';
1.1055 raeburn 11930: push(@LONCAPA::map::order,$newidx);
11931: my ($outtext,$errtext) =
11932: &LONCAPA::map::storemap('/uploaded/'.$docudom.'/'.
11933: $docuname.'/'.$folders{$outer}.
1.1087 raeburn 11934: '.'.$containers{$outer},1,1);
1.1056 raeburn 11935: $newseqid{$i} = $newidx;
1.1067 raeburn 11936: unless ($errtext) {
11937: $result .= '<li>'.&mt('Folder: [_1] added to course',$docstitle).'</li>'."\n";
11938: }
1.1055 raeburn 11939: }
11940: } else {
11941: if ($context eq 'coursedocs') {
11942: my $newidx=&LONCAPA::map::getresidx();
11943: my $url = '/uploaded/'.$docudom.'/'.$docuname.'/'.
11944: $docstype.'/'.$mapinner{$outer}.'/'.$newidx.'/'.
11945: $title;
11946: if (!-e "$prefix$dir/$docstype/$mapinner{$outer}") {
11947: mkdir("$prefix$dir/$docstype/$mapinner{$outer}",0755);
11948: }
11949: if (!-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx") {
11950: mkdir("$prefix$dir/$docstype/$mapinner{$outer}/$newidx");
11951: }
11952: if (-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx") {
11953: system("mv $prefix$path $prefix$dir/$docstype/$mapinner{$outer}/$newidx/$title");
1.1056 raeburn 11954: $newdest{$i} = "$prefix$dir/$docstype/$mapinner{$outer}/$newidx";
1.1067 raeburn 11955: unless ($ishome) {
11956: my $fetch = "$newdest{$i}/$title";
11957: $fetch =~ s/^\Q$prefix$dir\E//;
11958: $prompttofetch{$fetch} = 1;
11959: }
1.1055 raeburn 11960: }
11961: $LONCAPA::map::resources[$newidx]=
1.1059 raeburn 11962: $docstitle.':'.$url.':false:normal:res';
1.1055 raeburn 11963: push(@LONCAPA::map::order, $newidx);
11964: my ($outtext,$errtext)=
11965: &LONCAPA::map::storemap('/uploaded/'.$docudom.'/'.
11966: $docuname.'/'.$folders{$outer}.
1.1087 raeburn 11967: '.'.$containers{$outer},1,1);
1.1067 raeburn 11968: unless ($errtext) {
11969: if (-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx/$title") {
11970: $result .= '<li>'.&mt('File: [_1] added to course',$docstitle).'</li>'."\n";
11971: }
11972: }
1.1055 raeburn 11973: }
11974: }
1.1086 raeburn 11975: }
11976: } else {
11977: $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',$path).'<br />';
11978: }
11979: }
11980: for (my $i=1; $i<=$numitems; $i++) {
11981: next unless ($env{'form.archive_'.$i} eq 'dependency');
11982: my $path = $env{'form.archive_content_'.$i};
11983: if ($path =~ /^\Q$pathtocheck\E/) {
11984: my ($title) = ($path =~ m{/([^/]+)$});
11985: $referrer{$i} = $env{'form.archive_dependent_on_'.$i};
11986: if ($env{'form.archive_'.$referrer{$i}} eq 'display') {
11987: if (ref($dirorder{$i}) eq 'ARRAY') {
11988: my ($itemidx,$fullpath,$relpath);
11989: if (ref($dirorder{$referrer{$i}}) eq 'ARRAY') {
11990: my $container = $dirorder{$referrer{$i}}->[-1];
1.1056 raeburn 11991: for (my $j=0; $j<@{$dirorder{$i}}; $j++) {
1.1086 raeburn 11992: if ($dirorder{$i}->[$j] eq $container) {
11993: $itemidx = $j;
1.1056 raeburn 11994: }
11995: }
1.1086 raeburn 11996: }
11997: if ($itemidx eq '') {
11998: $itemidx = 0;
11999: }
12000: if (grep(/^\Q$referrer{$i}\E$/,@archdirs)) {
12001: if ($mapinner{$referrer{$i}}) {
12002: $fullpath = "$prefix$dir/$docstype/$mapinner{$referrer{$i}}";
12003: for (my $j=$itemidx; $j<@{$dirorder{$i}}; $j++) {
12004: if (grep(/^\Q$dirorder{$i}->[$j]\E$/,@archdirs)) {
12005: unless (defined($newseqid{$dirorder{$i}->[$j]})) {
12006: $fullpath .= '/'.$titles{$dirorder{$i}->[$j]};
12007: $relpath .= '/'.$titles{$dirorder{$i}->[$j]};
12008: if (!-e $fullpath) {
12009: mkdir($fullpath,0755);
1.1056 raeburn 12010: }
12011: }
1.1086 raeburn 12012: } else {
12013: last;
1.1056 raeburn 12014: }
1.1086 raeburn 12015: }
12016: }
12017: } elsif ($newdest{$referrer{$i}}) {
12018: $fullpath = $newdest{$referrer{$i}};
12019: for (my $j=$itemidx; $j<@{$dirorder{$i}}; $j++) {
12020: if ($env{'form.archive_'.$dirorder{$i}->[$j]} eq 'discard') {
12021: $orphaned{$i} = $env{'form.archive_'.$dirorder{$i}->[$j]};
12022: last;
12023: } elsif (grep(/^\Q$dirorder{$i}->[$j]\E$/,@archdirs)) {
12024: unless (defined($newseqid{$dirorder{$i}->[$j]})) {
12025: $fullpath .= '/'.$titles{$dirorder{$i}->[$j]};
12026: $relpath .= '/'.$titles{$dirorder{$i}->[$j]};
12027: if (!-e $fullpath) {
12028: mkdir($fullpath,0755);
1.1056 raeburn 12029: }
12030: }
1.1086 raeburn 12031: } else {
12032: last;
1.1056 raeburn 12033: }
1.1055 raeburn 12034: }
12035: }
1.1086 raeburn 12036: if ($fullpath ne '') {
12037: if (-e "$prefix$path") {
12038: system("mv $prefix$path $fullpath/$title");
12039: }
12040: if (-e "$fullpath/$title") {
12041: my $showpath;
12042: if ($relpath ne '') {
12043: $showpath = "$relpath/$title";
12044: } else {
12045: $showpath = "/$title";
12046: }
12047: $result .= '<li>'.&mt('[_1] included as a dependency',$showpath).'</li>'."\n";
12048: }
12049: unless ($ishome) {
12050: my $fetch = "$fullpath/$title";
12051: $fetch =~ s/^\Q$prefix$dir\E//;
12052: $prompttofetch{$fetch} = 1;
12053: }
12054: }
1.1055 raeburn 12055: }
1.1086 raeburn 12056: } elsif ($env{'form.archive_'.$referrer{$i}} eq 'discard') {
12057: $warning .= &mt('[_1] is a dependency of [_2], which was discarded.',
12058: $path,$env{'form.archive_content_'.$referrer{$i}}).'<br />';
1.1055 raeburn 12059: }
12060: } else {
12061: $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',$path).'<br />';
12062: }
12063: }
12064: if (keys(%todelete)) {
12065: foreach my $key (keys(%todelete)) {
12066: unlink($key);
1.1066 raeburn 12067: }
12068: }
12069: if (keys(%todeletedir)) {
12070: foreach my $key (keys(%todeletedir)) {
12071: rmdir($key);
12072: }
12073: }
12074: foreach my $dir (sort(keys(%is_dir))) {
12075: if (($pathtocheck ne '') && ($dir ne '')) {
12076: &cleanup_empty_dirs($prefix."$pathtocheck/$dir");
1.1055 raeburn 12077: }
12078: }
1.1067 raeburn 12079: if ($result ne '') {
12080: $output .= '<ul>'."\n".
12081: $result."\n".
12082: '</ul>';
12083: }
12084: unless ($ishome) {
12085: my $replicationfail;
12086: foreach my $item (keys(%prompttofetch)) {
12087: my $fetchresult= &Apache::lonnet::reply('fetchuserfile:'.$item,$docuhome);
12088: unless ($fetchresult eq 'ok') {
12089: $replicationfail .= '<li>'.$item.'</li>'."\n";
12090: }
12091: }
12092: if ($replicationfail) {
12093: $output .= '<p class="LC_error">'.
12094: &mt('Course home server failed to retrieve:').'<ul>'.
12095: $replicationfail.
12096: '</ul></p>';
12097: }
12098: }
1.1055 raeburn 12099: } else {
12100: $warning = &mt('No items found in archive.');
12101: }
12102: if ($error) {
12103: $output .= '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
12104: $error.'</p>'."\n";
12105: }
12106: if ($warning) {
12107: $output .= '<p class="LC_warning">'.$warning.'</p>'."\n";
12108: }
12109: return $output;
12110: }
12111:
1.1066 raeburn 12112: sub cleanup_empty_dirs {
12113: my ($path) = @_;
12114: if (($path ne '') && (-d $path)) {
12115: if (opendir(my $dirh,$path)) {
12116: my @dircontents = grep(!/^\./,readdir($dirh));
12117: my $numitems = 0;
12118: foreach my $item (@dircontents) {
12119: if (-d "$path/$item") {
1.1111 raeburn 12120: &cleanup_empty_dirs("$path/$item");
1.1066 raeburn 12121: if (-e "$path/$item") {
12122: $numitems ++;
12123: }
12124: } else {
12125: $numitems ++;
12126: }
12127: }
12128: if ($numitems == 0) {
12129: rmdir($path);
12130: }
12131: closedir($dirh);
12132: }
12133: }
12134: return;
12135: }
12136:
1.41 ng 12137: =pod
1.45 matthew 12138:
1.1162 raeburn 12139: =item * &get_folder_hierarchy()
1.1068 raeburn 12140:
12141: Provides hierarchy of names of folders/sub-folders containing the current
12142: item,
12143:
12144: Inputs: 3
12145: - $navmap - navmaps object
12146:
12147: - $map - url for map (either the trigger itself, or map containing
12148: the resource, which is the trigger).
12149:
12150: - $showitem - 1 => show title for map itself; 0 => do not show.
12151:
12152: Outputs: 1 @pathitems - array of folder/subfolder names.
12153:
12154: =cut
12155:
12156: sub get_folder_hierarchy {
12157: my ($navmap,$map,$showitem) = @_;
12158: my @pathitems;
12159: if (ref($navmap)) {
12160: my $mapres = $navmap->getResourceByUrl($map);
12161: if (ref($mapres)) {
12162: my $pcslist = $mapres->map_hierarchy();
12163: if ($pcslist ne '') {
12164: my @pcs = split(/,/,$pcslist);
12165: foreach my $pc (@pcs) {
12166: if ($pc == 1) {
1.1129 raeburn 12167: push(@pathitems,&mt('Main Content'));
1.1068 raeburn 12168: } else {
12169: my $res = $navmap->getByMapPc($pc);
12170: if (ref($res)) {
12171: my $title = $res->compTitle();
12172: $title =~ s/\W+/_/g;
12173: if ($title ne '') {
12174: push(@pathitems,$title);
12175: }
12176: }
12177: }
12178: }
12179: }
1.1071 raeburn 12180: if ($showitem) {
12181: if ($mapres->{ID} eq '0.0') {
1.1129 raeburn 12182: push(@pathitems,&mt('Main Content'));
1.1071 raeburn 12183: } else {
12184: my $maptitle = $mapres->compTitle();
12185: $maptitle =~ s/\W+/_/g;
12186: if ($maptitle ne '') {
12187: push(@pathitems,$maptitle);
12188: }
1.1068 raeburn 12189: }
12190: }
12191: }
12192: }
12193: return @pathitems;
12194: }
12195:
12196: =pod
12197:
1.1015 raeburn 12198: =item * &get_turnedin_filepath()
12199:
12200: Determines path in a user's portfolio file for storage of files uploaded
12201: to a specific essayresponse or dropbox item.
12202:
12203: Inputs: 3 required + 1 optional.
12204: $symb is symb for resource, $uname and $udom are for current user (required).
12205: $caller is optional (can be "submission", if routine is called when storing
12206: an upoaded file when "Submit Answer" button was pressed).
12207:
12208: Returns array containing $path and $multiresp.
12209: $path is path in portfolio. $multiresp is 1 if this resource contains more
12210: than one file upload item. Callers of routine should append partid as a
12211: subdirectory to $path in cases where $multiresp is 1.
12212:
12213: Called by: homework/essayresponse.pm and homework/structuretags.pm
12214:
12215: =cut
12216:
12217: sub get_turnedin_filepath {
12218: my ($symb,$uname,$udom,$caller) = @_;
12219: my ($map,$resid,$resurl)=&Apache::lonnet::decode_symb($symb);
12220: my $turnindir;
12221: my %userhash = &Apache::lonnet::userenvironment($udom,$uname,'turnindir');
12222: $turnindir = $userhash{'turnindir'};
12223: my ($path,$multiresp);
12224: if ($turnindir eq '') {
12225: if ($caller eq 'submission') {
12226: $turnindir = &mt('turned in');
12227: $turnindir =~ s/\W+/_/g;
12228: my %newhash = (
12229: 'turnindir' => $turnindir,
12230: );
12231: &Apache::lonnet::put('environment',\%newhash,$udom,$uname);
12232: }
12233: }
12234: if ($turnindir ne '') {
12235: $path = '/'.$turnindir.'/';
12236: my ($multipart,$turnin,@pathitems);
12237: my $navmap = Apache::lonnavmaps::navmap->new();
12238: if (defined($navmap)) {
12239: my $mapres = $navmap->getResourceByUrl($map);
12240: if (ref($mapres)) {
12241: my $pcslist = $mapres->map_hierarchy();
12242: if ($pcslist ne '') {
12243: foreach my $pc (split(/,/,$pcslist)) {
12244: my $res = $navmap->getByMapPc($pc);
12245: if (ref($res)) {
12246: my $title = $res->compTitle();
12247: $title =~ s/\W+/_/g;
12248: if ($title ne '') {
1.1149 raeburn 12249: if (($pc > 1) && (length($title) > 12)) {
12250: $title = substr($title,0,12);
12251: }
1.1015 raeburn 12252: push(@pathitems,$title);
12253: }
12254: }
12255: }
12256: }
12257: my $maptitle = $mapres->compTitle();
12258: $maptitle =~ s/\W+/_/g;
12259: if ($maptitle ne '') {
1.1149 raeburn 12260: if (length($maptitle) > 12) {
12261: $maptitle = substr($maptitle,0,12);
12262: }
1.1015 raeburn 12263: push(@pathitems,$maptitle);
12264: }
12265: unless ($env{'request.state'} eq 'construct') {
12266: my $res = $navmap->getBySymb($symb);
12267: if (ref($res)) {
12268: my $partlist = $res->parts();
12269: my $totaluploads = 0;
12270: if (ref($partlist) eq 'ARRAY') {
12271: foreach my $part (@{$partlist}) {
12272: my @types = $res->responseType($part);
12273: my @ids = $res->responseIds($part);
12274: for (my $i=0; $i < scalar(@ids); $i++) {
12275: if ($types[$i] eq 'essay') {
12276: my $partid = $part.'_'.$ids[$i];
12277: if (&Apache::lonnet::EXT("resource.$partid.uploadedfiletypes") ne '') {
12278: $totaluploads ++;
12279: }
12280: }
12281: }
12282: }
12283: if ($totaluploads > 1) {
12284: $multiresp = 1;
12285: }
12286: }
12287: }
12288: }
12289: } else {
12290: return;
12291: }
12292: } else {
12293: return;
12294: }
12295: my $restitle=&Apache::lonnet::gettitle($symb);
12296: $restitle =~ s/\W+/_/g;
12297: if ($restitle eq '') {
12298: $restitle = ($resurl =~ m{/[^/]+$});
12299: if ($restitle eq '') {
12300: $restitle = time;
12301: }
12302: }
1.1149 raeburn 12303: if (length($restitle) > 12) {
12304: $restitle = substr($restitle,0,12);
12305: }
1.1015 raeburn 12306: push(@pathitems,$restitle);
12307: $path .= join('/',@pathitems);
12308: }
12309: return ($path,$multiresp);
12310: }
12311:
12312: =pod
12313:
1.464 albertel 12314: =back
1.41 ng 12315:
1.112 bowersj2 12316: =head1 CSV Upload/Handling functions
1.38 albertel 12317:
1.41 ng 12318: =over 4
12319:
1.648 raeburn 12320: =item * &upfile_store($r)
1.41 ng 12321:
12322: Store uploaded file, $r should be the HTTP Request object,
1.258 albertel 12323: needs $env{'form.upfile'}
1.41 ng 12324: returns $datatoken to be put into hidden field
12325:
12326: =cut
1.31 albertel 12327:
12328: sub upfile_store {
12329: my $r=shift;
1.258 albertel 12330: $env{'form.upfile'}=~s/\r/\n/gs;
12331: $env{'form.upfile'}=~s/\f/\n/gs;
12332: $env{'form.upfile'}=~s/\n+/\n/gs;
12333: $env{'form.upfile'}=~s/\n+$//gs;
1.31 albertel 12334:
1.258 albertel 12335: my $datatoken=$env{'user.name'}.'_'.$env{'user.domain'}.
12336: '_enroll_'.$env{'request.course.id'}.'_'.time.'_'.$$;
1.31 albertel 12337: {
1.158 raeburn 12338: my $datafile = $r->dir_config('lonDaemons').
12339: '/tmp/'.$datatoken.'.tmp';
12340: if ( open(my $fh,">$datafile") ) {
1.258 albertel 12341: print $fh $env{'form.upfile'};
1.158 raeburn 12342: close($fh);
12343: }
1.31 albertel 12344: }
12345: return $datatoken;
12346: }
12347:
1.56 matthew 12348: =pod
12349:
1.648 raeburn 12350: =item * &load_tmp_file($r)
1.41 ng 12351:
12352: Load uploaded file from tmp, $r should be the HTTP Request object,
1.258 albertel 12353: needs $env{'form.datatoken'},
12354: sets $env{'form.upfile'} to the contents of the file
1.41 ng 12355:
12356: =cut
1.31 albertel 12357:
12358: sub load_tmp_file {
12359: my $r=shift;
12360: my @studentdata=();
12361: {
1.158 raeburn 12362: my $studentfile = $r->dir_config('lonDaemons').
1.258 albertel 12363: '/tmp/'.$env{'form.datatoken'}.'.tmp';
1.158 raeburn 12364: if ( open(my $fh,"<$studentfile") ) {
12365: @studentdata=<$fh>;
12366: close($fh);
12367: }
1.31 albertel 12368: }
1.258 albertel 12369: $env{'form.upfile'}=join('',@studentdata);
1.31 albertel 12370: }
12371:
1.56 matthew 12372: =pod
12373:
1.648 raeburn 12374: =item * &upfile_record_sep()
1.41 ng 12375:
12376: Separate uploaded file into records
12377: returns array of records,
1.258 albertel 12378: needs $env{'form.upfile'} and $env{'form.upfiletype'}
1.41 ng 12379:
12380: =cut
1.31 albertel 12381:
12382: sub upfile_record_sep {
1.258 albertel 12383: if ($env{'form.upfiletype'} eq 'xml') {
1.31 albertel 12384: } else {
1.248 albertel 12385: my @records;
1.258 albertel 12386: foreach my $line (split(/\n/,$env{'form.upfile'})) {
1.248 albertel 12387: if ($line=~/^\s*$/) { next; }
12388: push(@records,$line);
12389: }
12390: return @records;
1.31 albertel 12391: }
12392: }
12393:
1.56 matthew 12394: =pod
12395:
1.648 raeburn 12396: =item * &record_sep($record)
1.41 ng 12397:
1.258 albertel 12398: Separate a record into fields $record should be an item from the upfile_record_sep(), needs $env{'form.upfiletype'}
1.41 ng 12399:
12400: =cut
12401:
1.263 www 12402: sub takeleft {
12403: my $index=shift;
12404: return substr('0000'.$index,-4,4);
12405: }
12406:
1.31 albertel 12407: sub record_sep {
12408: my $record=shift;
12409: my %components=();
1.258 albertel 12410: if ($env{'form.upfiletype'} eq 'xml') {
12411: } elsif ($env{'form.upfiletype'} eq 'space') {
1.31 albertel 12412: my $i=0;
1.356 albertel 12413: foreach my $field (split(/\s+/,$record)) {
1.31 albertel 12414: $field=~s/^(\"|\')//;
12415: $field=~s/(\"|\')$//;
1.263 www 12416: $components{&takeleft($i)}=$field;
1.31 albertel 12417: $i++;
12418: }
1.258 albertel 12419: } elsif ($env{'form.upfiletype'} eq 'tab') {
1.31 albertel 12420: my $i=0;
1.356 albertel 12421: foreach my $field (split(/\t/,$record)) {
1.31 albertel 12422: $field=~s/^(\"|\')//;
12423: $field=~s/(\"|\')$//;
1.263 www 12424: $components{&takeleft($i)}=$field;
1.31 albertel 12425: $i++;
12426: }
12427: } else {
1.561 www 12428: my $separator=',';
1.480 banghart 12429: if ($env{'form.upfiletype'} eq 'semisv') {
1.561 www 12430: $separator=';';
1.480 banghart 12431: }
1.31 albertel 12432: my $i=0;
1.561 www 12433: # the character we are looking for to indicate the end of a quote or a record
12434: my $looking_for=$separator;
12435: # do not add the characters to the fields
12436: my $ignore=0;
12437: # we just encountered a separator (or the beginning of the record)
12438: my $just_found_separator=1;
12439: # store the field we are working on here
12440: my $field='';
12441: # work our way through all characters in record
12442: foreach my $character ($record=~/(.)/g) {
12443: if ($character eq $looking_for) {
12444: if ($character ne $separator) {
12445: # Found the end of a quote, again looking for separator
12446: $looking_for=$separator;
12447: $ignore=1;
12448: } else {
12449: # Found a separator, store away what we got
12450: $components{&takeleft($i)}=$field;
12451: $i++;
12452: $just_found_separator=1;
12453: $ignore=0;
12454: $field='';
12455: }
12456: next;
12457: }
12458: # single or double quotation marks after a separator indicate beginning of a quote
12459: # we are now looking for the end of the quote and need to ignore separators
12460: if ((($character eq '"') || ($character eq "'")) && ($just_found_separator)) {
12461: $looking_for=$character;
12462: next;
12463: }
12464: # ignore would be true after we reached the end of a quote
12465: if ($ignore) { next; }
12466: if (($just_found_separator) && ($character=~/\s/)) { next; }
12467: $field.=$character;
12468: $just_found_separator=0;
1.31 albertel 12469: }
1.561 www 12470: # catch the very last entry, since we never encountered the separator
12471: $components{&takeleft($i)}=$field;
1.31 albertel 12472: }
12473: return %components;
12474: }
12475:
1.144 matthew 12476: ######################################################
12477: ######################################################
12478:
1.56 matthew 12479: =pod
12480:
1.648 raeburn 12481: =item * &upfile_select_html()
1.41 ng 12482:
1.144 matthew 12483: Return HTML code to select a file from the users machine and specify
12484: the file type.
1.41 ng 12485:
12486: =cut
12487:
1.144 matthew 12488: ######################################################
12489: ######################################################
1.31 albertel 12490: sub upfile_select_html {
1.144 matthew 12491: my %Types = (
12492: csv => &mt('CSV (comma separated values, spreadsheet)'),
1.480 banghart 12493: semisv => &mt('Semicolon separated values'),
1.144 matthew 12494: space => &mt('Space separated'),
12495: tab => &mt('Tabulator separated'),
12496: # xml => &mt('HTML/XML'),
12497: );
12498: my $Str = '<input type="file" name="upfile" size="50" />'.
1.727 riegler 12499: '<br />'.&mt('Type').': <select name="upfiletype">';
1.144 matthew 12500: foreach my $type (sort(keys(%Types))) {
12501: $Str .= '<option value="'.$type.'" >'.$Types{$type}."</option>\n";
12502: }
12503: $Str .= "</select>\n";
12504: return $Str;
1.31 albertel 12505: }
12506:
1.301 albertel 12507: sub get_samples {
12508: my ($records,$toget) = @_;
12509: my @samples=({});
12510: my $got=0;
12511: foreach my $rec (@$records) {
12512: my %temp = &record_sep($rec);
12513: if (! grep(/\S/, values(%temp))) { next; }
12514: if (%temp) {
12515: $samples[$got]=\%temp;
12516: $got++;
12517: if ($got == $toget) { last; }
12518: }
12519: }
12520: return \@samples;
12521: }
12522:
1.144 matthew 12523: ######################################################
12524: ######################################################
12525:
1.56 matthew 12526: =pod
12527:
1.648 raeburn 12528: =item * &csv_print_samples($r,$records)
1.41 ng 12529:
12530: Prints a table of sample values from each column uploaded $r is an
12531: Apache Request ref, $records is an arrayref from
12532: &Apache::loncommon::upfile_record_sep
12533:
12534: =cut
12535:
1.144 matthew 12536: ######################################################
12537: ######################################################
1.31 albertel 12538: sub csv_print_samples {
12539: my ($r,$records) = @_;
1.662 bisitz 12540: my $samples = &get_samples($records,5);
1.301 albertel 12541:
1.594 raeburn 12542: $r->print(&mt('Samples').'<br />'.&start_data_table().
12543: &start_data_table_header_row());
1.356 albertel 12544: foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
1.845 bisitz 12545: $r->print('<th>'.&mt('Column [_1]',($sample+1)).'</th>'); }
1.594 raeburn 12546: $r->print(&end_data_table_header_row());
1.301 albertel 12547: foreach my $hash (@$samples) {
1.594 raeburn 12548: $r->print(&start_data_table_row());
1.356 albertel 12549: foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
1.31 albertel 12550: $r->print('<td>');
1.356 albertel 12551: if (defined($$hash{$sample})) { $r->print($$hash{$sample}); }
1.31 albertel 12552: $r->print('</td>');
12553: }
1.594 raeburn 12554: $r->print(&end_data_table_row());
1.31 albertel 12555: }
1.594 raeburn 12556: $r->print(&end_data_table().'<br />'."\n");
1.31 albertel 12557: }
12558:
1.144 matthew 12559: ######################################################
12560: ######################################################
12561:
1.56 matthew 12562: =pod
12563:
1.648 raeburn 12564: =item * &csv_print_select_table($r,$records,$d)
1.41 ng 12565:
12566: Prints a table to create associations between values and table columns.
1.144 matthew 12567:
1.41 ng 12568: $r is an Apache Request ref,
12569: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
1.174 matthew 12570: $d is an array of 2 element arrays (internal name, displayed name,defaultcol)
1.41 ng 12571:
12572: =cut
12573:
1.144 matthew 12574: ######################################################
12575: ######################################################
1.31 albertel 12576: sub csv_print_select_table {
12577: my ($r,$records,$d) = @_;
1.301 albertel 12578: my $i=0;
12579: my $samples = &get_samples($records,1);
1.144 matthew 12580: $r->print(&mt('Associate columns with student attributes.')."\n".
1.594 raeburn 12581: &start_data_table().&start_data_table_header_row().
1.144 matthew 12582: '<th>'.&mt('Attribute').'</th>'.
1.594 raeburn 12583: '<th>'.&mt('Column').'</th>'.
12584: &end_data_table_header_row()."\n");
1.356 albertel 12585: foreach my $array_ref (@$d) {
12586: my ($value,$display,$defaultcol)=@{ $array_ref };
1.729 raeburn 12587: $r->print(&start_data_table_row().'<td>'.$display.'</td>');
1.31 albertel 12588:
1.875 bisitz 12589: $r->print('<td><select name="f'.$i.'"'.
1.32 matthew 12590: ' onchange="javascript:flip(this.form,'.$i.');">');
1.31 albertel 12591: $r->print('<option value="none"></option>');
1.356 albertel 12592: foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
12593: $r->print('<option value="'.$sample.'"'.
12594: ($sample eq $defaultcol ? ' selected="selected" ' : '').
1.662 bisitz 12595: '>'.&mt('Column [_1]',($sample+1)).'</option>');
1.31 albertel 12596: }
1.594 raeburn 12597: $r->print('</select></td>'.&end_data_table_row()."\n");
1.31 albertel 12598: $i++;
12599: }
1.594 raeburn 12600: $r->print(&end_data_table());
1.31 albertel 12601: $i--;
12602: return $i;
12603: }
1.56 matthew 12604:
1.144 matthew 12605: ######################################################
12606: ######################################################
12607:
1.56 matthew 12608: =pod
1.31 albertel 12609:
1.648 raeburn 12610: =item * &csv_samples_select_table($r,$records,$d)
1.41 ng 12611:
12612: Prints a table of sample values from the upload and can make associate samples to internal names.
12613:
12614: $r is an Apache Request ref,
12615: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
12616: $d is an array of 2 element arrays (internal name, displayed name)
12617:
12618: =cut
12619:
1.144 matthew 12620: ######################################################
12621: ######################################################
1.31 albertel 12622: sub csv_samples_select_table {
12623: my ($r,$records,$d) = @_;
12624: my $i=0;
1.144 matthew 12625: #
1.662 bisitz 12626: my $max_samples = 5;
12627: my $samples = &get_samples($records,$max_samples);
1.594 raeburn 12628: $r->print(&start_data_table().
12629: &start_data_table_header_row().'<th>'.
12630: &mt('Field').'</th><th>'.&mt('Samples').'</th>'.
12631: &end_data_table_header_row());
1.301 albertel 12632:
12633: foreach my $key (sort(keys(%{ $samples->[0] }))) {
1.594 raeburn 12634: $r->print(&start_data_table_row().'<td><select name="f'.$i.'"'.
1.32 matthew 12635: ' onchange="javascript:flip(this.form,'.$i.');">');
1.301 albertel 12636: foreach my $option (@$d) {
12637: my ($value,$display,$defaultcol)=@{ $option };
1.174 matthew 12638: $r->print('<option value="'.$value.'"'.
1.253 albertel 12639: ($i eq $defaultcol ? ' selected="selected" ':'').'>'.
1.174 matthew 12640: $display.'</option>');
1.31 albertel 12641: }
12642: $r->print('</select></td><td>');
1.662 bisitz 12643: foreach my $line (0..($max_samples-1)) {
1.301 albertel 12644: if (defined($samples->[$line]{$key})) {
12645: $r->print($samples->[$line]{$key}."<br />\n");
12646: }
12647: }
1.594 raeburn 12648: $r->print('</td>'.&end_data_table_row());
1.31 albertel 12649: $i++;
12650: }
1.594 raeburn 12651: $r->print(&end_data_table());
1.31 albertel 12652: $i--;
12653: return($i);
1.115 matthew 12654: }
12655:
1.144 matthew 12656: ######################################################
12657: ######################################################
12658:
1.115 matthew 12659: =pod
12660:
1.648 raeburn 12661: =item * &clean_excel_name($name)
1.115 matthew 12662:
12663: Returns a replacement for $name which does not contain any illegal characters.
12664:
12665: =cut
12666:
1.144 matthew 12667: ######################################################
12668: ######################################################
1.115 matthew 12669: sub clean_excel_name {
12670: my ($name) = @_;
12671: $name =~ s/[:\*\?\/\\]//g;
12672: if (length($name) > 31) {
12673: $name = substr($name,0,31);
12674: }
12675: return $name;
1.25 albertel 12676: }
1.84 albertel 12677:
1.85 albertel 12678: =pod
12679:
1.648 raeburn 12680: =item * &check_if_partid_hidden($id,$symb,$udom,$uname)
1.85 albertel 12681:
12682: Returns either 1 or undef
12683:
12684: 1 if the part is to be hidden, undef if it is to be shown
12685:
12686: Arguments are:
12687:
12688: $id the id of the part to be checked
12689: $symb, optional the symb of the resource to check
12690: $udom, optional the domain of the user to check for
12691: $uname, optional the username of the user to check for
12692:
12693: =cut
1.84 albertel 12694:
12695: sub check_if_partid_hidden {
12696: my ($id,$symb,$udom,$uname) = @_;
1.133 albertel 12697: my $hiddenparts=&Apache::lonnet::EXT('resource.0.hiddenparts',
1.84 albertel 12698: $symb,$udom,$uname);
1.141 albertel 12699: my $truth=1;
12700: #if the string starts with !, then the list is the list to show not hide
12701: if ($hiddenparts=~s/^\s*!//) { $truth=undef; }
1.84 albertel 12702: my @hiddenlist=split(/,/,$hiddenparts);
12703: foreach my $checkid (@hiddenlist) {
1.141 albertel 12704: if ($checkid =~ /^\s*\Q$id\E\s*$/) { return $truth; }
1.84 albertel 12705: }
1.141 albertel 12706: return !$truth;
1.84 albertel 12707: }
1.127 matthew 12708:
1.138 matthew 12709:
12710: ############################################################
12711: ############################################################
12712:
12713: =pod
12714:
1.157 matthew 12715: =back
12716:
1.138 matthew 12717: =head1 cgi-bin script and graphing routines
12718:
1.157 matthew 12719: =over 4
12720:
1.648 raeburn 12721: =item * &get_cgi_id()
1.138 matthew 12722:
12723: Inputs: none
12724:
12725: Returns an id which can be used to pass environment variables
12726: to various cgi-bin scripts. These environment variables will
12727: be removed from the users environment after a given time by
12728: the routine &Apache::lonnet::transfer_profile_to_env.
12729:
12730: =cut
12731:
12732: ############################################################
12733: ############################################################
1.152 albertel 12734: my $uniq=0;
1.136 matthew 12735: sub get_cgi_id {
1.154 albertel 12736: $uniq=($uniq+1)%100000;
1.280 albertel 12737: return (time.'_'.$$.'_'.$uniq);
1.136 matthew 12738: }
12739:
1.127 matthew 12740: ############################################################
12741: ############################################################
12742:
12743: =pod
12744:
1.648 raeburn 12745: =item * &DrawBarGraph()
1.127 matthew 12746:
1.138 matthew 12747: Facilitates the plotting of data in a (stacked) bar graph.
12748: Puts plot definition data into the users environment in order for
12749: graph.png to plot it. Returns an <img> tag for the plot.
12750: The bars on the plot are labeled '1','2',...,'n'.
12751:
12752: Inputs:
12753:
12754: =over 4
12755:
12756: =item $Title: string, the title of the plot
12757:
12758: =item $xlabel: string, text describing the X-axis of the plot
12759:
12760: =item $ylabel: string, text describing the Y-axis of the plot
12761:
12762: =item $Max: scalar, the maximum Y value to use in the plot
12763: If $Max is < any data point, the graph will not be rendered.
12764:
1.140 matthew 12765: =item $colors: array ref holding the colors to be used for the data sets when
1.138 matthew 12766: they are plotted. If undefined, default values will be used.
12767:
1.178 matthew 12768: =item $labels: array ref holding the labels to use on the x-axis for the bars.
12769:
1.138 matthew 12770: =item @Values: An array of array references. Each array reference holds data
12771: to be plotted in a stacked bar chart.
12772:
1.239 matthew 12773: =item If the final element of @Values is a hash reference the key/value
12774: pairs will be added to the graph definition.
12775:
1.138 matthew 12776: =back
12777:
12778: Returns:
12779:
12780: An <img> tag which references graph.png and the appropriate identifying
12781: information for the plot.
12782:
1.127 matthew 12783: =cut
12784:
12785: ############################################################
12786: ############################################################
1.134 matthew 12787: sub DrawBarGraph {
1.178 matthew 12788: my ($Title,$xlabel,$ylabel,$Max,$colors,$labels,@Values)=@_;
1.134 matthew 12789: #
12790: if (! defined($colors)) {
12791: $colors = ['#33ff00',
12792: '#0033cc', '#990000', '#aaaa66', '#663399', '#ff9933',
12793: '#66ccff', '#ff9999', '#cccc33', '#660000', '#33cc66',
12794: ];
12795: }
1.228 matthew 12796: my $extra_settings = {};
12797: if (ref($Values[-1]) eq 'HASH') {
12798: $extra_settings = pop(@Values);
12799: }
1.127 matthew 12800: #
1.136 matthew 12801: my $identifier = &get_cgi_id();
12802: my $id = 'cgi.'.$identifier;
1.129 matthew 12803: if (! @Values || ref($Values[0]) ne 'ARRAY') {
1.127 matthew 12804: return '';
12805: }
1.225 matthew 12806: #
12807: my @Labels;
12808: if (defined($labels)) {
12809: @Labels = @$labels;
12810: } else {
12811: for (my $i=0;$i<@{$Values[0]};$i++) {
12812: push (@Labels,$i+1);
12813: }
12814: }
12815: #
1.129 matthew 12816: my $NumBars = scalar(@{$Values[0]});
1.225 matthew 12817: if ($NumBars < scalar(@Labels)) { $NumBars = scalar(@Labels); }
1.129 matthew 12818: my %ValuesHash;
12819: my $NumSets=1;
12820: foreach my $array (@Values) {
12821: next if (! ref($array));
1.136 matthew 12822: $ValuesHash{$id.'.data.'.$NumSets++} =
1.132 matthew 12823: join(',',@$array);
1.129 matthew 12824: }
1.127 matthew 12825: #
1.136 matthew 12826: my ($height,$width,$xskip,$bar_width) = (200,120,1,15);
1.225 matthew 12827: if ($NumBars < 3) {
12828: $width = 120+$NumBars*32;
1.220 matthew 12829: $xskip = 1;
1.225 matthew 12830: $bar_width = 30;
12831: } elsif ($NumBars < 5) {
12832: $width = 120+$NumBars*20;
12833: $xskip = 1;
12834: $bar_width = 20;
1.220 matthew 12835: } elsif ($NumBars < 10) {
1.136 matthew 12836: $width = 120+$NumBars*15;
12837: $xskip = 1;
12838: $bar_width = 15;
12839: } elsif ($NumBars <= 25) {
12840: $width = 120+$NumBars*11;
12841: $xskip = 5;
12842: $bar_width = 8;
12843: } elsif ($NumBars <= 50) {
12844: $width = 120+$NumBars*8;
12845: $xskip = 5;
12846: $bar_width = 4;
12847: } else {
12848: $width = 120+$NumBars*8;
12849: $xskip = 5;
12850: $bar_width = 4;
12851: }
12852: #
1.137 matthew 12853: $Max = 1 if ($Max < 1);
12854: if ( int($Max) < $Max ) {
12855: $Max++;
12856: $Max = int($Max);
12857: }
1.127 matthew 12858: $Title = '' if (! defined($Title));
12859: $xlabel = '' if (! defined($xlabel));
12860: $ylabel = '' if (! defined($ylabel));
1.369 www 12861: $ValuesHash{$id.'.title'} = &escape($Title);
12862: $ValuesHash{$id.'.xlabel'} = &escape($xlabel);
12863: $ValuesHash{$id.'.ylabel'} = &escape($ylabel);
1.137 matthew 12864: $ValuesHash{$id.'.y_max_value'} = $Max;
1.136 matthew 12865: $ValuesHash{$id.'.NumBars'} = $NumBars;
12866: $ValuesHash{$id.'.NumSets'} = $NumSets;
12867: $ValuesHash{$id.'.PlotType'} = 'bar';
12868: $ValuesHash{$id.'.Colors'} = join(',',@{$colors});
12869: $ValuesHash{$id.'.height'} = $height;
12870: $ValuesHash{$id.'.width'} = $width;
12871: $ValuesHash{$id.'.xskip'} = $xskip;
12872: $ValuesHash{$id.'.bar_width'} = $bar_width;
12873: $ValuesHash{$id.'.labels'} = join(',',@Labels);
1.127 matthew 12874: #
1.228 matthew 12875: # Deal with other parameters
12876: while (my ($key,$value) = each(%$extra_settings)) {
12877: $ValuesHash{$id.'.'.$key} = $value;
12878: }
12879: #
1.646 raeburn 12880: &Apache::lonnet::appenv(\%ValuesHash);
1.137 matthew 12881: return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
12882: }
12883:
12884: ############################################################
12885: ############################################################
12886:
12887: =pod
12888:
1.648 raeburn 12889: =item * &DrawXYGraph()
1.137 matthew 12890:
1.138 matthew 12891: Facilitates the plotting of data in an XY graph.
12892: Puts plot definition data into the users environment in order for
12893: graph.png to plot it. Returns an <img> tag for the plot.
12894:
12895: Inputs:
12896:
12897: =over 4
12898:
12899: =item $Title: string, the title of the plot
12900:
12901: =item $xlabel: string, text describing the X-axis of the plot
12902:
12903: =item $ylabel: string, text describing the Y-axis of the plot
12904:
12905: =item $Max: scalar, the maximum Y value to use in the plot
12906: If $Max is < any data point, the graph will not be rendered.
12907:
12908: =item $colors: Array ref containing the hex color codes for the data to be
12909: plotted in. If undefined, default values will be used.
12910:
12911: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
12912:
12913: =item $Ydata: Array ref containing Array refs.
1.185 www 12914: Each of the contained arrays will be plotted as a separate curve.
1.138 matthew 12915:
12916: =item %Values: hash indicating or overriding any default values which are
12917: passed to graph.png.
12918: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
12919:
12920: =back
12921:
12922: Returns:
12923:
12924: An <img> tag which references graph.png and the appropriate identifying
12925: information for the plot.
12926:
1.137 matthew 12927: =cut
12928:
12929: ############################################################
12930: ############################################################
12931: sub DrawXYGraph {
12932: my ($Title,$xlabel,$ylabel,$Max,$colors,$Xlabels,$Ydata,%Values)=@_;
12933: #
12934: # Create the identifier for the graph
12935: my $identifier = &get_cgi_id();
12936: my $id = 'cgi.'.$identifier;
12937: #
12938: $Title = '' if (! defined($Title));
12939: $xlabel = '' if (! defined($xlabel));
12940: $ylabel = '' if (! defined($ylabel));
12941: my %ValuesHash =
12942: (
1.369 www 12943: $id.'.title' => &escape($Title),
12944: $id.'.xlabel' => &escape($xlabel),
12945: $id.'.ylabel' => &escape($ylabel),
1.137 matthew 12946: $id.'.y_max_value'=> $Max,
12947: $id.'.labels' => join(',',@$Xlabels),
12948: $id.'.PlotType' => 'XY',
12949: );
12950: #
12951: if (defined($colors) && ref($colors) eq 'ARRAY') {
12952: $ValuesHash{$id.'.Colors'} = join(',',@{$colors});
12953: }
12954: #
12955: if (! ref($Ydata) || ref($Ydata) ne 'ARRAY') {
12956: return '';
12957: }
12958: my $NumSets=1;
1.138 matthew 12959: foreach my $array (@{$Ydata}){
1.137 matthew 12960: next if (! ref($array));
12961: $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
12962: }
1.138 matthew 12963: $ValuesHash{$id.'.NumSets'} = $NumSets-1;
1.137 matthew 12964: #
12965: # Deal with other parameters
12966: while (my ($key,$value) = each(%Values)) {
12967: $ValuesHash{$id.'.'.$key} = $value;
1.127 matthew 12968: }
12969: #
1.646 raeburn 12970: &Apache::lonnet::appenv(\%ValuesHash);
1.136 matthew 12971: return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
12972: }
12973:
12974: ############################################################
12975: ############################################################
12976:
12977: =pod
12978:
1.648 raeburn 12979: =item * &DrawXYYGraph()
1.138 matthew 12980:
12981: Facilitates the plotting of data in an XY graph with two Y axes.
12982: Puts plot definition data into the users environment in order for
12983: graph.png to plot it. Returns an <img> tag for the plot.
12984:
12985: Inputs:
12986:
12987: =over 4
12988:
12989: =item $Title: string, the title of the plot
12990:
12991: =item $xlabel: string, text describing the X-axis of the plot
12992:
12993: =item $ylabel: string, text describing the Y-axis of the plot
12994:
12995: =item $colors: Array ref containing the hex color codes for the data to be
12996: plotted in. If undefined, default values will be used.
12997:
12998: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
12999:
13000: =item $Ydata1: The first data set
13001:
13002: =item $Min1: The minimum value of the left Y-axis
13003:
13004: =item $Max1: The maximum value of the left Y-axis
13005:
13006: =item $Ydata2: The second data set
13007:
13008: =item $Min2: The minimum value of the right Y-axis
13009:
13010: =item $Max2: The maximum value of the left Y-axis
13011:
13012: =item %Values: hash indicating or overriding any default values which are
13013: passed to graph.png.
13014: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
13015:
13016: =back
13017:
13018: Returns:
13019:
13020: An <img> tag which references graph.png and the appropriate identifying
13021: information for the plot.
1.136 matthew 13022:
13023: =cut
13024:
13025: ############################################################
13026: ############################################################
1.137 matthew 13027: sub DrawXYYGraph {
13028: my ($Title,$xlabel,$ylabel,$colors,$Xlabels,$Ydata1,$Min1,$Max1,
13029: $Ydata2,$Min2,$Max2,%Values)=@_;
1.136 matthew 13030: #
13031: # Create the identifier for the graph
13032: my $identifier = &get_cgi_id();
13033: my $id = 'cgi.'.$identifier;
13034: #
13035: $Title = '' if (! defined($Title));
13036: $xlabel = '' if (! defined($xlabel));
13037: $ylabel = '' if (! defined($ylabel));
13038: my %ValuesHash =
13039: (
1.369 www 13040: $id.'.title' => &escape($Title),
13041: $id.'.xlabel' => &escape($xlabel),
13042: $id.'.ylabel' => &escape($ylabel),
1.136 matthew 13043: $id.'.labels' => join(',',@$Xlabels),
13044: $id.'.PlotType' => 'XY',
13045: $id.'.NumSets' => 2,
1.137 matthew 13046: $id.'.two_axes' => 1,
13047: $id.'.y1_max_value' => $Max1,
13048: $id.'.y1_min_value' => $Min1,
13049: $id.'.y2_max_value' => $Max2,
13050: $id.'.y2_min_value' => $Min2,
1.136 matthew 13051: );
13052: #
1.137 matthew 13053: if (defined($colors) && ref($colors) eq 'ARRAY') {
13054: $ValuesHash{$id.'.Colors'} = join(',',@{$colors});
13055: }
13056: #
13057: if (! ref($Ydata1) || ref($Ydata1) ne 'ARRAY' ||
13058: ! ref($Ydata2) || ref($Ydata2) ne 'ARRAY'){
1.136 matthew 13059: return '';
13060: }
13061: my $NumSets=1;
1.137 matthew 13062: foreach my $array ($Ydata1,$Ydata2){
1.136 matthew 13063: next if (! ref($array));
13064: $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
1.137 matthew 13065: }
13066: #
13067: # Deal with other parameters
13068: while (my ($key,$value) = each(%Values)) {
13069: $ValuesHash{$id.'.'.$key} = $value;
1.136 matthew 13070: }
13071: #
1.646 raeburn 13072: &Apache::lonnet::appenv(\%ValuesHash);
1.130 albertel 13073: return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
1.139 matthew 13074: }
13075:
13076: ############################################################
13077: ############################################################
13078:
13079: =pod
13080:
1.157 matthew 13081: =back
13082:
1.139 matthew 13083: =head1 Statistics helper routines?
13084:
13085: Bad place for them but what the hell.
13086:
1.157 matthew 13087: =over 4
13088:
1.648 raeburn 13089: =item * &chartlink()
1.139 matthew 13090:
13091: Returns a link to the chart for a specific student.
13092:
13093: Inputs:
13094:
13095: =over 4
13096:
13097: =item $linktext: The text of the link
13098:
13099: =item $sname: The students username
13100:
13101: =item $sdomain: The students domain
13102:
13103: =back
13104:
1.157 matthew 13105: =back
13106:
1.139 matthew 13107: =cut
13108:
13109: ############################################################
13110: ############################################################
13111: sub chartlink {
13112: my ($linktext, $sname, $sdomain) = @_;
13113: my $link = '<a href="/adm/statistics?reportSelected=student_assessment'.
1.369 www 13114: '&SelectedStudent='.&escape($sname.':'.$sdomain).
1.219 albertel 13115: '&chartoutputmode='.HTML::Entities::encode('html, with all links').
1.139 matthew 13116: '">'.$linktext.'</a>';
1.153 matthew 13117: }
13118:
13119: #######################################################
13120: #######################################################
13121:
13122: =pod
13123:
13124: =head1 Course Environment Routines
1.157 matthew 13125:
13126: =over 4
1.153 matthew 13127:
1.648 raeburn 13128: =item * &restore_course_settings()
1.153 matthew 13129:
1.648 raeburn 13130: =item * &store_course_settings()
1.153 matthew 13131:
13132: Restores/Store indicated form parameters from the course environment.
13133: Will not overwrite existing values of the form parameters.
13134:
13135: Inputs:
13136: a scalar describing the data (e.g. 'chart', 'problem_analysis')
13137:
13138: a hash ref describing the data to be stored. For example:
13139:
13140: %Save_Parameters = ('Status' => 'scalar',
13141: 'chartoutputmode' => 'scalar',
13142: 'chartoutputdata' => 'scalar',
13143: 'Section' => 'array',
1.373 raeburn 13144: 'Group' => 'array',
1.153 matthew 13145: 'StudentData' => 'array',
13146: 'Maps' => 'array');
13147:
13148: Returns: both routines return nothing
13149:
1.631 raeburn 13150: =back
13151:
1.153 matthew 13152: =cut
13153:
13154: #######################################################
13155: #######################################################
13156: sub store_course_settings {
1.496 albertel 13157: return &store_settings($env{'request.course.id'},@_);
13158: }
13159:
13160: sub store_settings {
1.153 matthew 13161: # save to the environment
13162: # appenv the same items, just to be safe
1.300 albertel 13163: my $udom = $env{'user.domain'};
13164: my $uname = $env{'user.name'};
1.496 albertel 13165: my ($context,$prefix,$Settings) = @_;
1.153 matthew 13166: my %SaveHash;
13167: my %AppHash;
13168: while (my ($setting,$type) = each(%$Settings)) {
1.496 albertel 13169: my $basename = join('.','internal',$context,$prefix,$setting);
1.300 albertel 13170: my $envname = 'environment.'.$basename;
1.258 albertel 13171: if (exists($env{'form.'.$setting})) {
1.153 matthew 13172: # Save this value away
13173: if ($type eq 'scalar' &&
1.258 albertel 13174: (! exists($env{$envname}) ||
13175: $env{$envname} ne $env{'form.'.$setting})) {
13176: $SaveHash{$basename} = $env{'form.'.$setting};
13177: $AppHash{$envname} = $env{'form.'.$setting};
1.153 matthew 13178: } elsif ($type eq 'array') {
13179: my $stored_form;
1.258 albertel 13180: if (ref($env{'form.'.$setting})) {
1.153 matthew 13181: $stored_form = join(',',
13182: map {
1.369 www 13183: &escape($_);
1.258 albertel 13184: } sort(@{$env{'form.'.$setting}}));
1.153 matthew 13185: } else {
13186: $stored_form =
1.369 www 13187: &escape($env{'form.'.$setting});
1.153 matthew 13188: }
13189: # Determine if the array contents are the same.
1.258 albertel 13190: if ($stored_form ne $env{$envname}) {
1.153 matthew 13191: $SaveHash{$basename} = $stored_form;
13192: $AppHash{$envname} = $stored_form;
13193: }
13194: }
13195: }
13196: }
13197: my $put_result = &Apache::lonnet::put('environment',\%SaveHash,
1.300 albertel 13198: $udom,$uname);
1.153 matthew 13199: if ($put_result !~ /^(ok|delayed)/) {
13200: &Apache::lonnet::logthis('unable to save form parameters, '.
13201: 'got error:'.$put_result);
13202: }
13203: # Make sure these settings stick around in this session, too
1.646 raeburn 13204: &Apache::lonnet::appenv(\%AppHash);
1.153 matthew 13205: return;
13206: }
13207:
13208: sub restore_course_settings {
1.499 albertel 13209: return &restore_settings($env{'request.course.id'},@_);
1.496 albertel 13210: }
13211:
13212: sub restore_settings {
13213: my ($context,$prefix,$Settings) = @_;
1.153 matthew 13214: while (my ($setting,$type) = each(%$Settings)) {
1.258 albertel 13215: next if (exists($env{'form.'.$setting}));
1.496 albertel 13216: my $envname = 'environment.internal.'.$context.'.'.$prefix.
1.153 matthew 13217: '.'.$setting;
1.258 albertel 13218: if (exists($env{$envname})) {
1.153 matthew 13219: if ($type eq 'scalar') {
1.258 albertel 13220: $env{'form.'.$setting} = $env{$envname};
1.153 matthew 13221: } elsif ($type eq 'array') {
1.258 albertel 13222: $env{'form.'.$setting} = [
1.153 matthew 13223: map {
1.369 www 13224: &unescape($_);
1.258 albertel 13225: } split(',',$env{$envname})
1.153 matthew 13226: ];
13227: }
13228: }
13229: }
1.127 matthew 13230: }
13231:
1.618 raeburn 13232: #######################################################
13233: #######################################################
13234:
13235: =pod
13236:
13237: =head1 Domain E-mail Routines
13238:
13239: =over 4
13240:
1.648 raeburn 13241: =item * &build_recipient_list()
1.618 raeburn 13242:
1.1144 raeburn 13243: Build recipient lists for following types of e-mail:
1.766 raeburn 13244: (a) Error Reports, (b) Package Updates, (c) lonstatus warnings/errors
1.1144 raeburn 13245: (d) Help requests, (e) Course requests needing approval, (f) loncapa
13246: module change checking, student/employee ID conflict checks, as
13247: generated by lonerrorhandler.pm, CHECKRPMS, loncron,
13248: lonsupportreq.pm, loncoursequeueadmin.pm, searchcat.pl respectively.
1.618 raeburn 13249:
13250: Inputs:
1.619 raeburn 13251: defmail (scalar - email address of default recipient),
1.1144 raeburn 13252: mailing type (scalar: errormail, packagesmail, helpdeskmail,
13253: requestsmail, updatesmail, or idconflictsmail).
13254:
1.619 raeburn 13255: defdom (domain for which to retrieve configuration settings),
1.1144 raeburn 13256:
1.619 raeburn 13257: origmail (scalar - email address of recipient from loncapa.conf,
13258: i.e., predates configuration by DC via domainprefs.pm
1.618 raeburn 13259:
1.655 raeburn 13260: Returns: comma separated list of addresses to which to send e-mail.
13261:
13262: =back
1.618 raeburn 13263:
13264: =cut
13265:
13266: ############################################################
13267: ############################################################
13268: sub build_recipient_list {
1.619 raeburn 13269: my ($defmail,$mailing,$defdom,$origmail) = @_;
1.618 raeburn 13270: my @recipients;
13271: my $otheremails;
13272: my %domconfig =
13273: &Apache::lonnet::get_dom('configuration',['contacts'],$defdom);
13274: if (ref($domconfig{'contacts'}) eq 'HASH') {
1.766 raeburn 13275: if (exists($domconfig{'contacts'}{$mailing})) {
13276: if (ref($domconfig{'contacts'}{$mailing}) eq 'HASH') {
13277: my @contacts = ('adminemail','supportemail');
13278: foreach my $item (@contacts) {
13279: if ($domconfig{'contacts'}{$mailing}{$item}) {
13280: my $addr = $domconfig{'contacts'}{$item};
13281: if (!grep(/^\Q$addr\E$/,@recipients)) {
13282: push(@recipients,$addr);
13283: }
1.619 raeburn 13284: }
1.766 raeburn 13285: $otheremails = $domconfig{'contacts'}{$mailing}{'others'};
1.618 raeburn 13286: }
13287: }
1.766 raeburn 13288: } elsif ($origmail ne '') {
13289: push(@recipients,$origmail);
1.618 raeburn 13290: }
1.619 raeburn 13291: } elsif ($origmail ne '') {
13292: push(@recipients,$origmail);
1.618 raeburn 13293: }
1.688 raeburn 13294: if (defined($defmail)) {
13295: if ($defmail ne '') {
13296: push(@recipients,$defmail);
13297: }
1.618 raeburn 13298: }
13299: if ($otheremails) {
1.619 raeburn 13300: my @others;
13301: if ($otheremails =~ /,/) {
13302: @others = split(/,/,$otheremails);
1.618 raeburn 13303: } else {
1.619 raeburn 13304: push(@others,$otheremails);
13305: }
13306: foreach my $addr (@others) {
13307: if (!grep(/^\Q$addr\E$/,@recipients)) {
13308: push(@recipients,$addr);
13309: }
1.618 raeburn 13310: }
13311: }
1.619 raeburn 13312: my $recipientlist = join(',',@recipients);
1.618 raeburn 13313: return $recipientlist;
13314: }
13315:
1.127 matthew 13316: ############################################################
13317: ############################################################
1.154 albertel 13318:
1.655 raeburn 13319: =pod
13320:
13321: =head1 Course Catalog Routines
13322:
13323: =over 4
13324:
13325: =item * &gather_categories()
13326:
13327: Converts category definitions - keys of categories hash stored in
13328: coursecategories in configuration.db on the primary library server in a
13329: domain - to an array. Also generates javascript and idx hash used to
13330: generate Domain Coordinator interface for editing Course Categories.
13331:
13332: Inputs:
1.663 raeburn 13333:
1.655 raeburn 13334: categories (reference to hash of category definitions).
1.663 raeburn 13335:
1.655 raeburn 13336: cats (reference to array of arrays/hashes which encapsulates hierarchy of
13337: categories and subcategories).
1.663 raeburn 13338:
1.655 raeburn 13339: idx (reference to hash of counters used in Domain Coordinator interface for
13340: editing Course Categories).
1.663 raeburn 13341:
1.655 raeburn 13342: jsarray (reference to array of categories used to create Javascript arrays for
13343: Domain Coordinator interface for editing Course Categories).
13344:
13345: Returns: nothing
13346:
13347: Side effects: populates cats, idx and jsarray.
13348:
13349: =cut
13350:
13351: sub gather_categories {
13352: my ($categories,$cats,$idx,$jsarray) = @_;
13353: my %counters;
13354: my $num = 0;
13355: foreach my $item (keys(%{$categories})) {
13356: my ($cat,$container,$depth) = map { &unescape($_); } split(/:/,$item);
13357: if ($container eq '' && $depth == 0) {
13358: $cats->[$depth][$categories->{$item}] = $cat;
13359: } else {
13360: $cats->[$depth]{$container}[$categories->{$item}] = $cat;
13361: }
13362: my ($escitem,$tail) = split(/:/,$item,2);
13363: if ($counters{$tail} eq '') {
13364: $counters{$tail} = $num;
13365: $num ++;
13366: }
13367: if (ref($idx) eq 'HASH') {
13368: $idx->{$item} = $counters{$tail};
13369: }
13370: if (ref($jsarray) eq 'ARRAY') {
13371: push(@{$jsarray->[$counters{$tail}]},$item);
13372: }
13373: }
13374: return;
13375: }
13376:
13377: =pod
13378:
13379: =item * &extract_categories()
13380:
13381: Used to generate breadcrumb trails for course categories.
13382:
13383: Inputs:
1.663 raeburn 13384:
1.655 raeburn 13385: categories (reference to hash of category definitions).
1.663 raeburn 13386:
1.655 raeburn 13387: cats (reference to array of arrays/hashes which encapsulates hierarchy of
13388: categories and subcategories).
1.663 raeburn 13389:
1.655 raeburn 13390: trails (reference to array of breacrumb trails for each category).
1.663 raeburn 13391:
1.655 raeburn 13392: allitems (reference to hash - key is category key
13393: (format: escaped(name):escaped(parent category):depth in hierarchy).
1.663 raeburn 13394:
1.655 raeburn 13395: idx (reference to hash of counters used in Domain Coordinator interface for
13396: editing Course Categories).
1.663 raeburn 13397:
1.655 raeburn 13398: jsarray (reference to array of categories used to create Javascript arrays for
13399: Domain Coordinator interface for editing Course Categories).
13400:
1.665 raeburn 13401: subcats (reference to hash of arrays containing all subcategories within each
13402: category, -recursive)
13403:
1.655 raeburn 13404: Returns: nothing
13405:
13406: Side effects: populates trails and allitems hash references.
13407:
13408: =cut
13409:
13410: sub extract_categories {
1.665 raeburn 13411: my ($categories,$cats,$trails,$allitems,$idx,$jsarray,$subcats) = @_;
1.655 raeburn 13412: if (ref($categories) eq 'HASH') {
13413: &gather_categories($categories,$cats,$idx,$jsarray);
13414: if (ref($cats->[0]) eq 'ARRAY') {
13415: for (my $i=0; $i<@{$cats->[0]}; $i++) {
13416: my $name = $cats->[0][$i];
13417: my $item = &escape($name).'::0';
13418: my $trailstr;
13419: if ($name eq 'instcode') {
13420: $trailstr = &mt('Official courses (with institutional codes)');
1.919 raeburn 13421: } elsif ($name eq 'communities') {
13422: $trailstr = &mt('Communities');
1.655 raeburn 13423: } else {
13424: $trailstr = $name;
13425: }
13426: if ($allitems->{$item} eq '') {
13427: push(@{$trails},$trailstr);
13428: $allitems->{$item} = scalar(@{$trails})-1;
13429: }
13430: my @parents = ($name);
13431: if (ref($cats->[1]{$name}) eq 'ARRAY') {
13432: for (my $j=0; $j<@{$cats->[1]{$name}}; $j++) {
13433: my $category = $cats->[1]{$name}[$j];
1.665 raeburn 13434: if (ref($subcats) eq 'HASH') {
13435: push(@{$subcats->{$item}},&escape($category).':'.&escape($name).':1');
13436: }
13437: &recurse_categories($cats,2,$category,$trails,$allitems,\@parents,$subcats);
13438: }
13439: } else {
13440: if (ref($subcats) eq 'HASH') {
13441: $subcats->{$item} = [];
1.655 raeburn 13442: }
13443: }
13444: }
13445: }
13446: }
13447: return;
13448: }
13449:
13450: =pod
13451:
1.1162 raeburn 13452: =item * &recurse_categories()
1.655 raeburn 13453:
13454: Recursively used to generate breadcrumb trails for course categories.
13455:
13456: Inputs:
1.663 raeburn 13457:
1.655 raeburn 13458: cats (reference to array of arrays/hashes which encapsulates hierarchy of
13459: categories and subcategories).
1.663 raeburn 13460:
1.655 raeburn 13461: depth (current depth in hierarchy of categories and sub-categories - 0 indexed).
1.663 raeburn 13462:
13463: category (current course category, for which breadcrumb trail is being generated).
13464:
13465: trails (reference to array of breadcrumb trails for each category).
13466:
1.655 raeburn 13467: allitems (reference to hash - key is category key
13468: (format: escaped(name):escaped(parent category):depth in hierarchy).
1.663 raeburn 13469:
1.655 raeburn 13470: parents (array containing containers directories for current category,
13471: back to top level).
13472:
13473: Returns: nothing
13474:
13475: Side effects: populates trails and allitems hash references
13476:
13477: =cut
13478:
13479: sub recurse_categories {
1.665 raeburn 13480: my ($cats,$depth,$category,$trails,$allitems,$parents,$subcats) = @_;
1.655 raeburn 13481: my $shallower = $depth - 1;
13482: if (ref($cats->[$depth]{$category}) eq 'ARRAY') {
13483: for (my $k=0; $k<@{$cats->[$depth]{$category}}; $k++) {
13484: my $name = $cats->[$depth]{$category}[$k];
13485: my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
13486: my $trailstr = join(' -> ',(@{$parents},$category));
13487: if ($allitems->{$item} eq '') {
13488: push(@{$trails},$trailstr);
13489: $allitems->{$item} = scalar(@{$trails})-1;
13490: }
13491: my $deeper = $depth+1;
13492: push(@{$parents},$category);
1.665 raeburn 13493: if (ref($subcats) eq 'HASH') {
13494: my $subcat = &escape($name).':'.$category.':'.$depth;
13495: for (my $j=@{$parents}; $j>=0; $j--) {
13496: my $higher;
13497: if ($j > 0) {
13498: $higher = &escape($parents->[$j]).':'.
13499: &escape($parents->[$j-1]).':'.$j;
13500: } else {
13501: $higher = &escape($parents->[$j]).'::'.$j;
13502: }
13503: push(@{$subcats->{$higher}},$subcat);
13504: }
13505: }
13506: &recurse_categories($cats,$deeper,$name,$trails,$allitems,$parents,
13507: $subcats);
1.655 raeburn 13508: pop(@{$parents});
13509: }
13510: } else {
13511: my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
13512: my $trailstr = join(' -> ',(@{$parents},$category));
13513: if ($allitems->{$item} eq '') {
13514: push(@{$trails},$trailstr);
13515: $allitems->{$item} = scalar(@{$trails})-1;
13516: }
13517: }
13518: return;
13519: }
13520:
1.663 raeburn 13521: =pod
13522:
1.1162 raeburn 13523: =item * &assign_categories_table()
1.663 raeburn 13524:
13525: Create a datatable for display of hierarchical categories in a domain,
13526: with checkboxes to allow a course to be categorized.
13527:
13528: Inputs:
13529:
13530: cathash - reference to hash of categories defined for the domain (from
13531: configuration.db)
13532:
13533: currcat - scalar with an & separated list of categories assigned to a course.
13534:
1.919 raeburn 13535: type - scalar contains course type (Course or Community).
13536:
1.663 raeburn 13537: Returns: $output (markup to be displayed)
13538:
13539: =cut
13540:
13541: sub assign_categories_table {
1.919 raeburn 13542: my ($cathash,$currcat,$type) = @_;
1.663 raeburn 13543: my $output;
13544: if (ref($cathash) eq 'HASH') {
13545: my (@cats,@trails,%allitems,%idx,@jsarray,@path,$maxdepth);
13546: &extract_categories($cathash,\@cats,\@trails,\%allitems,\%idx,\@jsarray);
13547: $maxdepth = scalar(@cats);
13548: if (@cats > 0) {
13549: my $itemcount = 0;
13550: if (ref($cats[0]) eq 'ARRAY') {
13551: my @currcategories;
13552: if ($currcat ne '') {
13553: @currcategories = split('&',$currcat);
13554: }
1.919 raeburn 13555: my $table;
1.663 raeburn 13556: for (my $i=0; $i<@{$cats[0]}; $i++) {
13557: my $parent = $cats[0][$i];
1.919 raeburn 13558: next if ($parent eq 'instcode');
13559: if ($type eq 'Community') {
13560: next unless ($parent eq 'communities');
13561: } else {
13562: next if ($parent eq 'communities');
13563: }
1.663 raeburn 13564: my $css_class = $itemcount%2?' class="LC_odd_row"':'';
13565: my $item = &escape($parent).'::0';
13566: my $checked = '';
13567: if (@currcategories > 0) {
13568: if (grep(/^\Q$item\E$/,@currcategories)) {
1.772 bisitz 13569: $checked = ' checked="checked"';
1.663 raeburn 13570: }
13571: }
1.919 raeburn 13572: my $parent_title = $parent;
13573: if ($parent eq 'communities') {
13574: $parent_title = &mt('Communities');
13575: }
13576: $table .= '<tr '.$css_class.'><td><span class="LC_nobreak">'.
13577: '<input type="checkbox" name="usecategory" value="'.
13578: $item.'"'.$checked.' />'.$parent_title.'</span>'.
13579: '<input type="hidden" name="catname" value="'.$parent.'" /></td>';
1.663 raeburn 13580: my $depth = 1;
13581: push(@path,$parent);
1.919 raeburn 13582: $table .= &assign_category_rows($itemcount,\@cats,$depth,$parent,\@path,\@currcategories);
1.663 raeburn 13583: pop(@path);
1.919 raeburn 13584: $table .= '</tr><tr><td colspan="'.$maxdepth.'" class="LC_row_separator"></td></tr>';
1.663 raeburn 13585: $itemcount ++;
13586: }
1.919 raeburn 13587: if ($itemcount) {
13588: $output = &Apache::loncommon::start_data_table().
13589: $table.
13590: &Apache::loncommon::end_data_table();
13591: }
1.663 raeburn 13592: }
13593: }
13594: }
13595: return $output;
13596: }
13597:
13598: =pod
13599:
1.1162 raeburn 13600: =item * &assign_category_rows()
1.663 raeburn 13601:
13602: Create a datatable row for display of nested categories in a domain,
13603: with checkboxes to allow a course to be categorized,called recursively.
13604:
13605: Inputs:
13606:
13607: itemcount - track row number for alternating colors
13608:
13609: cats - reference to array of arrays/hashes which encapsulates hierarchy of
13610: categories and subcategories.
13611:
13612: depth - current depth in hierarchy of categories and sub-categories - 0 indexed.
13613:
13614: parent - parent of current category item
13615:
13616: path - Array containing all categories back up through the hierarchy from the
13617: current category to the top level.
13618:
13619: currcategories - reference to array of current categories assigned to the course
13620:
13621: Returns: $output (markup to be displayed).
13622:
13623: =cut
13624:
13625: sub assign_category_rows {
13626: my ($itemcount,$cats,$depth,$parent,$path,$currcategories) = @_;
13627: my ($text,$name,$item,$chgstr);
13628: if (ref($cats) eq 'ARRAY') {
13629: my $maxdepth = scalar(@{$cats});
13630: if (ref($cats->[$depth]) eq 'HASH') {
13631: if (ref($cats->[$depth]{$parent}) eq 'ARRAY') {
13632: my $numchildren = @{$cats->[$depth]{$parent}};
13633: my $css_class = $itemcount%2?' class="LC_odd_row"':'';
1.1145 raeburn 13634: $text .= '<td><table class="LC_data_table">';
1.663 raeburn 13635: for (my $j=0; $j<$numchildren; $j++) {
13636: $name = $cats->[$depth]{$parent}[$j];
13637: $item = &escape($name).':'.&escape($parent).':'.$depth;
13638: my $deeper = $depth+1;
13639: my $checked = '';
13640: if (ref($currcategories) eq 'ARRAY') {
13641: if (@{$currcategories} > 0) {
13642: if (grep(/^\Q$item\E$/,@{$currcategories})) {
1.772 bisitz 13643: $checked = ' checked="checked"';
1.663 raeburn 13644: }
13645: }
13646: }
1.664 raeburn 13647: $text .= '<tr><td><span class="LC_nobreak"><label>'.
13648: '<input type="checkbox" name="usecategory" value="'.
1.675 raeburn 13649: $item.'"'.$checked.' />'.$name.'</label></span>'.
13650: '<input type="hidden" name="catname" value="'.$name.'" />'.
13651: '</td><td>';
1.663 raeburn 13652: if (ref($path) eq 'ARRAY') {
13653: push(@{$path},$name);
13654: $text .= &assign_category_rows($itemcount,$cats,$deeper,$name,$path,$currcategories);
13655: pop(@{$path});
13656: }
13657: $text .= '</td></tr>';
13658: }
13659: $text .= '</table></td>';
13660: }
13661: }
13662: }
13663: return $text;
13664: }
13665:
1.655 raeburn 13666: ############################################################
13667: ############################################################
13668:
13669:
1.443 albertel 13670: sub commit_customrole {
1.664 raeburn 13671: my ($udom,$uname,$url,$three,$four,$five,$start,$end,$context) = @_;
1.630 raeburn 13672: my $output = &mt('Assigning custom role').' "'.$five.'" by '.$four.':'.$three.' in '.$url.
1.443 albertel 13673: ($start?', '.&mt('starting').' '.localtime($start):'').
13674: ($end?', ending '.localtime($end):'').': <b>'.
13675: &Apache::lonnet::assigncustomrole(
1.664 raeburn 13676: $udom,$uname,$url,$three,$four,$five,$end,$start,undef,undef,$context).
1.443 albertel 13677: '</b><br />';
13678: return $output;
13679: }
13680:
13681: sub commit_standardrole {
1.1116 raeburn 13682: my ($udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context,$credits) = @_;
1.541 raeburn 13683: my ($output,$logmsg,$linefeed);
13684: if ($context eq 'auto') {
13685: $linefeed = "\n";
13686: } else {
13687: $linefeed = "<br />\n";
13688: }
1.443 albertel 13689: if ($three eq 'st') {
1.541 raeburn 13690: my $result = &commit_studentrole(\$logmsg,$udom,$uname,$url,$three,$start,$end,
1.1116 raeburn 13691: $one,$two,$sec,$context,$credits);
1.541 raeburn 13692: if (($result =~ /^error/) || ($result eq 'not_in_class') ||
1.626 raeburn 13693: ($result eq 'unknown_course') || ($result eq 'refused')) {
13694: $output = $logmsg.' '.&mt('Error: ').$result."\n";
1.443 albertel 13695: } else {
1.541 raeburn 13696: $output = $logmsg.$linefeed.&mt('Assigning').' '.$three.' in '.$url.
1.443 albertel 13697: ($start?', '.&mt('starting').' '.localtime($start):'').
1.541 raeburn 13698: ($end?', '.&mt('ending').' '.localtime($end):'').': ';
13699: if ($context eq 'auto') {
13700: $output .= $result.$linefeed.&mt('Add to classlist').': ok';
13701: } else {
13702: $output .= '<b>'.$result.'</b>'.$linefeed.
13703: &mt('Add to classlist').': <b>ok</b>';
13704: }
13705: $output .= $linefeed;
1.443 albertel 13706: }
13707: } else {
13708: $output = &mt('Assigning').' '.$three.' in '.$url.
13709: ($start?', '.&mt('starting').' '.localtime($start):'').
1.541 raeburn 13710: ($end?', '.&mt('ending').' '.localtime($end):'').': ';
1.652 raeburn 13711: my $result = &Apache::lonnet::assignrole($udom,$uname,$url,$three,$end,$start,'','',$context);
1.541 raeburn 13712: if ($context eq 'auto') {
13713: $output .= $result.$linefeed;
13714: } else {
13715: $output .= '<b>'.$result.'</b>'.$linefeed;
13716: }
1.443 albertel 13717: }
13718: return $output;
13719: }
13720:
13721: sub commit_studentrole {
1.1116 raeburn 13722: my ($logmsg,$udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context,
13723: $credits) = @_;
1.626 raeburn 13724: my ($result,$linefeed,$oldsecurl,$newsecurl);
1.541 raeburn 13725: if ($context eq 'auto') {
13726: $linefeed = "\n";
13727: } else {
13728: $linefeed = '<br />'."\n";
13729: }
1.443 albertel 13730: if (defined($one) && defined($two)) {
13731: my $cid=$one.'_'.$two;
13732: my $oldsec=&Apache::lonnet::getsection($udom,$uname,$cid);
13733: my $secchange = 0;
13734: my $expire_role_result;
13735: my $modify_section_result;
1.628 raeburn 13736: if ($oldsec ne '-1') {
13737: if ($oldsec ne $sec) {
1.443 albertel 13738: $secchange = 1;
1.628 raeburn 13739: my $now = time;
1.443 albertel 13740: my $uurl='/'.$cid;
13741: $uurl=~s/\_/\//g;
13742: if ($oldsec) {
13743: $uurl.='/'.$oldsec;
13744: }
1.626 raeburn 13745: $oldsecurl = $uurl;
1.628 raeburn 13746: $expire_role_result =
1.652 raeburn 13747: &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',$now,'','',$context);
1.628 raeburn 13748: if ($env{'request.course.sec'} ne '') {
13749: if ($expire_role_result eq 'refused') {
13750: my @roles = ('st');
13751: my @statuses = ('previous');
13752: my @roledoms = ($one);
13753: my $withsec = 1;
13754: my %roleshash =
13755: &Apache::lonnet::get_my_roles($uname,$udom,'userroles',
13756: \@statuses,\@roles,\@roledoms,$withsec);
13757: if (defined ($roleshash{$two.':'.$one.':st:'.$oldsec})) {
13758: my ($oldstart,$oldend) =
13759: split(':',$roleshash{$two.':'.$one.':st:'.$oldsec});
13760: if ($oldend > 0 && $oldend <= $now) {
13761: $expire_role_result = 'ok';
13762: }
13763: }
13764: }
13765: }
1.443 albertel 13766: $result = $expire_role_result;
13767: }
13768: }
13769: if (($expire_role_result eq 'ok') || ($secchange == 0)) {
1.1116 raeburn 13770: $modify_section_result =
13771: &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,
13772: undef,undef,undef,$sec,
13773: $end,$start,'','',$cid,
13774: '',$context,$credits);
1.443 albertel 13775: if ($modify_section_result =~ /^ok/) {
13776: if ($secchange == 1) {
1.628 raeburn 13777: if ($sec eq '') {
13778: $$logmsg .= &mt('Section for [_1] switched from (possibly expired) old section: [_2] to student role without a section.',$uname,$oldsec).$linefeed;
13779: } else {
13780: $$logmsg .= &mt('Section for [_1] switched from (possibly expired) old section: [_2] to new section: [_3].',$uname,$oldsec,$sec).$linefeed;
13781: }
1.443 albertel 13782: } elsif ($oldsec eq '-1') {
1.628 raeburn 13783: if ($sec eq '') {
13784: $$logmsg .= &mt('New student role without a section for [_1] in course [_2].',$uname,$cid).$linefeed;
13785: } else {
13786: $$logmsg .= &mt('New student role for [_1] in section [_2] in course [_3].',$uname,$sec,$cid).$linefeed;
13787: }
1.443 albertel 13788: } else {
1.628 raeburn 13789: if ($sec eq '') {
13790: $$logmsg .= &mt('Student [_1] assigned to course [_2] without a section.',$uname,$cid).$linefeed;
13791: } else {
13792: $$logmsg .= &mt('Student [_1] assigned to section [_2] in course [_3].',$uname,$sec,$cid).$linefeed;
13793: }
1.443 albertel 13794: }
13795: } else {
1.1115 raeburn 13796: if ($secchange) {
1.628 raeburn 13797: $$logmsg .= &mt('Error when attempting section change for [_1] from old section "[_2]" to new section: "[_3]" in course [_4] -error:',$uname,$oldsec,$sec,$cid).' '.$modify_section_result.$linefeed;
13798: } else {
13799: $$logmsg .= &mt('Error when attempting to modify role for [_1] for section: "[_2]" in course [_3] -error:',$uname,$sec,$cid).' '.$modify_section_result.$linefeed;
13800: }
1.443 albertel 13801: }
13802: $result = $modify_section_result;
13803: } elsif ($secchange == 1) {
1.628 raeburn 13804: if ($oldsec eq '') {
1.1103 raeburn 13805: $$logmsg .= &mt('Error when attempting to expire existing role without a section for [_1] in course [_2] -error: ',$uname,$cid).' '.$expire_role_result.$linefeed;
1.628 raeburn 13806: } else {
13807: $$logmsg .= &mt('Error when attempting to expire existing role for [_1] in section [_2] in course [_3] -error: ',$uname,$oldsec,$cid).' '.$expire_role_result.$linefeed;
13808: }
1.626 raeburn 13809: if ($expire_role_result eq 'refused') {
13810: my $newsecurl = '/'.$cid;
13811: $newsecurl =~ s/\_/\//g;
13812: if ($sec ne '') {
13813: $newsecurl.='/'.$sec;
13814: }
13815: if (&Apache::lonnet::allowed('cst',$newsecurl) && !(&Apache::lonnet::allowed('cst',$oldsecurl))) {
13816: if ($sec eq '') {
13817: $$logmsg .= &mt('Although your current role has privileges to add students to section "[_1]", you do not have privileges to modify existing enrollments unaffiliated with any section.',$sec).$linefeed;
13818: } else {
13819: $$logmsg .= &mt('Although your current role has privileges to add students to section "[_1]", you do not have privileges to modify existing enrollments in other sections.',$sec).$linefeed;
13820: }
13821: }
13822: }
1.443 albertel 13823: }
13824: } else {
1.626 raeburn 13825: $$logmsg .= &mt('Incomplete course id defined.').$linefeed.&mt('Addition of user [_1] from domain [_2] to course [_3], section [_4] not completed.',$uname,$udom,$one.'_'.$two,$sec).$linefeed;
1.443 albertel 13826: $result = "error: incomplete course id\n";
13827: }
13828: return $result;
13829: }
13830:
1.1108 raeburn 13831: sub show_role_extent {
13832: my ($scope,$context,$role) = @_;
13833: $scope =~ s{^/}{};
13834: my @courseroles = &Apache::lonuserutils::roles_by_context('course',1);
13835: push(@courseroles,'co');
13836: my @authorroles = &Apache::lonuserutils::roles_by_context('author');
13837: if (($context eq 'course') || (grep(/^\Q$role\E/,@courseroles))) {
13838: $scope =~ s{/}{_};
13839: return '<span class="LC_cusr_emph">'.$env{'course.'.$scope.'.description'}.'</span>';
13840: } elsif (($context eq 'author') || (grep(/^\Q$role\E/,@authorroles))) {
13841: my ($audom,$auname) = split(/\//,$scope);
13842: return &mt('[_1] Author Space','<span class="LC_cusr_emph">'.
13843: &Apache::loncommon::plainname($auname,$audom).'</span>');
13844: } else {
13845: $scope =~ s{/$}{};
13846: return &mt('Domain: [_1]','<span class="LC_cusr_emph">'.
13847: &Apache::lonnet::domain($scope,'description').'</span>');
13848: }
13849: }
13850:
1.443 albertel 13851: ############################################################
13852: ############################################################
13853:
1.566 albertel 13854: sub check_clone {
1.578 raeburn 13855: my ($args,$linefeed) = @_;
1.566 albertel 13856: my $cloneid='/'.$args->{'clonedomain'}.'/'.$args->{'clonecourse'};
13857: my ($clonecrsudom,$clonecrsunum)= &LONCAPA::split_courseid($cloneid);
13858: my $clonehome=&Apache::lonnet::homeserver($clonecrsunum,$clonecrsudom);
13859: my $clonemsg;
13860: my $can_clone = 0;
1.944 raeburn 13861: my $lctype = lc($args->{'crstype'});
1.908 raeburn 13862: if ($lctype ne 'community') {
13863: $lctype = 'course';
13864: }
1.566 albertel 13865: if ($clonehome eq 'no_host') {
1.944 raeburn 13866: if ($args->{'crstype'} eq 'Community') {
1.908 raeburn 13867: $clonemsg = &mt('No new community created.').$linefeed.&mt('A new community could not be cloned from the specified original - [_1] - because it is a non-existent community.',$args->{'clonecourse'}.':'.$args->{'clonedomain'});
13868: } else {
13869: $clonemsg = &mt('No new course created.').$linefeed.&mt('A new course could not be cloned from the specified original - [_1] - because it is a non-existent course.',$args->{'clonecourse'}.':'.$args->{'clonedomain'});
13870: }
1.566 albertel 13871: } else {
13872: my %clonedesc = &Apache::lonnet::coursedescription($cloneid,{'one_time' => 1});
1.944 raeburn 13873: if ($args->{'crstype'} eq 'Community') {
1.908 raeburn 13874: if ($clonedesc{'type'} ne 'Community') {
13875: $clonemsg = &mt('No new community created.').$linefeed.&mt('A new community could not be cloned from the specified original - [_1] - because it is a course not a community.',$args->{'clonecourse'}.':'.$args->{'clonedomain'});
13876: return ($can_clone, $clonemsg, $cloneid, $clonehome);
13877: }
13878: }
1.882 raeburn 13879: if (($env{'request.role.domain'} eq $args->{'clonedomain'}) &&
13880: (&Apache::lonnet::allowed('ccc',$env{'request.role.domain'}))) {
1.566 albertel 13881: $can_clone = 1;
13882: } else {
13883: my %clonehash = &Apache::lonnet::get('environment',['cloners'],
13884: $args->{'clonedomain'},$args->{'clonecourse'});
13885: my @cloners = split(/,/,$clonehash{'cloners'});
1.578 raeburn 13886: if (grep(/^\*$/,@cloners)) {
13887: $can_clone = 1;
13888: } elsif (grep(/^\*\:\Q$args->{'ccdomain'}\E$/,@cloners)) {
13889: $can_clone = 1;
13890: } else {
1.908 raeburn 13891: my $ccrole = 'cc';
1.944 raeburn 13892: if ($args->{'crstype'} eq 'Community') {
1.908 raeburn 13893: $ccrole = 'co';
13894: }
1.578 raeburn 13895: my %roleshash =
13896: &Apache::lonnet::get_my_roles($args->{'ccuname'},
13897: $args->{'ccdomain'},
1.908 raeburn 13898: 'userroles',['active'],[$ccrole],
1.578 raeburn 13899: [$args->{'clonedomain'}]);
1.908 raeburn 13900: if (($roleshash{$args->{'clonecourse'}.':'.$args->{'clonedomain'}.':'.$ccrole}) || (grep(/^\Q$args->{'ccuname'}\E:\Q$args->{'ccdomain'}\E$/,@cloners))) {
1.942 raeburn 13901: $can_clone = 1;
13902: } elsif (&Apache::lonnet::is_course_owner($args->{'clonedomain'},$args->{'clonecourse'},$args->{'ccuname'},$args->{'ccdomain'})) {
13903: $can_clone = 1;
13904: } else {
1.944 raeburn 13905: if ($args->{'crstype'} eq 'Community') {
1.908 raeburn 13906: $clonemsg = &mt('No new community created.').$linefeed.&mt('The new community could not be cloned from the existing community because the new community owner ([_1]) does not have cloning rights in the existing community ([_2]).',$args->{'ccuname'}.':'.$args->{'ccdomain'},$clonedesc{'description'});
13907: } else {
13908: $clonemsg = &mt('No new course created.').$linefeed.&mt('The new course could not be cloned from the existing course because the new course owner ([_1]) does not have cloning rights in the existing course ([_2]).',$args->{'ccuname'}.':'.$args->{'ccdomain'},$clonedesc{'description'});
13909: }
1.578 raeburn 13910: }
1.566 albertel 13911: }
1.578 raeburn 13912: }
1.566 albertel 13913: }
13914: return ($can_clone, $clonemsg, $cloneid, $clonehome);
13915: }
13916:
1.444 albertel 13917: sub construct_course {
1.1166 raeburn 13918: my ($args,$logmsg,$courseid,$crsudom,$crsunum,$udom,$uname,$context,$cnum,$category,$coderef) = @_;
1.444 albertel 13919: my $outcome;
1.541 raeburn 13920: my $linefeed = '<br />'."\n";
13921: if ($context eq 'auto') {
13922: $linefeed = "\n";
13923: }
1.566 albertel 13924:
13925: #
13926: # Are we cloning?
13927: #
13928: my ($can_clone, $clonemsg, $cloneid, $clonehome);
13929: if (($args->{'clonecourse'}) && ($args->{'clonedomain'})) {
1.578 raeburn 13930: ($can_clone, $clonemsg, $cloneid, $clonehome) = &check_clone($args,$linefeed);
1.566 albertel 13931: if ($context ne 'auto') {
1.578 raeburn 13932: if ($clonemsg ne '') {
13933: $clonemsg = '<span class="LC_error">'.$clonemsg.'</span>';
13934: }
1.566 albertel 13935: }
13936: $outcome .= $clonemsg.$linefeed;
13937:
13938: if (!$can_clone) {
13939: return (0,$outcome);
13940: }
13941: }
13942:
1.444 albertel 13943: #
13944: # Open course
13945: #
13946: my $crstype = lc($args->{'crstype'});
13947: my %cenv=();
13948: $$courseid=&Apache::lonnet::createcourse($args->{'course_domain'},
13949: $args->{'cdescr'},
13950: $args->{'curl'},
13951: $args->{'course_home'},
13952: $args->{'nonstandard'},
13953: $args->{'crscode'},
13954: $args->{'ccuname'}.':'.
13955: $args->{'ccdomain'},
1.882 raeburn 13956: $args->{'crstype'},
1.885 raeburn 13957: $cnum,$context,$category);
1.444 albertel 13958:
13959: # Note: The testing routines depend on this being output; see
13960: # Utils::Course. This needs to at least be output as a comment
13961: # if anyone ever decides to not show this, and Utils::Course::new
13962: # will need to be suitably modified.
1.541 raeburn 13963: $outcome .= &mt('New LON-CAPA [_1] ID: [_2]',$crstype,$$courseid).$linefeed;
1.943 raeburn 13964: if ($$courseid =~ /^error:/) {
13965: return (0,$outcome);
13966: }
13967:
1.444 albertel 13968: #
13969: # Check if created correctly
13970: #
1.479 albertel 13971: ($$crsudom,$$crsunum)= &LONCAPA::split_courseid($$courseid);
1.444 albertel 13972: my $crsuhome=&Apache::lonnet::homeserver($$crsunum,$$crsudom);
1.943 raeburn 13973: if ($crsuhome eq 'no_host') {
13974: $outcome .= &mt('Course creation failed, unrecognized course home server.').$linefeed;
13975: return (0,$outcome);
13976: }
1.541 raeburn 13977: $outcome .= &mt('Created on').': '.$crsuhome.$linefeed;
1.566 albertel 13978:
1.444 albertel 13979: #
1.566 albertel 13980: # Do the cloning
13981: #
13982: if ($can_clone && $cloneid) {
13983: $clonemsg = &mt('Cloning [_1] from [_2]',$crstype,$clonehome);
13984: if ($context ne 'auto') {
13985: $clonemsg = '<span class="LC_success">'.$clonemsg.'</span>';
13986: }
13987: $outcome .= $clonemsg.$linefeed;
13988: my %oldcenv=&Apache::lonnet::dump('environment',$$crsudom,$$crsunum);
1.444 albertel 13989: # Copy all files
1.637 www 13990: &Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid,$args->{'datemode'},$args->{'dateshift'});
1.444 albertel 13991: # Restore URL
1.566 albertel 13992: $cenv{'url'}=$oldcenv{'url'};
1.444 albertel 13993: # Restore title
1.566 albertel 13994: $cenv{'description'}=$oldcenv{'description'};
1.955 raeburn 13995: # Restore creation date, creator and creation context.
13996: $cenv{'internal.created'}=$oldcenv{'internal.created'};
13997: $cenv{'internal.creator'}=$oldcenv{'internal.creator'};
13998: $cenv{'internal.creationcontext'}=$oldcenv{'internal.creationcontext'};
1.444 albertel 13999: # Mark as cloned
1.566 albertel 14000: $cenv{'clonedfrom'}=$cloneid;
1.638 www 14001: # Need to clone grading mode
14002: my %newenv=&Apache::lonnet::get('environment',['grading'],$$crsudom,$$crsunum);
14003: $cenv{'grading'}=$newenv{'grading'};
14004: # Do not clone these environment entries
14005: &Apache::lonnet::del('environment',
14006: ['default_enrollment_start_date',
14007: 'default_enrollment_end_date',
14008: 'question.email',
14009: 'policy.email',
14010: 'comment.email',
14011: 'pch.users.denied',
1.725 raeburn 14012: 'plc.users.denied',
14013: 'hidefromcat',
1.1121 raeburn 14014: 'checkforpriv',
1.1166 raeburn 14015: 'categories',
14016: 'internal.uniquecode'],
1.638 www 14017: $$crsudom,$$crsunum);
1.1170 raeburn 14018: if ($args->{'textbook'}) {
14019: $cenv{'internal.textbook'} = $args->{'textbook'};
14020: }
1.444 albertel 14021: }
1.566 albertel 14022:
1.444 albertel 14023: #
14024: # Set environment (will override cloned, if existing)
14025: #
14026: my @sections = ();
14027: my @xlists = ();
14028: if ($args->{'crstype'}) {
14029: $cenv{'type'}=$args->{'crstype'};
14030: }
14031: if ($args->{'crsid'}) {
14032: $cenv{'courseid'}=$args->{'crsid'};
14033: }
14034: if ($args->{'crscode'}) {
14035: $cenv{'internal.coursecode'}=$args->{'crscode'};
14036: }
14037: if ($args->{'crsquota'} ne '') {
14038: $cenv{'internal.coursequota'}=$args->{'crsquota'};
14039: } else {
14040: $cenv{'internal.coursequota'}=$args->{'crsquota'} = 20;
14041: }
14042: if ($args->{'ccuname'}) {
14043: $cenv{'internal.courseowner'} = $args->{'ccuname'}.
14044: ':'.$args->{'ccdomain'};
14045: } else {
14046: $cenv{'internal.courseowner'} = $args->{'curruser'};
14047: }
1.1116 raeburn 14048: if ($args->{'defaultcredits'}) {
14049: $cenv{'internal.defaultcredits'} = $args->{'defaultcredits'};
14050: }
1.444 albertel 14051: my @badclasses = (); # Used to accumulate sections/crosslistings that did not pass classlist access check for course owner.
14052: if ($args->{'crssections'}) {
14053: $cenv{'internal.sectionnums'} = '';
14054: if ($args->{'crssections'} =~ m/,/) {
14055: @sections = split/,/,$args->{'crssections'};
14056: } else {
14057: $sections[0] = $args->{'crssections'};
14058: }
14059: if (@sections > 0) {
14060: foreach my $item (@sections) {
14061: my ($sec,$gp) = split/:/,$item;
14062: my $class = $args->{'crscode'}.$sec;
14063: my $addcheck = &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$class,$cenv{'internal.courseowner'});
14064: $cenv{'internal.sectionnums'} .= $item.',';
14065: unless ($addcheck eq 'ok') {
14066: push @badclasses, $class;
14067: }
14068: }
14069: $cenv{'internal.sectionnums'} =~ s/,$//;
14070: }
14071: }
14072: # do not hide course coordinator from staff listing,
14073: # even if privileged
14074: $cenv{'nothideprivileged'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
1.1121 raeburn 14075: # add course coordinator's domain to domains to check for privileged users
14076: # if different to course domain
14077: if ($$crsudom ne $args->{'ccdomain'}) {
14078: $cenv{'checkforpriv'} = $args->{'ccdomain'};
14079: }
1.444 albertel 14080: # add crosslistings
14081: if ($args->{'crsxlist'}) {
14082: $cenv{'internal.crosslistings'}='';
14083: if ($args->{'crsxlist'} =~ m/,/) {
14084: @xlists = split/,/,$args->{'crsxlist'};
14085: } else {
14086: $xlists[0] = $args->{'crsxlist'};
14087: }
14088: if (@xlists > 0) {
14089: foreach my $item (@xlists) {
14090: my ($xl,$gp) = split/:/,$item;
14091: my $addcheck = &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$xl,$cenv{'internal.courseowner'});
14092: $cenv{'internal.crosslistings'} .= $item.',';
14093: unless ($addcheck eq 'ok') {
14094: push @badclasses, $xl;
14095: }
14096: }
14097: $cenv{'internal.crosslistings'} =~ s/,$//;
14098: }
14099: }
14100: if ($args->{'autoadds'}) {
14101: $cenv{'internal.autoadds'}=$args->{'autoadds'};
14102: }
14103: if ($args->{'autodrops'}) {
14104: $cenv{'internal.autodrops'}=$args->{'autodrops'};
14105: }
14106: # check for notification of enrollment changes
14107: my @notified = ();
14108: if ($args->{'notify_owner'}) {
14109: if ($args->{'ccuname'} ne '') {
14110: push(@notified,$args->{'ccuname'}.':'.$args->{'ccdomain'});
14111: }
14112: }
14113: if ($args->{'notify_dc'}) {
14114: if ($uname ne '') {
1.630 raeburn 14115: push(@notified,$uname.':'.$udom);
1.444 albertel 14116: }
14117: }
14118: if (@notified > 0) {
14119: my $notifylist;
14120: if (@notified > 1) {
14121: $notifylist = join(',',@notified);
14122: } else {
14123: $notifylist = $notified[0];
14124: }
14125: $cenv{'internal.notifylist'} = $notifylist;
14126: }
14127: if (@badclasses > 0) {
14128: my %lt=&Apache::lonlocal::texthash(
14129: 'tclb' => 'The courses listed below were included as sections or crosslistings affiliated with your new LON-CAPA course. However, if automated course roster updates are enabled for this class, these particular sections/crosslistings will not contribute towards enrollment, because the user identified as the course owner for this LON-CAPA course',
14130: 'dnhr' => 'does not have rights to access enrollment in these classes',
14131: 'adby' => 'as determined by the policies of your institution on access to official classlists'
14132: );
1.541 raeburn 14133: my $badclass_msg = $cenv{'internal.courseowner'}.') - '.$lt{'dnhr'}.
14134: ' ('.$lt{'adby'}.')';
14135: if ($context eq 'auto') {
14136: $outcome .= $badclass_msg.$linefeed;
1.566 albertel 14137: $outcome .= '<div class="LC_warning">'.$badclass_msg.$linefeed.'<ul>'."\n";
1.541 raeburn 14138: foreach my $item (@badclasses) {
14139: if ($context eq 'auto') {
14140: $outcome .= " - $item\n";
14141: } else {
14142: $outcome .= "<li>$item</li>\n";
14143: }
14144: }
14145: if ($context eq 'auto') {
14146: $outcome .= $linefeed;
14147: } else {
1.566 albertel 14148: $outcome .= "</ul><br /><br /></div>\n";
1.541 raeburn 14149: }
14150: }
1.444 albertel 14151: }
14152: if ($args->{'no_end_date'}) {
14153: $args->{'endaccess'} = 0;
14154: }
14155: $cenv{'internal.autostart'}=$args->{'enrollstart'};
14156: $cenv{'internal.autoend'}=$args->{'enrollend'};
14157: $cenv{'default_enrollment_start_date'}=$args->{'startaccess'};
14158: $cenv{'default_enrollment_end_date'}=$args->{'endaccess'};
14159: if ($args->{'showphotos'}) {
14160: $cenv{'internal.showphotos'}=$args->{'showphotos'};
14161: }
14162: $cenv{'internal.authtype'} = $args->{'authtype'};
14163: $cenv{'internal.autharg'} = $args->{'autharg'};
14164: if ( ($cenv{'internal.authtype'} =~ /^krb/) && ($cenv{'internal.autoadds'} == 1)) {
14165: if (! defined($cenv{'internal.autharg'}) || $cenv{'internal.autharg'} eq '') {
1.541 raeburn 14166: my $krb_msg = &mt('As you did not include the default Kerberos domain to be used for authentication in this class, the institutional data used by the automated enrollment process must include the Kerberos domain for each new student');
14167: if ($context eq 'auto') {
14168: $outcome .= $krb_msg;
14169: } else {
1.566 albertel 14170: $outcome .= '<span class="LC_error">'.$krb_msg.'</span>';
1.541 raeburn 14171: }
14172: $outcome .= $linefeed;
1.444 albertel 14173: }
14174: }
14175: if (($args->{'ccdomain'}) && ($args->{'ccuname'})) {
14176: if ($args->{'setpolicy'}) {
14177: $cenv{'policy.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
14178: }
14179: if ($args->{'setcontent'}) {
14180: $cenv{'question.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
14181: }
14182: }
14183: if ($args->{'reshome'}) {
14184: $cenv{'reshome'}=$args->{'reshome'}.'/';
14185: $cenv{'reshome'}=~s/\/+$/\//;
14186: }
14187: #
14188: # course has keyed access
14189: #
14190: if ($args->{'setkeys'}) {
14191: $cenv{'keyaccess'}='yes';
14192: }
14193: # if specified, key authority is not course, but user
14194: # only active if keyaccess is yes
14195: if ($args->{'keyauth'}) {
1.487 albertel 14196: my ($user,$domain) = split(':',$args->{'keyauth'});
14197: $user = &LONCAPA::clean_username($user);
14198: $domain = &LONCAPA::clean_username($domain);
1.488 foxr 14199: if ($user ne '' && $domain ne '') {
1.487 albertel 14200: $cenv{'keyauth'}=$user.':'.$domain;
1.444 albertel 14201: }
14202: }
14203:
1.1166 raeburn 14204: #
1.1167 raeburn 14205: # generate and store uniquecode (available to course requester), if course should have one.
1.1166 raeburn 14206: #
14207: if ($args->{'uniquecode'}) {
14208: my ($code,$error) = &make_unique_code($$crsudom,$$crsunum);
14209: if ($code) {
14210: $cenv{'internal.uniquecode'} = $code;
1.1167 raeburn 14211: my %crsinfo =
14212: &Apache::lonnet::courseiddump($$crsudom,'.',1,'.','.',$$crsunum,undef,undef,'.');
14213: if (ref($crsinfo{$$crsudom.'_'.$$crsunum}) eq 'HASH') {
14214: $crsinfo{$$crsudom.'_'.$$crsunum}{'uniquecode'} = $code;
14215: my $putres = &Apache::lonnet::courseidput($$crsudom,\%crsinfo,$crsuhome,'notime');
14216: }
1.1166 raeburn 14217: if (ref($coderef)) {
14218: $$coderef = $code;
14219: }
14220: }
14221: }
14222:
1.444 albertel 14223: if ($args->{'disresdis'}) {
14224: $cenv{'pch.roles.denied'}='st';
14225: }
14226: if ($args->{'disablechat'}) {
14227: $cenv{'plc.roles.denied'}='st';
14228: }
14229:
14230: # Record we've not yet viewed the Course Initialization Helper for this
14231: # course
14232: $cenv{'course.helper.not.run'} = 1;
14233: #
14234: # Use new Randomseed
14235: #
14236: $cenv{'rndseed'}=&Apache::lonnet::latest_rnd_algorithm_id();;
14237: $cenv{'receiptalg'}=&Apache::lonnet::latest_receipt_algorithm_id();;
14238: #
14239: # The encryption code and receipt prefix for this course
14240: #
14241: $cenv{'internal.encseed'}=$Apache::lonnet::perlvar{'lonReceipt'}.$$.time.int(rand(9999));
14242: $cenv{'internal.encpref'}=100+int(9*rand(99));
14243: #
14244: # By default, use standard grading
14245: if (!defined($cenv{'grading'})) { $cenv{'grading'} = 'standard'; }
14246:
1.541 raeburn 14247: $outcome .= $linefeed.&mt('Setting environment').': '.
14248: &Apache::lonnet::put('environment',\%cenv,$$crsudom,$$crsunum).$linefeed;
1.444 albertel 14249: #
14250: # Open all assignments
14251: #
14252: if ($args->{'openall'}) {
14253: my $storeunder=$$crsudom.'_'.$$crsunum.'.0.opendate';
14254: my %storecontent = ($storeunder => time,
14255: $storeunder.'.type' => 'date_start');
14256:
14257: $outcome .= &mt('Opening all assignments').': '.&Apache::lonnet::cput
1.541 raeburn 14258: ('resourcedata',\%storecontent,$$crsudom,$$crsunum).$linefeed;
1.444 albertel 14259: }
14260: #
14261: # Set first page
14262: #
14263: unless (($args->{'nonstandard'}) || ($args->{'firstres'} eq 'blank')
14264: || ($cloneid)) {
1.445 albertel 14265: use LONCAPA::map;
1.444 albertel 14266: $outcome .= &mt('Setting first resource').': ';
1.445 albertel 14267:
14268: my $map = '/uploaded/'.$$crsudom.'/'.$$crsunum.'/default.sequence';
14269: my ($errtext,$fatal)=&LONCAPA::map::mapread($map);
14270:
1.444 albertel 14271: $outcome .= ($fatal?$errtext:'read ok').' - ';
14272: my $title; my $url;
14273: if ($args->{'firstres'} eq 'syl') {
1.690 bisitz 14274: $title=&mt('Syllabus');
1.444 albertel 14275: $url='/public/'.$$crsudom.'/'.$$crsunum.'/syllabus';
14276: } else {
1.963 raeburn 14277: $title=&mt('Table of Contents');
1.444 albertel 14278: $url='/adm/navmaps';
14279: }
1.445 albertel 14280:
14281: $LONCAPA::map::resources[1]=$title.':'.$url.':false:start:res';
14282: (my $outtext,$errtext) = &LONCAPA::map::storemap($map,1);
14283:
14284: if ($errtext) { $fatal=2; }
1.541 raeburn 14285: $outcome .= ($fatal?$errtext:'write ok').$linefeed;
1.444 albertel 14286: }
1.566 albertel 14287:
14288: return (1,$outcome);
1.444 albertel 14289: }
14290:
1.1166 raeburn 14291: sub make_unique_code {
14292: my ($cdom,$cnum) = @_;
14293: # get lock on uniquecodes db
14294: my $lockhash = {
14295: $cnum."\0".'uniquecodes' => $env{'user.name'}.
14296: ':'.$env{'user.domain'},
14297: };
14298: my $tries = 0;
14299: my $gotlock = &Apache::lonnet::newput_dom('uniquecodes',$lockhash,$cdom);
14300: my ($code,$error);
14301:
14302: while (($gotlock ne 'ok') && ($tries<3)) {
14303: $tries ++;
14304: sleep 1;
14305: $gotlock = &Apache::lonnet::newput_dom('uniquecodes',$lockhash,$cdom);
14306: }
14307: if ($gotlock eq 'ok') {
14308: my %currcodes = &Apache::lonnet::dump_dom('uniquecodes',$cdom);
14309: my $gotcode;
14310: my $attempts = 0;
14311: while ((!$gotcode) && ($attempts < 100)) {
14312: $code = &generate_code();
14313: if (!exists($currcodes{$code})) {
14314: $gotcode = 1;
14315: unless (&Apache::lonnet::newput_dom('uniquecodes',{ $code => $cnum },$cdom) eq 'ok') {
14316: $error = 'nostore';
14317: }
14318: }
14319: $attempts ++;
14320: }
14321: my @del_lock = ($cnum."\0".'uniquecodes');
14322: my $dellockoutcome = &Apache::lonnet::del_dom('uniquecodes',\@del_lock,$cdom);
14323: } else {
14324: $error = 'nolock';
14325: }
14326: return ($code,$error);
14327: }
14328:
14329: sub generate_code {
14330: my $code;
14331: my @letts = qw(B C D G H J K M N P Q R S T V W X Z);
14332: for (my $i=0; $i<6; $i++) {
14333: my $lettnum = int (rand 2);
14334: my $item = '';
14335: if ($lettnum) {
14336: $item = $letts[int( rand(18) )];
14337: } else {
14338: $item = 1+int( rand(8) );
14339: }
14340: $code .= $item;
14341: }
14342: return $code;
14343: }
14344:
1.444 albertel 14345: ############################################################
14346: ############################################################
14347:
1.953 droeschl 14348: #SD
14349: # only Community and Course, or anything else?
1.378 raeburn 14350: sub course_type {
14351: my ($cid) = @_;
14352: if (!defined($cid)) {
14353: $cid = $env{'request.course.id'};
14354: }
1.404 albertel 14355: if (defined($env{'course.'.$cid.'.type'})) {
14356: return $env{'course.'.$cid.'.type'};
1.378 raeburn 14357: } else {
14358: return 'Course';
1.377 raeburn 14359: }
14360: }
1.156 albertel 14361:
1.406 raeburn 14362: sub group_term {
14363: my $crstype = &course_type();
14364: my %names = (
14365: 'Course' => 'group',
1.865 raeburn 14366: 'Community' => 'group',
1.406 raeburn 14367: );
14368: return $names{$crstype};
14369: }
14370:
1.902 raeburn 14371: sub course_types {
1.1165 raeburn 14372: my @types = ('official','unofficial','community','textbook');
1.902 raeburn 14373: my %typename = (
14374: official => 'Official course',
14375: unofficial => 'Unofficial course',
14376: community => 'Community',
1.1165 raeburn 14377: textbook => 'Textbook course',
1.902 raeburn 14378: );
14379: return (\@types,\%typename);
14380: }
14381:
1.156 albertel 14382: sub icon {
14383: my ($file)=@_;
1.505 albertel 14384: my $curfext = lc((split(/\./,$file))[-1]);
1.168 albertel 14385: my $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/unknown.gif';
1.156 albertel 14386: my $embstyle = &Apache::loncommon::fileembstyle($curfext);
1.168 albertel 14387: if (!(!defined($embstyle) || $embstyle eq 'unk' || $embstyle eq 'hdn')) {
14388: if (-e $Apache::lonnet::perlvar{'lonDocRoot'}.'/'.
14389: $Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
14390: $curfext.".gif") {
14391: $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
14392: $curfext.".gif";
14393: }
14394: }
1.249 albertel 14395: return &lonhttpdurl($iconname);
1.154 albertel 14396: }
1.84 albertel 14397:
1.575 albertel 14398: sub lonhttpdurl {
1.692 www 14399: #
14400: # Had been used for "small fry" static images on separate port 8080.
14401: # Modify here if lightweight http functionality desired again.
14402: # Currently eliminated due to increasing firewall issues.
14403: #
1.575 albertel 14404: my ($url)=@_;
1.692 www 14405: return $url;
1.215 albertel 14406: }
14407:
1.213 albertel 14408: sub connection_aborted {
14409: my ($r)=@_;
14410: $r->print(" ");$r->rflush();
14411: my $c = $r->connection;
14412: return $c->aborted();
14413: }
14414:
1.221 foxr 14415: # Escapes strings that may have embedded 's that will be put into
1.222 foxr 14416: # strings as 'strings'.
14417: sub escape_single {
1.221 foxr 14418: my ($input) = @_;
1.223 albertel 14419: $input =~ s/\\/\\\\/g; # Escape the \'s..(must be first)>
1.221 foxr 14420: $input =~ s/\'/\\\'/g; # Esacpe the 's....
14421: return $input;
14422: }
1.223 albertel 14423:
1.222 foxr 14424: # Same as escape_single, but escape's "'s This
14425: # can be used for "strings"
14426: sub escape_double {
14427: my ($input) = @_;
14428: $input =~ s/\\/\\\\/g; # Escape the /'s..(must be first)>
14429: $input =~ s/\"/\\\"/g; # Esacpe the "s....
14430: return $input;
14431: }
1.223 albertel 14432:
1.222 foxr 14433: # Escapes the last element of a full URL.
14434: sub escape_url {
14435: my ($url) = @_;
1.238 raeburn 14436: my @urlslices = split(/\//, $url,-1);
1.369 www 14437: my $lastitem = &escape(pop(@urlslices));
1.223 albertel 14438: return join('/',@urlslices).'/'.$lastitem;
1.222 foxr 14439: }
1.462 albertel 14440:
1.820 raeburn 14441: sub compare_arrays {
14442: my ($arrayref1,$arrayref2) = @_;
14443: my (@difference,%count);
14444: @difference = ();
14445: %count = ();
14446: if ((ref($arrayref1) eq 'ARRAY') && (ref($arrayref2) eq 'ARRAY')) {
14447: foreach my $element (@{$arrayref1}, @{$arrayref2}) { $count{$element}++; }
14448: foreach my $element (keys(%count)) {
14449: if ($count{$element} == 1) {
14450: push(@difference,$element);
14451: }
14452: }
14453: }
14454: return @difference;
14455: }
14456:
1.817 bisitz 14457: # -------------------------------------------------------- Initialize user login
1.462 albertel 14458: sub init_user_environment {
1.463 albertel 14459: my ($r, $username, $domain, $authhost, $form, $args) = @_;
1.462 albertel 14460: my $lonids=$Apache::lonnet::perlvar{'lonIDsDir'};
14461:
14462: my $public=($username eq 'public' && $domain eq 'public');
14463:
14464: # See if old ID present, if so, remove
14465:
1.1062 raeburn 14466: my ($filename,$cookie,$userroles,$firstaccenv,$timerintenv);
1.462 albertel 14467: my $now=time;
14468:
14469: if ($public) {
14470: my $max_public=100;
14471: my $oldest;
14472: my $oldest_time=0;
14473: for(my $next=1;$next<=$max_public;$next++) {
14474: if (-e $lonids."/publicuser_$next.id") {
14475: my $mtime=(stat($lonids."/publicuser_$next.id"))[9];
14476: if ($mtime<$oldest_time || !$oldest_time) {
14477: $oldest_time=$mtime;
14478: $oldest=$next;
14479: }
14480: } else {
14481: $cookie="publicuser_$next";
14482: last;
14483: }
14484: }
14485: if (!$cookie) { $cookie="publicuser_$oldest"; }
14486: } else {
1.463 albertel 14487: # if this isn't a robot, kill any existing non-robot sessions
14488: if (!$args->{'robot'}) {
14489: opendir(DIR,$lonids);
14490: while ($filename=readdir(DIR)) {
14491: if ($filename=~/^$username\_\d+\_$domain\_$authhost\.id$/) {
14492: unlink($lonids.'/'.$filename);
14493: }
1.462 albertel 14494: }
1.463 albertel 14495: closedir(DIR);
1.462 albertel 14496: }
14497: # Give them a new cookie
1.463 albertel 14498: my $id = ($args->{'robot'} ? 'robot'.$args->{'robot'}
1.684 www 14499: : $now.$$.int(rand(10000)));
1.463 albertel 14500: $cookie="$username\_$id\_$domain\_$authhost";
1.462 albertel 14501:
14502: # Initialize roles
14503:
1.1062 raeburn 14504: ($userroles,$firstaccenv,$timerintenv) =
14505: &Apache::lonnet::rolesinit($domain,$username,$authhost);
1.462 albertel 14506: }
14507: # ------------------------------------ Check browser type and MathML capability
14508:
14509: my ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,
1.1141 raeburn 14510: $clientunicode,$clientos,$clientmobile,$clientinfo) = &decode_user_agent($r);
1.462 albertel 14511:
14512: # ------------------------------------------------------------- Get environment
14513:
14514: my %userenv = &Apache::lonnet::dump('environment',$domain,$username);
14515: my ($tmp) = keys(%userenv);
14516: if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
14517: } else {
14518: undef(%userenv);
14519: }
14520: if (($userenv{'interface'}) && (!$form->{'interface'})) {
14521: $form->{'interface'}=$userenv{'interface'};
14522: }
14523: if ($userenv{'texengine'} eq 'ttm') { $clientmathml=1; }
14524:
14525: # --------------- Do not trust query string to be put directly into environment
1.817 bisitz 14526: foreach my $option ('interface','localpath','localres') {
14527: $form->{$option}=~s/[\n\r\=]//gs;
1.462 albertel 14528: }
14529: # --------------------------------------------------------- Write first profile
14530:
14531: {
14532: my %initial_env =
14533: ("user.name" => $username,
14534: "user.domain" => $domain,
14535: "user.home" => $authhost,
14536: "browser.type" => $clientbrowser,
14537: "browser.version" => $clientversion,
14538: "browser.mathml" => $clientmathml,
14539: "browser.unicode" => $clientunicode,
14540: "browser.os" => $clientos,
1.1137 raeburn 14541: "browser.mobile" => $clientmobile,
1.1141 raeburn 14542: "browser.info" => $clientinfo,
1.462 albertel 14543: "server.domain" => $Apache::lonnet::perlvar{'lonDefDomain'},
14544: "request.course.fn" => '',
14545: "request.course.uri" => '',
14546: "request.course.sec" => '',
14547: "request.role" => 'cm',
14548: "request.role.adv" => $env{'user.adv'},
14549: "request.host" => $ENV{'REMOTE_ADDR'},);
14550:
14551: if ($form->{'localpath'}) {
14552: $initial_env{"browser.localpath"} = $form->{'localpath'};
14553: $initial_env{"browser.localres"} = $form->{'localres'};
14554: }
14555:
14556: if ($form->{'interface'}) {
14557: $form->{'interface'}=~s/\W//gs;
14558: $initial_env{"browser.interface"} = $form->{'interface'};
14559: $env{'browser.interface'}=$form->{'interface'};
14560: }
14561:
1.1157 raeburn 14562: if ($form->{'iptoken'}) {
14563: my $lonhost = $r->dir_config('lonHostID');
14564: $initial_env{"user.noloadbalance"} = $lonhost;
14565: $env{'user.noloadbalance'} = $lonhost;
14566: }
14567:
1.981 raeburn 14568: my %is_adv = ( is_adv => $env{'user.adv'} );
1.1016 raeburn 14569: my %domdef;
14570: unless ($domain eq 'public') {
14571: %domdef = &Apache::lonnet::get_domain_defaults($domain);
14572: }
1.980 raeburn 14573:
1.1081 raeburn 14574: foreach my $tool ('aboutme','blog','webdav','portfolio') {
1.724 raeburn 14575: $userenv{'availabletools.'.$tool} =
1.980 raeburn 14576: &Apache::lonnet::usertools_access($username,$domain,$tool,'reload',
14577: undef,\%userenv,\%domdef,\%is_adv);
1.724 raeburn 14578: }
14579:
1.1165 raeburn 14580: foreach my $crstype ('official','unofficial','community','textbook') {
1.765 raeburn 14581: $userenv{'canrequest.'.$crstype} =
14582: &Apache::lonnet::usertools_access($username,$domain,$crstype,
1.980 raeburn 14583: 'reload','requestcourses',
14584: \%userenv,\%domdef,\%is_adv);
1.765 raeburn 14585: }
14586:
1.1092 raeburn 14587: $userenv{'canrequest.author'} =
14588: &Apache::lonnet::usertools_access($username,$domain,'requestauthor',
14589: 'reload','requestauthor',
14590: \%userenv,\%domdef,\%is_adv);
14591: my %reqauthor = &Apache::lonnet::get('requestauthor',['author_status','author'],
14592: $domain,$username);
14593: my $reqstatus = $reqauthor{'author_status'};
14594: if ($reqstatus eq 'approval' || $reqstatus eq 'approved') {
14595: if (ref($reqauthor{'author'}) eq 'HASH') {
14596: $userenv{'requestauthorqueued'} = $reqstatus.':'.
14597: $reqauthor{'author'}{'timestamp'};
14598: }
14599: }
14600:
1.462 albertel 14601: $env{'user.environment'} = "$lonids/$cookie.id";
1.1062 raeburn 14602:
1.462 albertel 14603: if (tie(my %disk_env,'GDBM_File',"$lonids/$cookie.id",
14604: &GDBM_WRCREAT(),0640)) {
14605: &_add_to_env(\%disk_env,\%initial_env);
14606: &_add_to_env(\%disk_env,\%userenv,'environment.');
14607: &_add_to_env(\%disk_env,$userroles);
1.1062 raeburn 14608: if (ref($firstaccenv) eq 'HASH') {
14609: &_add_to_env(\%disk_env,$firstaccenv);
14610: }
14611: if (ref($timerintenv) eq 'HASH') {
14612: &_add_to_env(\%disk_env,$timerintenv);
14613: }
1.463 albertel 14614: if (ref($args->{'extra_env'})) {
14615: &_add_to_env(\%disk_env,$args->{'extra_env'});
14616: }
1.462 albertel 14617: untie(%disk_env);
14618: } else {
1.705 tempelho 14619: &Apache::lonnet::logthis("<span style=\"color:blue;\">WARNING: ".
14620: 'Could not create environment storage in lonauth: '.$!.'</span>');
1.462 albertel 14621: return 'error: '.$!;
14622: }
14623: }
14624: $env{'request.role'}='cm';
14625: $env{'request.role.adv'}=$env{'user.adv'};
14626: $env{'browser.type'}=$clientbrowser;
14627:
14628: return $cookie;
14629:
14630: }
14631:
14632: sub _add_to_env {
14633: my ($idf,$env_data,$prefix) = @_;
1.676 raeburn 14634: if (ref($env_data) eq 'HASH') {
14635: while (my ($key,$value) = each(%$env_data)) {
14636: $idf->{$prefix.$key} = $value;
14637: $env{$prefix.$key} = $value;
14638: }
1.462 albertel 14639: }
14640: }
14641:
1.685 tempelho 14642: # --- Get the symbolic name of a problem and the url
14643: sub get_symb {
14644: my ($request,$silent) = @_;
1.726 raeburn 14645: (my $url=$env{'form.url'}) =~ s-^https?\://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
1.685 tempelho 14646: my $symb=($env{'form.symb'} ne '' ? $env{'form.symb'} : (&Apache::lonnet::symbread($url)));
14647: if ($symb eq '') {
14648: if (!$silent) {
1.1071 raeburn 14649: if (ref($request)) {
14650: $request->print("Unable to handle ambiguous references:$url:.");
14651: }
1.685 tempelho 14652: return ();
14653: }
14654: }
14655: &Apache::lonenc::check_decrypt(\$symb);
14656: return ($symb);
14657: }
14658:
14659: # --------------------------------------------------------------Get annotation
14660:
14661: sub get_annotation {
14662: my ($symb,$enc) = @_;
14663:
14664: my $key = $symb;
14665: if (!$enc) {
14666: $key =
14667: &Apache::lonnet::clutter((&Apache::lonnet::decode_symb($symb))[2]);
14668: }
14669: my %annotation=&Apache::lonnet::get('nohist_annotations',[$key]);
14670: return $annotation{$key};
14671: }
14672:
14673: sub clean_symb {
1.731 raeburn 14674: my ($symb,$delete_enc) = @_;
1.685 tempelho 14675:
14676: &Apache::lonenc::check_decrypt(\$symb);
14677: my $enc = $env{'request.enc'};
1.731 raeburn 14678: if ($delete_enc) {
1.730 raeburn 14679: delete($env{'request.enc'});
14680: }
1.685 tempelho 14681:
14682: return ($symb,$enc);
14683: }
1.462 albertel 14684:
1.990 raeburn 14685: sub build_release_hashes {
14686: my ($checkparms,$checkresponsetypes,$checkcrstypes,$anonsurvey,$randomizetry) = @_;
14687: return unless((ref($checkparms) eq 'HASH') && (ref($checkresponsetypes) eq 'HASH') &&
14688: (ref($checkcrstypes) eq 'HASH') && (ref($anonsurvey) eq 'HASH') &&
14689: (ref($randomizetry) eq 'HASH'));
14690: foreach my $key (keys(%Apache::lonnet::needsrelease)) {
14691: my ($item,$name,$value) = split(/:/,$key);
14692: if ($item eq 'parameter') {
14693: if (ref($checkparms->{$name}) eq 'ARRAY') {
14694: unless(grep(/^\Q$name\E$/,@{$checkparms->{$name}})) {
14695: push(@{$checkparms->{$name}},$value);
14696: }
14697: } else {
14698: push(@{$checkparms->{$name}},$value);
14699: }
14700: } elsif ($item eq 'resourcetag') {
14701: if ($name eq 'responsetype') {
14702: $checkresponsetypes->{$value} = $Apache::lonnet::needsrelease{$key}
14703: }
14704: } elsif ($item eq 'course') {
14705: if ($name eq 'crstype') {
14706: $checkcrstypes->{$value} = $Apache::lonnet::needsrelease{$key};
14707: }
14708: }
14709: }
14710: ($anonsurvey->{major},$anonsurvey->{minor}) = split(/\./,$Apache::lonnet::needsrelease{'parameter:type:anonsurvey'});
14711: ($randomizetry->{major},$randomizetry->{minor}) = split(/\./,$Apache::lonnet::needsrelease{'parameter:type:randomizetry'});
14712: return;
14713: }
14714:
1.1083 raeburn 14715: sub update_content_constraints {
14716: my ($cdom,$cnum,$chome,$cid) = @_;
14717: my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired');
14718: my ($reqdmajor,$reqdminor) = split(/\./,$curr_reqd_hash{'internal.releaserequired'});
14719: my %checkresponsetypes;
14720: foreach my $key (keys(%Apache::lonnet::needsrelease)) {
14721: my ($item,$name,$value) = split(/:/,$key);
14722: if ($item eq 'resourcetag') {
14723: if ($name eq 'responsetype') {
14724: $checkresponsetypes{$value} = $Apache::lonnet::needsrelease{$key}
14725: }
14726: }
14727: }
14728: my $navmap = Apache::lonnavmaps::navmap->new();
14729: if (defined($navmap)) {
14730: my %allresponses;
14731: foreach my $res ($navmap->retrieveResources(undef,sub { $_[0]->is_problem() },1,0)) {
14732: my %responses = $res->responseTypes();
14733: foreach my $key (keys(%responses)) {
14734: next unless(exists($checkresponsetypes{$key}));
14735: $allresponses{$key} += $responses{$key};
14736: }
14737: }
14738: foreach my $key (keys(%allresponses)) {
14739: my ($major,$minor) = split(/\./,$checkresponsetypes{$key});
14740: if (($major > $reqdmajor) || ($major == $reqdmajor && $minor > $reqdminor)) {
14741: ($reqdmajor,$reqdminor) = ($major,$minor);
14742: }
14743: }
14744: undef($navmap);
14745: }
14746: unless (($reqdmajor eq '') && ($reqdminor eq '')) {
14747: &Apache::lonnet::update_released_required($reqdmajor.'.'.$reqdminor,$cdom,$cnum,$chome,$cid);
14748: }
14749: return;
14750: }
14751:
1.1110 raeburn 14752: sub allmaps_incourse {
14753: my ($cdom,$cnum,$chome,$cid) = @_;
14754: if ($cdom eq '' || $cnum eq '' || $chome eq '' || $cid eq '') {
14755: $cid = $env{'request.course.id'};
14756: $cdom = $env{'course.'.$cid.'.domain'};
14757: $cnum = $env{'course.'.$cid.'.num'};
14758: $chome = $env{'course.'.$cid.'.home'};
14759: }
14760: my %allmaps = ();
14761: my $lastchange =
14762: &Apache::lonnet::get_coursechange($cdom,$cnum);
14763: if ($lastchange > $env{'request.course.tied'}) {
14764: my ($furl,$ferr) = &Apache::lonuserstate::readmap("$cdom/$cnum");
14765: unless ($ferr) {
14766: &update_content_constraints($cdom,$cnum,$chome,$cid);
14767: }
14768: }
14769: my $navmap = Apache::lonnavmaps::navmap->new();
14770: if (defined($navmap)) {
14771: foreach my $res ($navmap->retrieveResources(undef,sub { $_[0]->is_map() },1,0,1)) {
14772: $allmaps{$res->src()} = 1;
14773: }
14774: }
14775: return \%allmaps;
14776: }
14777:
1.1083 raeburn 14778: sub parse_supplemental_title {
14779: my ($title) = @_;
14780:
14781: my ($foldertitle,$renametitle);
14782: if ($title =~ /&&&/) {
14783: $title = &HTML::Entites::decode($title);
14784: }
14785: if ($title =~ m/^(\d+)___&&&___($match_username)___&&&___($match_domain)___&&&___(.*)$/) {
14786: $renametitle=$4;
14787: my ($time,$uname,$udom) = ($1,$2,$3);
14788: $foldertitle=&Apache::lontexconvert::msgtexconverted($4);
14789: my $name = &plainname($uname,$udom);
14790: $name = &HTML::Entities::encode($name,'"<>&\'');
14791: $renametitle = &HTML::Entities::encode($renametitle,'"<>&\'');
14792: $title='<i>'.&Apache::lonlocal::locallocaltime($time).'</i> '.
14793: $name.': <br />'.$foldertitle;
14794: }
14795: if (wantarray) {
14796: return ($title,$foldertitle,$renametitle);
14797: }
14798: return $title;
14799: }
14800:
1.1143 raeburn 14801: sub recurse_supplemental {
14802: my ($cnum,$cdom,$suppmap,$numfiles,$errors) = @_;
14803: if ($suppmap) {
14804: my ($errtext,$fatal) = &LONCAPA::map::mapread('/uploaded/'.$cdom.'/'.$cnum.'/'.$suppmap);
14805: if ($fatal) {
14806: $errors ++;
14807: } else {
14808: if ($#LONCAPA::map::resources > 0) {
14809: foreach my $res (@LONCAPA::map::resources) {
14810: my ($title,$src,$ext,$type,$status)=split(/\:/,$res);
14811: if (($src ne '') && ($status eq 'res')) {
1.1146 raeburn 14812: if ($src =~ m{^\Q/uploaded/$cdom/$cnum/\E(supplemental_\d+\.sequence)$}) {
14813: ($numfiles,$errors) = &recurse_supplemental($cnum,$cdom,$1,$numfiles,$errors);
1.1143 raeburn 14814: } else {
14815: $numfiles ++;
14816: }
14817: }
14818: }
14819: }
14820: }
14821: }
14822: return ($numfiles,$errors);
14823: }
14824:
1.1101 raeburn 14825: sub symb_to_docspath {
14826: my ($symb) = @_;
14827: return unless ($symb);
14828: my ($mapurl,$id,$resurl) = &Apache::lonnet::decode_symb($symb);
14829: if ($resurl=~/\.(sequence|page)$/) {
14830: $mapurl=$resurl;
14831: } elsif ($resurl eq 'adm/navmaps') {
14832: $mapurl=$env{'course.'.$env{'request.course.id'}.'.url'};
14833: }
14834: my $mapresobj;
14835: my $navmap = Apache::lonnavmaps::navmap->new();
14836: if (ref($navmap)) {
14837: $mapresobj = $navmap->getResourceByUrl($mapurl);
14838: }
14839: $mapurl=~s{^.*/([^/]+)\.(\w+)$}{$1};
14840: my $type=$2;
14841: my $path;
14842: if (ref($mapresobj)) {
14843: my $pcslist = $mapresobj->map_hierarchy();
14844: if ($pcslist ne '') {
14845: foreach my $pc (split(/,/,$pcslist)) {
14846: next if ($pc <= 1);
14847: my $res = $navmap->getByMapPc($pc);
14848: if (ref($res)) {
14849: my $thisurl = $res->src();
14850: $thisurl=~s{^.*/([^/]+)\.\w+$}{$1};
14851: my $thistitle = $res->title();
14852: $path .= '&'.
14853: &Apache::lonhtmlcommon::entity_encode($thisurl).'&'.
1.1146 raeburn 14854: &escape($thistitle).
1.1101 raeburn 14855: ':'.$res->randompick().
14856: ':'.$res->randomout().
14857: ':'.$res->encrypted().
14858: ':'.$res->randomorder().
14859: ':'.$res->is_page();
14860: }
14861: }
14862: }
14863: $path =~ s/^\&//;
14864: my $maptitle = $mapresobj->title();
14865: if ($mapurl eq 'default') {
1.1129 raeburn 14866: $maptitle = 'Main Content';
1.1101 raeburn 14867: }
14868: $path .= (($path ne '')? '&' : '').
14869: &Apache::lonhtmlcommon::entity_encode($mapurl).'&'.
1.1146 raeburn 14870: &escape($maptitle).
1.1101 raeburn 14871: ':'.$mapresobj->randompick().
14872: ':'.$mapresobj->randomout().
14873: ':'.$mapresobj->encrypted().
14874: ':'.$mapresobj->randomorder().
14875: ':'.$mapresobj->is_page();
14876: } else {
14877: my $maptitle = &Apache::lonnet::gettitle($mapurl);
14878: my $ispage = (($type eq 'page')? 1 : '');
14879: if ($mapurl eq 'default') {
1.1129 raeburn 14880: $maptitle = 'Main Content';
1.1101 raeburn 14881: }
14882: $path = &Apache::lonhtmlcommon::entity_encode($mapurl).'&'.
1.1146 raeburn 14883: &escape($maptitle).':::::'.$ispage;
1.1101 raeburn 14884: }
14885: unless ($mapurl eq 'default') {
14886: $path = 'default&'.
1.1146 raeburn 14887: &escape('Main Content').
1.1101 raeburn 14888: ':::::&'.$path;
14889: }
14890: return $path;
14891: }
14892:
1.1094 raeburn 14893: sub captcha_display {
14894: my ($context,$lonhost) = @_;
14895: my ($output,$error);
14896: my ($captcha,$pubkey,$privkey) = &get_captcha_config($context,$lonhost);
1.1095 raeburn 14897: if ($captcha eq 'original') {
1.1094 raeburn 14898: $output = &create_captcha();
14899: unless ($output) {
1.1172 raeburn 14900: $error = 'captcha';
1.1094 raeburn 14901: }
14902: } elsif ($captcha eq 'recaptcha') {
14903: $output = &create_recaptcha($pubkey);
14904: unless ($output) {
1.1172 raeburn 14905: $error = 'recaptcha';
1.1094 raeburn 14906: }
14907: }
1.1176 raeburn 14908: return ($output,$error,$captcha);
1.1094 raeburn 14909: }
14910:
14911: sub captcha_response {
14912: my ($context,$lonhost) = @_;
14913: my ($captcha_chk,$captcha_error);
14914: my ($captcha,$pubkey,$privkey) = &get_captcha_config($context,$lonhost);
1.1095 raeburn 14915: if ($captcha eq 'original') {
1.1094 raeburn 14916: ($captcha_chk,$captcha_error) = &check_captcha();
14917: } elsif ($captcha eq 'recaptcha') {
14918: $captcha_chk = &check_recaptcha($privkey);
14919: } else {
14920: $captcha_chk = 1;
14921: }
14922: return ($captcha_chk,$captcha_error);
14923: }
14924:
14925: sub get_captcha_config {
14926: my ($context,$lonhost) = @_;
1.1095 raeburn 14927: my ($captcha,$pubkey,$privkey,$hashtocheck);
1.1094 raeburn 14928: my $hostname = &Apache::lonnet::hostname($lonhost);
14929: my $serverhomeID = &Apache::lonnet::get_server_homeID($hostname);
14930: my $serverhomedom = &Apache::lonnet::host_domain($serverhomeID);
1.1095 raeburn 14931: if ($context eq 'usercreation') {
14932: my %domconfig = &Apache::lonnet::get_dom('configuration',[$context],$serverhomedom);
14933: if (ref($domconfig{$context}) eq 'HASH') {
14934: $hashtocheck = $domconfig{$context}{'cancreate'};
14935: if (ref($hashtocheck) eq 'HASH') {
14936: if ($hashtocheck->{'captcha'} eq 'recaptcha') {
14937: if (ref($hashtocheck->{'recaptchakeys'}) eq 'HASH') {
14938: $pubkey = $hashtocheck->{'recaptchakeys'}{'public'};
14939: $privkey = $hashtocheck->{'recaptchakeys'}{'private'};
14940: }
14941: if ($privkey && $pubkey) {
14942: $captcha = 'recaptcha';
14943: } else {
14944: $captcha = 'original';
14945: }
14946: } elsif ($hashtocheck->{'captcha'} ne 'notused') {
14947: $captcha = 'original';
14948: }
1.1094 raeburn 14949: }
1.1095 raeburn 14950: } else {
14951: $captcha = 'captcha';
14952: }
14953: } elsif ($context eq 'login') {
14954: my %domconfhash = &Apache::loncommon::get_domainconf($serverhomedom);
14955: if ($domconfhash{$serverhomedom.'.login.captcha'} eq 'recaptcha') {
14956: $pubkey = $domconfhash{$serverhomedom.'.login.recaptchakeys_public'};
14957: $privkey = $domconfhash{$serverhomedom.'.login.recaptchakeys_private'};
1.1094 raeburn 14958: if ($privkey && $pubkey) {
14959: $captcha = 'recaptcha';
1.1095 raeburn 14960: } else {
14961: $captcha = 'original';
1.1094 raeburn 14962: }
1.1095 raeburn 14963: } elsif ($domconfhash{$serverhomedom.'.login.captcha'} eq 'original') {
14964: $captcha = 'original';
1.1094 raeburn 14965: }
14966: }
14967: return ($captcha,$pubkey,$privkey);
14968: }
14969:
14970: sub create_captcha {
14971: my %captcha_params = &captcha_settings();
14972: my ($output,$maxtries,$tries) = ('',10,0);
14973: while ($tries < $maxtries) {
14974: $tries ++;
14975: my $captcha = Authen::Captcha->new (
14976: output_folder => $captcha_params{'output_dir'},
14977: data_folder => $captcha_params{'db_dir'},
14978: );
14979: my $md5sum = $captcha->generate_code($captcha_params{'numchars'});
14980:
14981: if (-e $Apache::lonnet::perlvar{'lonCaptchaDir'}.'/'.$md5sum.'.png') {
14982: $output = '<input type="hidden" name="crypt" value="'.$md5sum.'" />'."\n".
14983: &mt('Type in the letters/numbers shown below').' '.
1.1176 raeburn 14984: '<input type="text" size="5" name="code" value="" autocomplete="off" />'.
14985: '<br />'.
14986: '<img src="'.$captcha_params{'www_output_dir'}.'/'.$md5sum.'.png" alt="captcha" />';
1.1094 raeburn 14987: last;
14988: }
14989: }
14990: return $output;
14991: }
14992:
14993: sub captcha_settings {
14994: my %captcha_params = (
14995: output_dir => $Apache::lonnet::perlvar{'lonCaptchaDir'},
14996: www_output_dir => "/captchaspool",
14997: db_dir => $Apache::lonnet::perlvar{'lonCaptchaDb'},
14998: numchars => '5',
14999: );
15000: return %captcha_params;
15001: }
15002:
15003: sub check_captcha {
15004: my ($captcha_chk,$captcha_error);
15005: my $code = $env{'form.code'};
15006: my $md5sum = $env{'form.crypt'};
15007: my %captcha_params = &captcha_settings();
15008: my $captcha = Authen::Captcha->new(
15009: output_folder => $captcha_params{'output_dir'},
15010: data_folder => $captcha_params{'db_dir'},
15011: );
1.1109 raeburn 15012: $captcha_chk = $captcha->check_code($code,$md5sum);
1.1094 raeburn 15013: my %captcha_hash = (
15014: 0 => 'Code not checked (file error)',
15015: -1 => 'Failed: code expired',
15016: -2 => 'Failed: invalid code (not in database)',
15017: -3 => 'Failed: invalid code (code does not match crypt)',
15018: );
15019: if ($captcha_chk != 1) {
15020: $captcha_error = $captcha_hash{$captcha_chk}
15021: }
15022: return ($captcha_chk,$captcha_error);
15023: }
15024:
15025: sub create_recaptcha {
15026: my ($pubkey) = @_;
1.1153 raeburn 15027: my $use_ssl;
15028: if ($ENV{'SERVER_PORT'} == 443) {
15029: $use_ssl = 1;
15030: }
1.1094 raeburn 15031: my $captcha = Captcha::reCAPTCHA->new;
15032: return $captcha->get_options_setter({theme => 'white'})."\n".
1.1153 raeburn 15033: $captcha->get_html($pubkey,undef,$use_ssl).
1.1094 raeburn 15034: &mt('If either word is hard to read, [_1] will replace them.',
1.1133 raeburn 15035: '<img src="/res/adm/pages/refresh.gif" alt="reCAPTCHA refresh" />').
1.1094 raeburn 15036: '<br /><br />';
15037: }
15038:
15039: sub check_recaptcha {
15040: my ($privkey) = @_;
15041: my $captcha_chk;
15042: my $captcha = Captcha::reCAPTCHA->new;
15043: my $captcha_result =
15044: $captcha->check_answer(
15045: $privkey,
15046: $ENV{'REMOTE_ADDR'},
15047: $env{'form.recaptcha_challenge_field'},
15048: $env{'form.recaptcha_response_field'},
15049: );
15050: if ($captcha_result->{is_valid}) {
15051: $captcha_chk = 1;
15052: }
15053: return $captcha_chk;
15054: }
15055:
1.1174 raeburn 15056: sub emailusername_info {
1.1177 raeburn 15057: my @fields = ('firstname','lastname','institution','web','location','officialemail');
1.1174 raeburn 15058: my %titles = &Apache::lonlocal::texthash (
15059: lastname => 'Last Name',
15060: firstname => 'First Name',
15061: institution => 'School/college/university',
15062: location => "School's city, state/province, country",
15063: web => "School's web address",
15064: officialemail => 'E-mail address at institution (if different)',
15065: );
15066: return (\@fields,\%titles);
15067: }
15068:
1.1161 raeburn 15069: sub cleanup_html {
15070: my ($incoming) = @_;
15071: my $outgoing;
15072: if ($incoming ne '') {
15073: $outgoing = $incoming;
15074: $outgoing =~ s/;/;/g;
15075: $outgoing =~ s/\#/#/g;
15076: $outgoing =~ s/\&/&/g;
15077: $outgoing =~ s/</</g;
15078: $outgoing =~ s/>/>/g;
15079: $outgoing =~ s/\(/(/g;
15080: $outgoing =~ s/\)/)/g;
15081: $outgoing =~ s/"/"/g;
15082: $outgoing =~ s/'/'/g;
15083: $outgoing =~ s/\$/$/g;
15084: $outgoing =~ s{/}{/}g;
15085: $outgoing =~ s/=/=/g;
15086: $outgoing =~ s/\\/\/g
15087: }
15088: return $outgoing;
15089: }
15090:
1.1174 raeburn 15091: # Use:
15092: # my $answer=reply("encrypt:passwd:$udom:$uname:$upass",$tryserver);
15093: #
15094: ##################################################
15095: # password associated functions #
15096: ##################################################
15097: sub des_keys {
15098: # Make a new key for DES encryption.
15099: # Each key has two parts which are returned separately.
15100: # Please note: Each key must be passed through the &hex function
15101: # before it is output to the web browser. The hex versions cannot
15102: # be used to decrypt.
15103: my @hexstr=('0','1','2','3','4','5','6','7',
15104: '8','9','a','b','c','d','e','f');
15105: my $lkey='';
15106: for (0..7) {
15107: $lkey.=$hexstr[rand(15)];
15108: }
15109: my $ukey='';
15110: for (0..7) {
15111: $ukey.=$hexstr[rand(15)];
15112: }
15113: return ($lkey,$ukey);
15114: }
15115:
15116: sub des_decrypt {
15117: my ($key,$cyphertext) = @_;
15118: my $keybin=pack("H16",$key);
15119: my $cypher;
15120: if ($Crypt::DES::VERSION>=2.03) {
15121: $cypher=new Crypt::DES $keybin;
15122: } else {
15123: $cypher=new DES $keybin;
15124: }
15125: my $plaintext=
15126: $cypher->decrypt(unpack("a8",pack("H16",substr($cyphertext,0,16))));
15127: $plaintext.=
15128: $cypher->decrypt(unpack("a8",pack("H16",substr($cyphertext,16,16))));
15129: $plaintext=substr($plaintext,1,ord(substr($plaintext,0,1)) );
15130: return $plaintext;
15131: }
15132:
1.41 ng 15133: =pod
15134:
15135: =back
15136:
1.112 bowersj2 15137: =cut
1.41 ng 15138:
1.112 bowersj2 15139: 1;
15140: __END__;
1.41 ng 15141:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>