Annotation of loncom/interface/loncommon.pm, revision 1.1175
1.10 albertel 1: # The LearningOnline Network with CAPA
1.1 albertel 2: # a pile of common routines
1.10 albertel 3: #
1.1175 ! raeburn 4: # $Id: loncommon.pm,v 1.1174 2014/02/12 20:29:35 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.378 raeburn 5154: my ($role,$realm) = split(/\./,$env{'request.role'},2);
5155: if ($role eq 'ca') {
1.479 albertel 5156: my ($rdom,$rname) = ($realm =~ m{^/($match_domain)/($match_username)$});
1.500 albertel 5157: $realm = &plainname($rname,$rdom);
1.378 raeburn 5158: }
1.55 www 5159: # realm
1.258 albertel 5160: if ($env{'request.course.id'}) {
1.378 raeburn 5161: if ($env{'request.role'} !~ /^cr/) {
5162: $role = &Apache::lonnet::plaintext($role,&course_type());
5163: }
1.898 raeburn 5164: if ($env{'request.course.sec'}) {
5165: $role .= (' 'x2).'- '.&mt('section:').' '.$env{'request.course.sec'};
5166: }
1.359 albertel 5167: $realm = $env{'course.'.$env{'request.course.id'}.'.description'};
1.378 raeburn 5168: } else {
5169: $role = &Apache::lonnet::plaintext($role);
1.54 www 5170: }
1.433 albertel 5171:
1.359 albertel 5172: if (!$realm) { $realm=' '; }
1.330 albertel 5173:
1.438 albertel 5174: my $extra_body_attr = &make_attr_string($forcereg,\%design);
1.329 albertel 5175:
1.101 www 5176: # construct main body tag
1.359 albertel 5177: my $bodytag = "<body $extra_body_attr>".
1.562 albertel 5178: &Apache::lontexconvert::init_math_support($args->{'inherit_jsmath'});
1.252 albertel 5179:
1.1131 raeburn 5180: &get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['inhibitmenu']);
5181:
1.1130 raeburn 5182: if (($bodyonly) || ($no_nav_bar) || ($env{'form.inhibitmenu'} eq 'yes')) {
1.60 matthew 5183: return $bodytag;
1.1130 raeburn 5184: }
1.359 albertel 5185:
1.954 raeburn 5186: if ($public) {
1.433 albertel 5187: undef($role);
5188: }
1.359 albertel 5189:
1.762 bisitz 5190: my $titleinfo = '<h1>'.$title.'</h1>';
1.359 albertel 5191: #
5192: # Extra info if you are the DC
5193: my $dc_info = '';
5194: if ($env{'user.adv'} && exists($env{'user.role.dc./'.
5195: $env{'course.'.$env{'request.course.id'}.
5196: '.domain'}.'/'})) {
5197: my $cid = $env{'request.course.id'};
1.917 raeburn 5198: $dc_info = $cid.' '.$env{'course.'.$cid.'.internal.coursecode'};
1.380 www 5199: $dc_info =~ s/\s+$//;
1.359 albertel 5200: }
5201:
1.898 raeburn 5202: $role = '<span class="LC_nobreak">('.$role.')</span>' if $role;
1.853 droeschl 5203:
1.903 droeschl 5204: if ($env{'request.state'} eq 'construct') { $forcereg=1; }
5205:
5206: # if ($env{'request.state'} eq 'construct') {
5207: # $titleinfo = &CSTR_pageheader(); #FIXME: Will be removed once all scripts have their own calls
5208: # }
5209:
1.1130 raeburn 5210: $bodytag .= Apache::lonhtmlcommon::scripttag(
1.1154 raeburn 5211: Apache::lonmenu::utilityfunctions($httphost), 'start');
1.359 albertel 5212:
1.1130 raeburn 5213: my ($left,$right) = Apache::lonmenu::primary_menu();
1.359 albertel 5214:
1.916 droeschl 5215: if ($env{'request.noversionuri'} =~ m{^/res/adm/pages/}) {
1.917 raeburn 5216: if ($dc_info) {
5217: $dc_info = qq|<span class="LC_cusr_subheading">$dc_info</span>|;
5218: }
1.1130 raeburn 5219: $bodytag .= qq|<div id="LC_nav_bar">$left $role<br />
1.916 droeschl 5220: <em>$realm</em> $dc_info</div>|;
1.903 droeschl 5221: return $bodytag;
5222: }
1.894 droeschl 5223:
1.927 raeburn 5224: unless ($env{'request.symb'} =~ m/\.page___\d+___/) {
1.1130 raeburn 5225: $bodytag .= qq|<div id="LC_nav_bar">$left $role</div>|;
1.927 raeburn 5226: }
1.916 droeschl 5227:
1.1130 raeburn 5228: $bodytag .= $right;
1.852 droeschl 5229:
1.917 raeburn 5230: if ($dc_info) {
5231: $dc_info = &dc_courseid_toggle($dc_info);
5232: }
5233: $bodytag .= qq|<div id="LC_realm">$realm $dc_info</div>|;
1.916 droeschl 5234:
1.1169 raeburn 5235: #if directed to not display the secondary menu, don't.
1.1168 raeburn 5236: if ($args->{'no_secondary_menu'}) {
5237: return $bodytag;
5238: }
1.1169 raeburn 5239: #don't show menus for public users
1.954 raeburn 5240: if (!$public){
1.1154 raeburn 5241: $bodytag .= Apache::lonmenu::secondary_menu($httphost);
1.903 droeschl 5242: $bodytag .= Apache::lonmenu::serverform();
1.920 raeburn 5243: $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');
5244: if ($env{'request.state'} eq 'construct') {
1.962 droeschl 5245: $bodytag .= &Apache::lonmenu::innerregister($forcereg,
1.920 raeburn 5246: $args->{'bread_crumbs'});
1.1096 raeburn 5247: } elsif ($forcereg) {
5248: $bodytag .= &Apache::lonmenu::innerregister($forcereg,undef,
5249: $args->{'group'});
5250: } else {
5251: $bodytag .=
5252: &Apache::lonmenu::prepare_functions($env{'request.noversionuri'},
5253: $forcereg,$args->{'group'},
5254: $args->{'bread_crumbs'},
5255: $advtoolsref);
1.920 raeburn 5256: }
1.903 droeschl 5257: }else{
5258: # this is to seperate menu from content when there's no secondary
5259: # menu. Especially needed for public accessible ressources.
5260: $bodytag .= '<hr style="clear:both" />';
5261: $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end');
1.235 raeburn 5262: }
1.903 droeschl 5263:
1.235 raeburn 5264: return $bodytag;
1.182 matthew 5265: }
5266:
1.917 raeburn 5267: sub dc_courseid_toggle {
5268: my ($dc_info) = @_;
1.980 raeburn 5269: return ' <span id="dccidtext" class="LC_cusr_subheading LC_nobreak">'.
1.1069 raeburn 5270: '<a href="javascript:showCourseID();" class="LC_menubuttons_link">'.
1.917 raeburn 5271: &mt('(More ...)').'</a></span>'.
5272: '<div id="dccid" class="LC_dccid">'.$dc_info.'</div>';
5273: }
5274:
1.330 albertel 5275: sub make_attr_string {
5276: my ($register,$attr_ref) = @_;
5277:
5278: if ($attr_ref && !ref($attr_ref)) {
5279: die("addentries Must be a hash ref ".
5280: join(':',caller(1))." ".
5281: join(':',caller(0))." ");
5282: }
5283:
5284: if ($register) {
1.339 albertel 5285: my ($on_load,$on_unload);
5286: foreach my $key (keys(%{$attr_ref})) {
5287: if (lc($key) eq 'onload') {
5288: $on_load.=$attr_ref->{$key}.';';
5289: delete($attr_ref->{$key});
5290:
5291: } elsif (lc($key) eq 'onunload') {
5292: $on_unload.=$attr_ref->{$key}.';';
5293: delete($attr_ref->{$key});
5294: }
5295: }
1.953 droeschl 5296: $attr_ref->{'onload'} = $on_load;
5297: $attr_ref->{'onunload'}= $on_unload;
1.330 albertel 5298: }
1.339 albertel 5299:
1.330 albertel 5300: my $attr_string;
1.1159 raeburn 5301: foreach my $attr (sort(keys(%$attr_ref))) {
1.330 albertel 5302: $attr_string .= " $attr=\"".$attr_ref->{$attr}.'" ';
5303: }
5304: return $attr_string;
5305: }
5306:
5307:
1.182 matthew 5308: ###############################################
1.251 albertel 5309: ###############################################
5310:
5311: =pod
5312:
5313: =item * &endbodytag()
5314:
5315: Returns a uniform footer for LON-CAPA web pages.
5316:
1.635 raeburn 5317: Inputs: 1 - optional reference to an args hash
5318: If in the hash, key for noredirectlink has a value which evaluates to true,
5319: a 'Continue' link is not displayed if the page contains an
5320: internal redirect in the <head></head> section,
5321: i.e., $env{'internal.head.redirect'} exists
1.251 albertel 5322:
5323: =cut
5324:
5325: sub endbodytag {
1.635 raeburn 5326: my ($args) = @_;
1.1080 raeburn 5327: my $endbodytag;
5328: unless ((ref($args) eq 'HASH') && ($args->{'notbody'})) {
5329: $endbodytag='</body>';
5330: }
1.269 albertel 5331: $endbodytag=&Apache::lontexconvert::jsMath_process()."\n".$endbodytag;
1.315 albertel 5332: if ( exists( $env{'internal.head.redirect'} ) ) {
1.635 raeburn 5333: if (!(ref($args) eq 'HASH' && $args->{'noredirectlink'})) {
5334: $endbodytag=
5335: "<br /><a href=\"$env{'internal.head.redirect'}\">".
5336: &mt('Continue').'</a>'.
5337: $endbodytag;
5338: }
1.315 albertel 5339: }
1.251 albertel 5340: return $endbodytag;
5341: }
5342:
1.352 albertel 5343: =pod
5344:
5345: =item * &standard_css()
5346:
5347: Returns a style sheet
5348:
5349: Inputs: (all optional)
5350: domain -> force to color decorate a page for a specific
5351: domain
5352: function -> force usage of a specific rolish color scheme
5353: bgcolor -> override the default page bgcolor
5354:
5355: =cut
5356:
1.343 albertel 5357: sub standard_css {
1.345 albertel 5358: my ($function,$domain,$bgcolor) = @_;
1.352 albertel 5359: $function = &get_users_function() if (!$function);
5360: my $img = &designparm($function.'.img', $domain);
5361: my $tabbg = &designparm($function.'.tabbg', $domain);
5362: my $font = &designparm($function.'.font', $domain);
1.801 tempelho 5363: my $fontmenu = &designparm($function.'.fontmenu', $domain);
1.791 tempelho 5364: #second colour for later usage
1.345 albertel 5365: my $sidebg = &designparm($function.'.sidebg',$domain);
1.382 albertel 5366: my $pgbg_or_bgcolor =
5367: $bgcolor ||
1.352 albertel 5368: &designparm($function.'.pgbg', $domain);
1.382 albertel 5369: my $pgbg = &designparm($function.'.pgbg', $domain);
1.352 albertel 5370: my $alink = &designparm($function.'.alink', $domain);
5371: my $vlink = &designparm($function.'.vlink', $domain);
5372: my $link = &designparm($function.'.link', $domain);
5373:
1.602 albertel 5374: my $sans = 'Verdana,Arial,Helvetica,sans-serif';
1.395 albertel 5375: my $mono = 'monospace';
1.850 bisitz 5376: my $data_table_head = $sidebg;
5377: my $data_table_light = '#FAFAFA';
1.1060 bisitz 5378: my $data_table_dark = '#E0E0E0';
1.470 banghart 5379: my $data_table_darker = '#CCCCCC';
1.349 albertel 5380: my $data_table_highlight = '#FFFF00';
1.352 albertel 5381: my $mail_new = '#FFBB77';
5382: my $mail_new_hover = '#DD9955';
5383: my $mail_read = '#BBBB77';
5384: my $mail_read_hover = '#999944';
5385: my $mail_replied = '#AAAA88';
5386: my $mail_replied_hover = '#888855';
5387: my $mail_other = '#99BBBB';
5388: my $mail_other_hover = '#669999';
1.391 albertel 5389: my $table_header = '#DDDDDD';
1.489 raeburn 5390: my $feedback_link_bg = '#BBBBBB';
1.911 bisitz 5391: my $lg_border_color = '#C8C8C8';
1.952 onken 5392: my $button_hover = '#BF2317';
1.392 albertel 5393:
1.608 albertel 5394: my $border = ($env{'browser.type'} eq 'explorer' ||
1.911 bisitz 5395: $env{'browser.type'} eq 'safari' ) ? '0 2px 0 2px'
5396: : '0 3px 0 4px';
1.448 albertel 5397:
1.523 albertel 5398:
1.343 albertel 5399: return <<END;
1.947 droeschl 5400:
5401: /* needed for iframe to allow 100% height in FF */
5402: body, html {
5403: margin: 0;
5404: padding: 0 0.5%;
5405: height: 99%; /* to avoid scrollbars */
5406: }
5407:
1.795 www 5408: body {
1.911 bisitz 5409: font-family: $sans;
5410: line-height:130%;
5411: font-size:0.83em;
5412: color:$font;
1.795 www 5413: }
5414:
1.959 onken 5415: a:focus,
5416: a:focus img {
1.795 www 5417: color: red;
5418: }
1.698 harmsja 5419:
1.911 bisitz 5420: form, .inline {
5421: display: inline;
1.795 www 5422: }
1.721 harmsja 5423:
1.795 www 5424: .LC_right {
1.911 bisitz 5425: text-align:right;
1.795 www 5426: }
5427:
5428: .LC_middle {
1.911 bisitz 5429: vertical-align:middle;
1.795 www 5430: }
1.721 harmsja 5431:
1.1130 raeburn 5432: .LC_floatleft {
5433: float: left;
5434: }
5435:
5436: .LC_floatright {
5437: float: right;
5438: }
5439:
1.911 bisitz 5440: .LC_400Box {
5441: width:400px;
5442: }
1.721 harmsja 5443:
1.947 droeschl 5444: .LC_iframecontainer {
5445: width: 98%;
5446: margin: 0;
5447: position: fixed;
5448: top: 8.5em;
5449: bottom: 0;
5450: }
5451:
5452: .LC_iframecontainer iframe{
5453: border: none;
5454: width: 100%;
5455: height: 100%;
5456: }
5457:
1.778 bisitz 5458: .LC_filename {
5459: font-family: $mono;
5460: white-space:pre;
1.921 bisitz 5461: font-size: 120%;
1.778 bisitz 5462: }
5463:
5464: .LC_fileicon {
5465: border: none;
5466: height: 1.3em;
5467: vertical-align: text-bottom;
5468: margin-right: 0.3em;
5469: text-decoration:none;
5470: }
5471:
1.1008 www 5472: .LC_setting {
5473: text-decoration:underline;
5474: }
5475:
1.350 albertel 5476: .LC_error {
5477: color: red;
5478: }
1.795 www 5479:
1.1097 bisitz 5480: .LC_warning {
5481: color: darkorange;
5482: }
5483:
1.457 albertel 5484: .LC_diff_removed {
1.733 bisitz 5485: color: red;
1.394 albertel 5486: }
1.532 albertel 5487:
5488: .LC_info,
1.457 albertel 5489: .LC_success,
5490: .LC_diff_added {
1.350 albertel 5491: color: green;
5492: }
1.795 www 5493:
1.802 bisitz 5494: div.LC_confirm_box {
5495: background-color: #FAFAFA;
5496: border: 1px solid $lg_border_color;
5497: margin-right: 0;
5498: padding: 5px;
5499: }
5500:
5501: div.LC_confirm_box .LC_error img,
5502: div.LC_confirm_box .LC_success img {
5503: vertical-align: middle;
5504: }
5505:
1.440 albertel 5506: .LC_icon {
1.771 droeschl 5507: border: none;
1.790 droeschl 5508: vertical-align: middle;
1.771 droeschl 5509: }
5510:
1.543 albertel 5511: .LC_docs_spacer {
5512: width: 25px;
5513: height: 1px;
1.771 droeschl 5514: border: none;
1.543 albertel 5515: }
1.346 albertel 5516:
1.532 albertel 5517: .LC_internal_info {
1.735 bisitz 5518: color: #999999;
1.532 albertel 5519: }
5520:
1.794 www 5521: .LC_discussion {
1.1050 www 5522: background: $data_table_dark;
1.911 bisitz 5523: border: 1px solid black;
5524: margin: 2px;
1.794 www 5525: }
5526:
5527: .LC_disc_action_left {
1.1050 www 5528: background: $sidebg;
1.911 bisitz 5529: text-align: left;
1.1050 www 5530: padding: 4px;
5531: margin: 2px;
1.794 www 5532: }
5533:
5534: .LC_disc_action_right {
1.1050 www 5535: background: $sidebg;
1.911 bisitz 5536: text-align: right;
1.1050 www 5537: padding: 4px;
5538: margin: 2px;
1.794 www 5539: }
5540:
5541: .LC_disc_new_item {
1.911 bisitz 5542: background: white;
5543: border: 2px solid red;
1.1050 www 5544: margin: 4px;
5545: padding: 4px;
1.794 www 5546: }
5547:
5548: .LC_disc_old_item {
1.911 bisitz 5549: background: white;
1.1050 www 5550: margin: 4px;
5551: padding: 4px;
1.794 www 5552: }
5553:
1.458 albertel 5554: table.LC_pastsubmission {
5555: border: 1px solid black;
5556: margin: 2px;
5557: }
5558:
1.924 bisitz 5559: table#LC_menubuttons {
1.345 albertel 5560: width: 100%;
5561: background: $pgbg;
1.392 albertel 5562: border: 2px;
1.402 albertel 5563: border-collapse: separate;
1.803 bisitz 5564: padding: 0;
1.345 albertel 5565: }
1.392 albertel 5566:
1.801 tempelho 5567: table#LC_title_bar a {
5568: color: $fontmenu;
5569: }
1.836 bisitz 5570:
1.807 droeschl 5571: table#LC_title_bar {
1.819 tempelho 5572: clear: both;
1.836 bisitz 5573: display: none;
1.807 droeschl 5574: }
5575:
1.795 www 5576: table#LC_title_bar,
1.933 droeschl 5577: table.LC_breadcrumbs, /* obsolete? */
1.393 albertel 5578: table#LC_title_bar.LC_with_remote {
1.359 albertel 5579: width: 100%;
1.392 albertel 5580: border-color: $pgbg;
5581: border-style: solid;
5582: border-width: $border;
1.379 albertel 5583: background: $pgbg;
1.801 tempelho 5584: color: $fontmenu;
1.392 albertel 5585: border-collapse: collapse;
1.803 bisitz 5586: padding: 0;
1.819 tempelho 5587: margin: 0;
1.359 albertel 5588: }
1.795 www 5589:
1.933 droeschl 5590: ul.LC_breadcrumb_tools_outerlist {
1.913 droeschl 5591: margin: 0;
5592: padding: 0;
1.933 droeschl 5593: position: relative;
5594: list-style: none;
1.913 droeschl 5595: }
1.933 droeschl 5596: ul.LC_breadcrumb_tools_outerlist li {
1.913 droeschl 5597: display: inline;
5598: }
1.933 droeschl 5599:
5600: .LC_breadcrumb_tools_navigation {
1.913 droeschl 5601: padding: 0;
1.933 droeschl 5602: margin: 0;
5603: float: left;
1.913 droeschl 5604: }
1.933 droeschl 5605: .LC_breadcrumb_tools_tools {
5606: padding: 0;
5607: margin: 0;
1.913 droeschl 5608: float: right;
5609: }
5610:
1.359 albertel 5611: table#LC_title_bar td {
5612: background: $tabbg;
5613: }
1.795 www 5614:
1.911 bisitz 5615: table#LC_menubuttons img {
1.803 bisitz 5616: border: none;
1.346 albertel 5617: }
1.795 www 5618:
1.842 droeschl 5619: .LC_breadcrumbs_component {
1.911 bisitz 5620: float: right;
5621: margin: 0 1em;
1.357 albertel 5622: }
1.842 droeschl 5623: .LC_breadcrumbs_component img {
1.911 bisitz 5624: vertical-align: middle;
1.777 tempelho 5625: }
1.795 www 5626:
1.383 albertel 5627: td.LC_table_cell_checkbox {
5628: text-align: center;
5629: }
1.795 www 5630:
5631: .LC_fontsize_small {
1.911 bisitz 5632: font-size: 70%;
1.705 tempelho 5633: }
5634:
1.844 bisitz 5635: #LC_breadcrumbs {
1.911 bisitz 5636: clear:both;
5637: background: $sidebg;
5638: border-bottom: 1px solid $lg_border_color;
5639: line-height: 2.5em;
1.933 droeschl 5640: overflow: hidden;
1.911 bisitz 5641: margin: 0;
5642: padding: 0;
1.995 raeburn 5643: text-align: left;
1.819 tempelho 5644: }
1.862 bisitz 5645:
1.1098 bisitz 5646: .LC_head_subbox, .LC_actionbox {
1.911 bisitz 5647: clear:both;
5648: background: #F8F8F8; /* $sidebg; */
1.915 droeschl 5649: border: 1px solid $sidebg;
1.1098 bisitz 5650: margin: 0 0 10px 0;
1.966 bisitz 5651: padding: 3px;
1.995 raeburn 5652: text-align: left;
1.822 bisitz 5653: }
5654:
1.795 www 5655: .LC_fontsize_medium {
1.911 bisitz 5656: font-size: 85%;
1.705 tempelho 5657: }
5658:
1.795 www 5659: .LC_fontsize_large {
1.911 bisitz 5660: font-size: 120%;
1.705 tempelho 5661: }
5662:
1.346 albertel 5663: .LC_menubuttons_inline_text {
5664: color: $font;
1.698 harmsja 5665: font-size: 90%;
1.701 harmsja 5666: padding-left:3px;
1.346 albertel 5667: }
5668:
1.934 droeschl 5669: .LC_menubuttons_inline_text img{
5670: vertical-align: middle;
5671: }
5672:
1.1051 www 5673: li.LC_menubuttons_inline_text img {
1.951 onken 5674: cursor:pointer;
1.1002 droeschl 5675: text-decoration: none;
1.951 onken 5676: }
5677:
1.526 www 5678: .LC_menubuttons_link {
5679: text-decoration: none;
5680: }
1.795 www 5681:
1.522 albertel 5682: .LC_menubuttons_category {
1.521 www 5683: color: $font;
1.526 www 5684: background: $pgbg;
1.521 www 5685: font-size: larger;
5686: font-weight: bold;
5687: }
5688:
1.346 albertel 5689: td.LC_menubuttons_text {
1.911 bisitz 5690: color: $font;
1.346 albertel 5691: }
1.706 harmsja 5692:
1.346 albertel 5693: .LC_current_location {
5694: background: $tabbg;
5695: }
1.795 www 5696:
1.938 bisitz 5697: table.LC_data_table {
1.347 albertel 5698: border: 1px solid #000000;
1.402 albertel 5699: border-collapse: separate;
1.426 albertel 5700: border-spacing: 1px;
1.610 albertel 5701: background: $pgbg;
1.347 albertel 5702: }
1.795 www 5703:
1.422 albertel 5704: .LC_data_table_dense {
5705: font-size: small;
5706: }
1.795 www 5707:
1.507 raeburn 5708: table.LC_nested_outer {
5709: border: 1px solid #000000;
1.589 raeburn 5710: border-collapse: collapse;
1.803 bisitz 5711: border-spacing: 0;
1.507 raeburn 5712: width: 100%;
5713: }
1.795 www 5714:
1.879 raeburn 5715: table.LC_innerpickbox,
1.507 raeburn 5716: table.LC_nested {
1.803 bisitz 5717: border: none;
1.589 raeburn 5718: border-collapse: collapse;
1.803 bisitz 5719: border-spacing: 0;
1.507 raeburn 5720: width: 100%;
5721: }
1.795 www 5722:
1.911 bisitz 5723: table.LC_data_table tr th,
5724: table.LC_calendar tr th,
1.879 raeburn 5725: table.LC_prior_tries tr th,
5726: table.LC_innerpickbox tr th {
1.349 albertel 5727: font-weight: bold;
5728: background-color: $data_table_head;
1.801 tempelho 5729: color:$fontmenu;
1.701 harmsja 5730: font-size:90%;
1.347 albertel 5731: }
1.795 www 5732:
1.879 raeburn 5733: table.LC_innerpickbox tr th,
5734: table.LC_innerpickbox tr td {
5735: vertical-align: top;
5736: }
5737:
1.711 raeburn 5738: table.LC_data_table tr.LC_info_row > td {
1.735 bisitz 5739: background-color: #CCCCCC;
1.711 raeburn 5740: font-weight: bold;
5741: text-align: left;
5742: }
1.795 www 5743:
1.912 bisitz 5744: table.LC_data_table tr.LC_odd_row > td {
5745: background-color: $data_table_light;
5746: padding: 2px;
5747: vertical-align: top;
5748: }
5749:
1.809 bisitz 5750: table.LC_pick_box tr > td.LC_odd_row {
1.349 albertel 5751: background-color: $data_table_light;
1.912 bisitz 5752: vertical-align: top;
5753: }
5754:
5755: table.LC_data_table tr.LC_even_row > td {
5756: background-color: $data_table_dark;
1.425 albertel 5757: padding: 2px;
1.900 bisitz 5758: vertical-align: top;
1.347 albertel 5759: }
1.795 www 5760:
1.809 bisitz 5761: table.LC_pick_box tr > td.LC_even_row {
1.349 albertel 5762: background-color: $data_table_dark;
1.900 bisitz 5763: vertical-align: top;
1.347 albertel 5764: }
1.795 www 5765:
1.425 albertel 5766: table.LC_data_table tr.LC_data_table_highlight td {
5767: background-color: $data_table_darker;
5768: }
1.795 www 5769:
1.639 raeburn 5770: table.LC_data_table tr td.LC_leftcol_header {
5771: background-color: $data_table_head;
5772: font-weight: bold;
5773: }
1.795 www 5774:
1.451 albertel 5775: table.LC_data_table tr.LC_empty_row td,
1.507 raeburn 5776: table.LC_nested tr.LC_empty_row td {
1.421 albertel 5777: font-weight: bold;
5778: font-style: italic;
5779: text-align: center;
5780: padding: 8px;
1.347 albertel 5781: }
1.795 www 5782:
1.1114 raeburn 5783: table.LC_data_table tr.LC_empty_row td,
5784: table.LC_data_table tr.LC_footer_row td {
1.940 bisitz 5785: background-color: $sidebg;
5786: }
5787:
5788: table.LC_nested tr.LC_empty_row td {
5789: background-color: #FFFFFF;
5790: }
5791:
1.890 droeschl 5792: table.LC_caption {
5793: }
5794:
1.507 raeburn 5795: table.LC_nested tr.LC_empty_row td {
1.465 albertel 5796: padding: 4ex
5797: }
1.795 www 5798:
1.507 raeburn 5799: table.LC_nested_outer tr th {
5800: font-weight: bold;
1.801 tempelho 5801: color:$fontmenu;
1.507 raeburn 5802: background-color: $data_table_head;
1.701 harmsja 5803: font-size: small;
1.507 raeburn 5804: border-bottom: 1px solid #000000;
5805: }
1.795 www 5806:
1.507 raeburn 5807: table.LC_nested_outer tr td.LC_subheader {
5808: background-color: $data_table_head;
5809: font-weight: bold;
5810: font-size: small;
5811: border-bottom: 1px solid #000000;
5812: text-align: right;
1.451 albertel 5813: }
1.795 www 5814:
1.507 raeburn 5815: table.LC_nested tr.LC_info_row td {
1.735 bisitz 5816: background-color: #CCCCCC;
1.451 albertel 5817: font-weight: bold;
5818: font-size: small;
1.507 raeburn 5819: text-align: center;
5820: }
1.795 www 5821:
1.589 raeburn 5822: table.LC_nested tr.LC_info_row td.LC_left_item,
5823: table.LC_nested_outer tr th.LC_left_item {
1.507 raeburn 5824: text-align: left;
1.451 albertel 5825: }
1.795 www 5826:
1.507 raeburn 5827: table.LC_nested td {
1.735 bisitz 5828: background-color: #FFFFFF;
1.451 albertel 5829: font-size: small;
1.507 raeburn 5830: }
1.795 www 5831:
1.507 raeburn 5832: table.LC_nested_outer tr th.LC_right_item,
5833: table.LC_nested tr.LC_info_row td.LC_right_item,
5834: table.LC_nested tr.LC_odd_row td.LC_right_item,
5835: table.LC_nested tr td.LC_right_item {
1.451 albertel 5836: text-align: right;
5837: }
5838:
1.507 raeburn 5839: table.LC_nested tr.LC_odd_row td {
1.735 bisitz 5840: background-color: #EEEEEE;
1.451 albertel 5841: }
5842:
1.473 raeburn 5843: table.LC_createuser {
5844: }
5845:
5846: table.LC_createuser tr.LC_section_row td {
1.701 harmsja 5847: font-size: small;
1.473 raeburn 5848: }
5849:
5850: table.LC_createuser tr.LC_info_row td {
1.735 bisitz 5851: background-color: #CCCCCC;
1.473 raeburn 5852: font-weight: bold;
5853: text-align: center;
5854: }
5855:
1.349 albertel 5856: table.LC_calendar {
5857: border: 1px solid #000000;
5858: border-collapse: collapse;
1.917 raeburn 5859: width: 98%;
1.349 albertel 5860: }
1.795 www 5861:
1.349 albertel 5862: table.LC_calendar_pickdate {
5863: font-size: xx-small;
5864: }
1.795 www 5865:
1.349 albertel 5866: table.LC_calendar tr td {
5867: border: 1px solid #000000;
5868: vertical-align: top;
1.917 raeburn 5869: width: 14%;
1.349 albertel 5870: }
1.795 www 5871:
1.349 albertel 5872: table.LC_calendar tr td.LC_calendar_day_empty {
5873: background-color: $data_table_dark;
5874: }
1.795 www 5875:
1.779 bisitz 5876: table.LC_calendar tr td.LC_calendar_day_current {
5877: background-color: $data_table_highlight;
1.777 tempelho 5878: }
1.795 www 5879:
1.938 bisitz 5880: table.LC_data_table tr td.LC_mail_new {
1.349 albertel 5881: background-color: $mail_new;
5882: }
1.795 www 5883:
1.938 bisitz 5884: table.LC_data_table tr.LC_mail_new:hover {
1.349 albertel 5885: background-color: $mail_new_hover;
5886: }
1.795 www 5887:
1.938 bisitz 5888: table.LC_data_table tr td.LC_mail_read {
1.349 albertel 5889: background-color: $mail_read;
5890: }
1.795 www 5891:
1.938 bisitz 5892: /*
5893: table.LC_data_table tr.LC_mail_read:hover {
1.349 albertel 5894: background-color: $mail_read_hover;
5895: }
1.938 bisitz 5896: */
1.795 www 5897:
1.938 bisitz 5898: table.LC_data_table tr td.LC_mail_replied {
1.349 albertel 5899: background-color: $mail_replied;
5900: }
1.795 www 5901:
1.938 bisitz 5902: /*
5903: table.LC_data_table tr.LC_mail_replied:hover {
1.349 albertel 5904: background-color: $mail_replied_hover;
5905: }
1.938 bisitz 5906: */
1.795 www 5907:
1.938 bisitz 5908: table.LC_data_table tr td.LC_mail_other {
1.349 albertel 5909: background-color: $mail_other;
5910: }
1.795 www 5911:
1.938 bisitz 5912: /*
5913: table.LC_data_table tr.LC_mail_other:hover {
1.349 albertel 5914: background-color: $mail_other_hover;
5915: }
1.938 bisitz 5916: */
1.494 raeburn 5917:
1.777 tempelho 5918: table.LC_data_table tr > td.LC_browser_file,
5919: table.LC_data_table tr > td.LC_browser_file_published {
1.899 bisitz 5920: background: #AAEE77;
1.389 albertel 5921: }
1.795 www 5922:
1.777 tempelho 5923: table.LC_data_table tr > td.LC_browser_file_locked,
5924: table.LC_data_table tr > td.LC_browser_file_unpublished {
1.389 albertel 5925: background: #FFAA99;
1.387 albertel 5926: }
1.795 www 5927:
1.777 tempelho 5928: table.LC_data_table tr > td.LC_browser_file_obsolete {
1.899 bisitz 5929: background: #888888;
1.779 bisitz 5930: }
1.795 www 5931:
1.777 tempelho 5932: table.LC_data_table tr > td.LC_browser_file_modified,
1.779 bisitz 5933: table.LC_data_table tr > td.LC_browser_file_metamodified {
1.899 bisitz 5934: background: #F8F866;
1.777 tempelho 5935: }
1.795 www 5936:
1.696 bisitz 5937: table.LC_data_table tr.LC_browser_folder > td {
1.899 bisitz 5938: background: #E0E8FF;
1.387 albertel 5939: }
1.696 bisitz 5940:
1.707 bisitz 5941: table.LC_data_table tr > td.LC_roles_is {
1.911 bisitz 5942: /* background: #77FF77; */
1.707 bisitz 5943: }
1.795 www 5944:
1.707 bisitz 5945: table.LC_data_table tr > td.LC_roles_future {
1.939 bisitz 5946: border-right: 8px solid #FFFF77;
1.707 bisitz 5947: }
1.795 www 5948:
1.707 bisitz 5949: table.LC_data_table tr > td.LC_roles_will {
1.939 bisitz 5950: border-right: 8px solid #FFAA77;
1.707 bisitz 5951: }
1.795 www 5952:
1.707 bisitz 5953: table.LC_data_table tr > td.LC_roles_expired {
1.939 bisitz 5954: border-right: 8px solid #FF7777;
1.707 bisitz 5955: }
1.795 www 5956:
1.707 bisitz 5957: table.LC_data_table tr > td.LC_roles_will_not {
1.939 bisitz 5958: border-right: 8px solid #AAFF77;
1.707 bisitz 5959: }
1.795 www 5960:
1.707 bisitz 5961: table.LC_data_table tr > td.LC_roles_selected {
1.939 bisitz 5962: border-right: 8px solid #11CC55;
1.707 bisitz 5963: }
5964:
1.388 albertel 5965: span.LC_current_location {
1.701 harmsja 5966: font-size:larger;
1.388 albertel 5967: background: $pgbg;
5968: }
1.387 albertel 5969:
1.1029 www 5970: span.LC_current_nav_location {
5971: font-weight:bold;
5972: background: $sidebg;
5973: }
5974:
1.395 albertel 5975: span.LC_parm_menu_item {
5976: font-size: larger;
5977: }
1.795 www 5978:
1.395 albertel 5979: span.LC_parm_scope_all {
5980: color: red;
5981: }
1.795 www 5982:
1.395 albertel 5983: span.LC_parm_scope_folder {
5984: color: green;
5985: }
1.795 www 5986:
1.395 albertel 5987: span.LC_parm_scope_resource {
5988: color: orange;
5989: }
1.795 www 5990:
1.395 albertel 5991: span.LC_parm_part {
5992: color: blue;
5993: }
1.795 www 5994:
1.911 bisitz 5995: span.LC_parm_folder,
5996: span.LC_parm_symb {
1.395 albertel 5997: font-size: x-small;
5998: font-family: $mono;
5999: color: #AAAAAA;
6000: }
6001:
1.977 bisitz 6002: ul.LC_parm_parmlist li {
6003: display: inline-block;
6004: padding: 0.3em 0.8em;
6005: vertical-align: top;
6006: width: 150px;
6007: border-top:1px solid $lg_border_color;
6008: }
6009:
1.795 www 6010: td.LC_parm_overview_level_menu,
6011: td.LC_parm_overview_map_menu,
6012: td.LC_parm_overview_parm_selectors,
6013: td.LC_parm_overview_restrictions {
1.396 albertel 6014: border: 1px solid black;
6015: border-collapse: collapse;
6016: }
1.795 www 6017:
1.396 albertel 6018: table.LC_parm_overview_restrictions td {
6019: border-width: 1px 4px 1px 4px;
6020: border-style: solid;
6021: border-color: $pgbg;
6022: text-align: center;
6023: }
1.795 www 6024:
1.396 albertel 6025: table.LC_parm_overview_restrictions th {
6026: background: $tabbg;
6027: border-width: 1px 4px 1px 4px;
6028: border-style: solid;
6029: border-color: $pgbg;
6030: }
1.795 www 6031:
1.398 albertel 6032: table#LC_helpmenu {
1.803 bisitz 6033: border: none;
1.398 albertel 6034: height: 55px;
1.803 bisitz 6035: border-spacing: 0;
1.398 albertel 6036: }
6037:
6038: table#LC_helpmenu fieldset legend {
6039: font-size: larger;
6040: }
1.795 www 6041:
1.397 albertel 6042: table#LC_helpmenu_links {
6043: width: 100%;
6044: border: 1px solid black;
6045: background: $pgbg;
1.803 bisitz 6046: padding: 0;
1.397 albertel 6047: border-spacing: 1px;
6048: }
1.795 www 6049:
1.397 albertel 6050: table#LC_helpmenu_links tr td {
6051: padding: 1px;
6052: background: $tabbg;
1.399 albertel 6053: text-align: center;
6054: font-weight: bold;
1.397 albertel 6055: }
1.396 albertel 6056:
1.795 www 6057: table#LC_helpmenu_links a:link,
6058: table#LC_helpmenu_links a:visited,
1.397 albertel 6059: table#LC_helpmenu_links a:active {
6060: text-decoration: none;
6061: color: $font;
6062: }
1.795 www 6063:
1.397 albertel 6064: table#LC_helpmenu_links a:hover {
6065: text-decoration: underline;
6066: color: $vlink;
6067: }
1.396 albertel 6068:
1.417 albertel 6069: .LC_chrt_popup_exists {
6070: border: 1px solid #339933;
6071: margin: -1px;
6072: }
1.795 www 6073:
1.417 albertel 6074: .LC_chrt_popup_up {
6075: border: 1px solid yellow;
6076: margin: -1px;
6077: }
1.795 www 6078:
1.417 albertel 6079: .LC_chrt_popup {
6080: border: 1px solid #8888FF;
6081: background: #CCCCFF;
6082: }
1.795 www 6083:
1.421 albertel 6084: table.LC_pick_box {
6085: border-collapse: separate;
6086: background: white;
6087: border: 1px solid black;
6088: border-spacing: 1px;
6089: }
1.795 www 6090:
1.421 albertel 6091: table.LC_pick_box td.LC_pick_box_title {
1.850 bisitz 6092: background: $sidebg;
1.421 albertel 6093: font-weight: bold;
1.900 bisitz 6094: text-align: left;
1.740 bisitz 6095: vertical-align: top;
1.421 albertel 6096: width: 184px;
6097: padding: 8px;
6098: }
1.795 www 6099:
1.579 raeburn 6100: table.LC_pick_box td.LC_pick_box_value {
6101: text-align: left;
6102: padding: 8px;
6103: }
1.795 www 6104:
1.579 raeburn 6105: table.LC_pick_box td.LC_pick_box_select {
6106: text-align: left;
6107: padding: 8px;
6108: }
1.795 www 6109:
1.424 albertel 6110: table.LC_pick_box td.LC_pick_box_separator {
1.803 bisitz 6111: padding: 0;
1.421 albertel 6112: height: 1px;
6113: background: black;
6114: }
1.795 www 6115:
1.421 albertel 6116: table.LC_pick_box td.LC_pick_box_submit {
6117: text-align: right;
6118: }
1.795 www 6119:
1.579 raeburn 6120: table.LC_pick_box td.LC_evenrow_value {
6121: text-align: left;
6122: padding: 8px;
6123: background-color: $data_table_light;
6124: }
1.795 www 6125:
1.579 raeburn 6126: table.LC_pick_box td.LC_oddrow_value {
6127: text-align: left;
6128: padding: 8px;
6129: background-color: $data_table_light;
6130: }
1.795 www 6131:
1.579 raeburn 6132: span.LC_helpform_receipt_cat {
6133: font-weight: bold;
6134: }
1.795 www 6135:
1.424 albertel 6136: table.LC_group_priv_box {
6137: background: white;
6138: border: 1px solid black;
6139: border-spacing: 1px;
6140: }
1.795 www 6141:
1.424 albertel 6142: table.LC_group_priv_box td.LC_pick_box_title {
6143: background: $tabbg;
6144: font-weight: bold;
6145: text-align: right;
6146: width: 184px;
6147: }
1.795 www 6148:
1.424 albertel 6149: table.LC_group_priv_box td.LC_groups_fixed {
6150: background: $data_table_light;
6151: text-align: center;
6152: }
1.795 www 6153:
1.424 albertel 6154: table.LC_group_priv_box td.LC_groups_optional {
6155: background: $data_table_dark;
6156: text-align: center;
6157: }
1.795 www 6158:
1.424 albertel 6159: table.LC_group_priv_box td.LC_groups_functionality {
6160: background: $data_table_darker;
6161: text-align: center;
6162: font-weight: bold;
6163: }
1.795 www 6164:
1.424 albertel 6165: table.LC_group_priv td {
6166: text-align: left;
1.803 bisitz 6167: padding: 0;
1.424 albertel 6168: }
6169:
6170: .LC_navbuttons {
6171: margin: 2ex 0ex 2ex 0ex;
6172: }
1.795 www 6173:
1.423 albertel 6174: .LC_topic_bar {
6175: font-weight: bold;
6176: background: $tabbg;
1.918 wenzelju 6177: margin: 1em 0em 1em 2em;
1.805 bisitz 6178: padding: 3px;
1.918 wenzelju 6179: font-size: 1.2em;
1.423 albertel 6180: }
1.795 www 6181:
1.423 albertel 6182: .LC_topic_bar span {
1.918 wenzelju 6183: left: 0.5em;
6184: position: absolute;
1.423 albertel 6185: vertical-align: middle;
1.918 wenzelju 6186: font-size: 1.2em;
1.423 albertel 6187: }
1.795 www 6188:
1.423 albertel 6189: table.LC_course_group_status {
6190: margin: 20px;
6191: }
1.795 www 6192:
1.423 albertel 6193: table.LC_status_selector td {
6194: vertical-align: top;
6195: text-align: center;
1.424 albertel 6196: padding: 4px;
6197: }
1.795 www 6198:
1.599 albertel 6199: div.LC_feedback_link {
1.616 albertel 6200: clear: both;
1.829 kalberla 6201: background: $sidebg;
1.779 bisitz 6202: width: 100%;
1.829 kalberla 6203: padding-bottom: 10px;
6204: border: 1px $tabbg solid;
1.833 kalberla 6205: height: 22px;
6206: line-height: 22px;
6207: padding-top: 5px;
6208: }
6209:
6210: div.LC_feedback_link img {
6211: height: 22px;
1.867 kalberla 6212: vertical-align:middle;
1.829 kalberla 6213: }
6214:
1.911 bisitz 6215: div.LC_feedback_link a {
1.829 kalberla 6216: text-decoration: none;
1.489 raeburn 6217: }
1.795 www 6218:
1.867 kalberla 6219: div.LC_comblock {
1.911 bisitz 6220: display:inline;
1.867 kalberla 6221: color:$font;
6222: font-size:90%;
6223: }
6224:
6225: div.LC_feedback_link div.LC_comblock {
6226: padding-left:5px;
6227: }
6228:
6229: div.LC_feedback_link div.LC_comblock a {
6230: color:$font;
6231: }
6232:
1.489 raeburn 6233: span.LC_feedback_link {
1.858 bisitz 6234: /* background: $feedback_link_bg; */
1.599 albertel 6235: font-size: larger;
6236: }
1.795 www 6237:
1.599 albertel 6238: span.LC_message_link {
1.858 bisitz 6239: /* background: $feedback_link_bg; */
1.599 albertel 6240: font-size: larger;
6241: position: absolute;
6242: right: 1em;
1.489 raeburn 6243: }
1.421 albertel 6244:
1.515 albertel 6245: table.LC_prior_tries {
1.524 albertel 6246: border: 1px solid #000000;
6247: border-collapse: separate;
6248: border-spacing: 1px;
1.515 albertel 6249: }
1.523 albertel 6250:
1.515 albertel 6251: table.LC_prior_tries td {
1.524 albertel 6252: padding: 2px;
1.515 albertel 6253: }
1.523 albertel 6254:
6255: .LC_answer_correct {
1.795 www 6256: background: lightgreen;
6257: color: darkgreen;
6258: padding: 6px;
1.523 albertel 6259: }
1.795 www 6260:
1.523 albertel 6261: .LC_answer_charged_try {
1.797 www 6262: background: #FFAAAA;
1.795 www 6263: color: darkred;
6264: padding: 6px;
1.523 albertel 6265: }
1.795 www 6266:
1.779 bisitz 6267: .LC_answer_not_charged_try,
1.523 albertel 6268: .LC_answer_no_grade,
6269: .LC_answer_late {
1.795 www 6270: background: lightyellow;
1.523 albertel 6271: color: black;
1.795 www 6272: padding: 6px;
1.523 albertel 6273: }
1.795 www 6274:
1.523 albertel 6275: .LC_answer_previous {
1.795 www 6276: background: lightblue;
6277: color: darkblue;
6278: padding: 6px;
1.523 albertel 6279: }
1.795 www 6280:
1.779 bisitz 6281: .LC_answer_no_message {
1.777 tempelho 6282: background: #FFFFFF;
6283: color: black;
1.795 www 6284: padding: 6px;
1.779 bisitz 6285: }
1.795 www 6286:
1.779 bisitz 6287: .LC_answer_unknown {
6288: background: orange;
6289: color: black;
1.795 www 6290: padding: 6px;
1.777 tempelho 6291: }
1.795 www 6292:
1.529 albertel 6293: span.LC_prior_numerical,
6294: span.LC_prior_string,
6295: span.LC_prior_custom,
6296: span.LC_prior_reaction,
6297: span.LC_prior_math {
1.925 bisitz 6298: font-family: $mono;
1.523 albertel 6299: white-space: pre;
6300: }
6301:
1.525 albertel 6302: span.LC_prior_string {
1.925 bisitz 6303: font-family: $mono;
1.525 albertel 6304: white-space: pre;
6305: }
6306:
1.523 albertel 6307: table.LC_prior_option {
6308: width: 100%;
6309: border-collapse: collapse;
6310: }
1.795 www 6311:
1.911 bisitz 6312: table.LC_prior_rank,
1.795 www 6313: table.LC_prior_match {
1.528 albertel 6314: border-collapse: collapse;
6315: }
1.795 www 6316:
1.528 albertel 6317: table.LC_prior_option tr td,
6318: table.LC_prior_rank tr td,
6319: table.LC_prior_match tr td {
1.524 albertel 6320: border: 1px solid #000000;
1.515 albertel 6321: }
6322:
1.855 bisitz 6323: .LC_nobreak {
1.544 albertel 6324: white-space: nowrap;
1.519 raeburn 6325: }
6326:
1.576 raeburn 6327: span.LC_cusr_emph {
6328: font-style: italic;
6329: }
6330:
1.633 raeburn 6331: span.LC_cusr_subheading {
6332: font-weight: normal;
6333: font-size: 85%;
6334: }
6335:
1.861 bisitz 6336: div.LC_docs_entry_move {
1.859 bisitz 6337: border: 1px solid #BBBBBB;
1.545 albertel 6338: background: #DDDDDD;
1.861 bisitz 6339: width: 22px;
1.859 bisitz 6340: padding: 1px;
6341: margin: 0;
1.545 albertel 6342: }
6343:
1.861 bisitz 6344: table.LC_data_table tr > td.LC_docs_entry_commands,
6345: table.LC_data_table tr > td.LC_docs_entry_parameter {
1.545 albertel 6346: font-size: x-small;
6347: }
1.795 www 6348:
1.861 bisitz 6349: .LC_docs_entry_parameter {
6350: white-space: nowrap;
6351: }
6352:
1.544 albertel 6353: .LC_docs_copy {
1.545 albertel 6354: color: #000099;
1.544 albertel 6355: }
1.795 www 6356:
1.544 albertel 6357: .LC_docs_cut {
1.545 albertel 6358: color: #550044;
1.544 albertel 6359: }
1.795 www 6360:
1.544 albertel 6361: .LC_docs_rename {
1.545 albertel 6362: color: #009900;
1.544 albertel 6363: }
1.795 www 6364:
1.544 albertel 6365: .LC_docs_remove {
1.545 albertel 6366: color: #990000;
6367: }
6368:
1.547 albertel 6369: .LC_docs_reinit_warn,
6370: .LC_docs_ext_edit {
6371: font-size: x-small;
6372: }
6373:
1.545 albertel 6374: table.LC_docs_adddocs td,
6375: table.LC_docs_adddocs th {
6376: border: 1px solid #BBBBBB;
6377: padding: 4px;
6378: background: #DDDDDD;
1.543 albertel 6379: }
6380:
1.584 albertel 6381: table.LC_sty_begin {
6382: background: #BBFFBB;
6383: }
1.795 www 6384:
1.584 albertel 6385: table.LC_sty_end {
6386: background: #FFBBBB;
6387: }
6388:
1.589 raeburn 6389: table.LC_double_column {
1.803 bisitz 6390: border-width: 0;
1.589 raeburn 6391: border-collapse: collapse;
6392: width: 100%;
6393: padding: 2px;
6394: }
6395:
6396: table.LC_double_column tr td.LC_left_col {
1.590 raeburn 6397: top: 2px;
1.589 raeburn 6398: left: 2px;
6399: width: 47%;
6400: vertical-align: top;
6401: }
6402:
6403: table.LC_double_column tr td.LC_right_col {
6404: top: 2px;
1.779 bisitz 6405: right: 2px;
1.589 raeburn 6406: width: 47%;
6407: vertical-align: top;
6408: }
6409:
1.591 raeburn 6410: div.LC_left_float {
6411: float: left;
6412: padding-right: 5%;
1.597 albertel 6413: padding-bottom: 4px;
1.591 raeburn 6414: }
6415:
6416: div.LC_clear_float_header {
1.597 albertel 6417: padding-bottom: 2px;
1.591 raeburn 6418: }
6419:
6420: div.LC_clear_float_footer {
1.597 albertel 6421: padding-top: 10px;
1.591 raeburn 6422: clear: both;
6423: }
6424:
1.597 albertel 6425: div.LC_grade_show_user {
1.941 bisitz 6426: /* border-left: 5px solid $sidebg; */
6427: border-top: 5px solid #000000;
6428: margin: 50px 0 0 0;
1.936 bisitz 6429: padding: 15px 0 5px 10px;
1.597 albertel 6430: }
1.795 www 6431:
1.936 bisitz 6432: div.LC_grade_show_user_odd_row {
1.941 bisitz 6433: /* border-left: 5px solid #000000; */
6434: }
6435:
6436: div.LC_grade_show_user div.LC_Box {
6437: margin-right: 50px;
1.597 albertel 6438: }
6439:
6440: div.LC_grade_submissions,
6441: div.LC_grade_message_center,
1.936 bisitz 6442: div.LC_grade_info_links {
1.597 albertel 6443: margin: 5px;
6444: width: 99%;
6445: background: #FFFFFF;
6446: }
1.795 www 6447:
1.597 albertel 6448: div.LC_grade_submissions_header,
1.936 bisitz 6449: div.LC_grade_message_center_header {
1.705 tempelho 6450: font-weight: bold;
6451: font-size: large;
1.597 albertel 6452: }
1.795 www 6453:
1.597 albertel 6454: div.LC_grade_submissions_body,
1.936 bisitz 6455: div.LC_grade_message_center_body {
1.597 albertel 6456: border: 1px solid black;
6457: width: 99%;
6458: background: #FFFFFF;
6459: }
1.795 www 6460:
1.613 albertel 6461: table.LC_scantron_action {
6462: width: 100%;
6463: }
1.795 www 6464:
1.613 albertel 6465: table.LC_scantron_action tr th {
1.698 harmsja 6466: font-weight:bold;
6467: font-style:normal;
1.613 albertel 6468: }
1.795 www 6469:
1.779 bisitz 6470: .LC_edit_problem_header,
1.614 albertel 6471: div.LC_edit_problem_footer {
1.705 tempelho 6472: font-weight: normal;
6473: font-size: medium;
1.602 albertel 6474: margin: 2px;
1.1060 bisitz 6475: background-color: $sidebg;
1.600 albertel 6476: }
1.795 www 6477:
1.600 albertel 6478: div.LC_edit_problem_header,
1.602 albertel 6479: div.LC_edit_problem_header div,
1.614 albertel 6480: div.LC_edit_problem_footer,
6481: div.LC_edit_problem_footer div,
1.602 albertel 6482: div.LC_edit_problem_editxml_header,
6483: div.LC_edit_problem_editxml_header div {
1.600 albertel 6484: margin-top: 5px;
6485: }
1.795 www 6486:
1.600 albertel 6487: div.LC_edit_problem_header_title {
1.705 tempelho 6488: font-weight: bold;
6489: font-size: larger;
1.602 albertel 6490: background: $tabbg;
6491: padding: 3px;
1.1060 bisitz 6492: margin: 0 0 5px 0;
1.602 albertel 6493: }
1.795 www 6494:
1.602 albertel 6495: table.LC_edit_problem_header_title {
6496: width: 100%;
1.600 albertel 6497: background: $tabbg;
1.602 albertel 6498: }
6499:
6500: div.LC_edit_problem_discards {
6501: float: left;
6502: padding-bottom: 5px;
6503: }
1.795 www 6504:
1.602 albertel 6505: div.LC_edit_problem_saves {
6506: float: right;
6507: padding-bottom: 5px;
1.600 albertel 6508: }
1.795 www 6509:
1.1124 bisitz 6510: .LC_edit_opt {
6511: padding-left: 1em;
6512: white-space: nowrap;
6513: }
6514:
1.1152 golterma 6515: .LC_edit_problem_latexhelper{
6516: text-align: right;
6517: }
6518:
6519: #LC_edit_problem_colorful div{
6520: margin-left: 40px;
6521: }
6522:
1.911 bisitz 6523: img.stift {
1.803 bisitz 6524: border-width: 0;
6525: vertical-align: middle;
1.677 riegler 6526: }
1.680 riegler 6527:
1.923 bisitz 6528: table td.LC_mainmenu_col_fieldset {
1.680 riegler 6529: vertical-align: top;
1.777 tempelho 6530: }
1.795 www 6531:
1.716 raeburn 6532: div.LC_createcourse {
1.911 bisitz 6533: margin: 10px 10px 10px 10px;
1.716 raeburn 6534: }
6535:
1.917 raeburn 6536: .LC_dccid {
1.1130 raeburn 6537: float: right;
1.917 raeburn 6538: margin: 0.2em 0 0 0;
6539: padding: 0;
6540: font-size: 90%;
6541: display:none;
6542: }
6543:
1.897 wenzelju 6544: ol.LC_primary_menu a:hover,
1.721 harmsja 6545: ol#LC_MenuBreadcrumbs a:hover,
6546: ol#LC_PathBreadcrumbs a:hover,
1.897 wenzelju 6547: ul#LC_secondary_menu a:hover,
1.721 harmsja 6548: .LC_FormSectionClearButton input:hover
1.795 www 6549: ul.LC_TabContent li:hover a {
1.952 onken 6550: color:$button_hover;
1.911 bisitz 6551: text-decoration:none;
1.693 droeschl 6552: }
6553:
1.779 bisitz 6554: h1 {
1.911 bisitz 6555: padding: 0;
6556: line-height:130%;
1.693 droeschl 6557: }
1.698 harmsja 6558:
1.911 bisitz 6559: h2,
6560: h3,
6561: h4,
6562: h5,
6563: h6 {
6564: margin: 5px 0 5px 0;
6565: padding: 0;
6566: line-height:130%;
1.693 droeschl 6567: }
1.795 www 6568:
6569: .LC_hcell {
1.911 bisitz 6570: padding:3px 15px 3px 15px;
6571: margin: 0;
6572: background-color:$tabbg;
6573: color:$fontmenu;
6574: border-bottom:solid 1px $lg_border_color;
1.693 droeschl 6575: }
1.795 www 6576:
1.840 bisitz 6577: .LC_Box > .LC_hcell {
1.911 bisitz 6578: margin: 0 -10px 10px -10px;
1.835 bisitz 6579: }
6580:
1.721 harmsja 6581: .LC_noBorder {
1.911 bisitz 6582: border: 0;
1.698 harmsja 6583: }
1.693 droeschl 6584:
1.721 harmsja 6585: .LC_FormSectionClearButton input {
1.911 bisitz 6586: background-color:transparent;
6587: border: none;
6588: cursor:pointer;
6589: text-decoration:underline;
1.693 droeschl 6590: }
1.763 bisitz 6591:
6592: .LC_help_open_topic {
1.911 bisitz 6593: color: #FFFFFF;
6594: background-color: #EEEEFF;
6595: margin: 1px;
6596: padding: 4px;
6597: border: 1px solid #000033;
6598: white-space: nowrap;
6599: /* vertical-align: middle; */
1.759 neumanie 6600: }
1.693 droeschl 6601:
1.911 bisitz 6602: dl,
6603: ul,
6604: div,
6605: fieldset {
6606: margin: 10px 10px 10px 0;
6607: /* overflow: hidden; */
1.693 droeschl 6608: }
1.795 www 6609:
1.838 bisitz 6610: fieldset > legend {
1.911 bisitz 6611: font-weight: bold;
6612: padding: 0 5px 0 5px;
1.838 bisitz 6613: }
6614:
1.813 bisitz 6615: #LC_nav_bar {
1.911 bisitz 6616: float: left;
1.995 raeburn 6617: background-color: $pgbg_or_bgcolor;
1.966 bisitz 6618: margin: 0 0 2px 0;
1.807 droeschl 6619: }
6620:
1.916 droeschl 6621: #LC_realm {
6622: margin: 0.2em 0 0 0;
6623: padding: 0;
6624: font-weight: bold;
6625: text-align: center;
1.995 raeburn 6626: background-color: $pgbg_or_bgcolor;
1.916 droeschl 6627: }
6628:
1.911 bisitz 6629: #LC_nav_bar em {
6630: font-weight: bold;
6631: font-style: normal;
1.807 droeschl 6632: }
6633:
1.897 wenzelju 6634: ol.LC_primary_menu {
1.934 droeschl 6635: margin: 0;
1.1076 raeburn 6636: padding: 0;
1.995 raeburn 6637: background-color: $pgbg_or_bgcolor;
1.807 droeschl 6638: }
6639:
1.852 droeschl 6640: ol#LC_PathBreadcrumbs {
1.911 bisitz 6641: margin: 0;
1.693 droeschl 6642: }
6643:
1.897 wenzelju 6644: ol.LC_primary_menu li {
1.1076 raeburn 6645: color: RGB(80, 80, 80);
6646: vertical-align: middle;
6647: text-align: left;
6648: list-style: none;
6649: float: left;
6650: }
6651:
6652: ol.LC_primary_menu li a {
6653: display: block;
6654: margin: 0;
6655: padding: 0 5px 0 10px;
6656: text-decoration: none;
6657: }
6658:
6659: ol.LC_primary_menu li ul {
6660: display: none;
6661: width: 10em;
6662: background-color: $data_table_light;
6663: }
6664:
6665: ol.LC_primary_menu li:hover ul, ol.LC_primary_menu li.hover ul {
6666: display: block;
6667: position: absolute;
6668: margin: 0;
6669: padding: 0;
1.1078 raeburn 6670: z-index: 2;
1.1076 raeburn 6671: }
6672:
6673: ol.LC_primary_menu li:hover li, ol.LC_primary_menu li.hover li {
6674: font-size: 90%;
1.911 bisitz 6675: vertical-align: top;
1.1076 raeburn 6676: float: none;
1.1079 raeburn 6677: border-left: 1px solid black;
6678: border-right: 1px solid black;
1.1076 raeburn 6679: }
6680:
6681: ol.LC_primary_menu li:hover li a, ol.LC_primary_menu li.hover li a {
1.1078 raeburn 6682: background-color:$data_table_light;
1.1076 raeburn 6683: }
6684:
6685: ol.LC_primary_menu li li a:hover {
6686: color:$button_hover;
6687: background-color:$data_table_dark;
1.693 droeschl 6688: }
6689:
1.897 wenzelju 6690: ol.LC_primary_menu li img {
1.911 bisitz 6691: vertical-align: bottom;
1.934 droeschl 6692: height: 1.1em;
1.1077 raeburn 6693: margin: 0.2em 0 0 0;
1.693 droeschl 6694: }
6695:
1.897 wenzelju 6696: ol.LC_primary_menu a {
1.911 bisitz 6697: color: RGB(80, 80, 80);
6698: text-decoration: none;
1.693 droeschl 6699: }
1.795 www 6700:
1.949 droeschl 6701: ol.LC_primary_menu a.LC_new_message {
6702: font-weight:bold;
6703: color: darkred;
6704: }
6705:
1.975 raeburn 6706: ol.LC_docs_parameters {
6707: margin-left: 0;
6708: padding: 0;
6709: list-style: none;
6710: }
6711:
6712: ol.LC_docs_parameters li {
6713: margin: 0;
6714: padding-right: 20px;
6715: display: inline;
6716: }
6717:
1.976 raeburn 6718: ol.LC_docs_parameters li:before {
6719: content: "\\002022 \\0020";
6720: }
6721:
6722: li.LC_docs_parameters_title {
6723: font-weight: bold;
6724: }
6725:
6726: ol.LC_docs_parameters li.LC_docs_parameters_title:before {
6727: content: "";
6728: }
6729:
1.897 wenzelju 6730: ul#LC_secondary_menu {
1.1107 raeburn 6731: clear: right;
1.911 bisitz 6732: color: $fontmenu;
6733: background: $tabbg;
6734: list-style: none;
6735: padding: 0;
6736: margin: 0;
6737: width: 100%;
1.995 raeburn 6738: text-align: left;
1.1107 raeburn 6739: float: left;
1.808 droeschl 6740: }
6741:
1.897 wenzelju 6742: ul#LC_secondary_menu li {
1.911 bisitz 6743: font-weight: bold;
6744: line-height: 1.8em;
1.1107 raeburn 6745: border-right: 1px solid black;
6746: float: left;
6747: }
6748:
6749: ul#LC_secondary_menu li.LC_hoverable:hover, ul#LC_secondary_menu li.hover {
6750: background-color: $data_table_light;
6751: }
6752:
6753: ul#LC_secondary_menu li a {
1.911 bisitz 6754: padding: 0 0.8em;
1.1107 raeburn 6755: }
6756:
6757: ul#LC_secondary_menu li ul {
6758: display: none;
6759: }
6760:
6761: ul#LC_secondary_menu li:hover ul, ul#LC_secondary_menu li.hover ul {
6762: display: block;
6763: position: absolute;
6764: margin: 0;
6765: padding: 0;
6766: list-style:none;
6767: float: none;
6768: background-color: $data_table_light;
6769: z-index: 2;
6770: margin-left: -1px;
6771: }
6772:
6773: ul#LC_secondary_menu li ul li {
6774: font-size: 90%;
6775: vertical-align: top;
6776: border-left: 1px solid black;
1.911 bisitz 6777: border-right: 1px solid black;
1.1119 raeburn 6778: background-color: $data_table_light;
1.1107 raeburn 6779: list-style:none;
6780: float: none;
6781: }
6782:
6783: ul#LC_secondary_menu li ul li:hover, ul#LC_secondary_menu li ul li.hover {
6784: background-color: $data_table_dark;
1.807 droeschl 6785: }
6786:
1.847 tempelho 6787: ul.LC_TabContent {
1.911 bisitz 6788: display:block;
6789: background: $sidebg;
6790: border-bottom: solid 1px $lg_border_color;
6791: list-style:none;
1.1020 raeburn 6792: margin: -1px -10px 0 -10px;
1.911 bisitz 6793: padding: 0;
1.693 droeschl 6794: }
6795:
1.795 www 6796: ul.LC_TabContent li,
6797: ul.LC_TabContentBigger li {
1.911 bisitz 6798: float:left;
1.741 harmsja 6799: }
1.795 www 6800:
1.897 wenzelju 6801: ul#LC_secondary_menu li a {
1.911 bisitz 6802: color: $fontmenu;
6803: text-decoration: none;
1.693 droeschl 6804: }
1.795 www 6805:
1.721 harmsja 6806: ul.LC_TabContent {
1.952 onken 6807: min-height:20px;
1.721 harmsja 6808: }
1.795 www 6809:
6810: ul.LC_TabContent li {
1.911 bisitz 6811: vertical-align:middle;
1.959 onken 6812: padding: 0 16px 0 10px;
1.911 bisitz 6813: background-color:$tabbg;
6814: border-bottom:solid 1px $lg_border_color;
1.1020 raeburn 6815: border-left: solid 1px $font;
1.721 harmsja 6816: }
1.795 www 6817:
1.847 tempelho 6818: ul.LC_TabContent .right {
1.911 bisitz 6819: float:right;
1.847 tempelho 6820: }
6821:
1.911 bisitz 6822: ul.LC_TabContent li a,
6823: ul.LC_TabContent li {
6824: color:rgb(47,47,47);
6825: text-decoration:none;
6826: font-size:95%;
6827: font-weight:bold;
1.952 onken 6828: min-height:20px;
6829: }
6830:
1.959 onken 6831: ul.LC_TabContent li a:hover,
6832: ul.LC_TabContent li a:focus {
1.952 onken 6833: color: $button_hover;
1.959 onken 6834: background:none;
6835: outline:none;
1.952 onken 6836: }
6837:
6838: ul.LC_TabContent li:hover {
6839: color: $button_hover;
6840: cursor:pointer;
1.721 harmsja 6841: }
1.795 www 6842:
1.911 bisitz 6843: ul.LC_TabContent li.active {
1.952 onken 6844: color: $font;
1.911 bisitz 6845: background:#FFFFFF url(/adm/lonIcons/open.gif) no-repeat scroll right center;
1.952 onken 6846: border-bottom:solid 1px #FFFFFF;
6847: cursor: default;
1.744 ehlerst 6848: }
1.795 www 6849:
1.959 onken 6850: ul.LC_TabContent li.active a {
6851: color:$font;
6852: background:#FFFFFF;
6853: outline: none;
6854: }
1.1047 raeburn 6855:
6856: ul.LC_TabContent li.goback {
6857: float: left;
6858: border-left: none;
6859: }
6860:
1.870 tempelho 6861: #maincoursedoc {
1.911 bisitz 6862: clear:both;
1.870 tempelho 6863: }
6864:
6865: ul.LC_TabContentBigger {
1.911 bisitz 6866: display:block;
6867: list-style:none;
6868: padding: 0;
1.870 tempelho 6869: }
6870:
1.795 www 6871: ul.LC_TabContentBigger li {
1.911 bisitz 6872: vertical-align:bottom;
6873: height: 30px;
6874: font-size:110%;
6875: font-weight:bold;
6876: color: #737373;
1.841 tempelho 6877: }
6878:
1.957 onken 6879: ul.LC_TabContentBigger li.active {
6880: position: relative;
6881: top: 1px;
6882: }
6883:
1.870 tempelho 6884: ul.LC_TabContentBigger li a {
1.911 bisitz 6885: background:url('/adm/lonIcons/tabbgleft.gif') left bottom no-repeat;
6886: height: 30px;
6887: line-height: 30px;
6888: text-align: center;
6889: display: block;
6890: text-decoration: none;
1.958 onken 6891: outline: none;
1.741 harmsja 6892: }
1.795 www 6893:
1.870 tempelho 6894: ul.LC_TabContentBigger li.active a {
1.911 bisitz 6895: background:url('/adm/lonIcons/tabbgleft.gif') left top no-repeat;
6896: color:$font;
1.744 ehlerst 6897: }
1.795 www 6898:
1.870 tempelho 6899: ul.LC_TabContentBigger li b {
1.911 bisitz 6900: background: url('/adm/lonIcons/tabbgright.gif') no-repeat right bottom;
6901: display: block;
6902: float: left;
6903: padding: 0 30px;
1.957 onken 6904: border-bottom: 1px solid $lg_border_color;
1.870 tempelho 6905: }
6906:
1.956 onken 6907: ul.LC_TabContentBigger li:hover b {
6908: color:$button_hover;
6909: }
6910:
1.870 tempelho 6911: ul.LC_TabContentBigger li.active b {
1.911 bisitz 6912: background:url('/adm/lonIcons/tabbgright.gif') right top no-repeat;
6913: color:$font;
1.957 onken 6914: border: 0;
1.741 harmsja 6915: }
1.693 droeschl 6916:
1.870 tempelho 6917:
1.862 bisitz 6918: ul.LC_CourseBreadcrumbs {
6919: background: $sidebg;
1.1020 raeburn 6920: height: 2em;
1.862 bisitz 6921: padding-left: 10px;
1.1020 raeburn 6922: margin: 0;
1.862 bisitz 6923: list-style-position: inside;
6924: }
6925:
1.911 bisitz 6926: ol#LC_MenuBreadcrumbs,
1.862 bisitz 6927: ol#LC_PathBreadcrumbs {
1.911 bisitz 6928: padding-left: 10px;
6929: margin: 0;
1.933 droeschl 6930: height: 2.5em; /* equal to #LC_breadcrumbs line-height */
1.693 droeschl 6931: }
6932:
1.911 bisitz 6933: ol#LC_MenuBreadcrumbs li,
6934: ol#LC_PathBreadcrumbs li,
1.862 bisitz 6935: ul.LC_CourseBreadcrumbs li {
1.911 bisitz 6936: display: inline;
1.933 droeschl 6937: white-space: normal;
1.693 droeschl 6938: }
6939:
1.823 bisitz 6940: ol#LC_MenuBreadcrumbs li a,
1.862 bisitz 6941: ul.LC_CourseBreadcrumbs li a {
1.911 bisitz 6942: text-decoration: none;
6943: font-size:90%;
1.693 droeschl 6944: }
1.795 www 6945:
1.969 droeschl 6946: ol#LC_MenuBreadcrumbs h1 {
6947: display: inline;
6948: font-size: 90%;
6949: line-height: 2.5em;
6950: margin: 0;
6951: padding: 0;
6952: }
6953:
1.795 www 6954: ol#LC_PathBreadcrumbs li a {
1.911 bisitz 6955: text-decoration:none;
6956: font-size:100%;
6957: font-weight:bold;
1.693 droeschl 6958: }
1.795 www 6959:
1.840 bisitz 6960: .LC_Box {
1.911 bisitz 6961: border: solid 1px $lg_border_color;
6962: padding: 0 10px 10px 10px;
1.746 neumanie 6963: }
1.795 www 6964:
1.1020 raeburn 6965: .LC_DocsBox {
6966: border: solid 1px $lg_border_color;
6967: padding: 0 0 10px 10px;
6968: }
6969:
1.795 www 6970: .LC_AboutMe_Image {
1.911 bisitz 6971: float:left;
6972: margin-right:10px;
1.747 neumanie 6973: }
1.795 www 6974:
6975: .LC_Clear_AboutMe_Image {
1.911 bisitz 6976: clear:left;
1.747 neumanie 6977: }
1.795 www 6978:
1.721 harmsja 6979: dl.LC_ListStyleClean dt {
1.911 bisitz 6980: padding-right: 5px;
6981: display: table-header-group;
1.693 droeschl 6982: }
6983:
1.721 harmsja 6984: dl.LC_ListStyleClean dd {
1.911 bisitz 6985: display: table-row;
1.693 droeschl 6986: }
6987:
1.721 harmsja 6988: .LC_ListStyleClean,
6989: .LC_ListStyleSimple,
6990: .LC_ListStyleNormal,
1.795 www 6991: .LC_ListStyleSpecial {
1.911 bisitz 6992: /* display:block; */
6993: list-style-position: inside;
6994: list-style-type: none;
6995: overflow: hidden;
6996: padding: 0;
1.693 droeschl 6997: }
6998:
1.721 harmsja 6999: .LC_ListStyleSimple li,
7000: .LC_ListStyleSimple dd,
7001: .LC_ListStyleNormal li,
7002: .LC_ListStyleNormal dd,
7003: .LC_ListStyleSpecial li,
1.795 www 7004: .LC_ListStyleSpecial dd {
1.911 bisitz 7005: margin: 0;
7006: padding: 5px 5px 5px 10px;
7007: clear: both;
1.693 droeschl 7008: }
7009:
1.721 harmsja 7010: .LC_ListStyleClean li,
7011: .LC_ListStyleClean dd {
1.911 bisitz 7012: padding-top: 0;
7013: padding-bottom: 0;
1.693 droeschl 7014: }
7015:
1.721 harmsja 7016: .LC_ListStyleSimple dd,
1.795 www 7017: .LC_ListStyleSimple li {
1.911 bisitz 7018: border-bottom: solid 1px $lg_border_color;
1.693 droeschl 7019: }
7020:
1.721 harmsja 7021: .LC_ListStyleSpecial li,
7022: .LC_ListStyleSpecial dd {
1.911 bisitz 7023: list-style-type: none;
7024: background-color: RGB(220, 220, 220);
7025: margin-bottom: 4px;
1.693 droeschl 7026: }
7027:
1.721 harmsja 7028: table.LC_SimpleTable {
1.911 bisitz 7029: margin:5px;
7030: border:solid 1px $lg_border_color;
1.795 www 7031: }
1.693 droeschl 7032:
1.721 harmsja 7033: table.LC_SimpleTable tr {
1.911 bisitz 7034: padding: 0;
7035: border:solid 1px $lg_border_color;
1.693 droeschl 7036: }
1.795 www 7037:
7038: table.LC_SimpleTable thead {
1.911 bisitz 7039: background:rgb(220,220,220);
1.693 droeschl 7040: }
7041:
1.721 harmsja 7042: div.LC_columnSection {
1.911 bisitz 7043: display: block;
7044: clear: both;
7045: overflow: hidden;
7046: margin: 0;
1.693 droeschl 7047: }
7048:
1.721 harmsja 7049: div.LC_columnSection>* {
1.911 bisitz 7050: float: left;
7051: margin: 10px 20px 10px 0;
7052: overflow:hidden;
1.693 droeschl 7053: }
1.721 harmsja 7054:
1.795 www 7055: table em {
1.911 bisitz 7056: font-weight: bold;
7057: font-style: normal;
1.748 schulted 7058: }
1.795 www 7059:
1.779 bisitz 7060: table.LC_tableBrowseRes,
1.795 www 7061: table.LC_tableOfContent {
1.911 bisitz 7062: border:none;
7063: border-spacing: 1px;
7064: padding: 3px;
7065: background-color: #FFFFFF;
7066: font-size: 90%;
1.753 droeschl 7067: }
1.789 droeschl 7068:
1.911 bisitz 7069: table.LC_tableOfContent {
7070: border-collapse: collapse;
1.789 droeschl 7071: }
7072:
1.771 droeschl 7073: table.LC_tableBrowseRes a,
1.768 schulted 7074: table.LC_tableOfContent a {
1.911 bisitz 7075: background-color: transparent;
7076: text-decoration: none;
1.753 droeschl 7077: }
7078:
1.795 www 7079: table.LC_tableOfContent img {
1.911 bisitz 7080: border: none;
7081: height: 1.3em;
7082: vertical-align: text-bottom;
7083: margin-right: 0.3em;
1.753 droeschl 7084: }
1.757 schulted 7085:
1.795 www 7086: a#LC_content_toolbar_firsthomework {
1.911 bisitz 7087: background-image:url(/res/adm/pages/open-first-problem.gif);
1.774 ehlerst 7088: }
7089:
1.795 www 7090: a#LC_content_toolbar_everything {
1.911 bisitz 7091: background-image:url(/res/adm/pages/show-all.gif);
1.774 ehlerst 7092: }
7093:
1.795 www 7094: a#LC_content_toolbar_uncompleted {
1.911 bisitz 7095: background-image:url(/res/adm/pages/show-incomplete-problems.gif);
1.774 ehlerst 7096: }
7097:
1.795 www 7098: #LC_content_toolbar_clearbubbles {
1.911 bisitz 7099: background-image:url(/res/adm/pages/mark-discussionentries-read.gif);
1.774 ehlerst 7100: }
7101:
1.795 www 7102: a#LC_content_toolbar_changefolder {
1.911 bisitz 7103: background : url(/res/adm/pages/close-all-folders.gif) top center ;
1.757 schulted 7104: }
7105:
1.795 www 7106: a#LC_content_toolbar_changefolder_toggled {
1.911 bisitz 7107: background-image:url(/res/adm/pages/open-all-folders.gif);
1.757 schulted 7108: }
7109:
1.1043 raeburn 7110: a#LC_content_toolbar_edittoplevel {
7111: background-image:url(/res/adm/pages/edittoplevel.gif);
7112: }
7113:
1.795 www 7114: ul#LC_toolbar li a:hover {
1.911 bisitz 7115: background-position: bottom center;
1.757 schulted 7116: }
7117:
1.795 www 7118: ul#LC_toolbar {
1.911 bisitz 7119: padding: 0;
7120: margin: 2px;
7121: list-style:none;
7122: position:relative;
7123: background-color:white;
1.1082 raeburn 7124: overflow: auto;
1.757 schulted 7125: }
7126:
1.795 www 7127: ul#LC_toolbar li {
1.911 bisitz 7128: border:1px solid white;
7129: padding: 0;
7130: margin: 0;
7131: float: left;
7132: display:inline;
7133: vertical-align:middle;
1.1082 raeburn 7134: white-space: nowrap;
1.911 bisitz 7135: }
1.757 schulted 7136:
1.783 amueller 7137:
1.795 www 7138: a.LC_toolbarItem {
1.911 bisitz 7139: display:block;
7140: padding: 0;
7141: margin: 0;
7142: height: 32px;
7143: width: 32px;
7144: color:white;
7145: border: none;
7146: background-repeat:no-repeat;
7147: background-color:transparent;
1.757 schulted 7148: }
7149:
1.915 droeschl 7150: ul.LC_funclist {
7151: margin: 0;
7152: padding: 0.5em 1em 0.5em 0;
7153: }
7154:
1.933 droeschl 7155: ul.LC_funclist > li:first-child {
7156: font-weight:bold;
7157: margin-left:0.8em;
7158: }
7159:
1.915 droeschl 7160: ul.LC_funclist + ul.LC_funclist {
7161: /*
7162: left border as a seperator if we have more than
7163: one list
7164: */
7165: border-left: 1px solid $sidebg;
7166: /*
7167: this hides the left border behind the border of the
7168: outer box if element is wrapped to the next 'line'
7169: */
7170: margin-left: -1px;
7171: }
7172:
1.843 bisitz 7173: ul.LC_funclist li {
1.915 droeschl 7174: display: inline;
1.782 bisitz 7175: white-space: nowrap;
1.915 droeschl 7176: margin: 0 0 0 25px;
7177: line-height: 150%;
1.782 bisitz 7178: }
7179:
1.974 wenzelju 7180: .LC_hidden {
7181: display: none;
7182: }
7183:
1.1030 www 7184: .LCmodal-overlay {
7185: position:fixed;
7186: top:0;
7187: right:0;
7188: bottom:0;
7189: left:0;
7190: height:100%;
7191: width:100%;
7192: margin:0;
7193: padding:0;
7194: background:#999;
7195: opacity:.75;
7196: filter: alpha(opacity=75);
7197: -moz-opacity: 0.75;
7198: z-index:101;
7199: }
7200:
7201: * html .LCmodal-overlay {
7202: position: absolute;
7203: height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px');
7204: }
7205:
7206: .LCmodal-window {
7207: position:fixed;
7208: top:50%;
7209: left:50%;
7210: margin:0;
7211: padding:0;
7212: z-index:102;
7213: }
7214:
7215: * html .LCmodal-window {
7216: position:absolute;
7217: }
7218:
7219: .LCclose-window {
7220: position:absolute;
7221: width:32px;
7222: height:32px;
7223: right:8px;
7224: top:8px;
7225: background:transparent url('/res/adm/pages/process-stop.png') no-repeat scroll right top;
7226: text-indent:-99999px;
7227: overflow:hidden;
7228: cursor:pointer;
7229: }
7230:
1.1100 raeburn 7231: /*
7232: styles used by TTH when "Default set of options to pass to tth/m
7233: when converting TeX" in course settings has been set
7234:
7235: option passed: -t
7236:
7237: */
7238:
7239: td div.comp { margin-top: -0.6ex; margin-bottom: -1ex;}
7240: td div.comb { margin-top: -0.6ex; margin-bottom: -.6ex;}
7241: td div.hrcomp { line-height: 0.9; margin-top: -0.8ex; margin-bottom: -1ex;}
7242: td div.norm {line-height:normal;}
7243:
7244: /*
7245: option passed -y3
7246: */
7247:
7248: span.roman {font-family: serif; font-style: normal; font-weight: normal;}
7249: span.overacc2 {position: relative; left: .8em; top: -1.2ex;}
7250: span.overacc1 {position: relative; left: .6em; top: -1.2ex;}
7251:
1.343 albertel 7252: END
7253: }
7254:
1.306 albertel 7255: =pod
7256:
7257: =item * &headtag()
7258:
7259: Returns a uniform footer for LON-CAPA web pages.
7260:
1.307 albertel 7261: Inputs: $title - optional title for the head
7262: $head_extra - optional extra HTML to put inside the <head>
1.315 albertel 7263: $args - optional arguments
1.319 albertel 7264: force_register - if is true call registerurl so the remote is
7265: informed
1.415 albertel 7266: redirect -> array ref of
7267: 1- seconds before redirect occurs
7268: 2- url to redirect to
7269: 3- whether the side effect should occur
1.315 albertel 7270: (side effect of setting
7271: $env{'internal.head.redirect'} to the url
7272: redirected too)
1.352 albertel 7273: domain -> force to color decorate a page for a specific
7274: domain
7275: function -> force usage of a specific rolish color scheme
7276: bgcolor -> override the default page bgcolor
1.460 albertel 7277: no_auto_mt_title
7278: -> prevent &mt()ing the title arg
1.464 albertel 7279:
1.306 albertel 7280: =cut
7281:
7282: sub headtag {
1.313 albertel 7283: my ($title,$head_extra,$args) = @_;
1.306 albertel 7284:
1.363 albertel 7285: my $function = $args->{'function'} || &get_users_function();
7286: my $domain = $args->{'domain'} || &determinedomain();
7287: my $bgcolor = $args->{'bgcolor'} || &designparm($function.'.pgbg',$domain);
1.1154 raeburn 7288: my $httphost = $args->{'use_absolute'};
1.418 albertel 7289: my $url = join(':',$env{'user.name'},$env{'user.domain'},
1.458 albertel 7290: $Apache::lonnet::perlvar{'lonVersion'},
1.531 albertel 7291: #time(),
1.418 albertel 7292: $env{'environment.color.timestamp'},
1.363 albertel 7293: $function,$domain,$bgcolor);
7294:
1.369 www 7295: $url = '/adm/css/'.&escape($url).'.css';
1.363 albertel 7296:
1.308 albertel 7297: my $result =
7298: '<head>'.
1.1160 raeburn 7299: &font_settings($args);
1.319 albertel 7300:
1.1064 raeburn 7301: my $inhibitprint = &print_suppression();
7302:
1.461 albertel 7303: if (!$args->{'frameset'}) {
7304: $result .= &Apache::lonhtmlcommon::htmlareaheaders();
7305: }
1.962 droeschl 7306: if ($args->{'force_register'} && $env{'request.noversionuri'} !~ m{^/res/adm/pages/}) {
7307: $result .= Apache::lonxml::display_title();
1.319 albertel 7308: }
1.436 albertel 7309: if (!$args->{'no_nav_bar'}
7310: && !$args->{'only_body'}
7311: && !$args->{'frameset'}) {
1.1154 raeburn 7312: $result .= &help_menu_js($httphost);
1.1032 www 7313: $result.=&modal_window();
1.1038 www 7314: $result.=&togglebox_script();
1.1034 www 7315: $result.=&wishlist_window();
1.1041 www 7316: $result.=&LCprogressbarUpdate_script();
1.1034 www 7317: } else {
7318: if ($args->{'add_modal'}) {
7319: $result.=&modal_window();
7320: }
7321: if ($args->{'add_wishlist'}) {
7322: $result.=&wishlist_window();
7323: }
1.1038 www 7324: if ($args->{'add_togglebox'}) {
7325: $result.=&togglebox_script();
7326: }
1.1041 www 7327: if ($args->{'add_progressbar'}) {
7328: $result.=&LCprogressbarUpdate_script();
7329: }
1.436 albertel 7330: }
1.314 albertel 7331: if (ref($args->{'redirect'})) {
1.414 albertel 7332: my ($time,$url,$inhibit_continue) = @{$args->{'redirect'}};
1.315 albertel 7333: $url = &Apache::lonenc::check_encrypt($url);
1.414 albertel 7334: if (!$inhibit_continue) {
7335: $env{'internal.head.redirect'} = $url;
7336: }
1.313 albertel 7337: $result.=<<ADDMETA
7338: <meta http-equiv="pragma" content="no-cache" />
1.344 albertel 7339: <meta http-equiv="Refresh" content="$time; url=$url" />
1.313 albertel 7340: ADDMETA
7341: }
1.306 albertel 7342: if (!defined($title)) {
7343: $title = 'The LearningOnline Network with CAPA';
7344: }
1.460 albertel 7345: if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); }
7346: $result .= '<title> LON-CAPA '.$title.'</title>'
1.1168 raeburn 7347: .'<link rel="stylesheet" type="text/css" href="'.$url.'"';
7348: if (!$args->{'frameset'}) {
7349: $result .= ' /';
7350: }
7351: $result .= '>'
1.1064 raeburn 7352: .$inhibitprint
1.414 albertel 7353: .$head_extra;
1.1137 raeburn 7354: if ($env{'browser.mobile'}) {
7355: $result .= '
7356: <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0">
7357: <meta name="apple-mobile-web-app-capable" content="yes" />';
7358: }
1.962 droeschl 7359: return $result.'</head>';
1.306 albertel 7360: }
7361:
7362: =pod
7363:
1.340 albertel 7364: =item * &font_settings()
7365:
7366: Returns neccessary <meta> to set the proper encoding
7367:
1.1160 raeburn 7368: Inputs: optional reference to HASH -- $args passed to &headtag()
1.340 albertel 7369:
7370: =cut
7371:
7372: sub font_settings {
1.1160 raeburn 7373: my ($args) = @_;
1.340 albertel 7374: my $headerstring='';
1.1160 raeburn 7375: if ((!$env{'browser.mathml'} && $env{'browser.unicode'}) ||
7376: ((ref($args) eq 'HASH') && ($args->{'browser.unicode'}))) {
1.1168 raeburn 7377: $headerstring.=
7378: '<meta http-equiv="Content-Type" content="text/html; charset=utf-8"';
7379: if (!$args->{'frameset'}) {
7380: $headerstring.= ' /';
7381: }
7382: $headerstring .= '>'."\n";
1.340 albertel 7383: }
7384: return $headerstring;
7385: }
7386:
1.341 albertel 7387: =pod
7388:
1.1064 raeburn 7389: =item * &print_suppression()
7390:
7391: In course context returns css which causes the body to be blank when media="print",
7392: if printout generation is unavailable for the current resource.
7393:
7394: This could be because:
7395:
7396: (a) printstartdate is in the future
7397:
7398: (b) printenddate is in the past
7399:
7400: (c) there is an active exam block with "printout"
7401: functionality blocked
7402:
7403: Users with pav, pfo or evb privileges are exempt.
7404:
7405: Inputs: none
7406:
7407: =cut
7408:
7409:
7410: sub print_suppression {
7411: my $noprint;
7412: if ($env{'request.course.id'}) {
7413: my $scope = $env{'request.course.id'};
7414: if ((&Apache::lonnet::allowed('pav',$scope)) ||
7415: (&Apache::lonnet::allowed('pfo',$scope))) {
7416: return;
7417: }
7418: if ($env{'request.course.sec'} ne '') {
7419: $scope .= "/$env{'request.course.sec'}";
7420: if ((&Apache::lonnet::allowed('pav',$scope)) ||
7421: (&Apache::lonnet::allowed('pfo',$scope))) {
1.1065 raeburn 7422: return;
1.1064 raeburn 7423: }
7424: }
7425: my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
7426: my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
1.1065 raeburn 7427: my $blocked = &blocking_status('printout',$cnum,$cdom);
1.1064 raeburn 7428: if ($blocked) {
7429: my $checkrole = "cm./$cdom/$cnum";
7430: if ($env{'request.course.sec'} ne '') {
7431: $checkrole .= "/$env{'request.course.sec'}";
7432: }
7433: unless ((&Apache::lonnet::allowed('evb',undef,undef,$checkrole)) &&
7434: ($env{'request.role'} !~ m{^st\./$cdom/$cnum})) {
7435: $noprint = 1;
7436: }
7437: }
7438: unless ($noprint) {
7439: my $symb = &Apache::lonnet::symbread();
7440: if ($symb ne '') {
7441: my $navmap = Apache::lonnavmaps::navmap->new();
7442: if (ref($navmap)) {
7443: my $res = $navmap->getBySymb($symb);
7444: if (ref($res)) {
7445: if (!$res->resprintable()) {
7446: $noprint = 1;
7447: }
7448: }
7449: }
7450: }
7451: }
7452: if ($noprint) {
7453: return <<"ENDSTYLE";
7454: <style type="text/css" media="print">
7455: body { display:none }
7456: </style>
7457: ENDSTYLE
7458: }
7459: }
7460: return;
7461: }
7462:
7463: =pod
7464:
1.341 albertel 7465: =item * &xml_begin()
7466:
7467: Returns the needed doctype and <html>
7468:
7469: Inputs: none
7470:
7471: =cut
7472:
7473: sub xml_begin {
1.1168 raeburn 7474: my ($is_frameset) = @_;
1.341 albertel 7475: my $output='';
7476:
7477: if ($env{'browser.mathml'}) {
7478: $output='<?xml version="1.0"?>'
7479: #.'<?xml-stylesheet type="text/css" href="/adm/MathML/mathml.css"?>'."\n"
7480: # .'<!DOCTYPE html SYSTEM "/adm/MathML/mathml.dtd" '
7481:
7482: # .'<!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">] >'
7483: .'<!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">'
7484: .'<html xmlns:math="http://www.w3.org/1998/Math/MathML" '
7485: .'xmlns="http://www.w3.org/1999/xhtml">';
1.1168 raeburn 7486: } elsif ($is_frameset) {
7487: $output='<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">'."\n".
7488: '<html>'."\n";
1.341 albertel 7489: } else {
1.1168 raeburn 7490: $output='<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'."\n".
7491: '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">'."\n";
1.341 albertel 7492: }
7493: return $output;
7494: }
1.340 albertel 7495:
7496: =pod
7497:
1.306 albertel 7498: =item * &start_page()
7499:
7500: Returns a complete <html> .. <body> section for LON-CAPA web pages.
7501:
1.648 raeburn 7502: Inputs:
7503:
7504: =over 4
7505:
7506: $title - optional title for the page
7507:
7508: $head_extra - optional extra HTML to incude inside the <head>
7509:
7510: $args - additional optional args supported are:
7511:
7512: =over 8
7513:
7514: only_body -> is true will set &bodytag() onlybodytag
1.317 albertel 7515: arg on
1.814 bisitz 7516: no_nav_bar -> is true will set &bodytag() no_nav_bar arg on
1.648 raeburn 7517: add_entries -> additional attributes to add to the <body>
7518: domain -> force to color decorate a page for a
1.317 albertel 7519: specific domain
1.648 raeburn 7520: function -> force usage of a specific rolish color
1.317 albertel 7521: scheme
1.648 raeburn 7522: redirect -> see &headtag()
7523: bgcolor -> override the default page bg color
7524: js_ready -> return a string ready for being used in
1.317 albertel 7525: a javascript writeln
1.648 raeburn 7526: html_encode -> return a string ready for being used in
1.320 albertel 7527: a html attribute
1.648 raeburn 7528: force_register -> if is true will turn on the &bodytag()
1.317 albertel 7529: $forcereg arg
1.648 raeburn 7530: frameset -> if true will start with a <frameset>
1.330 albertel 7531: rather than <body>
1.648 raeburn 7532: skip_phases -> hash ref of
1.338 albertel 7533: head -> skip the <html><head> generation
7534: body -> skip all <body> generation
1.648 raeburn 7535: no_auto_mt_title -> prevent &mt()ing the title arg
7536: inherit_jsmath -> when creating popup window in a page,
7537: should it have jsmath forced on by the
7538: current page
1.867 kalberla 7539: bread_crumbs -> Array containing breadcrumbs
1.983 raeburn 7540: bread_crumbs_component -> if exists show it as headline else show only the breadcrumbs
1.1096 raeburn 7541: group -> includes the current group, if page is for a
7542: specific group
1.361 albertel 7543:
1.648 raeburn 7544: =back
1.460 albertel 7545:
1.648 raeburn 7546: =back
1.562 albertel 7547:
1.306 albertel 7548: =cut
7549:
7550: sub start_page {
1.309 albertel 7551: my ($title,$head_extra,$args) = @_;
1.318 albertel 7552: #&Apache::lonnet::logthis("start_page ".join(':',caller(0)));
1.319 albertel 7553:
1.315 albertel 7554: $env{'internal.start_page'}++;
1.1096 raeburn 7555: my ($result,@advtools);
1.964 droeschl 7556:
1.338 albertel 7557: if (! exists($args->{'skip_phases'}{'head'}) ) {
1.1168 raeburn 7558: $result .= &xml_begin($args->{'frameset'}) . &headtag($title, $head_extra, $args);
1.338 albertel 7559: }
7560:
7561: if (! exists($args->{'skip_phases'}{'body'}) ) {
7562: if ($args->{'frameset'}) {
7563: my $attr_string = &make_attr_string($args->{'force_register'},
7564: $args->{'add_entries'});
7565: $result .= "\n<frameset $attr_string>\n";
1.831 bisitz 7566: } else {
7567: $result .=
7568: &bodytag($title,
7569: $args->{'function'}, $args->{'add_entries'},
7570: $args->{'only_body'}, $args->{'domain'},
7571: $args->{'force_register'}, $args->{'no_nav_bar'},
1.1096 raeburn 7572: $args->{'bgcolor'}, $args,
7573: \@advtools);
1.831 bisitz 7574: }
1.330 albertel 7575: }
1.338 albertel 7576:
1.315 albertel 7577: if ($args->{'js_ready'}) {
1.713 kaisler 7578: $result = &js_ready($result);
1.315 albertel 7579: }
1.320 albertel 7580: if ($args->{'html_encode'}) {
1.713 kaisler 7581: $result = &html_encode($result);
7582: }
7583:
1.813 bisitz 7584: # Preparation for new and consistent functionlist at top of screen
7585: # if ($args->{'functionlist'}) {
7586: # $result .= &build_functionlist();
7587: #}
7588:
1.964 droeschl 7589: # Don't add anything more if only_body wanted or in const space
7590: return $result if $args->{'only_body'}
7591: || $env{'request.state'} eq 'construct';
1.813 bisitz 7592:
7593: #Breadcrumbs
1.758 kaisler 7594: if (exists($args->{'bread_crumbs'}) or exists($args->{'bread_crumbs_component'})) {
7595: &Apache::lonhtmlcommon::clear_breadcrumbs();
7596: #if any br links exists, add them to the breadcrumbs
7597: if (exists($args->{'bread_crumbs'}) and ref($args->{'bread_crumbs'}) eq 'ARRAY') {
7598: foreach my $crumb (@{$args->{'bread_crumbs'}}){
7599: &Apache::lonhtmlcommon::add_breadcrumb($crumb);
7600: }
7601: }
1.1096 raeburn 7602: # if @advtools array contains items add then to the breadcrumbs
7603: if (@advtools > 0) {
7604: &Apache::lonmenu::advtools_crumbs(@advtools);
7605: }
1.758 kaisler 7606:
7607: #if bread_crumbs_component exists show it as headline else show only the breadcrumbs
7608: if(exists($args->{'bread_crumbs_component'})){
7609: $result .= &Apache::lonhtmlcommon::breadcrumbs($args->{'bread_crumbs_component'});
7610: }else{
7611: $result .= &Apache::lonhtmlcommon::breadcrumbs();
7612: }
1.320 albertel 7613: }
1.315 albertel 7614: return $result;
1.306 albertel 7615: }
7616:
7617: sub end_page {
1.315 albertel 7618: my ($args) = @_;
7619: $env{'internal.end_page'}++;
1.330 albertel 7620: my $result;
1.335 albertel 7621: if ($args->{'discussion'}) {
7622: my ($target,$parser);
7623: if (ref($args->{'discussion'})) {
7624: ($target,$parser) =($args->{'discussion'}{'target'},
7625: $args->{'discussion'}{'parser'});
7626: }
7627: $result .= &Apache::lonxml::xmlend($target,$parser);
7628: }
1.330 albertel 7629: if ($args->{'frameset'}) {
7630: $result .= '</frameset>';
7631: } else {
1.635 raeburn 7632: $result .= &endbodytag($args);
1.330 albertel 7633: }
1.1080 raeburn 7634: unless ($args->{'notbody'}) {
7635: $result .= "\n</html>";
7636: }
1.330 albertel 7637:
1.315 albertel 7638: if ($args->{'js_ready'}) {
1.317 albertel 7639: $result = &js_ready($result);
1.315 albertel 7640: }
1.335 albertel 7641:
1.320 albertel 7642: if ($args->{'html_encode'}) {
7643: $result = &html_encode($result);
7644: }
1.335 albertel 7645:
1.315 albertel 7646: return $result;
7647: }
7648:
1.1034 www 7649: sub wishlist_window {
7650: return(<<'ENDWISHLIST');
1.1046 raeburn 7651: <script type="text/javascript">
1.1034 www 7652: // <![CDATA[
7653: // <!-- BEGIN LON-CAPA Internal
7654: function set_wishlistlink(title, path) {
7655: if (!title) {
7656: title = document.title;
7657: title = title.replace(/^LON-CAPA /,'');
7658: }
1.1175 ! raeburn 7659: title = encodeURIComponent(title);
1.1034 www 7660: if (!path) {
7661: path = location.pathname;
7662: }
1.1175 ! raeburn 7663: path = encodeURIComponent(path);
1.1034 www 7664: Win = window.open('/adm/wishlist?mode=newLink&setTitle='+title+'&setPath='+path,
7665: 'wishlistNewLink','width=560,height=350,scrollbars=0');
7666: }
7667: // END LON-CAPA Internal -->
7668: // ]]>
7669: </script>
7670: ENDWISHLIST
7671: }
7672:
1.1030 www 7673: sub modal_window {
7674: return(<<'ENDMODAL');
1.1046 raeburn 7675: <script type="text/javascript">
1.1030 www 7676: // <![CDATA[
7677: // <!-- BEGIN LON-CAPA Internal
7678: var modalWindow = {
7679: parent:"body",
7680: windowId:null,
7681: content:null,
7682: width:null,
7683: height:null,
7684: close:function()
7685: {
7686: $(".LCmodal-window").remove();
7687: $(".LCmodal-overlay").remove();
7688: },
7689: open:function()
7690: {
7691: var modal = "";
7692: modal += "<div class=\"LCmodal-overlay\"></div>";
7693: 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;\">";
7694: modal += this.content;
7695: modal += "</div>";
7696:
7697: $(this.parent).append(modal);
7698:
7699: $(".LCmodal-window").append("<a class=\"LCclose-window\"></a>");
7700: $(".LCclose-window").click(function(){modalWindow.close();});
7701: $(".LCmodal-overlay").click(function(){modalWindow.close();});
7702: }
7703: };
1.1140 raeburn 7704: var openMyModal = function(source,width,height,scrolling,transparency,style)
1.1030 www 7705: {
7706: modalWindow.windowId = "myModal";
7707: modalWindow.width = width;
7708: modalWindow.height = height;
1.1140 raeburn 7709: modalWindow.content = "<iframe width='"+width+"' height='"+height+"' frameborder='0' scrolling='"+scrolling+"' allowtransparency='"+transparency+"' src='" + source + "' style='"+style+"'></iframe>";
1.1030 www 7710: modalWindow.open();
7711: };
7712: // END LON-CAPA Internal -->
7713: // ]]>
7714: </script>
7715: ENDMODAL
7716: }
7717:
7718: sub modal_link {
1.1140 raeburn 7719: my ($link,$linktext,$width,$height,$target,$scrolling,$title,$transparency,$style)=@_;
1.1030 www 7720: unless ($width) { $width=480; }
7721: unless ($height) { $height=400; }
1.1031 www 7722: unless ($scrolling) { $scrolling='yes'; }
1.1140 raeburn 7723: unless ($transparency) { $transparency='true'; }
7724:
1.1074 raeburn 7725: my $target_attr;
7726: if (defined($target)) {
7727: $target_attr = 'target="'.$target.'"';
7728: }
7729: return <<"ENDLINK";
1.1140 raeburn 7730: <a href="$link" $target_attr title="$title" onclick="javascript:openMyModal('$link',$width,$height,'$scrolling','$transparency','$style'); return false;">
1.1074 raeburn 7731: $linktext</a>
7732: ENDLINK
1.1030 www 7733: }
7734:
1.1032 www 7735: sub modal_adhoc_script {
7736: my ($funcname,$width,$height,$content)=@_;
7737: return (<<ENDADHOC);
1.1046 raeburn 7738: <script type="text/javascript">
1.1032 www 7739: // <![CDATA[
7740: var $funcname = function()
7741: {
7742: modalWindow.windowId = "myModal";
7743: modalWindow.width = $width;
7744: modalWindow.height = $height;
7745: modalWindow.content = '$content';
7746: modalWindow.open();
7747: };
7748: // ]]>
7749: </script>
7750: ENDADHOC
7751: }
7752:
1.1041 www 7753: sub modal_adhoc_inner {
7754: my ($funcname,$width,$height,$content)=@_;
7755: my $innerwidth=$width-20;
7756: $content=&js_ready(
1.1140 raeburn 7757: &start_page('Dialog',undef,{'only_body'=>1,'bgcolor'=>'#FFFFFF'}).
7758: &start_scrollbox($width.'px',$innerwidth.'px',$height.'px','myModal','#FFFFFF',undef,1).
7759: $content.
1.1041 www 7760: &end_scrollbox().
1.1140 raeburn 7761: &end_page()
1.1041 www 7762: );
7763: return &modal_adhoc_script($funcname,$width,$height,$content);
7764: }
7765:
7766: sub modal_adhoc_window {
7767: my ($funcname,$width,$height,$content,$linktext)=@_;
7768: return &modal_adhoc_inner($funcname,$width,$height,$content).
7769: "<a href=\"javascript:$funcname();void(0);\">".$linktext."</a>";
7770: }
7771:
7772: sub modal_adhoc_launch {
7773: my ($funcname,$width,$height,$content)=@_;
7774: return &modal_adhoc_inner($funcname,$width,$height,$content).(<<ENDLAUNCH);
7775: <script type="text/javascript">
7776: // <![CDATA[
7777: $funcname();
7778: // ]]>
7779: </script>
7780: ENDLAUNCH
7781: }
7782:
7783: sub modal_adhoc_close {
7784: return (<<ENDCLOSE);
7785: <script type="text/javascript">
7786: // <![CDATA[
7787: modalWindow.close();
7788: // ]]>
7789: </script>
7790: ENDCLOSE
7791: }
7792:
1.1038 www 7793: sub togglebox_script {
7794: return(<<ENDTOGGLE);
7795: <script type="text/javascript">
7796: // <![CDATA[
7797: function LCtoggleDisplay(id,hidetext,showtext) {
7798: link = document.getElementById(id + "link").childNodes[0];
7799: with (document.getElementById(id).style) {
7800: if (display == "none" ) {
7801: display = "inline";
7802: link.nodeValue = hidetext;
7803: } else {
7804: display = "none";
7805: link.nodeValue = showtext;
7806: }
7807: }
7808: }
7809: // ]]>
7810: </script>
7811: ENDTOGGLE
7812: }
7813:
1.1039 www 7814: sub start_togglebox {
7815: my ($id,$heading,$headerbg,$hidetext,$showtext)=@_;
7816: unless ($heading) { $heading=''; } else { $heading.=' '; }
7817: unless ($showtext) { $showtext=&mt('show'); }
7818: unless ($hidetext) { $hidetext=&mt('hide'); }
7819: unless ($headerbg) { $headerbg='#FFFFFF'; }
7820: return &start_data_table().
7821: &start_data_table_header_row().
7822: '<td bgcolor="'.$headerbg.'">'.$heading.
7823: '[<a id="'.$id.'link" href="javascript:LCtoggleDisplay(\''.$id.'\',\''.$hidetext.'\',\''.
7824: $showtext.'\')">'.$showtext.'</a>]</td>'.
7825: &end_data_table_header_row().
7826: '<tr id="'.$id.'" style="display:none""><td>';
7827: }
7828:
7829: sub end_togglebox {
7830: return '</td></tr>'.&end_data_table();
7831: }
7832:
1.1041 www 7833: sub LCprogressbar_script {
1.1045 www 7834: my ($id)=@_;
1.1041 www 7835: return(<<ENDPROGRESS);
7836: <script type="text/javascript">
7837: // <![CDATA[
1.1045 www 7838: \$('#progressbar$id').progressbar({
1.1041 www 7839: value: 0,
7840: change: function(event, ui) {
7841: var newVal = \$(this).progressbar('option', 'value');
7842: \$('.pblabel', this).text(LCprogressTxt);
7843: }
7844: });
7845: // ]]>
7846: </script>
7847: ENDPROGRESS
7848: }
7849:
7850: sub LCprogressbarUpdate_script {
7851: return(<<ENDPROGRESSUPDATE);
7852: <style type="text/css">
7853: .ui-progressbar { position:relative; }
7854: .pblabel { position: absolute; width: 100%; text-align: center; line-height: 1.9em; }
7855: </style>
7856: <script type="text/javascript">
7857: // <![CDATA[
1.1045 www 7858: var LCprogressTxt='---';
7859:
7860: function LCupdateProgress(percent,progresstext,id) {
1.1041 www 7861: LCprogressTxt=progresstext;
1.1045 www 7862: \$('#progressbar'+id).progressbar('value',percent);
1.1041 www 7863: }
7864: // ]]>
7865: </script>
7866: ENDPROGRESSUPDATE
7867: }
7868:
1.1042 www 7869: my $LClastpercent;
1.1045 www 7870: my $LCidcnt;
7871: my $LCcurrentid;
1.1042 www 7872:
1.1041 www 7873: sub LCprogressbar {
1.1042 www 7874: my ($r)=(@_);
7875: $LClastpercent=0;
1.1045 www 7876: $LCidcnt++;
7877: $LCcurrentid=$$.'_'.$LCidcnt;
1.1041 www 7878: my $starting=&mt('Starting');
7879: my $content=(<<ENDPROGBAR);
1.1045 www 7880: <div id="progressbar$LCcurrentid">
1.1041 www 7881: <span class="pblabel">$starting</span>
7882: </div>
7883: ENDPROGBAR
1.1045 www 7884: &r_print($r,$content.&LCprogressbar_script($LCcurrentid));
1.1041 www 7885: }
7886:
7887: sub LCprogressbarUpdate {
1.1042 www 7888: my ($r,$val,$text)=@_;
7889: unless ($val) {
7890: if ($LClastpercent) {
7891: $val=$LClastpercent;
7892: } else {
7893: $val=0;
7894: }
7895: }
1.1041 www 7896: if ($val<0) { $val=0; }
7897: if ($val>100) { $val=0; }
1.1042 www 7898: $LClastpercent=$val;
1.1041 www 7899: unless ($text) { $text=$val.'%'; }
7900: $text=&js_ready($text);
1.1044 www 7901: &r_print($r,<<ENDUPDATE);
1.1041 www 7902: <script type="text/javascript">
7903: // <![CDATA[
1.1045 www 7904: LCupdateProgress($val,'$text','$LCcurrentid');
1.1041 www 7905: // ]]>
7906: </script>
7907: ENDUPDATE
1.1035 www 7908: }
7909:
1.1042 www 7910: sub LCprogressbarClose {
7911: my ($r)=@_;
7912: $LClastpercent=0;
1.1044 www 7913: &r_print($r,<<ENDCLOSE);
1.1042 www 7914: <script type="text/javascript">
7915: // <![CDATA[
1.1045 www 7916: \$("#progressbar$LCcurrentid").hide('slow');
1.1042 www 7917: // ]]>
7918: </script>
7919: ENDCLOSE
1.1044 www 7920: }
7921:
7922: sub r_print {
7923: my ($r,$to_print)=@_;
7924: if ($r) {
7925: $r->print($to_print);
7926: $r->rflush();
7927: } else {
7928: print($to_print);
7929: }
1.1042 www 7930: }
7931:
1.320 albertel 7932: sub html_encode {
7933: my ($result) = @_;
7934:
1.322 albertel 7935: $result = &HTML::Entities::encode($result,'<>&"');
1.320 albertel 7936:
7937: return $result;
7938: }
1.1044 www 7939:
1.317 albertel 7940: sub js_ready {
7941: my ($result) = @_;
7942:
1.323 albertel 7943: $result =~ s/[\n\r]/ /xmsg;
7944: $result =~ s/\\/\\\\/xmsg;
7945: $result =~ s/'/\\'/xmsg;
1.372 albertel 7946: $result =~ s{</}{<\\/}xmsg;
1.317 albertel 7947:
7948: return $result;
7949: }
7950:
1.315 albertel 7951: sub validate_page {
7952: if ( exists($env{'internal.start_page'})
1.316 albertel 7953: && $env{'internal.start_page'} > 1) {
7954: &Apache::lonnet::logthis('start_page called multiple times '.
1.318 albertel 7955: $env{'internal.start_page'}.' '.
1.316 albertel 7956: $ENV{'request.filename'});
1.315 albertel 7957: }
7958: if ( exists($env{'internal.end_page'})
1.316 albertel 7959: && $env{'internal.end_page'} > 1) {
7960: &Apache::lonnet::logthis('end_page called multiple times '.
1.318 albertel 7961: $env{'internal.end_page'}.' '.
1.316 albertel 7962: $env{'request.filename'});
1.315 albertel 7963: }
7964: if ( exists($env{'internal.start_page'})
7965: && ! exists($env{'internal.end_page'})) {
1.316 albertel 7966: &Apache::lonnet::logthis('start_page called without end_page '.
7967: $env{'request.filename'});
1.315 albertel 7968: }
7969: if ( ! exists($env{'internal.start_page'})
7970: && exists($env{'internal.end_page'})) {
1.316 albertel 7971: &Apache::lonnet::logthis('end_page called without start_page'.
7972: $env{'request.filename'});
1.315 albertel 7973: }
1.306 albertel 7974: }
1.315 albertel 7975:
1.996 www 7976:
7977: sub start_scrollbox {
1.1140 raeburn 7978: my ($outerwidth,$width,$height,$id,$bgcolor,$cursor,$needjsready) = @_;
1.998 raeburn 7979: unless ($outerwidth) { $outerwidth='520px'; }
7980: unless ($width) { $width='500px'; }
7981: unless ($height) { $height='200px'; }
1.1075 raeburn 7982: my ($table_id,$div_id,$tdcol);
1.1018 raeburn 7983: if ($id ne '') {
1.1140 raeburn 7984: $table_id = ' id="table_'.$id.'"';
1.1137 raeburn 7985: $div_id = ' id="div_'.$id.'"';
1.1018 raeburn 7986: }
1.1075 raeburn 7987: if ($bgcolor ne '') {
7988: $tdcol = "background-color: $bgcolor;";
7989: }
1.1137 raeburn 7990: my $nicescroll_js;
7991: if ($env{'browser.mobile'}) {
1.1140 raeburn 7992: $nicescroll_js = &nicescroll_javascript('div_'.$id,$cursor,$needjsready);
7993: }
7994: return <<"END";
7995: $nicescroll_js
7996:
7997: <table style="width: $outerwidth; border: 1px solid none;"$table_id><tr><td style="width: $width;$tdcol">
7998: <div style="overflow:auto; width:$width; height:$height;"$div_id>
7999: END
8000: }
8001:
8002: sub end_scrollbox {
8003: return '</div></td></tr></table>';
8004: }
8005:
8006: sub nicescroll_javascript {
8007: my ($id,$cursor,$needjsready,$framecheck,$location) = @_;
8008: my %options;
8009: if (ref($cursor) eq 'HASH') {
8010: %options = %{$cursor};
8011: }
8012: unless ($options{'railalign'} =~ /^left|right$/) {
8013: $options{'railalign'} = 'left';
8014: }
8015: unless ($options{'cursorcolor'} =~ /^\#\w+$/) {
8016: my $function = &get_users_function();
8017: $options{'cursorcolor'} = &designparm($function.'.sidebg',$env{'request.role.domain'});
1.1138 raeburn 8018: unless ($options{'cursorcolor'} =~ /^\#\w+$/) {
1.1140 raeburn 8019: $options{'cursorcolor'} = '#00F';
1.1138 raeburn 8020: }
1.1140 raeburn 8021: }
8022: if ($options{'cursoropacity'} =~ /^[\d.]+$/) {
8023: unless ($options{'cursoropacity'} >= 0.0 && $options{'cursoropacity'} <=1.0) {
1.1138 raeburn 8024: $options{'cursoropacity'}='1.0';
8025: }
1.1140 raeburn 8026: } else {
8027: $options{'cursoropacity'}='1.0';
8028: }
8029: if ($options{'cursorfixedheight'} eq 'none') {
8030: delete($options{'cursorfixedheight'});
8031: } else {
8032: unless ($options{'cursorfixedheight'} =~ /^\d+$/) { $options{'cursorfixedheight'}='50'; }
8033: }
8034: unless ($options{'railoffset'} =~ /^{[\w\:\d\-,]+}$/) {
8035: delete($options{'railoffset'});
8036: }
8037: my @niceoptions;
8038: while (my($key,$value) = each(%options)) {
8039: if ($value =~ /^\{.+\}$/) {
8040: push(@niceoptions,$key.':'.$value);
1.1138 raeburn 8041: } else {
1.1140 raeburn 8042: push(@niceoptions,$key.':"'.$value.'"');
1.1138 raeburn 8043: }
1.1140 raeburn 8044: }
8045: my $nicescroll_js = '
1.1137 raeburn 8046: $(document).ready(
1.1140 raeburn 8047: function() {
8048: $("#'.$id.'").niceScroll({'.join(',',@niceoptions).'});
8049: }
1.1137 raeburn 8050: );
8051: ';
1.1140 raeburn 8052: if ($framecheck) {
8053: $nicescroll_js .= '
8054: function expand_div(caller) {
8055: if (top === self) {
8056: document.getElementById("'.$id.'").style.width = "auto";
8057: document.getElementById("'.$id.'").style.height = "auto";
8058: } else {
8059: try {
8060: if (parent.frames) {
8061: if (parent.frames.length > 1) {
8062: var framesrc = parent.frames[1].location.href;
8063: var currsrc = framesrc.replace(/\#.*$/,"");
8064: if ((caller == "search") || (currsrc == "'.$location.'")) {
8065: document.getElementById("'.$id.'").style.width = "auto";
8066: document.getElementById("'.$id.'").style.height = "auto";
8067: }
8068: }
8069: }
8070: } catch (e) {
8071: return;
8072: }
1.1137 raeburn 8073: }
1.1140 raeburn 8074: return;
1.996 www 8075: }
1.1140 raeburn 8076: ';
8077: }
8078: if ($needjsready) {
8079: $nicescroll_js = '
8080: <script type="text/javascript">'."\n".$nicescroll_js."\n</script>\n";
8081: } else {
8082: $nicescroll_js = &Apache::lonhtmlcommon::scripttag($nicescroll_js);
8083: }
8084: return $nicescroll_js;
1.996 www 8085: }
8086:
1.318 albertel 8087: sub simple_error_page {
1.1150 bisitz 8088: my ($r,$title,$msg,$args) = @_;
1.1151 raeburn 8089: if (ref($args) eq 'HASH') {
8090: if (!$args->{'no_auto_mt_msg'}) { $msg = &mt($msg); }
8091: } else {
8092: $msg = &mt($msg);
8093: }
1.1150 bisitz 8094:
1.318 albertel 8095: my $page =
8096: &Apache::loncommon::start_page($title).
1.1150 bisitz 8097: '<p class="LC_error">'.$msg.'</p>'.
1.318 albertel 8098: &Apache::loncommon::end_page();
8099: if (ref($r)) {
8100: $r->print($page);
1.327 albertel 8101: return;
1.318 albertel 8102: }
8103: return $page;
8104: }
1.347 albertel 8105:
8106: {
1.610 albertel 8107: my @row_count;
1.961 onken 8108:
8109: sub start_data_table_count {
8110: unshift(@row_count, 0);
8111: return;
8112: }
8113:
8114: sub end_data_table_count {
8115: shift(@row_count);
8116: return;
8117: }
8118:
1.347 albertel 8119: sub start_data_table {
1.1018 raeburn 8120: my ($add_class,$id) = @_;
1.422 albertel 8121: my $css_class = (join(' ','LC_data_table',$add_class));
1.1018 raeburn 8122: my $table_id;
8123: if (defined($id)) {
8124: $table_id = ' id="'.$id.'"';
8125: }
1.961 onken 8126: &start_data_table_count();
1.1018 raeburn 8127: return '<table class="'.$css_class.'"'.$table_id.'>'."\n";
1.347 albertel 8128: }
8129:
8130: sub end_data_table {
1.961 onken 8131: &end_data_table_count();
1.389 albertel 8132: return '</table>'."\n";;
1.347 albertel 8133: }
8134:
8135: sub start_data_table_row {
1.974 wenzelju 8136: my ($add_class, $id) = @_;
1.610 albertel 8137: $row_count[0]++;
8138: my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row';
1.900 bisitz 8139: $css_class = (join(' ',$css_class,$add_class)) unless ($add_class eq '');
1.974 wenzelju 8140: $id = (' id="'.$id.'"') unless ($id eq '');
8141: return '<tr class="'.$css_class.'"'.$id.'>'."\n";
1.347 albertel 8142: }
1.471 banghart 8143:
8144: sub continue_data_table_row {
1.974 wenzelju 8145: my ($add_class, $id) = @_;
1.610 albertel 8146: my $css_class = ($row_count[0] % 2)?'LC_odd_row':'LC_even_row';
1.974 wenzelju 8147: $css_class = (join(' ',$css_class,$add_class)) unless ($add_class eq '');
8148: $id = (' id="'.$id.'"') unless ($id eq '');
8149: return '<tr class="'.$css_class.'"'.$id.'>'."\n";
1.471 banghart 8150: }
1.347 albertel 8151:
8152: sub end_data_table_row {
1.389 albertel 8153: return '</tr>'."\n";;
1.347 albertel 8154: }
1.367 www 8155:
1.421 albertel 8156: sub start_data_table_empty_row {
1.707 bisitz 8157: # $row_count[0]++;
1.421 albertel 8158: return '<tr class="LC_empty_row" >'."\n";;
8159: }
8160:
8161: sub end_data_table_empty_row {
8162: return '</tr>'."\n";;
8163: }
8164:
1.367 www 8165: sub start_data_table_header_row {
1.389 albertel 8166: return '<tr class="LC_header_row">'."\n";;
1.367 www 8167: }
8168:
8169: sub end_data_table_header_row {
1.389 albertel 8170: return '</tr>'."\n";;
1.367 www 8171: }
1.890 droeschl 8172:
8173: sub data_table_caption {
8174: my $caption = shift;
8175: return "<caption class=\"LC_caption\">$caption</caption>";
8176: }
1.347 albertel 8177: }
8178:
1.548 albertel 8179: =pod
8180:
8181: =item * &inhibit_menu_check($arg)
8182:
8183: Checks for a inhibitmenu state and generates output to preserve it
8184:
8185: Inputs: $arg - can be any of
8186: - undef - in which case the return value is a string
8187: to add into arguments list of a uri
8188: - 'input' - in which case the return value is a HTML
8189: <form> <input> field of type hidden to
8190: preserve the value
8191: - a url - in which case the return value is the url with
8192: the neccesary cgi args added to preserve the
8193: inhibitmenu state
8194: - a ref to a url - no return value, but the string is
8195: updated to include the neccessary cgi
8196: args to preserve the inhibitmenu state
8197:
8198: =cut
8199:
8200: sub inhibit_menu_check {
8201: my ($arg) = @_;
8202: &get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['inhibitmenu']);
8203: if ($arg eq 'input') {
8204: if ($env{'form.inhibitmenu'}) {
8205: return '<input type="hidden" name="inhibitmenu" value="'.$env{'form.inhibitmenu'}.'" />';
8206: } else {
8207: return
8208: }
8209: }
8210: if ($env{'form.inhibitmenu'}) {
8211: if (ref($arg)) {
8212: $$arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
8213: } elsif ($arg eq '') {
8214: $arg .= 'inhibitmenu='.$env{'form.inhibitmenu'};
8215: } else {
8216: $arg .= '?inhibitmenu='.$env{'form.inhibitmenu'};
8217: }
8218: }
8219: if (!ref($arg)) {
8220: return $arg;
8221: }
8222: }
8223:
1.251 albertel 8224: ###############################################
1.182 matthew 8225:
8226: =pod
8227:
1.549 albertel 8228: =back
8229:
8230: =head1 User Information Routines
8231:
8232: =over 4
8233:
1.405 albertel 8234: =item * &get_users_function()
1.182 matthew 8235:
8236: Used by &bodytag to determine the current users primary role.
8237: Returns either 'student','coordinator','admin', or 'author'.
8238:
8239: =cut
8240:
8241: ###############################################
8242: sub get_users_function {
1.815 tempelho 8243: my $function = 'norole';
1.818 tempelho 8244: if ($env{'request.role'}=~/^(st)/) {
8245: $function='student';
8246: }
1.907 raeburn 8247: if ($env{'request.role'}=~/^(cc|co|in|ta|ep)/) {
1.182 matthew 8248: $function='coordinator';
8249: }
1.258 albertel 8250: if ($env{'request.role'}=~/^(su|dc|ad|li)/) {
1.182 matthew 8251: $function='admin';
8252: }
1.826 bisitz 8253: if (($env{'request.role'}=~/^(au|ca|aa)/) ||
1.1025 raeburn 8254: ($ENV{'REQUEST_URI'}=~ m{/^(/priv)})) {
1.182 matthew 8255: $function='author';
8256: }
8257: return $function;
1.54 www 8258: }
1.99 www 8259:
8260: ###############################################
8261:
1.233 raeburn 8262: =pod
8263:
1.821 raeburn 8264: =item * &show_course()
8265:
8266: Used by lonmenu.pm and lonroles.pm to determine whether to use the word
8267: 'Courses' or 'Roles' in inline navigation and on screen displaying user's roles.
8268:
8269: Inputs:
8270: None
8271:
8272: Outputs:
8273: Scalar: 1 if 'Course' to be used, 0 otherwise.
8274:
8275: =cut
8276:
8277: ###############################################
8278: sub show_course {
8279: my $course = !$env{'user.adv'};
8280: if (!$env{'user.adv'}) {
8281: foreach my $env (keys(%env)) {
8282: next if ($env !~ m/^user\.priv\./);
8283: if ($env !~ m/^user\.priv\.(?:st|cm)/) {
8284: $course = 0;
8285: last;
8286: }
8287: }
8288: }
8289: return $course;
8290: }
8291:
8292: ###############################################
8293:
8294: =pod
8295:
1.542 raeburn 8296: =item * &check_user_status()
1.274 raeburn 8297:
8298: Determines current status of supplied role for a
8299: specific user. Roles can be active, previous or future.
8300:
8301: Inputs:
8302: user's domain, user's username, course's domain,
1.375 raeburn 8303: course's number, optional section ID.
1.274 raeburn 8304:
8305: Outputs:
8306: role status: active, previous or future.
8307:
8308: =cut
8309:
8310: sub check_user_status {
1.412 raeburn 8311: my ($udom,$uname,$cdom,$crs,$role,$sec) = @_;
1.1073 raeburn 8312: my %userinfo = &Apache::lonnet::dump('roles',$udom,$uname);
1.274 raeburn 8313: my @uroles = keys %userinfo;
8314: my $srchstr;
8315: my $active_chk = 'none';
1.412 raeburn 8316: my $now = time;
1.274 raeburn 8317: if (@uroles > 0) {
1.908 raeburn 8318: if (($role eq 'cc') || ($role eq 'co') || ($sec eq '') || (!defined($sec))) {
1.274 raeburn 8319: $srchstr = '/'.$cdom.'/'.$crs.'_'.$role;
8320: } else {
1.412 raeburn 8321: $srchstr = '/'.$cdom.'/'.$crs.'/'.$sec.'_'.$role;
8322: }
8323: if (grep/^\Q$srchstr\E$/,@uroles) {
1.274 raeburn 8324: my $role_end = 0;
8325: my $role_start = 0;
8326: $active_chk = 'active';
1.412 raeburn 8327: if ($userinfo{$srchstr} =~ m/^\Q$role\E_(\d+)/) {
8328: $role_end = $1;
8329: if ($userinfo{$srchstr} =~ m/^\Q$role\E_\Q$role_end\E_(\d+)$/) {
8330: $role_start = $1;
1.274 raeburn 8331: }
8332: }
8333: if ($role_start > 0) {
1.412 raeburn 8334: if ($now < $role_start) {
1.274 raeburn 8335: $active_chk = 'future';
8336: }
8337: }
8338: if ($role_end > 0) {
1.412 raeburn 8339: if ($now > $role_end) {
1.274 raeburn 8340: $active_chk = 'previous';
8341: }
8342: }
8343: }
8344: }
8345: return $active_chk;
8346: }
8347:
8348: ###############################################
8349:
8350: =pod
8351:
1.405 albertel 8352: =item * &get_sections()
1.233 raeburn 8353:
8354: Determines all the sections for a course including
8355: sections with students and sections containing other roles.
1.419 raeburn 8356: Incoming parameters:
8357:
8358: 1. domain
8359: 2. course number
8360: 3. reference to array containing roles for which sections should
8361: be gathered (optional).
8362: 4. reference to array containing status types for which sections
8363: should be gathered (optional).
8364:
8365: If the third argument is undefined, sections are gathered for any role.
8366: If the fourth argument is undefined, sections are gathered for any status.
8367: Permissible values are 'active' or 'future' or 'previous'.
1.233 raeburn 8368:
1.374 raeburn 8369: Returns section hash (keys are section IDs, values are
8370: number of users in each section), subject to the
1.419 raeburn 8371: optional roles filter, optional status filter
1.233 raeburn 8372:
8373: =cut
8374:
8375: ###############################################
8376: sub get_sections {
1.419 raeburn 8377: my ($cdom,$cnum,$possible_roles,$possible_status) = @_;
1.366 albertel 8378: if (!defined($cdom) || !defined($cnum)) {
8379: my $cid = $env{'request.course.id'};
8380:
8381: return if (!defined($cid));
8382:
8383: $cdom = $env{'course.'.$cid.'.domain'};
8384: $cnum = $env{'course.'.$cid.'.num'};
8385: }
8386:
8387: my %sectioncount;
1.419 raeburn 8388: my $now = time;
1.240 albertel 8389:
1.1118 raeburn 8390: my $check_students = 1;
8391: my $only_students = 0;
8392: if (ref($possible_roles) eq 'ARRAY') {
8393: if (grep(/^st$/,@{$possible_roles})) {
8394: if (@{$possible_roles} == 1) {
8395: $only_students = 1;
8396: }
8397: } else {
8398: $check_students = 0;
8399: }
8400: }
8401:
8402: if ($check_students) {
1.276 albertel 8403: my ($classlist) = &Apache::loncoursedata::get_classlist($cdom,$cnum);
1.240 albertel 8404: my $sec_index = &Apache::loncoursedata::CL_SECTION();
8405: my $status_index = &Apache::loncoursedata::CL_STATUS();
1.419 raeburn 8406: my $start_index = &Apache::loncoursedata::CL_START();
8407: my $end_index = &Apache::loncoursedata::CL_END();
8408: my $status;
1.366 albertel 8409: while (my ($student,$data) = each(%$classlist)) {
1.419 raeburn 8410: my ($section,$stu_status,$start,$end) = ($data->[$sec_index],
8411: $data->[$status_index],
8412: $data->[$start_index],
8413: $data->[$end_index]);
8414: if ($stu_status eq 'Active') {
8415: $status = 'active';
8416: } elsif ($end < $now) {
8417: $status = 'previous';
8418: } elsif ($start > $now) {
8419: $status = 'future';
8420: }
8421: if ($section ne '-1' && $section !~ /^\s*$/) {
8422: if ((!defined($possible_status)) || (($status ne '') &&
8423: (grep/^\Q$status\E$/,@{$possible_status}))) {
8424: $sectioncount{$section}++;
8425: }
1.240 albertel 8426: }
8427: }
8428: }
1.1118 raeburn 8429: if ($only_students) {
8430: return %sectioncount;
8431: }
1.240 albertel 8432: my %courseroles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
8433: foreach my $user (sort(keys(%courseroles))) {
8434: if ($user !~ /^(\w{2})/) { next; }
8435: my ($role) = ($user =~ /^(\w{2})/);
8436: if ($possible_roles && !(grep(/^$role$/,@$possible_roles))) { next; }
1.419 raeburn 8437: my ($section,$status);
1.240 albertel 8438: if ($role eq 'cr' &&
8439: $user =~ m-^$role/[^/]*/[^/]*/[^/]*:[^:]*:[^:]*:(\w+)-) {
8440: $section=$1;
8441: }
8442: if ($user =~ /^$role:[^:]*:[^:]*:(\w+)/) { $section=$1; }
8443: if (!defined($section) || $section eq '-1') { next; }
1.419 raeburn 8444: my ($end,$start) = ($courseroles{$user} =~ /^([^:]*):([^:]*)$/);
8445: if ($end == -1 && $start == -1) {
8446: next; #deleted role
8447: }
8448: if (!defined($possible_status)) {
8449: $sectioncount{$section}++;
8450: } else {
8451: if ((!$end || $end >= $now) && (!$start || $start <= $now)) {
8452: $status = 'active';
8453: } elsif ($end < $now) {
8454: $status = 'future';
8455: } elsif ($start > $now) {
8456: $status = 'previous';
8457: }
8458: if (($status ne '') && (grep/^\Q$status\E$/,@{$possible_status})) {
8459: $sectioncount{$section}++;
8460: }
8461: }
1.233 raeburn 8462: }
1.366 albertel 8463: return %sectioncount;
1.233 raeburn 8464: }
8465:
1.274 raeburn 8466: ###############################################
1.294 raeburn 8467:
8468: =pod
1.405 albertel 8469:
8470: =item * &get_course_users()
8471:
1.275 raeburn 8472: Retrieves usernames:domains for users in the specified course
8473: with specific role(s), and access status.
8474:
8475: Incoming parameters:
1.277 albertel 8476: 1. course domain
8477: 2. course number
8478: 3. access status: users must have - either active,
1.275 raeburn 8479: previous, future, or all.
1.277 albertel 8480: 4. reference to array of permissible roles
1.288 raeburn 8481: 5. reference to array of section restrictions (optional)
8482: 6. reference to results object (hash of hashes).
8483: 7. reference to optional userdata hash
1.609 raeburn 8484: 8. reference to optional statushash
1.630 raeburn 8485: 9. flag if privileged users (except those set to unhide in
8486: course settings) should be excluded
1.609 raeburn 8487: Keys of top level results hash are roles.
1.275 raeburn 8488: Keys of inner hashes are username:domain, with
8489: values set to access type.
1.288 raeburn 8490: Optional userdata hash returns an array with arguments in the
8491: same order as loncoursedata::get_classlist() for student data.
8492:
1.609 raeburn 8493: Optional statushash returns
8494:
1.288 raeburn 8495: Entries for end, start, section and status are blank because
8496: of the possibility of multiple values for non-student roles.
8497:
1.275 raeburn 8498: =cut
1.405 albertel 8499:
1.275 raeburn 8500: ###############################################
1.405 albertel 8501:
1.275 raeburn 8502: sub get_course_users {
1.630 raeburn 8503: my ($cdom,$cnum,$types,$roles,$sections,$users,$userdata,$statushash,$hidepriv) = @_;
1.288 raeburn 8504: my %idx = ();
1.419 raeburn 8505: my %seclists;
1.288 raeburn 8506:
8507: $idx{udom} = &Apache::loncoursedata::CL_SDOM();
8508: $idx{uname} = &Apache::loncoursedata::CL_SNAME();
8509: $idx{end} = &Apache::loncoursedata::CL_END();
8510: $idx{start} = &Apache::loncoursedata::CL_START();
8511: $idx{id} = &Apache::loncoursedata::CL_ID();
8512: $idx{section} = &Apache::loncoursedata::CL_SECTION();
8513: $idx{fullname} = &Apache::loncoursedata::CL_FULLNAME();
8514: $idx{status} = &Apache::loncoursedata::CL_STATUS();
8515:
1.290 albertel 8516: if (grep(/^st$/,@{$roles})) {
1.276 albertel 8517: my ($classlist,$keylist)=&Apache::loncoursedata::get_classlist($cdom,$cnum);
1.278 raeburn 8518: my $now = time;
1.277 albertel 8519: foreach my $student (keys(%{$classlist})) {
1.288 raeburn 8520: my $match = 0;
1.412 raeburn 8521: my $secmatch = 0;
1.419 raeburn 8522: my $section = $$classlist{$student}[$idx{section}];
1.609 raeburn 8523: my $status = $$classlist{$student}[$idx{status}];
1.419 raeburn 8524: if ($section eq '') {
8525: $section = 'none';
8526: }
1.291 albertel 8527: if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420 albertel 8528: if (grep(/^all$/,@{$sections})) {
1.412 raeburn 8529: $secmatch = 1;
8530: } elsif ($$classlist{$student}[$idx{section}] eq '') {
1.420 albertel 8531: if (grep(/^none$/,@{$sections})) {
1.412 raeburn 8532: $secmatch = 1;
8533: }
8534: } else {
1.419 raeburn 8535: if (grep(/^\Q$section\E$/,@{$sections})) {
1.412 raeburn 8536: $secmatch = 1;
8537: }
1.290 albertel 8538: }
1.412 raeburn 8539: if (!$secmatch) {
8540: next;
8541: }
1.419 raeburn 8542: }
1.275 raeburn 8543: if (defined($$types{'active'})) {
1.288 raeburn 8544: if ($$classlist{$student}[$idx{status}] eq 'Active') {
1.275 raeburn 8545: push(@{$$users{st}{$student}},'active');
1.288 raeburn 8546: $match = 1;
1.275 raeburn 8547: }
8548: }
8549: if (defined($$types{'previous'})) {
1.609 raeburn 8550: if ($$classlist{$student}[$idx{status}] eq 'Expired') {
1.275 raeburn 8551: push(@{$$users{st}{$student}},'previous');
1.288 raeburn 8552: $match = 1;
1.275 raeburn 8553: }
8554: }
8555: if (defined($$types{'future'})) {
1.609 raeburn 8556: if ($$classlist{$student}[$idx{status}] eq 'Future') {
1.275 raeburn 8557: push(@{$$users{st}{$student}},'future');
1.288 raeburn 8558: $match = 1;
1.275 raeburn 8559: }
8560: }
1.609 raeburn 8561: if ($match) {
8562: push(@{$seclists{$student}},$section);
8563: if (ref($userdata) eq 'HASH') {
8564: $$userdata{$student} = $$classlist{$student};
8565: }
8566: if (ref($statushash) eq 'HASH') {
8567: $statushash->{$student}{'st'}{$section} = $status;
8568: }
1.288 raeburn 8569: }
1.275 raeburn 8570: }
8571: }
1.412 raeburn 8572: if ((@{$roles} > 1) || ((@{$roles} == 1) && ($$roles[0] ne "st"))) {
1.439 raeburn 8573: my %coursepersonnel = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
8574: my $now = time;
1.609 raeburn 8575: my %displaystatus = ( previous => 'Expired',
8576: active => 'Active',
8577: future => 'Future',
8578: );
1.1121 raeburn 8579: my (%nothide,@possdoms);
1.630 raeburn 8580: if ($hidepriv) {
8581: my %coursehash=&Apache::lonnet::coursedescription($cdom.'_'.$cnum);
8582: foreach my $user (split(/\s*\,\s*/,$coursehash{'nothideprivileged'})) {
8583: if ($user !~ /:/) {
8584: $nothide{join(':',split(/[\@]/,$user))}=1;
8585: } else {
8586: $nothide{$user} = 1;
8587: }
8588: }
1.1121 raeburn 8589: my @possdoms = ($cdom);
8590: if ($coursehash{'checkforpriv'}) {
8591: push(@possdoms,split(/,/,$coursehash{'checkforpriv'}));
8592: }
1.630 raeburn 8593: }
1.439 raeburn 8594: foreach my $person (sort(keys(%coursepersonnel))) {
1.288 raeburn 8595: my $match = 0;
1.412 raeburn 8596: my $secmatch = 0;
1.439 raeburn 8597: my $status;
1.412 raeburn 8598: my ($role,$user,$usec) = ($person =~ /^([^:]*):([^:]+:[^:]+):([^:]*)/);
1.275 raeburn 8599: $user =~ s/:$//;
1.439 raeburn 8600: my ($end,$start) = split(/:/,$coursepersonnel{$person});
8601: if ($end == -1 || $start == -1) {
8602: next;
8603: }
8604: if (($role) && ((grep(/^\Q$role\E$/,@{$roles})) ||
8605: (grep(/^cr$/,@{$roles}) && $role =~ /^cr\//))) {
1.412 raeburn 8606: my ($uname,$udom) = split(/:/,$user);
8607: if ((ref($sections) eq 'ARRAY') && (@{$sections} > 0)) {
1.420 albertel 8608: if (grep(/^all$/,@{$sections})) {
1.412 raeburn 8609: $secmatch = 1;
8610: } elsif ($usec eq '') {
1.420 albertel 8611: if (grep(/^none$/,@{$sections})) {
1.412 raeburn 8612: $secmatch = 1;
8613: }
8614: } else {
8615: if (grep(/^\Q$usec\E$/,@{$sections})) {
8616: $secmatch = 1;
8617: }
8618: }
8619: if (!$secmatch) {
8620: next;
8621: }
1.288 raeburn 8622: }
1.419 raeburn 8623: if ($usec eq '') {
8624: $usec = 'none';
8625: }
1.275 raeburn 8626: if ($uname ne '' && $udom ne '') {
1.630 raeburn 8627: if ($hidepriv) {
1.1121 raeburn 8628: if ((&Apache::lonnet::privileged($uname,$udom,\@possdoms)) &&
1.630 raeburn 8629: (!$nothide{$uname.':'.$udom})) {
8630: next;
8631: }
8632: }
1.503 raeburn 8633: if ($end > 0 && $end < $now) {
1.439 raeburn 8634: $status = 'previous';
8635: } elsif ($start > $now) {
8636: $status = 'future';
8637: } else {
8638: $status = 'active';
8639: }
1.277 albertel 8640: foreach my $type (keys(%{$types})) {
1.275 raeburn 8641: if ($status eq $type) {
1.420 albertel 8642: if (!grep(/^\Q$type\E$/,@{$$users{$role}{$user}})) {
1.419 raeburn 8643: push(@{$$users{$role}{$user}},$type);
8644: }
1.288 raeburn 8645: $match = 1;
8646: }
8647: }
1.419 raeburn 8648: if (($match) && (ref($userdata) eq 'HASH')) {
8649: if (!exists($$userdata{$uname.':'.$udom})) {
8650: &get_user_info($udom,$uname,\%idx,$userdata);
8651: }
1.420 albertel 8652: if (!grep(/^\Q$usec\E$/,@{$seclists{$uname.':'.$udom}})) {
1.419 raeburn 8653: push(@{$seclists{$uname.':'.$udom}},$usec);
8654: }
1.609 raeburn 8655: if (ref($statushash) eq 'HASH') {
8656: $statushash->{$uname.':'.$udom}{$role}{$usec} = $displaystatus{$status};
8657: }
1.275 raeburn 8658: }
8659: }
8660: }
8661: }
1.290 albertel 8662: if (grep(/^ow$/,@{$roles})) {
1.279 raeburn 8663: if ((defined($cdom)) && (defined($cnum))) {
8664: my %csettings = &Apache::lonnet::get('environment',['internal.courseowner'],$cdom,$cnum);
8665: if ( defined($csettings{'internal.courseowner'}) ) {
8666: my $owner = $csettings{'internal.courseowner'};
1.609 raeburn 8667: next if ($owner eq '');
8668: my ($ownername,$ownerdom);
8669: if ($owner =~ /^([^:]+):([^:]+)$/) {
8670: $ownername = $1;
8671: $ownerdom = $2;
8672: } else {
8673: $ownername = $owner;
8674: $ownerdom = $cdom;
8675: $owner = $ownername.':'.$ownerdom;
1.439 raeburn 8676: }
8677: @{$$users{'ow'}{$owner}} = 'any';
1.290 albertel 8678: if (defined($userdata) &&
1.609 raeburn 8679: !exists($$userdata{$owner})) {
8680: &get_user_info($ownerdom,$ownername,\%idx,$userdata);
8681: if (!grep(/^none$/,@{$seclists{$owner}})) {
8682: push(@{$seclists{$owner}},'none');
8683: }
8684: if (ref($statushash) eq 'HASH') {
8685: $statushash->{$owner}{'ow'}{'none'} = 'Any';
1.419 raeburn 8686: }
1.290 albertel 8687: }
1.279 raeburn 8688: }
8689: }
8690: }
1.419 raeburn 8691: foreach my $user (keys(%seclists)) {
8692: @{$seclists{$user}} = (sort {$a <=> $b} @{$seclists{$user}});
8693: $$userdata{$user}[$idx{section}] = join(',',@{$seclists{$user}});
8694: }
1.275 raeburn 8695: }
8696: return;
8697: }
8698:
1.288 raeburn 8699: sub get_user_info {
8700: my ($udom,$uname,$idx,$userdata) = @_;
1.289 albertel 8701: $$userdata{$uname.':'.$udom}[$$idx{fullname}] =
8702: &plainname($uname,$udom,'lastname');
1.291 albertel 8703: $$userdata{$uname.':'.$udom}[$$idx{uname}] = $uname;
1.297 raeburn 8704: $$userdata{$uname.':'.$udom}[$$idx{udom}] = $udom;
1.609 raeburn 8705: my %idhash = &Apache::lonnet::idrget($udom,($uname));
8706: $$userdata{$uname.':'.$udom}[$$idx{id}] = $idhash{$uname};
1.288 raeburn 8707: return;
8708: }
1.275 raeburn 8709:
1.472 raeburn 8710: ###############################################
8711:
8712: =pod
8713:
8714: =item * &get_user_quota()
8715:
1.1134 raeburn 8716: Retrieves quota assigned for storage of user files.
8717: Default is to report quota for portfolio files.
1.472 raeburn 8718:
8719: Incoming parameters:
8720: 1. user's username
8721: 2. user's domain
1.1134 raeburn 8722: 3. quota name - portfolio, author, or course
1.1136 raeburn 8723: (if no quota name provided, defaults to portfolio).
1.1165 raeburn 8724: 4. crstype - official, unofficial, textbook or community, if quota name is
1.1136 raeburn 8725: course
1.472 raeburn 8726:
8727: Returns:
1.1163 raeburn 8728: 1. Disk quota (in MB) assigned to student.
1.536 raeburn 8729: 2. (Optional) Type of setting: custom or default
8730: (individually assigned or default for user's
8731: institutional status).
8732: 3. (Optional) - User's institutional status (e.g., faculty, staff
8733: or student - types as defined in localenroll::inst_usertypes
8734: for user's domain, which determines default quota for user.
8735: 4. (Optional) - Default quota which would apply to the user.
1.472 raeburn 8736:
8737: If a value has been stored in the user's environment,
1.536 raeburn 8738: it will return that, otherwise it returns the maximal default
1.1134 raeburn 8739: defined for the user's institutional status(es) in the domain.
1.472 raeburn 8740:
8741: =cut
8742:
8743: ###############################################
8744:
8745:
8746: sub get_user_quota {
1.1136 raeburn 8747: my ($uname,$udom,$quotaname,$crstype) = @_;
1.536 raeburn 8748: my ($quota,$quotatype,$settingstatus,$defquota);
1.472 raeburn 8749: if (!defined($udom)) {
8750: $udom = $env{'user.domain'};
8751: }
8752: if (!defined($uname)) {
8753: $uname = $env{'user.name'};
8754: }
8755: if (($udom eq '' || $uname eq '') ||
8756: ($udom eq 'public') && ($uname eq 'public')) {
8757: $quota = 0;
1.536 raeburn 8758: $quotatype = 'default';
8759: $defquota = 0;
1.472 raeburn 8760: } else {
1.536 raeburn 8761: my $inststatus;
1.1134 raeburn 8762: if ($quotaname eq 'course') {
8763: if (($env{'course.'.$udom.'_'.$uname.'.num'} eq $uname) &&
8764: ($env{'course.'.$udom.'_'.$uname.'.domain'} eq $udom)) {
8765: $quota = $env{'course.'.$udom.'_'.$uname.'.internal.uploadquota'};
8766: } else {
8767: my %cenv = &Apache::lonnet::coursedescription("$udom/$uname");
8768: $quota = $cenv{'internal.uploadquota'};
8769: }
1.536 raeburn 8770: } else {
1.1134 raeburn 8771: if ($udom eq $env{'user.domain'} && $uname eq $env{'user.name'}) {
8772: if ($quotaname eq 'author') {
8773: $quota = $env{'environment.authorquota'};
8774: } else {
8775: $quota = $env{'environment.portfolioquota'};
8776: }
8777: $inststatus = $env{'environment.inststatus'};
8778: } else {
8779: my %userenv =
8780: &Apache::lonnet::get('environment',['portfolioquota',
8781: 'authorquota','inststatus'],$udom,$uname);
8782: my ($tmp) = keys(%userenv);
8783: if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
8784: if ($quotaname eq 'author') {
8785: $quota = $userenv{'authorquota'};
8786: } else {
8787: $quota = $userenv{'portfolioquota'};
8788: }
8789: $inststatus = $userenv{'inststatus'};
8790: } else {
8791: undef(%userenv);
8792: }
8793: }
8794: }
8795: if ($quota eq '' || wantarray) {
8796: if ($quotaname eq 'course') {
8797: my %domdefs = &Apache::lonnet::get_domain_defaults($udom);
1.1165 raeburn 8798: if (($crstype eq 'official') || ($crstype eq 'unofficial') ||
8799: ($crstype eq 'community') || ($crstype eq 'textbook')) {
1.1136 raeburn 8800: $defquota = $domdefs{$crstype.'quota'};
8801: }
8802: if ($defquota eq '') {
8803: $defquota = 500;
8804: }
1.1134 raeburn 8805: } else {
8806: ($defquota,$settingstatus) = &default_quota($udom,$inststatus,$quotaname);
8807: }
8808: if ($quota eq '') {
8809: $quota = $defquota;
8810: $quotatype = 'default';
8811: } else {
8812: $quotatype = 'custom';
8813: }
1.472 raeburn 8814: }
8815: }
1.536 raeburn 8816: if (wantarray) {
8817: return ($quota,$quotatype,$settingstatus,$defquota);
8818: } else {
8819: return $quota;
8820: }
1.472 raeburn 8821: }
8822:
8823: ###############################################
8824:
8825: =pod
8826:
8827: =item * &default_quota()
8828:
1.536 raeburn 8829: Retrieves default quota assigned for storage of user portfolio files,
8830: given an (optional) user's institutional status.
1.472 raeburn 8831:
8832: Incoming parameters:
1.1142 raeburn 8833:
1.472 raeburn 8834: 1. domain
1.536 raeburn 8835: 2. (Optional) institutional status(es). This is a : separated list of
8836: status types (e.g., faculty, staff, student etc.)
8837: which apply to the user for whom the default is being retrieved.
8838: If the institutional status string in undefined, the domain
1.1134 raeburn 8839: default quota will be returned.
8840: 3. quota name - portfolio, author, or course
8841: (if no quota name provided, defaults to portfolio).
1.472 raeburn 8842:
8843: Returns:
1.1142 raeburn 8844:
1.1163 raeburn 8845: 1. Default disk quota (in MB) for user portfolios in the domain.
1.536 raeburn 8846: 2. (Optional) institutional type which determined the value of the
8847: default quota.
1.472 raeburn 8848:
8849: If a value has been stored in the domain's configuration db,
8850: it will return that, otherwise it returns 20 (for backwards
8851: compatibility with domains which have not set up a configuration
1.1163 raeburn 8852: db file; the original statically defined portfolio quota was 20 MB).
1.472 raeburn 8853:
1.536 raeburn 8854: If the user's status includes multiple types (e.g., staff and student),
8855: the largest default quota which applies to the user determines the
8856: default quota returned.
8857:
1.472 raeburn 8858: =cut
8859:
8860: ###############################################
8861:
8862:
8863: sub default_quota {
1.1134 raeburn 8864: my ($udom,$inststatus,$quotaname) = @_;
1.536 raeburn 8865: my ($defquota,$settingstatus);
8866: my %quotahash = &Apache::lonnet::get_dom('configuration',
1.622 raeburn 8867: ['quotas'],$udom);
1.1134 raeburn 8868: my $key = 'defaultquota';
8869: if ($quotaname eq 'author') {
8870: $key = 'authorquota';
8871: }
1.622 raeburn 8872: if (ref($quotahash{'quotas'}) eq 'HASH') {
1.536 raeburn 8873: if ($inststatus ne '') {
1.765 raeburn 8874: my @statuses = map { &unescape($_); } split(/:/,$inststatus);
1.536 raeburn 8875: foreach my $item (@statuses) {
1.1134 raeburn 8876: if (ref($quotahash{'quotas'}{$key}) eq 'HASH') {
8877: if ($quotahash{'quotas'}{$key}{$item} ne '') {
1.711 raeburn 8878: if ($defquota eq '') {
1.1134 raeburn 8879: $defquota = $quotahash{'quotas'}{$key}{$item};
1.711 raeburn 8880: $settingstatus = $item;
1.1134 raeburn 8881: } elsif ($quotahash{'quotas'}{$key}{$item} > $defquota) {
8882: $defquota = $quotahash{'quotas'}{$key}{$item};
1.711 raeburn 8883: $settingstatus = $item;
8884: }
8885: }
1.1134 raeburn 8886: } elsif ($key eq 'defaultquota') {
1.711 raeburn 8887: if ($quotahash{'quotas'}{$item} ne '') {
8888: if ($defquota eq '') {
8889: $defquota = $quotahash{'quotas'}{$item};
8890: $settingstatus = $item;
8891: } elsif ($quotahash{'quotas'}{$item} > $defquota) {
8892: $defquota = $quotahash{'quotas'}{$item};
8893: $settingstatus = $item;
8894: }
1.536 raeburn 8895: }
8896: }
8897: }
8898: }
8899: if ($defquota eq '') {
1.1134 raeburn 8900: if (ref($quotahash{'quotas'}{$key}) eq 'HASH') {
8901: $defquota = $quotahash{'quotas'}{$key}{'default'};
8902: } elsif ($key eq 'defaultquota') {
1.711 raeburn 8903: $defquota = $quotahash{'quotas'}{'default'};
8904: }
1.536 raeburn 8905: $settingstatus = 'default';
1.1139 raeburn 8906: if ($defquota eq '') {
8907: if ($quotaname eq 'author') {
8908: $defquota = 500;
8909: }
8910: }
1.536 raeburn 8911: }
8912: } else {
8913: $settingstatus = 'default';
1.1134 raeburn 8914: if ($quotaname eq 'author') {
8915: $defquota = 500;
8916: } else {
8917: $defquota = 20;
8918: }
1.536 raeburn 8919: }
8920: if (wantarray) {
8921: return ($defquota,$settingstatus);
1.472 raeburn 8922: } else {
1.536 raeburn 8923: return $defquota;
1.472 raeburn 8924: }
8925: }
8926:
1.1135 raeburn 8927: ###############################################
8928:
8929: =pod
8930:
1.1136 raeburn 8931: =item * &excess_filesize_warning()
1.1135 raeburn 8932:
8933: Returns warning message if upload of file to authoring space, or copying
1.1136 raeburn 8934: of existing file within authoring space will cause quota for the authoring
1.1146 raeburn 8935: space to be exceeded.
1.1136 raeburn 8936:
8937: Same, if upload of a file directly to a course/community via Course Editor
1.1137 raeburn 8938: will cause quota for uploaded content for the course to be exceeded.
1.1135 raeburn 8939:
1.1165 raeburn 8940: Inputs: 7
1.1136 raeburn 8941: 1. username or coursenum
1.1135 raeburn 8942: 2. domain
1.1136 raeburn 8943: 3. context ('author' or 'course')
1.1135 raeburn 8944: 4. filename of file for which action is being requested
8945: 5. filesize (kB) of file
8946: 6. action being taken: copy or upload.
1.1165 raeburn 8947: 7. quotatype (in course context -- official, unofficial, community or textbook).
1.1135 raeburn 8948:
8949: Returns: 1 scalar: HTML to display containing warning if quota would be exceeded,
1.1142 raeburn 8950: otherwise return null.
8951:
8952: =back
1.1135 raeburn 8953:
8954: =cut
8955:
1.1136 raeburn 8956: sub excess_filesize_warning {
1.1165 raeburn 8957: my ($uname,$udom,$context,$filename,$filesize,$action,$quotatype) = @_;
1.1136 raeburn 8958: my $current_disk_usage = 0;
1.1165 raeburn 8959: my $disk_quota = &get_user_quota($uname,$udom,$context,$quotatype); #expressed in MB
1.1136 raeburn 8960: if ($context eq 'author') {
8961: my $authorspace = $Apache::lonnet::perlvar{'lonDocRoot'}."/priv/$udom/$uname";
8962: $current_disk_usage = &Apache::lonnet::diskusage($udom,$uname,$authorspace);
8963: } else {
8964: foreach my $subdir ('docs','supplemental') {
8965: $current_disk_usage += &Apache::lonnet::diskusage($udom,$uname,"userfiles/$subdir",1);
8966: }
8967: }
1.1135 raeburn 8968: $disk_quota = int($disk_quota * 1000);
8969: if (($current_disk_usage + $filesize) > $disk_quota) {
8970: return '<p><span class="LC_warning">'.
8971: &mt("Unable to $action [_1]. (size = [_2] kilobytes). Disk quota will be exceeded.",
8972: '<span class="LC_filename">'.$filename.'</span>',$filesize).'</span>'.
8973: '<br />'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',
8974: $disk_quota,$current_disk_usage).
8975: '</p>';
8976: }
8977: return;
8978: }
8979:
8980: ###############################################
8981:
8982:
1.1136 raeburn 8983:
8984:
1.384 raeburn 8985: sub get_secgrprole_info {
8986: my ($cdom,$cnum,$needroles,$type) = @_;
8987: my %sections_count = &get_sections($cdom,$cnum);
8988: my @sections = (sort {$a <=> $b} keys(%sections_count));
8989: my %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum);
8990: my @groups = sort(keys(%curr_groups));
8991: my $allroles = [];
8992: my $rolehash;
8993: my $accesshash = {
8994: active => 'Currently has access',
8995: future => 'Will have future access',
8996: previous => 'Previously had access',
8997: };
8998: if ($needroles) {
8999: $rolehash = {'all' => 'all'};
1.385 albertel 9000: my %user_roles = &Apache::lonnet::dump('nohist_userroles',$cdom,$cnum);
9001: if (&Apache::lonnet::error(%user_roles)) {
9002: undef(%user_roles);
9003: }
9004: foreach my $item (keys(%user_roles)) {
1.384 raeburn 9005: my ($role)=split(/\:/,$item,2);
9006: if ($role eq 'cr') { next; }
9007: if ($role =~ /^cr/) {
9008: $$rolehash{$role} = (split('/',$role))[3];
9009: } else {
9010: $$rolehash{$role} = &Apache::lonnet::plaintext($role,$type);
9011: }
9012: }
9013: foreach my $key (sort(keys(%{$rolehash}))) {
9014: push(@{$allroles},$key);
9015: }
9016: push (@{$allroles},'st');
9017: $$rolehash{'st'} = &Apache::lonnet::plaintext('st',$type);
9018: }
9019: return (\@sections,\@groups,$allroles,$rolehash,$accesshash);
9020: }
9021:
1.555 raeburn 9022: sub user_picker {
1.994 raeburn 9023: my ($dom,$srch,$forcenewuser,$caller,$cancreate,$usertype,$context) = @_;
1.555 raeburn 9024: my $currdom = $dom;
9025: my %curr_selected = (
9026: srchin => 'dom',
1.580 raeburn 9027: srchby => 'lastname',
1.555 raeburn 9028: );
9029: my $srchterm;
1.625 raeburn 9030: if ((ref($srch) eq 'HASH') && ($env{'form.origform'} ne 'crtusername')) {
1.555 raeburn 9031: if ($srch->{'srchby'} ne '') {
9032: $curr_selected{'srchby'} = $srch->{'srchby'};
9033: }
9034: if ($srch->{'srchin'} ne '') {
9035: $curr_selected{'srchin'} = $srch->{'srchin'};
9036: }
9037: if ($srch->{'srchtype'} ne '') {
9038: $curr_selected{'srchtype'} = $srch->{'srchtype'};
9039: }
9040: if ($srch->{'srchdomain'} ne '') {
9041: $currdom = $srch->{'srchdomain'};
9042: }
9043: $srchterm = $srch->{'srchterm'};
9044: }
9045: my %lt=&Apache::lonlocal::texthash(
1.573 raeburn 9046: 'usr' => 'Search criteria',
1.563 raeburn 9047: 'doma' => 'Domain/institution to search',
1.558 albertel 9048: 'uname' => 'username',
9049: 'lastname' => 'last name',
1.555 raeburn 9050: 'lastfirst' => 'last name, first name',
1.558 albertel 9051: 'crs' => 'in this course',
1.576 raeburn 9052: 'dom' => 'in selected LON-CAPA domain',
1.558 albertel 9053: 'alc' => 'all LON-CAPA',
1.573 raeburn 9054: 'instd' => 'in institutional directory for selected domain',
1.558 albertel 9055: 'exact' => 'is',
9056: 'contains' => 'contains',
1.569 raeburn 9057: 'begins' => 'begins with',
1.571 raeburn 9058: 'youm' => "You must include some text to search for.",
9059: 'thte' => "The text you are searching for must contain at least two characters when using a 'begins' type search.",
9060: 'thet' => "The text you are searching for must contain at least three characters when using a 'contains' type search.",
9061: 'yomc' => "You must choose a domain when using an institutional directory search.",
9062: 'ymcd' => "You must choose a domain when using a domain search.",
9063: 'whus' => "When using searching by last,first you must include a comma as separator between last name and first name.",
9064: 'whse' => "When searching by last,first you must include at least one character in the first name.",
9065: 'thfo' => "The following need to be corrected before the search can be run:",
1.555 raeburn 9066: );
1.563 raeburn 9067: my $domform = &select_dom_form($currdom,'srchdomain',1,1);
9068: my $srchinsel = ' <select name="srchin">';
1.555 raeburn 9069:
9070: my @srchins = ('crs','dom','alc','instd');
9071:
9072: foreach my $option (@srchins) {
9073: # FIXME 'alc' option unavailable until
9074: # loncreateuser::print_user_query_page()
9075: # has been completed.
9076: next if ($option eq 'alc');
1.880 raeburn 9077: next if (($option eq 'crs') && ($env{'form.form'} eq 'requestcrs'));
1.555 raeburn 9078: next if ($option eq 'crs' && !$env{'request.course.id'});
1.563 raeburn 9079: if ($curr_selected{'srchin'} eq $option) {
9080: $srchinsel .= '
9081: <option value="'.$option.'" selected="selected">'.$lt{$option}.'</option>';
9082: } else {
9083: $srchinsel .= '
9084: <option value="'.$option.'">'.$lt{$option}.'</option>';
9085: }
1.555 raeburn 9086: }
1.563 raeburn 9087: $srchinsel .= "\n </select>\n";
1.555 raeburn 9088:
9089: my $srchbysel = ' <select name="srchby">';
1.580 raeburn 9090: foreach my $option ('lastname','lastfirst','uname') {
1.555 raeburn 9091: if ($curr_selected{'srchby'} eq $option) {
9092: $srchbysel .= '
9093: <option value="'.$option.'" selected="selected">'.$lt{$option}.'</option>';
9094: } else {
9095: $srchbysel .= '
9096: <option value="'.$option.'">'.$lt{$option}.'</option>';
9097: }
9098: }
9099: $srchbysel .= "\n </select>\n";
9100:
9101: my $srchtypesel = ' <select name="srchtype">';
1.580 raeburn 9102: foreach my $option ('begins','contains','exact') {
1.555 raeburn 9103: if ($curr_selected{'srchtype'} eq $option) {
9104: $srchtypesel .= '
9105: <option value="'.$option.'" selected="selected">'.$lt{$option}.'</option>';
9106: } else {
9107: $srchtypesel .= '
9108: <option value="'.$option.'">'.$lt{$option}.'</option>';
9109: }
9110: }
9111: $srchtypesel .= "\n </select>\n";
9112:
1.558 albertel 9113: my ($newuserscript,$new_user_create);
1.994 raeburn 9114: my $context_dom = $env{'request.role.domain'};
9115: if ($context eq 'requestcrs') {
9116: if ($env{'form.coursedom'} ne '') {
9117: $context_dom = $env{'form.coursedom'};
9118: }
9119: }
1.556 raeburn 9120: if ($forcenewuser) {
1.576 raeburn 9121: if (ref($srch) eq 'HASH') {
1.994 raeburn 9122: if ($srch->{'srchby'} eq 'uname' && $srch->{'srchtype'} eq 'exact' && $srch->{'srchin'} eq 'dom' && $srch->{'srchdomain'} eq $context_dom) {
1.627 raeburn 9123: if ($cancreate) {
9124: $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>';
9125: } else {
1.799 bisitz 9126: my $helplink = 'javascript:helpMenu('."'display'".')';
1.627 raeburn 9127: my %usertypetext = (
9128: official => 'institutional',
9129: unofficial => 'non-institutional',
9130: );
1.799 bisitz 9131: $new_user_create = '<p class="LC_warning">'
9132: .&mt("You are not authorized to create new $usertypetext{$usertype} users in this domain.")
9133: .' '
9134: .&mt('Please contact the [_1]helpdesk[_2] for assistance.'
9135: ,'<a href="'.$helplink.'">','</a>')
9136: .'</p><br />';
1.627 raeburn 9137: }
1.576 raeburn 9138: }
9139: }
9140:
1.556 raeburn 9141: $newuserscript = <<"ENDSCRIPT";
9142:
1.570 raeburn 9143: function setSearch(createnew,callingForm) {
1.556 raeburn 9144: if (createnew == 1) {
1.570 raeburn 9145: for (var i=0; i<callingForm.srchby.length; i++) {
9146: if (callingForm.srchby.options[i].value == 'uname') {
9147: callingForm.srchby.selectedIndex = i;
1.556 raeburn 9148: }
9149: }
1.570 raeburn 9150: for (var i=0; i<callingForm.srchin.length; i++) {
9151: if ( callingForm.srchin.options[i].value == 'dom') {
9152: callingForm.srchin.selectedIndex = i;
1.556 raeburn 9153: }
9154: }
1.570 raeburn 9155: for (var i=0; i<callingForm.srchtype.length; i++) {
9156: if (callingForm.srchtype.options[i].value == 'exact') {
9157: callingForm.srchtype.selectedIndex = i;
1.556 raeburn 9158: }
9159: }
1.570 raeburn 9160: for (var i=0; i<callingForm.srchdomain.length; i++) {
1.994 raeburn 9161: if (callingForm.srchdomain.options[i].value == '$context_dom') {
1.570 raeburn 9162: callingForm.srchdomain.selectedIndex = i;
1.556 raeburn 9163: }
9164: }
9165: }
9166: }
9167: ENDSCRIPT
1.558 albertel 9168:
1.556 raeburn 9169: }
9170:
1.555 raeburn 9171: my $output = <<"END_BLOCK";
1.556 raeburn 9172: <script type="text/javascript">
1.824 bisitz 9173: // <![CDATA[
1.570 raeburn 9174: function validateEntry(callingForm) {
1.558 albertel 9175:
1.556 raeburn 9176: var checkok = 1;
1.558 albertel 9177: var srchin;
1.570 raeburn 9178: for (var i=0; i<callingForm.srchin.length; i++) {
9179: if ( callingForm.srchin[i].checked ) {
9180: srchin = callingForm.srchin[i].value;
1.558 albertel 9181: }
9182: }
9183:
1.570 raeburn 9184: var srchtype = callingForm.srchtype.options[callingForm.srchtype.selectedIndex].value;
9185: var srchby = callingForm.srchby.options[callingForm.srchby.selectedIndex].value;
9186: var srchdomain = callingForm.srchdomain.options[callingForm.srchdomain.selectedIndex].value;
9187: var srchterm = callingForm.srchterm.value;
9188: var srchin = callingForm.srchin.options[callingForm.srchin.selectedIndex].value;
1.556 raeburn 9189: var msg = "";
9190:
9191: if (srchterm == "") {
9192: checkok = 0;
1.571 raeburn 9193: msg += "$lt{'youm'}\\n";
1.556 raeburn 9194: }
9195:
1.569 raeburn 9196: if (srchtype== 'begins') {
9197: if (srchterm.length < 2) {
9198: checkok = 0;
1.571 raeburn 9199: msg += "$lt{'thte'}\\n";
1.569 raeburn 9200: }
9201: }
9202:
1.556 raeburn 9203: if (srchtype== 'contains') {
9204: if (srchterm.length < 3) {
9205: checkok = 0;
1.571 raeburn 9206: msg += "$lt{'thet'}\\n";
1.556 raeburn 9207: }
9208: }
9209: if (srchin == 'instd') {
9210: if (srchdomain == '') {
9211: checkok = 0;
1.571 raeburn 9212: msg += "$lt{'yomc'}\\n";
1.556 raeburn 9213: }
9214: }
9215: if (srchin == 'dom') {
9216: if (srchdomain == '') {
9217: checkok = 0;
1.571 raeburn 9218: msg += "$lt{'ymcd'}\\n";
1.556 raeburn 9219: }
9220: }
9221: if (srchby == 'lastfirst') {
9222: if (srchterm.indexOf(",") == -1) {
9223: checkok = 0;
1.571 raeburn 9224: msg += "$lt{'whus'}\\n";
1.556 raeburn 9225: }
9226: if (srchterm.indexOf(",") == srchterm.length -1) {
9227: checkok = 0;
1.571 raeburn 9228: msg += "$lt{'whse'}\\n";
1.556 raeburn 9229: }
9230: }
9231: if (checkok == 0) {
1.571 raeburn 9232: alert("$lt{'thfo'}\\n"+msg);
1.556 raeburn 9233: return;
9234: }
9235: if (checkok == 1) {
1.570 raeburn 9236: callingForm.submit();
1.556 raeburn 9237: }
9238: }
9239:
9240: $newuserscript
9241:
1.824 bisitz 9242: // ]]>
1.556 raeburn 9243: </script>
1.558 albertel 9244:
9245: $new_user_create
9246:
1.555 raeburn 9247: END_BLOCK
1.558 albertel 9248:
1.876 raeburn 9249: $output .= &Apache::lonhtmlcommon::start_pick_box().
9250: &Apache::lonhtmlcommon::row_title($lt{'doma'}).
9251: $domform.
9252: &Apache::lonhtmlcommon::row_closure().
9253: &Apache::lonhtmlcommon::row_title($lt{'usr'}).
9254: $srchbysel.
9255: $srchtypesel.
9256: '<input type="text" size="15" name="srchterm" value="'.$srchterm.'" />'.
9257: $srchinsel.
9258: &Apache::lonhtmlcommon::row_closure(1).
9259: &Apache::lonhtmlcommon::end_pick_box().
9260: '<br />';
1.555 raeburn 9261: return $output;
9262: }
9263:
1.612 raeburn 9264: sub user_rule_check {
1.615 raeburn 9265: my ($usershash,$checks,$alerts,$rulematch,$inst_results,$curr_rules,$got_rules) = @_;
1.612 raeburn 9266: my $response;
9267: if (ref($usershash) eq 'HASH') {
9268: foreach my $user (keys(%{$usershash})) {
9269: my ($uname,$udom) = split(/:/,$user);
9270: next if ($udom eq '' || $uname eq '');
1.615 raeburn 9271: my ($id,$newuser);
1.612 raeburn 9272: if (ref($usershash->{$user}) eq 'HASH') {
1.615 raeburn 9273: $newuser = $usershash->{$user}->{'newuser'};
1.612 raeburn 9274: $id = $usershash->{$user}->{'id'};
9275: }
9276: my $inst_response;
9277: if (ref($checks) eq 'HASH') {
9278: if (defined($checks->{'username'})) {
1.615 raeburn 9279: ($inst_response,%{$inst_results->{$user}}) =
1.612 raeburn 9280: &Apache::lonnet::get_instuser($udom,$uname);
9281: } elsif (defined($checks->{'id'})) {
1.615 raeburn 9282: ($inst_response,%{$inst_results->{$user}}) =
1.612 raeburn 9283: &Apache::lonnet::get_instuser($udom,undef,$id);
9284: }
1.615 raeburn 9285: } else {
9286: ($inst_response,%{$inst_results->{$user}}) =
9287: &Apache::lonnet::get_instuser($udom,$uname);
9288: return;
1.612 raeburn 9289: }
1.615 raeburn 9290: if (!$got_rules->{$udom}) {
1.612 raeburn 9291: my %domconfig = &Apache::lonnet::get_dom('configuration',
9292: ['usercreation'],$udom);
9293: if (ref($domconfig{'usercreation'}) eq 'HASH') {
1.615 raeburn 9294: foreach my $item ('username','id') {
1.612 raeburn 9295: if (ref($domconfig{'usercreation'}{$item.'_rule'}) eq 'ARRAY') {
9296: $$curr_rules{$udom}{$item} =
9297: $domconfig{'usercreation'}{$item.'_rule'};
1.585 raeburn 9298: }
9299: }
9300: }
1.615 raeburn 9301: $got_rules->{$udom} = 1;
1.585 raeburn 9302: }
1.612 raeburn 9303: foreach my $item (keys(%{$checks})) {
9304: if (ref($$curr_rules{$udom}) eq 'HASH') {
9305: if (ref($$curr_rules{$udom}{$item}) eq 'ARRAY') {
9306: if (@{$$curr_rules{$udom}{$item}} > 0) {
9307: my %rule_check = &Apache::lonnet::inst_rulecheck($udom,$uname,$id,$item,$$curr_rules{$udom}{$item});
9308: foreach my $rule (@{$$curr_rules{$udom}{$item}}) {
9309: if ($rule_check{$rule}) {
9310: $$rulematch{$user}{$item} = $rule;
9311: if ($inst_response eq 'ok') {
1.615 raeburn 9312: if (ref($inst_results) eq 'HASH') {
9313: if (ref($inst_results->{$user}) eq 'HASH') {
9314: if (keys(%{$inst_results->{$user}}) == 0) {
9315: $$alerts{$item}{$udom}{$uname} = 1;
9316: }
1.612 raeburn 9317: }
9318: }
1.615 raeburn 9319: }
9320: last;
1.585 raeburn 9321: }
9322: }
9323: }
9324: }
9325: }
9326: }
9327: }
9328: }
1.612 raeburn 9329: return;
9330: }
9331:
9332: sub user_rule_formats {
9333: my ($domain,$domdesc,$curr_rules,$check) = @_;
9334: my %text = (
9335: 'username' => 'Usernames',
9336: 'id' => 'IDs',
9337: );
9338: my $output;
9339: my ($rules,$ruleorder) = &Apache::lonnet::inst_userrules($domain,$check);
9340: if ((ref($rules) eq 'HASH') && (ref($ruleorder) eq 'ARRAY')) {
9341: if (@{$ruleorder} > 0) {
1.1102 raeburn 9342: $output = '<br />'.
9343: &mt($text{$check}.' with the following format(s) may [_1]only[_2] be used for verified users at [_3]:',
9344: '<span class="LC_cusr_emph">','</span>',$domdesc).
9345: ' <ul>';
1.612 raeburn 9346: foreach my $rule (@{$ruleorder}) {
9347: if (ref($curr_rules) eq 'ARRAY') {
9348: if (grep(/^\Q$rule\E$/,@{$curr_rules})) {
9349: if (ref($rules->{$rule}) eq 'HASH') {
9350: $output .= '<li>'.$rules->{$rule}{'name'}.': '.
9351: $rules->{$rule}{'desc'}.'</li>';
9352: }
9353: }
9354: }
9355: }
9356: $output .= '</ul>';
9357: }
9358: }
9359: return $output;
9360: }
9361:
9362: sub instrule_disallow_msg {
1.615 raeburn 9363: my ($checkitem,$domdesc,$count,$mode) = @_;
1.612 raeburn 9364: my $response;
9365: my %text = (
9366: item => 'username',
9367: items => 'usernames',
9368: match => 'matches',
9369: do => 'does',
9370: action => 'a username',
9371: one => 'one',
9372: );
9373: if ($count > 1) {
9374: $text{'item'} = 'usernames';
9375: $text{'match'} ='match';
9376: $text{'do'} = 'do';
9377: $text{'action'} = 'usernames',
9378: $text{'one'} = 'ones';
9379: }
9380: if ($checkitem eq 'id') {
9381: $text{'items'} = 'IDs';
9382: $text{'item'} = 'ID';
9383: $text{'action'} = 'an ID';
1.615 raeburn 9384: if ($count > 1) {
9385: $text{'item'} = 'IDs';
9386: $text{'action'} = 'IDs';
9387: }
1.612 raeburn 9388: }
1.674 bisitz 9389: $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 9390: if ($mode eq 'upload') {
9391: if ($checkitem eq 'username') {
9392: $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'}.");
9393: } elsif ($checkitem eq 'id') {
1.674 bisitz 9394: $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 9395: }
1.669 raeburn 9396: } elsif ($mode eq 'selfcreate') {
9397: if ($checkitem eq 'id') {
9398: $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.");
9399: }
1.615 raeburn 9400: } else {
9401: if ($checkitem eq 'username') {
9402: $response .= &mt("You must choose $text{'action'} with a different format -- $text{'one'} that will not conflict with 'official' institutional $text{'items'}.");
9403: } elsif ($checkitem eq 'id') {
9404: $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.");
9405: }
1.612 raeburn 9406: }
9407: return $response;
1.585 raeburn 9408: }
9409:
1.624 raeburn 9410: sub personal_data_fieldtitles {
9411: my %fieldtitles = &Apache::lonlocal::texthash (
9412: id => 'Student/Employee ID',
9413: permanentemail => 'E-mail address',
9414: lastname => 'Last Name',
9415: firstname => 'First Name',
9416: middlename => 'Middle Name',
9417: generation => 'Generation',
9418: gen => 'Generation',
1.765 raeburn 9419: inststatus => 'Affiliation',
1.624 raeburn 9420: );
9421: return %fieldtitles;
9422: }
9423:
1.642 raeburn 9424: sub sorted_inst_types {
9425: my ($dom) = @_;
9426: my ($usertypes,$order) = &Apache::lonnet::retrieve_inst_usertypes($dom);
9427: my $othertitle = &mt('All users');
9428: if ($env{'request.course.id'}) {
1.668 raeburn 9429: $othertitle = &mt('Any users');
1.642 raeburn 9430: }
9431: my @types;
9432: if (ref($order) eq 'ARRAY') {
9433: @types = @{$order};
9434: }
9435: if (@types == 0) {
9436: if (ref($usertypes) eq 'HASH') {
9437: @types = sort(keys(%{$usertypes}));
9438: }
9439: }
9440: if (keys(%{$usertypes}) > 0) {
9441: $othertitle = &mt('Other users');
9442: }
9443: return ($othertitle,$usertypes,\@types);
9444: }
9445:
1.645 raeburn 9446: sub get_institutional_codes {
9447: my ($settings,$allcourses,$LC_code) = @_;
9448: # Get complete list of course sections to update
9449: my @currsections = ();
9450: my @currxlists = ();
9451: my $coursecode = $$settings{'internal.coursecode'};
9452:
9453: if ($$settings{'internal.sectionnums'} ne '') {
9454: @currsections = split(/,/,$$settings{'internal.sectionnums'});
9455: }
9456:
9457: if ($$settings{'internal.crosslistings'} ne '') {
9458: @currxlists = split(/,/,$$settings{'internal.crosslistings'});
9459: }
9460:
9461: if (@currxlists > 0) {
9462: foreach (@currxlists) {
9463: if (m/^([^:]+):(\w*)$/) {
9464: unless (grep/^$1$/,@{$allcourses}) {
9465: push @{$allcourses},$1;
9466: $$LC_code{$1} = $2;
9467: }
9468: }
9469: }
9470: }
9471:
9472: if (@currsections > 0) {
9473: foreach (@currsections) {
9474: if (m/^(\w+):(\w*)$/) {
9475: my $sec = $coursecode.$1;
9476: my $lc_sec = $2;
9477: unless (grep/^$sec$/,@{$allcourses}) {
9478: push @{$allcourses},$sec;
9479: $$LC_code{$sec} = $lc_sec;
9480: }
9481: }
9482: }
9483: }
9484: return;
9485: }
9486:
1.971 raeburn 9487: sub get_standard_codeitems {
9488: return ('Year','Semester','Department','Number','Section');
9489: }
9490:
1.112 bowersj2 9491: =pod
9492:
1.780 raeburn 9493: =head1 Slot Helpers
9494:
9495: =over 4
9496:
9497: =item * sorted_slots()
9498:
1.1040 raeburn 9499: Sorts an array of slot names in order of an optional sort key,
9500: default sort is by slot start time (earliest first).
1.780 raeburn 9501:
9502: Inputs:
9503:
9504: =over 4
9505:
9506: slotsarr - Reference to array of unsorted slot names.
9507:
9508: slots - Reference to hash of hash, where outer hash keys are slot names.
9509:
1.1040 raeburn 9510: sortkey - Name of key in inner hash to be sorted on (e.g., starttime).
9511:
1.549 albertel 9512: =back
9513:
1.780 raeburn 9514: Returns:
9515:
9516: =over 4
9517:
1.1040 raeburn 9518: sorted - An array of slot names sorted by a specified sort key
9519: (default sort key is start time of the slot).
1.780 raeburn 9520:
9521: =back
9522:
9523: =cut
9524:
9525:
9526: sub sorted_slots {
1.1040 raeburn 9527: my ($slotsarr,$slots,$sortkey) = @_;
9528: if ($sortkey eq '') {
9529: $sortkey = 'starttime';
9530: }
1.780 raeburn 9531: my @sorted;
9532: if ((ref($slotsarr) eq 'ARRAY') && (ref($slots) eq 'HASH')) {
9533: @sorted =
9534: sort {
9535: if (ref($slots->{$a}) && ref($slots->{$b})) {
1.1040 raeburn 9536: return $slots->{$a}{$sortkey} <=> $slots->{$b}{$sortkey}
1.780 raeburn 9537: }
9538: if (ref($slots->{$a})) { return -1;}
9539: if (ref($slots->{$b})) { return 1;}
9540: return 0;
9541: } @{$slotsarr};
9542: }
9543: return @sorted;
9544: }
9545:
1.1040 raeburn 9546: =pod
9547:
9548: =item * get_future_slots()
9549:
9550: Inputs:
9551:
9552: =over 4
9553:
9554: cnum - course number
9555:
9556: cdom - course domain
9557:
9558: now - current UNIX time
9559:
9560: symb - optional symb
9561:
9562: =back
9563:
9564: Returns:
9565:
9566: =over 4
9567:
9568: sorted_reservable - ref to array of student_schedulable slots currently
9569: reservable, ordered by end date of reservation period.
9570:
9571: reservable_now - ref to hash of student_schedulable slots currently
9572: reservable.
9573:
9574: Keys in inner hash are:
9575: (a) symb: either blank or symb to which slot use is restricted.
9576: (b) endreserve: end date of reservation period.
9577:
9578: sorted_future - ref to array of student_schedulable slots reservable in
9579: the future, ordered by start date of reservation period.
9580:
9581: future_reservable - ref to hash of student_schedulable slots reservable
9582: in the future.
9583:
9584: Keys in inner hash are:
9585: (a) symb: either blank or symb to which slot use is restricted.
9586: (b) startreserve: start date of reservation period.
9587:
9588: =back
9589:
9590: =cut
9591:
9592: sub get_future_slots {
9593: my ($cnum,$cdom,$now,$symb) = @_;
9594: my (%reservable_now,%future_reservable,@sorted_reservable,@sorted_future);
9595: my %slots = &Apache::lonnet::get_course_slots($cnum,$cdom);
9596: foreach my $slot (keys(%slots)) {
9597: next unless($slots{$slot}->{'type'} eq 'schedulable_student');
9598: if ($symb) {
9599: next if (($slots{$slot}->{'symb'} ne '') &&
9600: ($slots{$slot}->{'symb'} ne $symb));
9601: }
9602: if (($slots{$slot}->{'starttime'} > $now) &&
9603: ($slots{$slot}->{'endtime'} > $now)) {
9604: if (($slots{$slot}->{'allowedsections'}) || ($slots{$slot}->{'allowedusers'})) {
9605: my $userallowed = 0;
9606: if ($slots{$slot}->{'allowedsections'}) {
9607: my @allowed_sec = split(',',$slots{$slot}->{'allowedsections'});
9608: if (!defined($env{'request.role.sec'})
9609: && grep(/^No section assigned$/,@allowed_sec)) {
9610: $userallowed=1;
9611: } else {
9612: if (grep(/^\Q$env{'request.role.sec'}\E$/,@allowed_sec)) {
9613: $userallowed=1;
9614: }
9615: }
9616: unless ($userallowed) {
9617: if (defined($env{'request.course.groups'})) {
9618: my @groups = split(/:/,$env{'request.course.groups'});
9619: foreach my $group (@groups) {
9620: if (grep(/^\Q$group\E$/,@allowed_sec)) {
9621: $userallowed=1;
9622: last;
9623: }
9624: }
9625: }
9626: }
9627: }
9628: if ($slots{$slot}->{'allowedusers'}) {
9629: my @allowed_users = split(',',$slots{$slot}->{'allowedusers'});
9630: my $user = $env{'user.name'}.':'.$env{'user.domain'};
9631: if (grep(/^\Q$user\E$/,@allowed_users)) {
9632: $userallowed = 1;
9633: }
9634: }
9635: next unless($userallowed);
9636: }
9637: my $startreserve = $slots{$slot}->{'startreserve'};
9638: my $endreserve = $slots{$slot}->{'endreserve'};
9639: my $symb = $slots{$slot}->{'symb'};
9640: if (($startreserve < $now) &&
9641: (!$endreserve || $endreserve > $now)) {
9642: my $lastres = $endreserve;
9643: if (!$lastres) {
9644: $lastres = $slots{$slot}->{'starttime'};
9645: }
9646: $reservable_now{$slot} = {
9647: symb => $symb,
9648: endreserve => $lastres
9649: };
9650: } elsif (($startreserve > $now) &&
9651: (!$endreserve || $endreserve > $startreserve)) {
9652: $future_reservable{$slot} = {
9653: symb => $symb,
9654: startreserve => $startreserve
9655: };
9656: }
9657: }
9658: }
9659: my @unsorted_reservable = keys(%reservable_now);
9660: if (@unsorted_reservable > 0) {
9661: @sorted_reservable =
9662: &sorted_slots(\@unsorted_reservable,\%reservable_now,'endreserve');
9663: }
9664: my @unsorted_future = keys(%future_reservable);
9665: if (@unsorted_future > 0) {
9666: @sorted_future =
9667: &sorted_slots(\@unsorted_future,\%future_reservable,'startreserve');
9668: }
9669: return (\@sorted_reservable,\%reservable_now,\@sorted_future,\%future_reservable);
9670: }
1.780 raeburn 9671:
9672: =pod
9673:
1.1057 foxr 9674: =back
9675:
1.549 albertel 9676: =head1 HTTP Helpers
9677:
9678: =over 4
9679:
1.648 raeburn 9680: =item * &get_unprocessed_cgi($query,$possible_names)
1.112 bowersj2 9681:
1.258 albertel 9682: Modify the %env hash to contain unprocessed CGI form parameters held in
1.112 bowersj2 9683: $query. The parameters listed in $possible_names (an array reference),
1.258 albertel 9684: will be set in $env{'form.name'} if they do not already exist.
1.112 bowersj2 9685:
9686: Typically called with $ENV{'QUERY_STRING'} as the first parameter.
9687: $possible_names is an ref to an array of form element names. As an example:
9688: get_unprocessed_cgi($ENV{'QUERY_STRING'},['uname','udom']);
1.258 albertel 9689: will result in $env{'form.uname'} and $env{'form.udom'} being set.
1.112 bowersj2 9690:
9691: =cut
1.1 albertel 9692:
1.6 albertel 9693: sub get_unprocessed_cgi {
1.25 albertel 9694: my ($query,$possible_names)= @_;
1.26 matthew 9695: # $Apache::lonxml::debug=1;
1.356 albertel 9696: foreach my $pair (split(/&/,$query)) {
9697: my ($name, $value) = split(/=/,$pair);
1.369 www 9698: $name = &unescape($name);
1.25 albertel 9699: if (!defined($possible_names) || (grep {$_ eq $name} @$possible_names)) {
9700: $value =~ tr/+/ /;
9701: $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
1.258 albertel 9702: unless (defined($env{'form.'.$name})) { &add_to_env('form.'.$name,$value) };
1.25 albertel 9703: }
1.16 harris41 9704: }
1.6 albertel 9705: }
9706:
1.112 bowersj2 9707: =pod
9708:
1.648 raeburn 9709: =item * &cacheheader()
1.112 bowersj2 9710:
9711: returns cache-controlling header code
9712:
9713: =cut
9714:
1.7 albertel 9715: sub cacheheader {
1.258 albertel 9716: unless ($env{'request.method'} eq 'GET') { return ''; }
1.216 albertel 9717: my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime);
9718: my $output .='<meta HTTP-EQUIV="Expires" CONTENT="'.$date.'" />
1.7 albertel 9719: <meta HTTP-EQUIV="Cache-control" CONTENT="no-cache" />
9720: <meta HTTP-EQUIV="Pragma" CONTENT="no-cache" />';
1.216 albertel 9721: return $output;
1.7 albertel 9722: }
9723:
1.112 bowersj2 9724: =pod
9725:
1.648 raeburn 9726: =item * &no_cache($r)
1.112 bowersj2 9727:
9728: specifies header code to not have cache
9729:
9730: =cut
9731:
1.9 albertel 9732: sub no_cache {
1.216 albertel 9733: my ($r) = @_;
9734: if ($ENV{'REQUEST_METHOD'} ne 'GET' &&
1.258 albertel 9735: $env{'request.method'} ne 'GET') { return ''; }
1.216 albertel 9736: my $date=strftime("%a, %d %b %Y %H:%M:%S GMT",gmtime(time));
9737: $r->no_cache(1);
9738: $r->header_out("Expires" => $date);
9739: $r->header_out("Pragma" => "no-cache");
1.123 www 9740: }
9741:
9742: sub content_type {
1.181 albertel 9743: my ($r,$type,$charset) = @_;
1.299 foxr 9744: if ($r) {
9745: # Note that printout.pl calls this with undef for $r.
9746: &no_cache($r);
9747: }
1.258 albertel 9748: if ($env{'browser.mathml'} && $type eq 'text/html') { $type='text/xml'; }
1.181 albertel 9749: unless ($charset) {
9750: $charset=&Apache::lonlocal::current_encoding;
9751: }
9752: if ($charset) { $type.='; charset='.$charset; }
9753: if ($r) {
9754: $r->content_type($type);
9755: } else {
9756: print("Content-type: $type\n\n");
9757: }
1.9 albertel 9758: }
1.25 albertel 9759:
1.112 bowersj2 9760: =pod
9761:
1.648 raeburn 9762: =item * &add_to_env($name,$value)
1.112 bowersj2 9763:
1.258 albertel 9764: adds $name to the %env hash with value
1.112 bowersj2 9765: $value, if $name already exists, the entry is converted to an array
9766: reference and $value is added to the array.
9767:
9768: =cut
9769:
1.25 albertel 9770: sub add_to_env {
9771: my ($name,$value)=@_;
1.258 albertel 9772: if (defined($env{$name})) {
9773: if (ref($env{$name})) {
1.25 albertel 9774: #already have multiple values
1.258 albertel 9775: push(@{ $env{$name} },$value);
1.25 albertel 9776: } else {
9777: #first time seeing multiple values, convert hash entry to an arrayref
1.258 albertel 9778: my $first=$env{$name};
9779: undef($env{$name});
9780: push(@{ $env{$name} },$first,$value);
1.25 albertel 9781: }
9782: } else {
1.258 albertel 9783: $env{$name}=$value;
1.25 albertel 9784: }
1.31 albertel 9785: }
1.149 albertel 9786:
9787: =pod
9788:
1.648 raeburn 9789: =item * &get_env_multiple($name)
1.149 albertel 9790:
1.258 albertel 9791: gets $name from the %env hash, it seemlessly handles the cases where multiple
1.149 albertel 9792: values may be defined and end up as an array ref.
9793:
9794: returns an array of values
9795:
9796: =cut
9797:
9798: sub get_env_multiple {
9799: my ($name) = @_;
9800: my @values;
1.258 albertel 9801: if (defined($env{$name})) {
1.149 albertel 9802: # exists is it an array
1.258 albertel 9803: if (ref($env{$name})) {
9804: @values=@{ $env{$name} };
1.149 albertel 9805: } else {
1.258 albertel 9806: $values[0]=$env{$name};
1.149 albertel 9807: }
9808: }
9809: return(@values);
9810: }
9811:
1.660 raeburn 9812: sub ask_for_embedded_content {
9813: my ($actionurl,$state,$allfiles,$codebase,$args)=@_;
1.1071 raeburn 9814: my (%subdependencies,%dependencies,%mapping,%existing,%newfiles,%pathchanges,
1.1085 raeburn 9815: %currsubfile,%unused,$rem);
1.1071 raeburn 9816: my $counter = 0;
9817: my $numnew = 0;
1.987 raeburn 9818: my $numremref = 0;
9819: my $numinvalid = 0;
9820: my $numpathchg = 0;
9821: my $numexisting = 0;
1.1071 raeburn 9822: my $numunused = 0;
9823: my ($output,$upload_output,$toplevel,$url,$udom,$uname,$getpropath,$cdom,$cnum,
1.1156 raeburn 9824: $fileloc,$filename,$delete_output,$modify_output,$title,$symb,$path,$navmap);
1.1071 raeburn 9825: my $heading = &mt('Upload embedded files');
9826: my $buttontext = &mt('Upload');
9827:
1.1085 raeburn 9828: if ($env{'request.course.id'}) {
1.1123 raeburn 9829: if ($actionurl eq '/adm/dependencies') {
9830: $navmap = Apache::lonnavmaps::navmap->new();
9831: }
9832: $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
9833: $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
1.1085 raeburn 9834: }
1.1123 raeburn 9835: if (($actionurl eq '/adm/portfolio') ||
9836: ($actionurl eq '/adm/coursegrp_portfolio')) {
1.984 raeburn 9837: my $current_path='/';
9838: if ($env{'form.currentpath'}) {
9839: $current_path = $env{'form.currentpath'};
9840: }
9841: if ($actionurl eq '/adm/coursegrp_portfolio') {
1.1123 raeburn 9842: $udom = $cdom;
9843: $uname = $cnum;
1.984 raeburn 9844: $url = '/userfiles/groups/'.$env{'form.group'}.'/portfolio';
9845: } else {
9846: $udom = $env{'user.domain'};
9847: $uname = $env{'user.name'};
9848: $url = '/userfiles/portfolio';
9849: }
1.987 raeburn 9850: $toplevel = $url.'/';
1.984 raeburn 9851: $url .= $current_path;
9852: $getpropath = 1;
1.987 raeburn 9853: } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank') ||
9854: ($actionurl eq '/adm/imsimport')) {
1.1022 www 9855: my ($udom,$uname,$rest) = ($args->{'current_path'} =~ m{/priv/($match_domain)/($match_username)/?(.*)$});
1.1026 raeburn 9856: $url = $Apache::lonnet::perlvar{'lonDocRoot'}."/priv/$udom/$uname/";
1.987 raeburn 9857: $toplevel = $url;
1.984 raeburn 9858: if ($rest ne '') {
1.987 raeburn 9859: $url .= $rest;
9860: }
9861: } elsif ($actionurl eq '/adm/coursedocs') {
9862: if (ref($args) eq 'HASH') {
1.1071 raeburn 9863: $url = $args->{'docs_url'};
9864: $toplevel = $url;
1.1084 raeburn 9865: if ($args->{'context'} eq 'paste') {
9866: ($cdom,$cnum) = ($url =~ m{^\Q/uploaded/\E($match_domain)/($match_courseid)/});
9867: ($path) =
9868: ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/\E(?:docs|supplemental)/(?:default|\d+)/\d+)/});
9869: $fileloc = &Apache::lonnet::filelocation('',$toplevel);
9870: $fileloc =~ s{^/}{};
9871: }
1.1071 raeburn 9872: }
1.1084 raeburn 9873: } elsif ($actionurl eq '/adm/dependencies') {
1.1071 raeburn 9874: if ($env{'request.course.id'} ne '') {
9875: if (ref($args) eq 'HASH') {
9876: $url = $args->{'docs_url'};
9877: $title = $args->{'docs_title'};
1.1126 raeburn 9878: $toplevel = $url;
9879: unless ($toplevel =~ m{^/}) {
9880: $toplevel = "/$url";
9881: }
1.1085 raeburn 9882: ($rem) = ($toplevel =~ m{^(.+/)[^/]+$});
1.1126 raeburn 9883: if ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/portfolio/syllabus\E)}) {
9884: $path = $1;
9885: } else {
9886: ($path) =
9887: ($toplevel =~ m{^(\Q/uploaded/$cdom/$cnum/\E(?:docs|supplemental)/(?:default|\d+)/\d+)/});
9888: }
1.1071 raeburn 9889: $fileloc = &Apache::lonnet::filelocation('',$toplevel);
9890: $fileloc =~ s{^/}{};
9891: ($filename) = ($fileloc =~ m{.+/([^/]+)$});
9892: $heading = &mt('Status of dependencies in [_1]',"$title ($filename)");
9893: }
1.987 raeburn 9894: }
1.1123 raeburn 9895: } elsif ($actionurl eq "/public/$cdom/$cnum/syllabus") {
9896: $udom = $cdom;
9897: $uname = $cnum;
9898: $url = "/uploaded/$cdom/$cnum/portfolio/syllabus";
9899: $toplevel = $url;
9900: $path = $url;
9901: $fileloc = &Apache::lonnet::filelocation('',$toplevel).'/';
9902: $fileloc =~ s{^/}{};
1.987 raeburn 9903: }
1.1126 raeburn 9904: foreach my $file (keys(%{$allfiles})) {
9905: my $embed_file;
9906: if (($path eq "/uploaded/$cdom/$cnum/portfolio/syllabus") && ($file =~ m{^\Q$path/\E(.+)$})) {
9907: $embed_file = $1;
9908: } else {
9909: $embed_file = $file;
9910: }
1.1158 raeburn 9911: my ($absolutepath,$cleaned_file);
9912: if ($embed_file =~ m{^\w+://}) {
9913: $cleaned_file = $embed_file;
1.1147 raeburn 9914: $newfiles{$cleaned_file} = 1;
9915: $mapping{$cleaned_file} = $embed_file;
1.987 raeburn 9916: } else {
1.1158 raeburn 9917: $cleaned_file = &clean_path($embed_file);
1.987 raeburn 9918: if ($embed_file =~ m{^/}) {
9919: $absolutepath = $embed_file;
9920: }
1.1147 raeburn 9921: if ($cleaned_file =~ m{/}) {
9922: my ($path,$fname) = ($cleaned_file =~ m{^(.+)/([^/]*)$});
1.987 raeburn 9923: $path = &check_for_traversal($path,$url,$toplevel);
9924: my $item = $fname;
9925: if ($path ne '') {
9926: $item = $path.'/'.$fname;
9927: $subdependencies{$path}{$fname} = 1;
9928: } else {
9929: $dependencies{$item} = 1;
9930: }
9931: if ($absolutepath) {
9932: $mapping{$item} = $absolutepath;
9933: } else {
9934: $mapping{$item} = $embed_file;
9935: }
9936: } else {
9937: $dependencies{$embed_file} = 1;
9938: if ($absolutepath) {
1.1147 raeburn 9939: $mapping{$cleaned_file} = $absolutepath;
1.987 raeburn 9940: } else {
1.1147 raeburn 9941: $mapping{$cleaned_file} = $embed_file;
1.987 raeburn 9942: }
9943: }
1.984 raeburn 9944: }
9945: }
1.1071 raeburn 9946: my $dirptr = 16384;
1.984 raeburn 9947: foreach my $path (keys(%subdependencies)) {
1.1071 raeburn 9948: $currsubfile{$path} = {};
1.1123 raeburn 9949: if (($actionurl eq '/adm/portfolio') ||
9950: ($actionurl eq '/adm/coursegrp_portfolio')) {
1.1021 raeburn 9951: my ($sublistref,$listerror) =
9952: &Apache::lonnet::dirlist($url.$path,$udom,$uname,$getpropath);
9953: if (ref($sublistref) eq 'ARRAY') {
9954: foreach my $line (@{$sublistref}) {
9955: my ($file_name,$rest) = split(/\&/,$line,2);
1.1071 raeburn 9956: $currsubfile{$path}{$file_name} = 1;
1.1021 raeburn 9957: }
1.984 raeburn 9958: }
1.987 raeburn 9959: } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
1.984 raeburn 9960: if (opendir(my $dir,$url.'/'.$path)) {
9961: my @subdir_list = grep(!/^\./,readdir($dir));
1.1071 raeburn 9962: map {$currsubfile{$path}{$_} = 1;} @subdir_list;
9963: }
1.1084 raeburn 9964: } elsif (($actionurl eq '/adm/dependencies') ||
9965: (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
1.1123 raeburn 9966: ($args->{'context'} eq 'paste')) ||
9967: ($actionurl eq "/public/$cdom/$cnum/syllabus")) {
1.1071 raeburn 9968: if ($env{'request.course.id'} ne '') {
1.1123 raeburn 9969: my $dir;
9970: if ($actionurl eq "/public/$cdom/$cnum/syllabus") {
9971: $dir = $fileloc;
9972: } else {
9973: ($dir) = ($fileloc =~ m{^(.+/)[^/]+$});
9974: }
1.1071 raeburn 9975: if ($dir ne '') {
9976: my ($sublistref,$listerror) =
9977: &Apache::lonnet::dirlist($dir.$path,$cdom,$cnum,$getpropath,undef,'/');
9978: if (ref($sublistref) eq 'ARRAY') {
9979: foreach my $line (@{$sublistref}) {
9980: my ($file_name,$dom,undef,$testdir,undef,undef,undef,undef,$size,
9981: undef,$mtime)=split(/\&/,$line,12);
9982: unless (($testdir&$dirptr) ||
9983: ($file_name =~ /^\.\.?$/)) {
9984: $currsubfile{$path}{$file_name} = [$size,$mtime];
9985: }
9986: }
9987: }
9988: }
1.984 raeburn 9989: }
9990: }
9991: foreach my $file (keys(%{$subdependencies{$path}})) {
1.1071 raeburn 9992: if (exists($currsubfile{$path}{$file})) {
1.987 raeburn 9993: my $item = $path.'/'.$file;
9994: unless ($mapping{$item} eq $item) {
9995: $pathchanges{$item} = 1;
9996: }
9997: $existing{$item} = 1;
9998: $numexisting ++;
9999: } else {
10000: $newfiles{$path.'/'.$file} = 1;
1.984 raeburn 10001: }
10002: }
1.1071 raeburn 10003: if ($actionurl eq '/adm/dependencies') {
10004: foreach my $path (keys(%currsubfile)) {
10005: if (ref($currsubfile{$path}) eq 'HASH') {
10006: foreach my $file (keys(%{$currsubfile{$path}})) {
10007: unless ($subdependencies{$path}{$file}) {
1.1085 raeburn 10008: next if (($rem ne '') &&
10009: (($env{"httpref.$rem"."$path/$file"} ne '') ||
10010: (ref($navmap) &&
10011: (($navmap->getResourceByUrl($rem."$path/$file") ne '') ||
10012: (($file =~ /^(.*\.s?html?)\.bak$/i) &&
10013: ($navmap->getResourceByUrl($rem."$path/$1")))))));
1.1071 raeburn 10014: $unused{$path.'/'.$file} = 1;
10015: }
10016: }
10017: }
10018: }
10019: }
1.984 raeburn 10020: }
1.987 raeburn 10021: my %currfile;
1.1123 raeburn 10022: if (($actionurl eq '/adm/portfolio') ||
10023: ($actionurl eq '/adm/coursegrp_portfolio')) {
1.1021 raeburn 10024: my ($dirlistref,$listerror) =
10025: &Apache::lonnet::dirlist($url,$udom,$uname,$getpropath);
10026: if (ref($dirlistref) eq 'ARRAY') {
10027: foreach my $line (@{$dirlistref}) {
10028: my ($file_name,$rest) = split(/\&/,$line,2);
10029: $currfile{$file_name} = 1;
10030: }
1.984 raeburn 10031: }
1.987 raeburn 10032: } elsif (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
1.984 raeburn 10033: if (opendir(my $dir,$url)) {
1.987 raeburn 10034: my @dir_list = grep(!/^\./,readdir($dir));
1.984 raeburn 10035: map {$currfile{$_} = 1;} @dir_list;
10036: }
1.1084 raeburn 10037: } elsif (($actionurl eq '/adm/dependencies') ||
10038: (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
1.1123 raeburn 10039: ($args->{'context'} eq 'paste')) ||
10040: ($actionurl eq "/public/$cdom/$cnum/syllabus")) {
1.1071 raeburn 10041: if ($env{'request.course.id'} ne '') {
10042: my ($dir) = ($fileloc =~ m{^(.+/)[^/]+$});
10043: if ($dir ne '') {
10044: my ($dirlistref,$listerror) =
10045: &Apache::lonnet::dirlist($dir,$cdom,$cnum,$getpropath,undef,'/');
10046: if (ref($dirlistref) eq 'ARRAY') {
10047: foreach my $line (@{$dirlistref}) {
10048: my ($file_name,$dom,undef,$testdir,undef,undef,undef,undef,
10049: $size,undef,$mtime)=split(/\&/,$line,12);
10050: unless (($testdir&$dirptr) ||
10051: ($file_name =~ /^\.\.?$/)) {
10052: $currfile{$file_name} = [$size,$mtime];
10053: }
10054: }
10055: }
10056: }
10057: }
1.984 raeburn 10058: }
10059: foreach my $file (keys(%dependencies)) {
1.1071 raeburn 10060: if (exists($currfile{$file})) {
1.987 raeburn 10061: unless ($mapping{$file} eq $file) {
10062: $pathchanges{$file} = 1;
10063: }
10064: $existing{$file} = 1;
10065: $numexisting ++;
10066: } else {
1.984 raeburn 10067: $newfiles{$file} = 1;
10068: }
10069: }
1.1071 raeburn 10070: foreach my $file (keys(%currfile)) {
10071: unless (($file eq $filename) ||
10072: ($file eq $filename.'.bak') ||
10073: ($dependencies{$file})) {
1.1085 raeburn 10074: if ($actionurl eq '/adm/dependencies') {
1.1126 raeburn 10075: unless ($toplevel =~ m{^\Q/uploaded/$cdom/$cnum/portfolio/syllabus\E}) {
10076: next if (($rem ne '') &&
10077: (($env{"httpref.$rem".$file} ne '') ||
10078: (ref($navmap) &&
10079: (($navmap->getResourceByUrl($rem.$file) ne '') ||
10080: (($file =~ /^(.*\.s?html?)\.bak$/i) &&
10081: ($navmap->getResourceByUrl($rem.$1)))))));
10082: }
1.1085 raeburn 10083: }
1.1071 raeburn 10084: $unused{$file} = 1;
10085: }
10086: }
1.1084 raeburn 10087: if (($actionurl eq '/adm/coursedocs') && (ref($args) eq 'HASH') &&
10088: ($args->{'context'} eq 'paste')) {
10089: $counter = scalar(keys(%existing));
10090: $numpathchg = scalar(keys(%pathchanges));
1.1123 raeburn 10091: return ($output,$counter,$numpathchg,\%existing);
10092: } elsif (($actionurl eq "/public/$cdom/$cnum/syllabus") &&
10093: (ref($args) eq 'HASH') && ($args->{'context'} eq 'rewrites')) {
10094: $counter = scalar(keys(%existing));
10095: $numpathchg = scalar(keys(%pathchanges));
10096: return ($output,$counter,$numpathchg,\%existing,\%mapping);
1.1084 raeburn 10097: }
1.984 raeburn 10098: foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%newfiles)) {
1.1071 raeburn 10099: if ($actionurl eq '/adm/dependencies') {
10100: next if ($embed_file =~ m{^\w+://});
10101: }
1.660 raeburn 10102: $upload_output .= &start_data_table_row().
1.1123 raeburn 10103: '<td valign="top"><img src="'.&icon($embed_file).'" /> '.
1.1071 raeburn 10104: '<span class="LC_filename">'.$embed_file.'</span>';
1.987 raeburn 10105: unless ($mapping{$embed_file} eq $embed_file) {
1.1123 raeburn 10106: $upload_output .= '<br /><span class="LC_info" style="font-size:smaller;">'.
10107: &mt('changed from: [_1]',$mapping{$embed_file}).'</span>';
1.987 raeburn 10108: }
1.1123 raeburn 10109: $upload_output .= '</td>';
1.1071 raeburn 10110: if ($args->{'ignore_remote_references'} && $embed_file =~ m{^\w+://}) {
1.1123 raeburn 10111: $upload_output.='<td align="right">'.
10112: '<span class="LC_info LC_fontsize_medium">'.
10113: &mt("URL points to web address").'</span>';
1.987 raeburn 10114: $numremref++;
1.660 raeburn 10115: } elsif ($args->{'error_on_invalid_names'}
10116: && $embed_file ne &Apache::lonnet::clean_filename($embed_file,{'keep_path' => 1,})) {
1.1123 raeburn 10117: $upload_output.='<td align="right"><span class="LC_warning">'.
10118: &mt('Invalid characters').'</span>';
1.987 raeburn 10119: $numinvalid++;
1.660 raeburn 10120: } else {
1.1123 raeburn 10121: $upload_output .= '<td>'.
10122: &embedded_file_element('upload_embedded',$counter,
1.987 raeburn 10123: $embed_file,\%mapping,
1.1071 raeburn 10124: $allfiles,$codebase,'upload');
10125: $counter ++;
10126: $numnew ++;
1.987 raeburn 10127: }
10128: $upload_output .= '</td>'.&Apache::loncommon::end_data_table_row()."\n";
10129: }
10130: foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%existing)) {
1.1071 raeburn 10131: if ($actionurl eq '/adm/dependencies') {
10132: my ($size,$mtime) = &get_dependency_details(\%currfile,\%currsubfile,$embed_file);
10133: $modify_output .= &start_data_table_row().
10134: '<td><a href="'.$path.'/'.$embed_file.'" style="text-decoration:none;">'.
10135: '<img src="'.&icon($embed_file).'" border="0" />'.
10136: ' <span class="LC_filename">'.$embed_file.'</span></a></td>'.
10137: '<td>'.$size.'</td>'.
10138: '<td>'.$mtime.'</td>'.
10139: '<td><label><input type="checkbox" name="mod_upload_dep" '.
10140: 'onclick="toggleBrowse('."'$counter'".')" id="mod_upload_dep_'.
10141: $counter.'" value="'.$counter.'" />'.&mt('Yes').'</label>'.
10142: '<div id="moduploaddep_'.$counter.'" style="display:none;">'.
10143: &embedded_file_element('upload_embedded',$counter,
10144: $embed_file,\%mapping,
10145: $allfiles,$codebase,'modify').
10146: '</div></td>'.
10147: &end_data_table_row()."\n";
10148: $counter ++;
10149: } else {
10150: $upload_output .= &start_data_table_row().
1.1123 raeburn 10151: '<td valign="top"><img src="'.&icon($embed_file).'" /> '.
10152: '<span class="LC_filename">'.$embed_file.'</span></td>'.
10153: '<td align="right"><span class="LC_info LC_fontsize_medium">'.&mt('Already exists').'</span></td>'.
1.1071 raeburn 10154: &Apache::loncommon::end_data_table_row()."\n";
10155: }
10156: }
10157: my $delidx = $counter;
10158: foreach my $oldfile (sort {lc($a) cmp lc($b)} keys(%unused)) {
10159: my ($size,$mtime) = &get_dependency_details(\%currfile,\%currsubfile,$oldfile);
10160: $delete_output .= &start_data_table_row().
10161: '<td><img src="'.&icon($oldfile).'" />'.
10162: ' <span class="LC_filename">'.$oldfile.'</span></td>'.
10163: '<td>'.$size.'</td>'.
10164: '<td>'.$mtime.'</td>'.
10165: '<td><label><input type="checkbox" name="del_upload_dep" '.
10166: ' value="'.$delidx.'" />'.&mt('Yes').'</label>'.
10167: &embedded_file_element('upload_embedded',$delidx,
10168: $oldfile,\%mapping,$allfiles,
10169: $codebase,'delete').'</td>'.
10170: &end_data_table_row()."\n";
10171: $numunused ++;
10172: $delidx ++;
1.987 raeburn 10173: }
10174: if ($upload_output) {
10175: $upload_output = &start_data_table().
10176: $upload_output.
10177: &end_data_table()."\n";
10178: }
1.1071 raeburn 10179: if ($modify_output) {
10180: $modify_output = &start_data_table().
10181: &start_data_table_header_row().
10182: '<th>'.&mt('File').'</th>'.
10183: '<th>'.&mt('Size (KB)').'</th>'.
10184: '<th>'.&mt('Modified').'</th>'.
10185: '<th>'.&mt('Upload replacement?').'</th>'.
10186: &end_data_table_header_row().
10187: $modify_output.
10188: &end_data_table()."\n";
10189: }
10190: if ($delete_output) {
10191: $delete_output = &start_data_table().
10192: &start_data_table_header_row().
10193: '<th>'.&mt('File').'</th>'.
10194: '<th>'.&mt('Size (KB)').'</th>'.
10195: '<th>'.&mt('Modified').'</th>'.
10196: '<th>'.&mt('Delete?').'</th>'.
10197: &end_data_table_header_row().
10198: $delete_output.
10199: &end_data_table()."\n";
10200: }
1.987 raeburn 10201: my $applies = 0;
10202: if ($numremref) {
10203: $applies ++;
10204: }
10205: if ($numinvalid) {
10206: $applies ++;
10207: }
10208: if ($numexisting) {
10209: $applies ++;
10210: }
1.1071 raeburn 10211: if ($counter || $numunused) {
1.987 raeburn 10212: $output = '<form name="upload_embedded" action="'.$actionurl.'"'.
10213: ' method="post" enctype="multipart/form-data">'."\n".
1.1071 raeburn 10214: $state.'<h3>'.$heading.'</h3>';
10215: if ($actionurl eq '/adm/dependencies') {
10216: if ($numnew) {
10217: $output .= '<h4>'.&mt('Missing dependencies').'</h4>'.
10218: '<p>'.&mt('The following files need to be uploaded.').'</p>'."\n".
10219: $upload_output.'<br />'."\n";
10220: }
10221: if ($numexisting) {
10222: $output .= '<h4>'.&mt('Uploaded dependencies (in use)').'</h4>'.
10223: '<p>'.&mt('Upload a new file to replace the one currently in use.').'</p>'."\n".
10224: $modify_output.'<br />'."\n";
10225: $buttontext = &mt('Save changes');
10226: }
10227: if ($numunused) {
10228: $output .= '<h4>'.&mt('Unused files').'</h4>'.
10229: '<p>'.&mt('The following uploaded files are no longer used.').'</p>'."\n".
10230: $delete_output.'<br />'."\n";
10231: $buttontext = &mt('Save changes');
10232: }
10233: } else {
10234: $output .= $upload_output.'<br />'."\n";
10235: }
10236: $output .= '<input type ="hidden" name="number_embedded_items" value="'.
10237: $counter.'" />'."\n";
10238: if ($actionurl eq '/adm/dependencies') {
10239: $output .= '<input type ="hidden" name="number_newemb_items" value="'.
10240: $numnew.'" />'."\n";
10241: } elsif ($actionurl eq '') {
1.987 raeburn 10242: $output .= '<input type="hidden" name="phase" value="three" />';
10243: }
10244: } elsif ($applies) {
10245: $output = '<b>'.&mt('Referenced files').'</b>:<br />';
10246: if ($applies > 1) {
10247: $output .=
1.1123 raeburn 10248: &mt('No dependencies need to be uploaded, as one of the following applies to each reference:').'<ul>';
1.987 raeburn 10249: if ($numremref) {
10250: $output .= '<li>'.&mt('reference is to a URL which points to another server').'</li>'."\n";
10251: }
10252: if ($numinvalid) {
10253: $output .= '<li>'.&mt('reference is to file with a name containing invalid characters').'</li>'."\n";
10254: }
10255: if ($numexisting) {
10256: $output .= '<li>'.&mt('reference is to an existing file at the specified location').'</li>'."\n";
10257: }
10258: $output .= '</ul><br />';
10259: } elsif ($numremref) {
10260: $output .= '<p>'.&mt('None to upload, as all references are to URLs pointing to another server.').'</p>';
10261: } elsif ($numinvalid) {
10262: $output .= '<p>'.&mt('None to upload, as all references are to files with names containing invalid characters.').'</p>';
10263: } elsif ($numexisting) {
10264: $output .= '<p>'.&mt('None to upload, as all references are to existing files.').'</p>';
10265: }
10266: $output .= $upload_output.'<br />';
10267: }
10268: my ($pathchange_output,$chgcount);
1.1071 raeburn 10269: $chgcount = $counter;
1.987 raeburn 10270: if (keys(%pathchanges) > 0) {
10271: foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%pathchanges)) {
1.1071 raeburn 10272: if ($counter) {
1.987 raeburn 10273: $output .= &embedded_file_element('pathchange',$chgcount,
10274: $embed_file,\%mapping,
1.1071 raeburn 10275: $allfiles,$codebase,'change');
1.987 raeburn 10276: } else {
10277: $pathchange_output .=
10278: &start_data_table_row().
10279: '<td><input type ="checkbox" name="namechange" value="'.
10280: $chgcount.'" checked="checked" /></td>'.
10281: '<td>'.$mapping{$embed_file}.'</td>'.
10282: '<td>'.$embed_file.
10283: &embedded_file_element('pathchange',$numpathchg,$embed_file,
1.1071 raeburn 10284: \%mapping,$allfiles,$codebase,'change').
1.987 raeburn 10285: '</td>'.&end_data_table_row();
1.660 raeburn 10286: }
1.987 raeburn 10287: $numpathchg ++;
10288: $chgcount ++;
1.660 raeburn 10289: }
10290: }
1.1127 raeburn 10291: if (($counter) || ($numunused)) {
1.987 raeburn 10292: if ($numpathchg) {
10293: $output .= '<input type ="hidden" name="number_pathchange_items" value="'.
10294: $numpathchg.'" />'."\n";
10295: }
10296: if (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank') ||
10297: ($actionurl eq '/adm/imsimport')) {
10298: $output .= '<input type="hidden" name="phase" value="three" />'."\n";
10299: } elsif ($actionurl eq '/adm/portfolio' || $actionurl eq '/adm/coursegrp_portfolio') {
10300: $output .= '<input type="hidden" name="action" value="upload_embedded" />';
1.1071 raeburn 10301: } elsif ($actionurl eq '/adm/dependencies') {
10302: $output .= '<input type="hidden" name="action" value="process_changes" />';
1.987 raeburn 10303: }
1.1123 raeburn 10304: $output .= '<input type ="submit" value="'.$buttontext.'" />'."\n".'</form>'."\n";
1.987 raeburn 10305: } elsif ($numpathchg) {
10306: my %pathchange = ();
10307: $output .= &modify_html_form('pathchange',$actionurl,$state,\%pathchange,$pathchange_output);
10308: if (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
10309: $output .= '<p>'.&mt('or').'</p>';
1.1123 raeburn 10310: }
1.987 raeburn 10311: }
1.1071 raeburn 10312: return ($output,$counter,$numpathchg);
1.987 raeburn 10313: }
10314:
1.1147 raeburn 10315: =pod
10316:
10317: =item * clean_path($name)
10318:
10319: Performs clean-up of directories, subdirectories and filename in an
10320: embedded object, referenced in an HTML file which is being uploaded
10321: to a course or portfolio, where
10322: "Upload embedded images/multimedia files if HTML file" checkbox was
10323: checked.
10324:
10325: Clean-up is similar to replacements in lonnet::clean_filename()
10326: except each / between sub-directory and next level is preserved.
10327:
10328: =cut
10329:
10330: sub clean_path {
10331: my ($embed_file) = @_;
10332: $embed_file =~s{^/+}{};
10333: my @contents;
10334: if ($embed_file =~ m{/}) {
10335: @contents = split(/\//,$embed_file);
10336: } else {
10337: @contents = ($embed_file);
10338: }
10339: my $lastidx = scalar(@contents)-1;
10340: for (my $i=0; $i<=$lastidx; $i++) {
10341: $contents[$i]=~s{\\}{/}g;
10342: $contents[$i]=~s/\s+/\_/g;
10343: $contents[$i]=~s{[^/\w\.\-]}{}g;
10344: if ($i == $lastidx) {
10345: $contents[$i]=~s/\.(\d+)(?=\.)/_$1/g;
10346: }
10347: }
10348: if ($lastidx > 0) {
10349: return join('/',@contents);
10350: } else {
10351: return $contents[0];
10352: }
10353: }
10354:
1.987 raeburn 10355: sub embedded_file_element {
1.1071 raeburn 10356: my ($context,$num,$embed_file,$mapping,$allfiles,$codebase,$type) = @_;
1.987 raeburn 10357: return unless ((ref($mapping) eq 'HASH') && (ref($allfiles) eq 'HASH') &&
10358: (ref($codebase) eq 'HASH'));
10359: my $output;
1.1071 raeburn 10360: if (($context eq 'upload_embedded') && ($type ne 'delete')) {
1.987 raeburn 10361: $output = '<input name="embedded_item_'.$num.'" type="file" value="" />'."\n";
10362: }
10363: $output .= '<input name="embedded_orig_'.$num.'" type="hidden" value="'.
10364: &escape($embed_file).'" />';
10365: unless (($context eq 'upload_embedded') &&
10366: ($mapping->{$embed_file} eq $embed_file)) {
10367: $output .='
10368: <input name="embedded_ref_'.$num.'" type="hidden" value="'.&escape($mapping->{$embed_file}).'" />';
10369: }
10370: my $attrib;
10371: if (ref($allfiles->{$mapping->{$embed_file}}) eq 'ARRAY') {
10372: $attrib = &escape(join(':',@{$allfiles->{$mapping->{$embed_file}}}));
10373: }
10374: $output .=
10375: "\n\t\t".
10376: '<input name="embedded_attrib_'.$num.'" type="hidden" value="'.
10377: $attrib.'" />';
10378: if (exists($codebase->{$mapping->{$embed_file}})) {
10379: $output .=
10380: "\n\t\t".
10381: '<input name="codebase_'.$num.'" type="hidden" value="'.
10382: &escape($codebase->{$mapping->{$embed_file}}).'" />';
1.984 raeburn 10383: }
1.987 raeburn 10384: return $output;
1.660 raeburn 10385: }
10386:
1.1071 raeburn 10387: sub get_dependency_details {
10388: my ($currfile,$currsubfile,$embed_file) = @_;
10389: my ($size,$mtime,$showsize,$showmtime);
10390: if ((ref($currfile) eq 'HASH') && (ref($currsubfile))) {
10391: if ($embed_file =~ m{/}) {
10392: my ($path,$fname) = split(/\//,$embed_file);
10393: if (ref($currsubfile->{$path}{$fname}) eq 'ARRAY') {
10394: ($size,$mtime) = @{$currsubfile->{$path}{$fname}};
10395: }
10396: } else {
10397: if (ref($currfile->{$embed_file}) eq 'ARRAY') {
10398: ($size,$mtime) = @{$currfile->{$embed_file}};
10399: }
10400: }
10401: $showsize = $size/1024.0;
10402: $showsize = sprintf("%.1f",$showsize);
10403: if ($mtime > 0) {
10404: $showmtime = &Apache::lonlocal::locallocaltime($mtime);
10405: }
10406: }
10407: return ($showsize,$showmtime);
10408: }
10409:
10410: sub ask_embedded_js {
10411: return <<"END";
10412: <script type="text/javascript"">
10413: // <![CDATA[
10414: function toggleBrowse(counter) {
10415: var chkboxid = document.getElementById('mod_upload_dep_'+counter);
10416: var fileid = document.getElementById('embedded_item_'+counter);
10417: var uploaddivid = document.getElementById('moduploaddep_'+counter);
10418: if (chkboxid.checked == true) {
10419: uploaddivid.style.display='block';
10420: } else {
10421: uploaddivid.style.display='none';
10422: fileid.value = '';
10423: }
10424: }
10425: // ]]>
10426: </script>
10427:
10428: END
10429: }
10430:
1.661 raeburn 10431: sub upload_embedded {
10432: my ($context,$dirpath,$uname,$udom,$dir_root,$url_root,$group,$disk_quota,
1.987 raeburn 10433: $current_disk_usage,$hiddenstate,$actionurl) = @_;
10434: my (%pathchange,$output,$modifyform,$footer,$returnflag);
1.661 raeburn 10435: for (my $i=0; $i<$env{'form.number_embedded_items'}; $i++) {
10436: next if (!exists($env{'form.embedded_item_'.$i.'.filename'}));
10437: my $orig_uploaded_filename =
10438: $env{'form.embedded_item_'.$i.'.filename'};
1.987 raeburn 10439: foreach my $type ('orig','ref','attrib','codebase') {
10440: if ($env{'form.embedded_'.$type.'_'.$i} ne '') {
10441: $env{'form.embedded_'.$type.'_'.$i} =
10442: &unescape($env{'form.embedded_'.$type.'_'.$i});
10443: }
10444: }
1.661 raeburn 10445: my ($path,$fname) =
10446: ($env{'form.embedded_orig_'.$i} =~ m{(.*/)([^/]*)});
10447: # no path, whole string is fname
10448: if (!$fname) { $fname = $env{'form.embedded_orig_'.$i} };
10449: $fname = &Apache::lonnet::clean_filename($fname);
10450: # See if there is anything left
10451: next if ($fname eq '');
10452:
10453: # Check if file already exists as a file or directory.
10454: my ($state,$msg);
10455: if ($context eq 'portfolio') {
10456: my $port_path = $dirpath;
10457: if ($group ne '') {
10458: $port_path = "groups/$group/$port_path";
10459: }
1.987 raeburn 10460: ($state,$msg) = &check_for_upload($env{'form.currentpath'}.$path,
10461: $fname,$group,'embedded_item_'.$i,
1.661 raeburn 10462: $dir_root,$port_path,$disk_quota,
10463: $current_disk_usage,$uname,$udom);
10464: if ($state eq 'will_exceed_quota'
1.984 raeburn 10465: || $state eq 'file_locked') {
1.661 raeburn 10466: $output .= $msg;
10467: next;
10468: }
10469: } elsif (($context eq 'author') || ($context eq 'testbank')) {
10470: ($state,$msg) = &check_for_existing($path,$fname,'embedded_item_'.$i);
10471: if ($state eq 'exists') {
10472: $output .= $msg;
10473: next;
10474: }
10475: }
10476: # Check if extension is valid
10477: if (($fname =~ /\.(\w+)$/) &&
10478: (&Apache::loncommon::fileembstyle($1) eq 'hdn')) {
1.1155 bisitz 10479: $output .= &mt('Invalid file extension ([_1]) - reserved for internal use.',$1)
10480: .' '.&mt('Rename the file with a different extension and re-upload.').'<br />';
1.661 raeburn 10481: next;
10482: } elsif (($fname =~ /\.(\w+)$/) &&
10483: (!defined(&Apache::loncommon::fileembstyle($1)))) {
1.987 raeburn 10484: $output .= &mt('Unrecognized file extension ([_1]) - rename the file with a proper extension and re-upload.',$1).'<br />';
1.661 raeburn 10485: next;
10486: } elsif ($fname=~/\.(\d+)\.(\w+)$/) {
1.1120 bisitz 10487: $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 10488: next;
10489: }
10490: $env{'form.embedded_item_'.$i.'.filename'}=$fname;
1.1123 raeburn 10491: my $subdir = $path;
10492: $subdir =~ s{/+$}{};
1.661 raeburn 10493: if ($context eq 'portfolio') {
1.984 raeburn 10494: my $result;
10495: if ($state eq 'existingfile') {
10496: $result=
10497: &Apache::lonnet::userfileupload('embedded_item_'.$i,'existingfile',
1.1123 raeburn 10498: $dirpath.$env{'form.currentpath'}.$subdir);
1.661 raeburn 10499: } else {
1.984 raeburn 10500: $result=
10501: &Apache::lonnet::userfileupload('embedded_item_'.$i,'',
1.987 raeburn 10502: $dirpath.
1.1123 raeburn 10503: $env{'form.currentpath'}.$subdir);
1.984 raeburn 10504: if ($result !~ m|^/uploaded/|) {
10505: $output .= '<span class="LC_error">'
10506: .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].'
10507: ,$result,$orig_uploaded_filename,$env{'form.embedded_orig_'.$i})
10508: .'</span><br />';
10509: next;
10510: } else {
1.987 raeburn 10511: $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
10512: $path.$fname.'</span>').'<br />';
1.984 raeburn 10513: }
1.661 raeburn 10514: }
1.1123 raeburn 10515: } elsif (($context eq 'coursedoc') || ($context eq 'syllabus')) {
1.1126 raeburn 10516: my $extendedsubdir = $dirpath.'/'.$subdir;
10517: $extendedsubdir =~ s{/+$}{};
1.987 raeburn 10518: my $result =
1.1126 raeburn 10519: &Apache::lonnet::userfileupload('embedded_item_'.$i,$context,$extendedsubdir);
1.987 raeburn 10520: if ($result !~ m|^/uploaded/|) {
10521: $output .= '<span class="LC_error">'
10522: .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].'
10523: ,$result,$orig_uploaded_filename,$env{'form.embedded_orig_'.$i})
10524: .'</span><br />';
10525: next;
10526: } else {
10527: $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
10528: $path.$fname.'</span>').'<br />';
1.1125 raeburn 10529: if ($context eq 'syllabus') {
10530: &Apache::lonnet::make_public_indefinitely($result);
10531: }
1.987 raeburn 10532: }
1.661 raeburn 10533: } else {
10534: # Save the file
10535: my $target = $env{'form.embedded_item_'.$i};
10536: my $fullpath = $dir_root.$dirpath.'/'.$path;
10537: my $dest = $fullpath.$fname;
10538: my $url = $url_root.$dirpath.'/'.$path.$fname;
1.1027 raeburn 10539: my @parts=split(/\//,"$dirpath/$path");
1.661 raeburn 10540: my $count;
10541: my $filepath = $dir_root;
1.1027 raeburn 10542: foreach my $subdir (@parts) {
10543: $filepath .= "/$subdir";
10544: if (!-e $filepath) {
1.661 raeburn 10545: mkdir($filepath,0770);
10546: }
10547: }
10548: my $fh;
10549: if (!open($fh,'>'.$dest)) {
10550: &Apache::lonnet::logthis('Failed to create '.$dest);
10551: $output .= '<span class="LC_error">'.
1.1071 raeburn 10552: &mt('An error occurred while trying to upload [_1] for embedded element [_2].',
10553: $orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
1.661 raeburn 10554: '</span><br />';
10555: } else {
10556: if (!print $fh $env{'form.embedded_item_'.$i}) {
10557: &Apache::lonnet::logthis('Failed to write to '.$dest);
10558: $output .= '<span class="LC_error">'.
1.1071 raeburn 10559: &mt('An error occurred while writing the file [_1] for embedded element [_2].',
10560: $orig_uploaded_filename,$env{'form.embedded_orig_'.$i}).
1.661 raeburn 10561: '</span><br />';
10562: } else {
1.987 raeburn 10563: $output .= &mt('Uploaded [_1]','<span class="LC_filename">'.
10564: $url.'</span>').'<br />';
10565: unless ($context eq 'testbank') {
10566: $footer .= &mt('View embedded file: [_1]',
10567: '<a href="'.$url.'">'.$fname.'</a>').'<br />';
10568: }
10569: }
10570: close($fh);
10571: }
10572: }
10573: if ($env{'form.embedded_ref_'.$i}) {
10574: $pathchange{$i} = 1;
10575: }
10576: }
10577: if ($output) {
10578: $output = '<p>'.$output.'</p>';
10579: }
10580: $output .= &modify_html_form('upload_embedded',$actionurl,$hiddenstate,\%pathchange);
10581: $returnflag = 'ok';
1.1071 raeburn 10582: my $numpathchgs = scalar(keys(%pathchange));
10583: if ($numpathchgs > 0) {
1.987 raeburn 10584: if ($context eq 'portfolio') {
10585: $output .= '<p>'.&mt('or').'</p>';
10586: } elsif ($context eq 'testbank') {
1.1071 raeburn 10587: $output .= '<p>'.&mt('Or [_1]continue[_2] the testbank import without modifying the reference(s).',
10588: '<a href="javascript:document.testbankForm.submit();">','</a>').'</p>';
1.987 raeburn 10589: $returnflag = 'modify_orightml';
10590: }
10591: }
1.1071 raeburn 10592: return ($output.$footer,$returnflag,$numpathchgs);
1.987 raeburn 10593: }
10594:
10595: sub modify_html_form {
10596: my ($context,$actionurl,$hiddenstate,$pathchange,$pathchgtable) = @_;
10597: my $end = 0;
10598: my $modifyform;
10599: if ($context eq 'upload_embedded') {
10600: return unless (ref($pathchange) eq 'HASH');
10601: if ($env{'form.number_embedded_items'}) {
10602: $end += $env{'form.number_embedded_items'};
10603: }
10604: if ($env{'form.number_pathchange_items'}) {
10605: $end += $env{'form.number_pathchange_items'};
10606: }
10607: if ($end) {
10608: for (my $i=0; $i<$end; $i++) {
10609: if ($i < $env{'form.number_embedded_items'}) {
10610: next unless($pathchange->{$i});
10611: }
10612: $modifyform .=
10613: &start_data_table_row().
10614: '<td><input type ="checkbox" name="namechange" value="'.$i.'" '.
10615: 'checked="checked" /></td>'.
10616: '<td>'.$env{'form.embedded_ref_'.$i}.
10617: '<input type="hidden" name="embedded_ref_'.$i.'" value="'.
10618: &escape($env{'form.embedded_ref_'.$i}).'" />'.
10619: '<input type="hidden" name="embedded_codebase_'.$i.'" value="'.
10620: &escape($env{'form.embedded_codebase_'.$i}).'" />'.
10621: '<input type="hidden" name="embedded_attrib_'.$i.'" value="'.
10622: &escape($env{'form.embedded_attrib_'.$i}).'" /></td>'.
10623: '<td>'.$env{'form.embedded_orig_'.$i}.
10624: '<input type="hidden" name="embedded_orig_'.$i.'" value="'.
10625: &escape($env{'form.embedded_orig_'.$i}).'" /></td>'.
10626: &end_data_table_row();
1.1071 raeburn 10627: }
1.987 raeburn 10628: }
10629: } else {
10630: $modifyform = $pathchgtable;
10631: if (($actionurl eq '/adm/upload') || ($actionurl eq '/adm/testbank')) {
10632: $hiddenstate .= '<input type="hidden" name="phase" value="four" />';
10633: } elsif (($actionurl eq '/adm/portfolio') || ($actionurl eq '/adm/coursegrp_portfolio')) {
10634: $hiddenstate .= '<input type="hidden" name="action" value="modify_orightml" />';
10635: }
10636: }
10637: if ($modifyform) {
1.1071 raeburn 10638: if ($actionurl eq '/adm/dependencies') {
10639: $hiddenstate .= '<input type="hidden" name="action" value="modifyhrefs" />';
10640: }
1.987 raeburn 10641: return '<h3>'.&mt('Changes in content of HTML file required').'</h3>'."\n".
10642: '<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".
10643: '<li>'.&mt('For consistency between the reference(s) and the location of the corresponding stored file within LON-CAPA.').'</li>'."\n".
10644: '<li>'.&mt('To change absolute paths to relative paths, or replace directory traversal via "../" within the original reference.').'</li>'."\n".
10645: '</ol></p>'."\n".'<p>'.
10646: &mt('LON-CAPA can make the required changes to your HTML file.').'</p>'."\n".
10647: '<form method="post" name="refchanger" action="'.$actionurl.'">'.
10648: &start_data_table()."\n".
10649: &start_data_table_header_row().
10650: '<th>'.&mt('Change?').'</th>'.
10651: '<th>'.&mt('Current reference').'</th>'.
10652: '<th>'.&mt('Required reference').'</th>'.
10653: &end_data_table_header_row()."\n".
10654: $modifyform.
10655: &end_data_table().'<br />'."\n".$hiddenstate.
10656: '<input type="submit" name="pathchanges" value="'.&mt('Modify HTML file').'" />'.
10657: '</form>'."\n";
10658: }
10659: return;
10660: }
10661:
10662: sub modify_html_refs {
1.1123 raeburn 10663: my ($context,$dirpath,$uname,$udom,$dir_root,$url) = @_;
1.987 raeburn 10664: my $container;
10665: if ($context eq 'portfolio') {
10666: $container = $env{'form.container'};
10667: } elsif ($context eq 'coursedoc') {
10668: $container = $env{'form.primaryurl'};
1.1071 raeburn 10669: } elsif ($context eq 'manage_dependencies') {
10670: (undef,undef,$container) = &Apache::lonnet::decode_symb($env{'form.symb'});
10671: $container = "/$container";
1.1123 raeburn 10672: } elsif ($context eq 'syllabus') {
10673: $container = $url;
1.987 raeburn 10674: } else {
1.1027 raeburn 10675: $container = $Apache::lonnet::perlvar{'lonDocRoot'}.$env{'form.filename'};
1.987 raeburn 10676: }
10677: my (%allfiles,%codebase,$output,$content);
10678: my @changes = &get_env_multiple('form.namechange');
1.1126 raeburn 10679: unless ((@changes > 0) || ($context eq 'syllabus')) {
1.1071 raeburn 10680: if (wantarray) {
10681: return ('',0,0);
10682: } else {
10683: return;
10684: }
10685: }
10686: if (($context eq 'portfolio') || ($context eq 'coursedoc') ||
1.1123 raeburn 10687: ($context eq 'manage_dependencies') || ($context eq 'syllabus')) {
1.1071 raeburn 10688: unless ($container =~ m{^/uploaded/\Q$udom\E/\Q$uname\E/}) {
10689: if (wantarray) {
10690: return ('',0,0);
10691: } else {
10692: return;
10693: }
10694: }
1.987 raeburn 10695: $content = &Apache::lonnet::getfile($container);
1.1071 raeburn 10696: if ($content eq '-1') {
10697: if (wantarray) {
10698: return ('',0,0);
10699: } else {
10700: return;
10701: }
10702: }
1.987 raeburn 10703: } else {
1.1071 raeburn 10704: unless ($container =~ /^\Q$dir_root\E/) {
10705: if (wantarray) {
10706: return ('',0,0);
10707: } else {
10708: return;
10709: }
10710: }
1.987 raeburn 10711: if (open(my $fh,"<$container")) {
10712: $content = join('', <$fh>);
10713: close($fh);
10714: } else {
1.1071 raeburn 10715: if (wantarray) {
10716: return ('',0,0);
10717: } else {
10718: return;
10719: }
1.987 raeburn 10720: }
10721: }
10722: my ($count,$codebasecount) = (0,0);
10723: my $mm = new File::MMagic;
10724: my $mime_type = $mm->checktype_contents($content);
10725: if ($mime_type eq 'text/html') {
10726: my $parse_result =
10727: &Apache::lonnet::extract_embedded_items($container,\%allfiles,
10728: \%codebase,\$content);
10729: if ($parse_result eq 'ok') {
10730: foreach my $i (@changes) {
10731: my $orig = &unescape($env{'form.embedded_orig_'.$i});
10732: my $ref = &unescape($env{'form.embedded_ref_'.$i});
10733: if ($allfiles{$ref}) {
10734: my $newname = $orig;
10735: my ($attrib_regexp,$codebase);
1.1006 raeburn 10736: $attrib_regexp = &unescape($env{'form.embedded_attrib_'.$i});
1.987 raeburn 10737: if ($attrib_regexp =~ /:/) {
10738: $attrib_regexp =~ s/\:/|/g;
10739: }
10740: if ($content =~ m{($attrib_regexp\s*=\s*['"]?)\Q$ref\E(['"]?)}) {
10741: my $numchg = ($content =~ s{($attrib_regexp\s*=\s*['"]?)\Q$ref\E(['"]?)}{$1$newname$2}gi);
10742: $count += $numchg;
1.1123 raeburn 10743: $allfiles{$newname} = $allfiles{$ref};
1.1148 raeburn 10744: delete($allfiles{$ref});
1.987 raeburn 10745: }
10746: if ($env{'form.embedded_codebase_'.$i} ne '') {
1.1006 raeburn 10747: $codebase = &unescape($env{'form.embedded_codebase_'.$i});
1.987 raeburn 10748: my $numchg = ($content =~ s/(codebase\s*=\s*["']?)\Q$codebase\E(["']?)/$1.$2/i); #' stupid emacs
10749: $codebasecount ++;
10750: }
10751: }
10752: }
1.1123 raeburn 10753: my $skiprewrites;
1.987 raeburn 10754: if ($count || $codebasecount) {
10755: my $saveresult;
1.1071 raeburn 10756: if (($context eq 'portfolio') || ($context eq 'coursedoc') ||
1.1123 raeburn 10757: ($context eq 'manage_dependencies') || ($context eq 'syllabus')) {
1.987 raeburn 10758: my $url = &Apache::lonnet::store_edited_file($container,$content,$udom,$uname,\$saveresult);
10759: if ($url eq $container) {
10760: my ($fname) = ($container =~ m{/([^/]+)$});
10761: $output = '<p>'.&mt('Updated [quant,_1,reference] in [_2].',
10762: $count,'<span class="LC_filename">'.
1.1071 raeburn 10763: $fname.'</span>').'</p>';
1.987 raeburn 10764: } else {
10765: $output = '<p class="LC_error">'.
10766: &mt('Error: update failed for: [_1].',
10767: '<span class="LC_filename">'.
10768: $container.'</span>').'</p>';
10769: }
1.1123 raeburn 10770: if ($context eq 'syllabus') {
10771: unless ($saveresult eq 'ok') {
10772: $skiprewrites = 1;
10773: }
10774: }
1.987 raeburn 10775: } else {
10776: if (open(my $fh,">$container")) {
10777: print $fh $content;
10778: close($fh);
10779: $output = '<p>'.&mt('Updated [quant,_1,reference] in [_2].',
10780: $count,'<span class="LC_filename">'.
10781: $container.'</span>').'</p>';
1.661 raeburn 10782: } else {
1.987 raeburn 10783: $output = '<p class="LC_error">'.
10784: &mt('Error: could not update [_1].',
10785: '<span class="LC_filename">'.
10786: $container.'</span>').'</p>';
1.661 raeburn 10787: }
10788: }
10789: }
1.1123 raeburn 10790: if (($context eq 'syllabus') && (!$skiprewrites)) {
10791: my ($actionurl,$state);
10792: $actionurl = "/public/$udom/$uname/syllabus";
10793: my ($ignore,$num,$numpathchanges,$existing,$mapping) =
10794: &ask_for_embedded_content($actionurl,$state,\%allfiles,
10795: \%codebase,
10796: {'context' => 'rewrites',
10797: 'ignore_remote_references' => 1,});
10798: if (ref($mapping) eq 'HASH') {
10799: my $rewrites = 0;
10800: foreach my $key (keys(%{$mapping})) {
10801: next if ($key =~ m{^https?://});
10802: my $ref = $mapping->{$key};
10803: my $newname = "/uploaded/$udom/$uname/portfolio/syllabus/$key";
10804: my $attrib;
10805: if (ref($allfiles{$mapping->{$key}}) eq 'ARRAY') {
10806: $attrib = join('|',@{$allfiles{$mapping->{$key}}});
10807: }
10808: if ($content =~ m{($attrib\s*=\s*['"]?)\Q$ref\E(['"]?)}) {
10809: my $numchg = ($content =~ s{($attrib\s*=\s*['"]?)\Q$ref\E(['"]?)}{$1$newname$2}gi);
10810: $rewrites += $numchg;
10811: }
10812: }
10813: if ($rewrites) {
10814: my $saveresult;
10815: my $url = &Apache::lonnet::store_edited_file($container,$content,$udom,$uname,\$saveresult);
10816: if ($url eq $container) {
10817: my ($fname) = ($container =~ m{/([^/]+)$});
10818: $output .= '<p>'.&mt('Rewrote [quant,_1,link] as [quant,_1,absolute link] in [_2].',
10819: $count,'<span class="LC_filename">'.
10820: $fname.'</span>').'</p>';
10821: } else {
10822: $output .= '<p class="LC_error">'.
10823: &mt('Error: could not update links in [_1].',
10824: '<span class="LC_filename">'.
10825: $container.'</span>').'</p>';
10826:
10827: }
10828: }
10829: }
10830: }
1.987 raeburn 10831: } else {
10832: &logthis('Failed to parse '.$container.
10833: ' to modify references: '.$parse_result);
1.661 raeburn 10834: }
10835: }
1.1071 raeburn 10836: if (wantarray) {
10837: return ($output,$count,$codebasecount);
10838: } else {
10839: return $output;
10840: }
1.661 raeburn 10841: }
10842:
10843: sub check_for_existing {
10844: my ($path,$fname,$element) = @_;
10845: my ($state,$msg);
10846: if (-d $path.'/'.$fname) {
10847: $state = 'exists';
10848: $msg = &mt('Unable to upload [_1]. A directory by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$path);
10849: } elsif (-e $path.'/'.$fname) {
10850: $state = 'exists';
10851: $msg = &mt('Unable to upload [_1]. A file by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$path);
10852: }
10853: if ($state eq 'exists') {
10854: $msg = '<span class="LC_error">'.$msg.'</span><br />';
10855: }
10856: return ($state,$msg);
10857: }
10858:
10859: sub check_for_upload {
10860: my ($path,$fname,$group,$element,$portfolio_root,$port_path,
10861: $disk_quota,$current_disk_usage,$uname,$udom) = @_;
1.985 raeburn 10862: my $filesize = length($env{'form.'.$element});
10863: if (!$filesize) {
10864: my $msg = '<span class="LC_error">'.
10865: &mt('Unable to upload [_1]. (size = [_2] bytes)',
10866: '<span class="LC_filename">'.$fname.'</span>',
10867: $filesize).'<br />'.
1.1007 raeburn 10868: &mt('Either the file you attempted to upload was empty, or your web browser was unable to read its contents.').'<br />'.
1.985 raeburn 10869: '</span>';
10870: return ('zero_bytes',$msg);
10871: }
10872: $filesize = $filesize/1000; #express in k (1024?)
1.661 raeburn 10873: my $getpropath = 1;
1.1021 raeburn 10874: my ($dirlistref,$listerror) =
10875: &Apache::lonnet::dirlist($portfolio_root.$path,$udom,$uname,$getpropath);
1.661 raeburn 10876: my $found_file = 0;
10877: my $locked_file = 0;
1.991 raeburn 10878: my @lockers;
10879: my $navmap;
10880: if ($env{'request.course.id'}) {
10881: $navmap = Apache::lonnavmaps::navmap->new();
10882: }
1.1021 raeburn 10883: if (ref($dirlistref) eq 'ARRAY') {
10884: foreach my $line (@{$dirlistref}) {
10885: my ($file_name,$rest)=split(/\&/,$line,2);
10886: if ($file_name eq $fname){
10887: $file_name = $path.$file_name;
10888: if ($group ne '') {
10889: $file_name = $group.$file_name;
10890: }
10891: $found_file = 1;
10892: if (&Apache::lonnet::is_locked($file_name,$udom,$uname,\@lockers) eq 'true') {
10893: foreach my $lock (@lockers) {
10894: if (ref($lock) eq 'ARRAY') {
10895: my ($symb,$crsid) = @{$lock};
10896: if ($crsid eq $env{'request.course.id'}) {
10897: if (ref($navmap)) {
10898: my $res = $navmap->getBySymb($symb);
10899: foreach my $part (@{$res->parts()}) {
10900: my ($slot_status,$slot_time,$slot_name)=$res->check_for_slot($part);
10901: unless (($slot_status == $res->RESERVED) ||
10902: ($slot_status == $res->RESERVED_LOCATION)) {
10903: $locked_file = 1;
10904: }
1.991 raeburn 10905: }
1.1021 raeburn 10906: } else {
10907: $locked_file = 1;
1.991 raeburn 10908: }
10909: } else {
10910: $locked_file = 1;
10911: }
10912: }
1.1021 raeburn 10913: }
10914: } else {
10915: my @info = split(/\&/,$rest);
10916: my $currsize = $info[6]/1000;
10917: if ($currsize < $filesize) {
10918: my $extra = $filesize - $currsize;
10919: if (($current_disk_usage + $extra) > $disk_quota) {
10920: my $msg = '<span class="LC_error">'.
10921: &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.',
10922: '<span class="LC_filename">'.$fname.'</span>',$filesize,$currsize).'</span>'.
10923: '<br />'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',
10924: $disk_quota,$current_disk_usage);
10925: return ('will_exceed_quota',$msg);
10926: }
1.984 raeburn 10927: }
10928: }
1.661 raeburn 10929: }
10930: }
10931: }
10932: if (($current_disk_usage + $filesize) > $disk_quota){
10933: my $msg = '<span class="LC_error">'.
10934: &mt('Unable to upload [_1]. (size = [_2] kilobytes). Disk quota will be exceeded.','<span class="LC_filename">'.$fname.'</span>',$filesize).'</span>'.
10935: '<br />'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',$disk_quota,$current_disk_usage);
10936: return ('will_exceed_quota',$msg);
10937: } elsif ($found_file) {
10938: if ($locked_file) {
10939: my $msg = '<span class="LC_error">';
10940: $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>');
10941: $msg .= '</span><br />';
10942: $msg .= &mt('You will be able to rename or delete existing [_1] after a grade has been assigned.','<span class="LC_filename">'.$fname.'</span>');
10943: return ('file_locked',$msg);
10944: } else {
10945: my $msg = '<span class="LC_error">';
1.984 raeburn 10946: $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 10947: $msg .= '</span>';
1.984 raeburn 10948: return ('existingfile',$msg);
1.661 raeburn 10949: }
10950: }
10951: }
10952:
1.987 raeburn 10953: sub check_for_traversal {
10954: my ($path,$url,$toplevel) = @_;
10955: my @parts=split(/\//,$path);
10956: my $cleanpath;
10957: my $fullpath = $url;
10958: for (my $i=0;$i<@parts;$i++) {
10959: next if ($parts[$i] eq '.');
10960: if ($parts[$i] eq '..') {
10961: $fullpath =~ s{([^/]+/)$}{};
10962: } else {
10963: $fullpath .= $parts[$i].'/';
10964: }
10965: }
10966: if ($fullpath =~ /^\Q$url\E(.*)$/) {
10967: $cleanpath = $1;
10968: } elsif ($fullpath =~ /^\Q$toplevel\E(.*)$/) {
10969: my $curr_toprel = $1;
10970: my @parts = split(/\//,$curr_toprel);
10971: my ($url_toprel) = ($url =~ /^\Q$toplevel\E(.*)$/);
10972: my @urlparts = split(/\//,$url_toprel);
10973: my $doubledots;
10974: my $startdiff = -1;
10975: for (my $i=0; $i<@urlparts; $i++) {
10976: if ($startdiff == -1) {
10977: unless ($urlparts[$i] eq $parts[$i]) {
10978: $startdiff = $i;
10979: $doubledots .= '../';
10980: }
10981: } else {
10982: $doubledots .= '../';
10983: }
10984: }
10985: if ($startdiff > -1) {
10986: $cleanpath = $doubledots;
10987: for (my $i=$startdiff; $i<@parts; $i++) {
10988: $cleanpath .= $parts[$i].'/';
10989: }
10990: }
10991: }
10992: $cleanpath =~ s{(/)$}{};
10993: return $cleanpath;
10994: }
1.31 albertel 10995:
1.1053 raeburn 10996: sub is_archive_file {
10997: my ($mimetype) = @_;
10998: if (($mimetype eq 'application/octet-stream') ||
10999: ($mimetype eq 'application/x-stuffit') ||
11000: ($mimetype =~ m{^application/(x\-)?(compressed|tar|zip|tgz|gz|gtar|gzip|gunzip|bz|bz2|bzip2)})) {
11001: return 1;
11002: }
11003: return;
11004: }
11005:
11006: sub decompress_form {
1.1065 raeburn 11007: my ($mimetype,$archiveurl,$action,$noextract,$hiddenelements,$dirlist) = @_;
1.1053 raeburn 11008: my %lt = &Apache::lonlocal::texthash (
11009: this => 'This file is an archive file.',
1.1067 raeburn 11010: camt => 'This file is a Camtasia archive file.',
1.1065 raeburn 11011: itsc => 'Its contents are as follows:',
1.1053 raeburn 11012: youm => 'You may wish to extract its contents.',
11013: extr => 'Extract contents',
1.1067 raeburn 11014: auto => 'LON-CAPA can process the files automatically, or you can decide how each should be handled.',
11015: proa => 'Process automatically?',
1.1053 raeburn 11016: yes => 'Yes',
11017: no => 'No',
1.1067 raeburn 11018: fold => 'Title for folder containing movie',
11019: movi => 'Title for page containing embedded movie',
1.1053 raeburn 11020: );
1.1065 raeburn 11021: my $fileloc = &Apache::lonnet::filelocation(undef,$archiveurl);
1.1067 raeburn 11022: my ($is_camtasia,$topdir,%toplevel,@paths);
1.1065 raeburn 11023: my $info = &list_archive_contents($fileloc,\@paths);
11024: if (@paths) {
11025: foreach my $path (@paths) {
11026: $path =~ s{^/}{};
1.1067 raeburn 11027: if ($path =~ m{^([^/]+)/$}) {
11028: $topdir = $1;
11029: }
1.1065 raeburn 11030: if ($path =~ m{^([^/]+)/}) {
11031: $toplevel{$1} = $path;
11032: } else {
11033: $toplevel{$path} = $path;
11034: }
11035: }
11036: }
1.1067 raeburn 11037: if ($mimetype =~ m{^application/(x\-)?(compressed|zip)}) {
1.1164 raeburn 11038: my @camtasia6 = ("$topdir/","$topdir/index.html",
1.1067 raeburn 11039: "$topdir/media/",
11040: "$topdir/media/$topdir.mp4",
11041: "$topdir/media/FirstFrame.png",
11042: "$topdir/media/player.swf",
11043: "$topdir/media/swfobject.js",
11044: "$topdir/media/expressInstall.swf");
1.1164 raeburn 11045: my @camtasia8 = ("$topdir/","$topdir/$topdir.html",
11046: "$topdir/$topdir.mp4",
11047: "$topdir/$topdir\_config.xml",
11048: "$topdir/$topdir\_controller.swf",
11049: "$topdir/$topdir\_embed.css",
11050: "$topdir/$topdir\_First_Frame.png",
11051: "$topdir/$topdir\_player.html",
11052: "$topdir/$topdir\_Thumbnails.png",
11053: "$topdir/playerProductInstall.swf",
11054: "$topdir/scripts/",
11055: "$topdir/scripts/config_xml.js",
11056: "$topdir/scripts/handlebars.js",
11057: "$topdir/scripts/jquery-1.7.1.min.js",
11058: "$topdir/scripts/jquery-ui-1.8.15.custom.min.js",
11059: "$topdir/scripts/modernizr.js",
11060: "$topdir/scripts/player-min.js",
11061: "$topdir/scripts/swfobject.js",
11062: "$topdir/skins/",
11063: "$topdir/skins/configuration_express.xml",
11064: "$topdir/skins/express_show/",
11065: "$topdir/skins/express_show/player-min.css",
11066: "$topdir/skins/express_show/spritesheet.png");
11067: my @diffs = &compare_arrays(\@paths,\@camtasia6);
1.1067 raeburn 11068: if (@diffs == 0) {
1.1164 raeburn 11069: $is_camtasia = 6;
11070: } else {
11071: @diffs = &compare_arrays(\@paths,\@camtasia8);
11072: if (@diffs == 0) {
11073: $is_camtasia = 8;
11074: }
1.1067 raeburn 11075: }
11076: }
11077: my $output;
11078: if ($is_camtasia) {
11079: $output = <<"ENDCAM";
11080: <script type="text/javascript" language="Javascript">
11081: // <![CDATA[
11082:
11083: function camtasiaToggle() {
11084: for (var i=0; i<document.uploaded_decompress.autoextract_camtasia.length; i++) {
11085: if (document.uploaded_decompress.autoextract_camtasia[i].checked) {
1.1164 raeburn 11086: if (document.uploaded_decompress.autoextract_camtasia[i].value == $is_camtasia) {
1.1067 raeburn 11087:
11088: document.getElementById('camtasia_titles').style.display='block';
11089: } else {
11090: document.getElementById('camtasia_titles').style.display='none';
11091: }
11092: }
11093: }
11094: return;
11095: }
11096:
11097: // ]]>
11098: </script>
11099: <p>$lt{'camt'}</p>
11100: ENDCAM
1.1065 raeburn 11101: } else {
1.1067 raeburn 11102: $output = '<p>'.$lt{'this'};
11103: if ($info eq '') {
11104: $output .= ' '.$lt{'youm'}.'</p>'."\n";
11105: } else {
11106: $output .= ' '.$lt{'itsc'}.'</p>'."\n".
11107: '<div><pre>'.$info.'</pre></div>';
11108: }
1.1065 raeburn 11109: }
1.1067 raeburn 11110: $output .= '<form name="uploaded_decompress" action="'.$action.'" method="post">'."\n";
1.1065 raeburn 11111: my $duplicates;
11112: my $num = 0;
11113: if (ref($dirlist) eq 'ARRAY') {
11114: foreach my $item (@{$dirlist}) {
11115: if (ref($item) eq 'ARRAY') {
11116: if (exists($toplevel{$item->[0]})) {
11117: $duplicates .=
11118: &start_data_table_row().
11119: '<td><label><input type="radio" name="archive_overwrite_'.$num.'" '.
11120: 'value="0" checked="checked" />'.&mt('No').'</label>'.
11121: ' <label><input type="radio" name="archive_overwrite_'.$num.'" '.
11122: 'value="1" />'.&mt('Yes').'</label>'.
11123: '<input type="hidden" name="archive_overwrite_name_'.$num.'" value="'.$item->[0].'" /></td>'."\n".
11124: '<td>'.$item->[0].'</td>';
11125: if ($item->[2]) {
11126: $duplicates .= '<td>'.&mt('Directory').'</td>';
11127: } else {
11128: $duplicates .= '<td>'.&mt('File').'</td>';
11129: }
11130: $duplicates .= '<td>'.$item->[3].'</td>'.
11131: '<td>'.
11132: &Apache::lonlocal::locallocaltime($item->[4]).
11133: '</td>'.
11134: &end_data_table_row();
11135: $num ++;
11136: }
11137: }
11138: }
11139: }
11140: my $itemcount;
11141: if (@paths > 0) {
11142: $itemcount = scalar(@paths);
11143: } else {
11144: $itemcount = 1;
11145: }
1.1067 raeburn 11146: if ($is_camtasia) {
11147: $output .= $lt{'auto'}.'<br />'.
11148: '<span class="LC_nobreak">'.$lt{'proa'}.'<label>'.
1.1164 raeburn 11149: '<input type="radio" name="autoextract_camtasia" value="'.$is_camtasia.'" onclick="javascript:camtasiaToggle();" checked="checked" />'.
1.1067 raeburn 11150: $lt{'yes'}.'</label> <label>'.
11151: '<input type="radio" name="autoextract_camtasia" value="0" onclick="javascript:camtasiaToggle();" />'.
11152: $lt{'no'}.'</label></span><br />'.
11153: '<div id="camtasia_titles" style="display:block">'.
11154: &Apache::lonhtmlcommon::start_pick_box().
11155: &Apache::lonhtmlcommon::row_title($lt{'fold'}).
11156: '<input type="textbox" name="camtasia_foldername" value="'.$env{'form.comment'}.'" />'."\n".
11157: &Apache::lonhtmlcommon::row_closure().
11158: &Apache::lonhtmlcommon::row_title($lt{'movi'}).
11159: '<input type="textbox" name="camtasia_moviename" value="" />'."\n".
11160: &Apache::lonhtmlcommon::row_closure(1).
11161: &Apache::lonhtmlcommon::end_pick_box().
11162: '</div>';
11163: }
1.1065 raeburn 11164: $output .=
11165: '<input type="hidden" name="archive_overwrite_total" value="'.$num.'" />'.
1.1067 raeburn 11166: '<input type="hidden" name="archive_itemcount" value="'.$itemcount.'" />'.
11167: "\n";
1.1065 raeburn 11168: if ($duplicates ne '') {
11169: $output .= '<p><span class="LC_warning">'.
11170: &mt('Warning: decompression of the archive will overwrite the following items which already exist:').'</span><br />'.
11171: &start_data_table().
11172: &start_data_table_header_row().
11173: '<th>'.&mt('Overwrite?').'</th>'.
11174: '<th>'.&mt('Name').'</th>'.
11175: '<th>'.&mt('Type').'</th>'.
11176: '<th>'.&mt('Size').'</th>'.
11177: '<th>'.&mt('Last modified').'</th>'.
11178: &end_data_table_header_row().
11179: $duplicates.
11180: &end_data_table().
11181: '</p>';
11182: }
1.1067 raeburn 11183: $output .= '<input type="hidden" name="archiveurl" value="'.$archiveurl.'" />'."\n";
1.1053 raeburn 11184: if (ref($hiddenelements) eq 'HASH') {
11185: foreach my $hidden (sort(keys(%{$hiddenelements}))) {
11186: $output .= '<input type="hidden" name="'.$hidden.'" value="'.$hiddenelements->{$hidden}.'" />'."\n";
11187: }
11188: }
11189: $output .= <<"END";
1.1067 raeburn 11190: <br />
1.1053 raeburn 11191: <input type="submit" name="decompress" value="$lt{'extr'}" />
11192: </form>
11193: $noextract
11194: END
11195: return $output;
11196: }
11197:
1.1065 raeburn 11198: sub decompression_utility {
11199: my ($program) = @_;
11200: my @utilities = ('tar','gunzip','bunzip2','unzip');
11201: my $location;
11202: if (grep(/^\Q$program\E$/,@utilities)) {
11203: foreach my $dir ('/bin/','/usr/bin/','/usr/local/bin/','/sbin/',
11204: '/usr/sbin/') {
11205: if (-x $dir.$program) {
11206: $location = $dir.$program;
11207: last;
11208: }
11209: }
11210: }
11211: return $location;
11212: }
11213:
11214: sub list_archive_contents {
11215: my ($file,$pathsref) = @_;
11216: my (@cmd,$output);
11217: my $needsregexp;
11218: if ($file =~ /\.zip$/) {
11219: @cmd = (&decompression_utility('unzip'),"-l");
11220: $needsregexp = 1;
11221: } elsif (($file =~ m/\.tar\.gz$/) ||
11222: ($file =~ /\.tgz$/)) {
11223: @cmd = (&decompression_utility('tar'),"-ztf");
11224: } elsif ($file =~ /\.tar\.bz2$/) {
11225: @cmd = (&decompression_utility('tar'),"-jtf");
11226: } elsif ($file =~ m|\.tar$|) {
11227: @cmd = (&decompression_utility('tar'),"-tf");
11228: }
11229: if (@cmd) {
11230: undef($!);
11231: undef($@);
11232: if (open(my $fh,"-|", @cmd, $file)) {
11233: while (my $line = <$fh>) {
11234: $output .= $line;
11235: chomp($line);
11236: my $item;
11237: if ($needsregexp) {
11238: ($item) = ($line =~ /^\s*\d+\s+[\d\-]+\s+[\d:]+\s*(.+)$/);
11239: } else {
11240: $item = $line;
11241: }
11242: if ($item ne '') {
11243: unless (grep(/^\Q$item\E$/,@{$pathsref})) {
11244: push(@{$pathsref},$item);
11245: }
11246: }
11247: }
11248: close($fh);
11249: }
11250: }
11251: return $output;
11252: }
11253:
1.1053 raeburn 11254: sub decompress_uploaded_file {
11255: my ($file,$dir) = @_;
11256: &Apache::lonnet::appenv({'cgi.file' => $file});
11257: &Apache::lonnet::appenv({'cgi.dir' => $dir});
11258: my $result = &Apache::lonnet::ssi_body('/cgi-bin/decompress.pl');
11259: my ($handle) = ($env{'user.environment'} =~m{/([^/]+)\.id$});
11260: my $lonidsdir = $Apache::lonnet::perlvar{'lonIDsDir'};
11261: &Apache::lonnet::transfer_profile_to_env($lonidsdir,$handle,1);
11262: my $decompressed = $env{'cgi.decompressed'};
11263: &Apache::lonnet::delenv('cgi.file');
11264: &Apache::lonnet::delenv('cgi.dir');
11265: &Apache::lonnet::delenv('cgi.decompressed');
11266: return ($decompressed,$result);
11267: }
11268:
1.1055 raeburn 11269: sub process_decompression {
11270: my ($docudom,$docuname,$file,$destination,$dir_root,$hiddenelem) = @_;
11271: my ($dir,$error,$warning,$output);
11272: if ($file !~ /\.(zip|tar|bz2|gz|tar.gz|tar.bz2|tgz)$/) {
1.1120 bisitz 11273: $error = &mt('Filename not a supported archive file type.').
11274: '<br />'.&mt('Filename should end with one of: [_1].',
1.1055 raeburn 11275: '.zip, .tar, .bz2, .gz, .tar.gz, .tar.bz2, .tgz');
11276: } else {
11277: my $docuhome = &Apache::lonnet::homeserver($docuname,$docudom);
11278: if ($docuhome eq 'no_host') {
11279: $error = &mt('Could not determine home server for course.');
11280: } else {
11281: my @ids=&Apache::lonnet::current_machine_ids();
11282: my $currdir = "$dir_root/$destination";
11283: if (grep(/^\Q$docuhome\E$/,@ids)) {
11284: $dir = &LONCAPA::propath($docudom,$docuname).
11285: "$dir_root/$destination";
11286: } else {
11287: $dir = $Apache::lonnet::perlvar{'lonDocRoot'}.
11288: "$dir_root/$docudom/$docuname/$destination";
11289: unless (&Apache::lonnet::repcopy_userfile("$dir/$file") eq 'ok') {
11290: $error = &mt('Archive file not found.');
11291: }
11292: }
1.1065 raeburn 11293: my (@to_overwrite,@to_skip);
11294: if ($env{'form.archive_overwrite_total'} > 0) {
11295: my $total = $env{'form.archive_overwrite_total'};
11296: for (my $i=0; $i<$total; $i++) {
11297: if ($env{'form.archive_overwrite_'.$i} == 1) {
11298: push(@to_overwrite,$env{'form.archive_overwrite_name_'.$i});
11299: } elsif ($env{'form.archive_overwrite_'.$i} == 0) {
11300: push(@to_skip,$env{'form.archive_overwrite_name_'.$i});
11301: }
11302: }
11303: }
11304: my $numskip = scalar(@to_skip);
11305: if (($numskip > 0) &&
11306: ($numskip == $env{'form.archive_itemcount'})) {
11307: $warning = &mt('All items in the archive file already exist, and no overwriting of existing files has been requested.');
11308: } elsif ($dir eq '') {
1.1055 raeburn 11309: $error = &mt('Directory containing archive file unavailable.');
11310: } elsif (!$error) {
1.1065 raeburn 11311: my ($decompressed,$display);
11312: if ($numskip > 0) {
11313: my $tempdir = time.'_'.$$.int(rand(10000));
11314: mkdir("$dir/$tempdir",0755);
11315: system("mv $dir/$file $dir/$tempdir/$file");
11316: ($decompressed,$display) =
11317: &decompress_uploaded_file($file,"$dir/$tempdir");
11318: foreach my $item (@to_skip) {
11319: if (($item ne '') && ($item !~ /\.\./)) {
11320: if (-f "$dir/$tempdir/$item") {
11321: unlink("$dir/$tempdir/$item");
11322: } elsif (-d "$dir/$tempdir/$item") {
11323: system("rm -rf $dir/$tempdir/$item");
11324: }
11325: }
11326: }
11327: system("mv $dir/$tempdir/* $dir");
11328: rmdir("$dir/$tempdir");
11329: } else {
11330: ($decompressed,$display) =
11331: &decompress_uploaded_file($file,$dir);
11332: }
1.1055 raeburn 11333: if ($decompressed eq 'ok') {
1.1065 raeburn 11334: $output = '<p class="LC_info">'.
11335: &mt('Files extracted successfully from archive.').
11336: '</p>'."\n";
1.1055 raeburn 11337: my ($warning,$result,@contents);
11338: my ($newdirlistref,$newlisterror) =
11339: &Apache::lonnet::dirlist($currdir,$docudom,
11340: $docuname,1);
11341: my (%is_dir,%changes,@newitems);
11342: my $dirptr = 16384;
1.1065 raeburn 11343: if (ref($newdirlistref) eq 'ARRAY') {
1.1055 raeburn 11344: foreach my $dir_line (@{$newdirlistref}) {
11345: my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,5);
1.1065 raeburn 11346: unless (($item =~ /^\.+$/) || ($item eq $file) ||
11347: ((@to_skip > 0) && (grep(/^\Q$item\E$/,@to_skip)))) {
1.1055 raeburn 11348: push(@newitems,$item);
11349: if ($dirptr&$testdir) {
11350: $is_dir{$item} = 1;
11351: }
11352: $changes{$item} = 1;
11353: }
11354: }
11355: }
11356: if (keys(%changes) > 0) {
11357: foreach my $item (sort(@newitems)) {
11358: if ($changes{$item}) {
11359: push(@contents,$item);
11360: }
11361: }
11362: }
11363: if (@contents > 0) {
1.1067 raeburn 11364: my $wantform;
11365: unless ($env{'form.autoextract_camtasia'}) {
11366: $wantform = 1;
11367: }
1.1056 raeburn 11368: my (%children,%parent,%dirorder,%titles);
1.1055 raeburn 11369: my ($count,$datatable) = &get_extracted($docudom,$docuname,
11370: $currdir,\%is_dir,
11371: \%children,\%parent,
1.1056 raeburn 11372: \@contents,\%dirorder,
11373: \%titles,$wantform);
1.1055 raeburn 11374: if ($datatable ne '') {
11375: $output .= &archive_options_form('decompressed',$datatable,
11376: $count,$hiddenelem);
1.1065 raeburn 11377: my $startcount = 6;
1.1055 raeburn 11378: $output .= &archive_javascript($startcount,$count,
1.1056 raeburn 11379: \%titles,\%children);
1.1055 raeburn 11380: }
1.1067 raeburn 11381: if ($env{'form.autoextract_camtasia'}) {
1.1164 raeburn 11382: my $version = $env{'form.autoextract_camtasia'};
1.1067 raeburn 11383: my %displayed;
11384: my $total = 1;
11385: $env{'form.archive_directory'} = [];
11386: foreach my $i (sort { $a <=> $b } keys(%dirorder)) {
11387: my $path = join('/',map { $titles{$_}; } @{$dirorder{$i}});
11388: $path =~ s{/$}{};
11389: my $item;
11390: if ($path ne '') {
11391: $item = "$path/$titles{$i}";
11392: } else {
11393: $item = $titles{$i};
11394: }
11395: $env{'form.archive_content_'.$i} = "$dir_root/$destination/$item";
11396: if ($item eq $contents[0]) {
11397: push(@{$env{'form.archive_directory'}},$i);
11398: $env{'form.archive_'.$i} = 'display';
11399: $env{'form.archive_title_'.$i} = $env{'form.camtasia_foldername'};
11400: $displayed{'folder'} = $i;
1.1164 raeburn 11401: } elsif ((($item eq "$contents[0]/index.html") && ($version == 6)) ||
11402: (($item eq "$contents[0]/$contents[0]".'.html') && ($version == 8))) {
1.1067 raeburn 11403: $env{'form.archive_'.$i} = 'display';
11404: $env{'form.archive_title_'.$i} = $env{'form.camtasia_moviename'};
11405: $displayed{'web'} = $i;
11406: } else {
1.1164 raeburn 11407: if ((($item eq "$contents[0]/media") && ($version == 6)) ||
11408: ((($item eq "$contents[0]/scripts") || ($item eq "$contents[0]/skins") ||
11409: ($item eq "$contents[0]/skins/express_show")) && ($version == 8))) {
1.1067 raeburn 11410: push(@{$env{'form.archive_directory'}},$i);
11411: }
11412: $env{'form.archive_'.$i} = 'dependency';
11413: }
11414: $total ++;
11415: }
11416: for (my $i=1; $i<$total; $i++) {
11417: next if ($i == $displayed{'web'});
11418: next if ($i == $displayed{'folder'});
11419: $env{'form.archive_dependent_on_'.$i} = $displayed{'web'};
11420: }
11421: $env{'form.phase'} = 'decompress_cleanup';
11422: $env{'form.archivedelete'} = 1;
11423: $env{'form.archive_count'} = $total-1;
11424: $output .=
11425: &process_extracted_files('coursedocs',$docudom,
11426: $docuname,$destination,
11427: $dir_root,$hiddenelem);
11428: }
1.1055 raeburn 11429: } else {
11430: $warning = &mt('No new items extracted from archive file.');
11431: }
11432: } else {
11433: $output = $display;
11434: $error = &mt('An error occurred during extraction from the archive file.');
11435: }
11436: }
11437: }
11438: }
11439: if ($error) {
11440: $output .= '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
11441: $error.'</p>'."\n";
11442: }
11443: if ($warning) {
11444: $output .= '<p class="LC_warning">'.$warning.'</p>'."\n";
11445: }
11446: return $output;
11447: }
11448:
11449: sub get_extracted {
1.1056 raeburn 11450: my ($docudom,$docuname,$currdir,$is_dir,$children,$parent,$contents,$dirorder,
11451: $titles,$wantform) = @_;
1.1055 raeburn 11452: my $count = 0;
11453: my $depth = 0;
11454: my $datatable;
1.1056 raeburn 11455: my @hierarchy;
1.1055 raeburn 11456: return unless ((ref($is_dir) eq 'HASH') && (ref($children) eq 'HASH') &&
1.1056 raeburn 11457: (ref($parent) eq 'HASH') && (ref($contents) eq 'ARRAY') &&
11458: (ref($dirorder) eq 'HASH') && (ref($titles) eq 'HASH'));
1.1055 raeburn 11459: foreach my $item (@{$contents}) {
11460: $count ++;
1.1056 raeburn 11461: @{$dirorder->{$count}} = @hierarchy;
11462: $titles->{$count} = $item;
1.1055 raeburn 11463: &archive_hierarchy($depth,$count,$parent,$children);
11464: if ($wantform) {
11465: $datatable .= &archive_row($is_dir->{$item},$item,
11466: $currdir,$depth,$count);
11467: }
11468: if ($is_dir->{$item}) {
11469: $depth ++;
1.1056 raeburn 11470: push(@hierarchy,$count);
11471: $parent->{$depth} = $count;
1.1055 raeburn 11472: $datatable .=
11473: &recurse_extracted_archive("$currdir/$item",$docudom,$docuname,
1.1056 raeburn 11474: \$depth,\$count,\@hierarchy,$dirorder,
11475: $children,$parent,$titles,$wantform);
1.1055 raeburn 11476: $depth --;
1.1056 raeburn 11477: pop(@hierarchy);
1.1055 raeburn 11478: }
11479: }
11480: return ($count,$datatable);
11481: }
11482:
11483: sub recurse_extracted_archive {
1.1056 raeburn 11484: my ($currdir,$docudom,$docuname,$depth,$count,$hierarchy,$dirorder,
11485: $children,$parent,$titles,$wantform) = @_;
1.1055 raeburn 11486: my $result='';
1.1056 raeburn 11487: unless ((ref($depth)) && (ref($count)) && (ref($hierarchy) eq 'ARRAY') &&
11488: (ref($children) eq 'HASH') && (ref($parent) eq 'HASH') &&
11489: (ref($dirorder) eq 'HASH')) {
1.1055 raeburn 11490: return $result;
11491: }
11492: my $dirptr = 16384;
11493: my ($newdirlistref,$newlisterror) =
11494: &Apache::lonnet::dirlist($currdir,$docudom,$docuname,1);
11495: if (ref($newdirlistref) eq 'ARRAY') {
11496: foreach my $dir_line (@{$newdirlistref}) {
11497: my ($item,undef,undef,$testdir)=split(/\&/,$dir_line,5);
11498: unless ($item =~ /^\.+$/) {
11499: $$count ++;
1.1056 raeburn 11500: @{$dirorder->{$$count}} = @{$hierarchy};
11501: $titles->{$$count} = $item;
1.1055 raeburn 11502: &archive_hierarchy($$depth,$$count,$parent,$children);
1.1056 raeburn 11503:
1.1055 raeburn 11504: my $is_dir;
11505: if ($dirptr&$testdir) {
11506: $is_dir = 1;
11507: }
11508: if ($wantform) {
11509: $result .= &archive_row($is_dir,$item,$currdir,$$depth,$$count);
11510: }
11511: if ($is_dir) {
11512: $$depth ++;
1.1056 raeburn 11513: push(@{$hierarchy},$$count);
11514: $parent->{$$depth} = $$count;
1.1055 raeburn 11515: $result .=
11516: &recurse_extracted_archive("$currdir/$item",$docudom,
11517: $docuname,$depth,$count,
1.1056 raeburn 11518: $hierarchy,$dirorder,$children,
11519: $parent,$titles,$wantform);
1.1055 raeburn 11520: $$depth --;
1.1056 raeburn 11521: pop(@{$hierarchy});
1.1055 raeburn 11522: }
11523: }
11524: }
11525: }
11526: return $result;
11527: }
11528:
11529: sub archive_hierarchy {
11530: my ($depth,$count,$parent,$children) =@_;
11531: if ((ref($parent) eq 'HASH') && (ref($children) eq 'HASH')) {
11532: if (exists($parent->{$depth})) {
11533: $children->{$parent->{$depth}} .= $count.':';
11534: }
11535: }
11536: return;
11537: }
11538:
11539: sub archive_row {
11540: my ($is_dir,$item,$currdir,$depth,$count) = @_;
11541: my ($name) = ($item =~ m{([^/]+)$});
11542: my %choices = &Apache::lonlocal::texthash (
1.1059 raeburn 11543: 'display' => 'Add as file',
1.1055 raeburn 11544: 'dependency' => 'Include as dependency',
11545: 'discard' => 'Discard',
11546: );
11547: if ($is_dir) {
1.1059 raeburn 11548: $choices{'display'} = &mt('Add as folder');
1.1055 raeburn 11549: }
1.1056 raeburn 11550: my $output = &start_data_table_row().'<td align="right">'.$count.'</td>'."\n";
11551: my $offset = 0;
1.1055 raeburn 11552: foreach my $action ('display','dependency','discard') {
1.1056 raeburn 11553: $offset ++;
1.1065 raeburn 11554: if ($action ne 'display') {
11555: $offset ++;
11556: }
1.1055 raeburn 11557: $output .= '<td><span class="LC_nobreak">'.
11558: '<label><input type="radio" name="archive_'.$count.
11559: '" id="archive_'.$action.'_'.$count.'" value="'.$action.'"';
11560: my $text = $choices{$action};
11561: if ($is_dir) {
11562: $output .= ' onclick="javascript:propagateCheck(this.form,'."'$count'".');"';
11563: if ($action eq 'display') {
1.1059 raeburn 11564: $text = &mt('Add as folder');
1.1055 raeburn 11565: }
1.1056 raeburn 11566: } else {
11567: $output .= ' onclick="javascript:dependencyCheck(this.form,'."$count,$offset".');"';
11568:
11569: }
11570: $output .= ' /> '.$choices{$action}.'</label></span>';
11571: if ($action eq 'dependency') {
11572: $output .= '<div id="arc_depon_'.$count.'" style="display:none;">'."\n".
11573: &mt('Used by:').' <select name="archive_dependent_on_'.$count.'" '.
11574: 'onchange="propagateSelect(this.form,'."$count,$offset".')">'."\n".
11575: '<option value=""></option>'."\n".
11576: '</select>'."\n".
11577: '</div>';
1.1059 raeburn 11578: } elsif ($action eq 'display') {
11579: $output .= '<div id="arc_title_'.$count.'" style="display:none;">'."\n".
11580: &mt('Title:').' <input type="text" name="archive_title_'.$count.'" id="archive_title_'.$count.'" />'."\n".
11581: '</div>';
1.1055 raeburn 11582: }
1.1056 raeburn 11583: $output .= '</td>';
1.1055 raeburn 11584: }
11585: $output .= '<td><input type="hidden" name="archive_content_'.$count.'" value="'.
11586: &HTML::Entities::encode("$currdir/$item",'"<>&').'" />'.(' ' x 2);
11587: for (my $i=0; $i<$depth; $i++) {
11588: $output .= ('<img src="/adm/lonIcons/whitespace1.gif" class="LC_docs_spacer" alt="" />' x2)."\n";
11589: }
11590: if ($is_dir) {
11591: $output .= '<img src="/adm/lonIcons/navmap.folder.open.gif" alt="" /> '."\n".
11592: '<input type="hidden" name="archive_directory" value="'.$count.'" />'."\n";
11593: } else {
11594: $output .= '<input type="hidden" name="archive_file" value="'.$count.'" />'."\n";
11595: }
11596: $output .= ' '.$name.'</td>'."\n".
11597: &end_data_table_row();
11598: return $output;
11599: }
11600:
11601: sub archive_options_form {
1.1065 raeburn 11602: my ($form,$display,$count,$hiddenelem) = @_;
11603: my %lt = &Apache::lonlocal::texthash(
11604: perm => 'Permanently remove archive file?',
11605: hows => 'How should each extracted item be incorporated in the course?',
11606: cont => 'Content actions for all',
11607: addf => 'Add as folder/file',
11608: incd => 'Include as dependency for a displayed file',
11609: disc => 'Discard',
11610: no => 'No',
11611: yes => 'Yes',
11612: save => 'Save',
11613: );
11614: my $output = <<"END";
11615: <form name="$form" method="post" action="">
11616: <p><span class="LC_nobreak">$lt{'perm'}
11617: <label>
11618: <input type="radio" name="archivedelete" value="0" checked="checked" />$lt{'no'}
11619: </label>
11620:
11621: <label>
11622: <input type="radio" name="archivedelete" value="1" />$lt{'yes'}</label>
11623: </span>
11624: </p>
11625: <input type="hidden" name="phase" value="decompress_cleanup" />
11626: <br />$lt{'hows'}
11627: <div class="LC_columnSection">
11628: <fieldset>
11629: <legend>$lt{'cont'}</legend>
11630: <input type="button" value="$lt{'addf'}" onclick="javascript:checkAll(document.$form,'display');" />
11631: <input type="button" value="$lt{'incd'}" onclick="javascript:checkAll(document.$form,'dependency');" />
11632: <input type="button" value="$lt{'disc'}" onclick="javascript:checkAll(document.$form,'discard');" />
11633: </fieldset>
11634: </div>
11635: END
11636: return $output.
1.1055 raeburn 11637: &start_data_table()."\n".
1.1065 raeburn 11638: $display."\n".
1.1055 raeburn 11639: &end_data_table()."\n".
11640: '<input type="hidden" name="archive_count" value="'.$count.'" />'.
11641: $hiddenelem.
1.1065 raeburn 11642: '<br /><input type="submit" name="archive_submit" value="'.$lt{'save'}.'" />'.
1.1055 raeburn 11643: '</form>';
11644: }
11645:
11646: sub archive_javascript {
1.1056 raeburn 11647: my ($startcount,$numitems,$titles,$children) = @_;
11648: return unless ((ref($titles) eq 'HASH') && (ref($children) eq 'HASH'));
1.1059 raeburn 11649: my $maintitle = $env{'form.comment'};
1.1055 raeburn 11650: my $scripttag = <<START;
11651: <script type="text/javascript">
11652: // <![CDATA[
11653:
11654: function checkAll(form,prefix) {
11655: var idstr = new RegExp("^archive_"+prefix+"_\\\\d+\$");
11656: for (var i=0; i < form.elements.length; i++) {
11657: var id = form.elements[i].id;
11658: if ((id != '') && (id != undefined)) {
11659: if (idstr.test(id)) {
11660: if (form.elements[i].type == 'radio') {
11661: form.elements[i].checked = true;
1.1056 raeburn 11662: var nostart = i-$startcount;
1.1059 raeburn 11663: var offset = nostart%7;
11664: var count = (nostart-offset)/7;
1.1056 raeburn 11665: dependencyCheck(form,count,offset);
1.1055 raeburn 11666: }
11667: }
11668: }
11669: }
11670: }
11671:
11672: function propagateCheck(form,count) {
11673: if (count > 0) {
1.1059 raeburn 11674: var startelement = $startcount + ((count-1) * 7);
11675: for (var j=1; j<6; j++) {
11676: if ((j != 2) && (j != 4)) {
1.1056 raeburn 11677: var item = startelement + j;
11678: if (form.elements[item].type == 'radio') {
11679: if (form.elements[item].checked) {
11680: containerCheck(form,count,j);
11681: break;
11682: }
1.1055 raeburn 11683: }
11684: }
11685: }
11686: }
11687: }
11688:
11689: numitems = $numitems
1.1056 raeburn 11690: var titles = new Array(numitems);
11691: var parents = new Array(numitems);
1.1055 raeburn 11692: for (var i=0; i<numitems; i++) {
1.1056 raeburn 11693: parents[i] = new Array;
1.1055 raeburn 11694: }
1.1059 raeburn 11695: var maintitle = '$maintitle';
1.1055 raeburn 11696:
11697: START
11698:
1.1056 raeburn 11699: foreach my $container (sort { $a <=> $b } (keys(%{$children}))) {
11700: my @contents = split(/:/,$children->{$container});
1.1055 raeburn 11701: for (my $i=0; $i<@contents; $i ++) {
11702: $scripttag .= 'parents['.$container.']['.$i.'] = '.$contents[$i]."\n";
11703: }
11704: }
11705:
1.1056 raeburn 11706: foreach my $key (sort { $a <=> $b } (keys(%{$titles}))) {
11707: $scripttag .= "titles[$key] = '".$titles->{$key}."';\n";
11708: }
11709:
1.1055 raeburn 11710: $scripttag .= <<END;
11711:
11712: function containerCheck(form,count,offset) {
11713: if (count > 0) {
1.1056 raeburn 11714: dependencyCheck(form,count,offset);
1.1059 raeburn 11715: var item = (offset+$startcount)+7*(count-1);
1.1055 raeburn 11716: form.elements[item].checked = true;
11717: if(Object.prototype.toString.call(parents[count]) === '[object Array]') {
11718: if (parents[count].length > 0) {
11719: for (var j=0; j<parents[count].length; j++) {
1.1056 raeburn 11720: containerCheck(form,parents[count][j],offset);
11721: }
11722: }
11723: }
11724: }
11725: }
11726:
11727: function dependencyCheck(form,count,offset) {
11728: if (count > 0) {
1.1059 raeburn 11729: var chosen = (offset+$startcount)+7*(count-1);
11730: var depitem = $startcount + ((count-1) * 7) + 4;
1.1056 raeburn 11731: var currtype = form.elements[depitem].type;
11732: if (form.elements[chosen].value == 'dependency') {
11733: document.getElementById('arc_depon_'+count).style.display='block';
11734: form.elements[depitem].options.length = 0;
11735: form.elements[depitem].options[0] = new Option('Select','',true,true);
1.1085 raeburn 11736: for (var i=1; i<=numitems; i++) {
11737: if (i == count) {
11738: continue;
11739: }
1.1059 raeburn 11740: var startelement = $startcount + (i-1) * 7;
11741: for (var j=1; j<6; j++) {
11742: if ((j != 2) && (j!= 4)) {
1.1056 raeburn 11743: var item = startelement + j;
11744: if (form.elements[item].type == 'radio') {
11745: if (form.elements[item].checked) {
11746: if (form.elements[item].value == 'display') {
11747: var n = form.elements[depitem].options.length;
11748: form.elements[depitem].options[n] = new Option(titles[i],i,false,false);
11749: }
11750: }
11751: }
11752: }
11753: }
11754: }
11755: } else {
11756: document.getElementById('arc_depon_'+count).style.display='none';
11757: form.elements[depitem].options.length = 0;
11758: form.elements[depitem].options[0] = new Option('Select','',true,true);
11759: }
1.1059 raeburn 11760: titleCheck(form,count,offset);
1.1056 raeburn 11761: }
11762: }
11763:
11764: function propagateSelect(form,count,offset) {
11765: if (count > 0) {
1.1065 raeburn 11766: var item = (1+offset+$startcount)+7*(count-1);
1.1056 raeburn 11767: var picked = form.elements[item].options[form.elements[item].selectedIndex].value;
11768: if (Object.prototype.toString.call(parents[count]) === '[object Array]') {
11769: if (parents[count].length > 0) {
11770: for (var j=0; j<parents[count].length; j++) {
11771: containerSelect(form,parents[count][j],offset,picked);
1.1055 raeburn 11772: }
11773: }
11774: }
11775: }
11776: }
1.1056 raeburn 11777:
11778: function containerSelect(form,count,offset,picked) {
11779: if (count > 0) {
1.1065 raeburn 11780: var item = (offset+$startcount)+7*(count-1);
1.1056 raeburn 11781: if (form.elements[item].type == 'radio') {
11782: if (form.elements[item].value == 'dependency') {
11783: if (form.elements[item+1].type == 'select-one') {
11784: for (var i=0; i<form.elements[item+1].options.length; i++) {
11785: if (form.elements[item+1].options[i].value == picked) {
11786: form.elements[item+1].selectedIndex = i;
11787: break;
11788: }
11789: }
11790: }
11791: if (Object.prototype.toString.call(parents[count]) === '[object Array]') {
11792: if (parents[count].length > 0) {
11793: for (var j=0; j<parents[count].length; j++) {
11794: containerSelect(form,parents[count][j],offset,picked);
11795: }
11796: }
11797: }
11798: }
11799: }
11800: }
11801: }
11802:
1.1059 raeburn 11803: function titleCheck(form,count,offset) {
11804: if (count > 0) {
11805: var chosen = (offset+$startcount)+7*(count-1);
11806: var depitem = $startcount + ((count-1) * 7) + 2;
11807: var currtype = form.elements[depitem].type;
11808: if (form.elements[chosen].value == 'display') {
11809: document.getElementById('arc_title_'+count).style.display='block';
11810: if ((count==1) && ((parents[count].length > 0) || (numitems == 1))) {
11811: document.getElementById('archive_title_'+count).value=maintitle;
11812: }
11813: } else {
11814: document.getElementById('arc_title_'+count).style.display='none';
11815: if (currtype == 'text') {
11816: document.getElementById('archive_title_'+count).value='';
11817: }
11818: }
11819: }
11820: return;
11821: }
11822:
1.1055 raeburn 11823: // ]]>
11824: </script>
11825: END
11826: return $scripttag;
11827: }
11828:
11829: sub process_extracted_files {
1.1067 raeburn 11830: my ($context,$docudom,$docuname,$destination,$dir_root,$hiddenelem) = @_;
1.1055 raeburn 11831: my $numitems = $env{'form.archive_count'};
11832: return unless ($numitems);
11833: my @ids=&Apache::lonnet::current_machine_ids();
11834: my ($prefix,$pathtocheck,$dir,$ishome,$error,$warning,%toplevelitems,%is_dir,
1.1067 raeburn 11835: %folders,%containers,%mapinner,%prompttofetch);
1.1055 raeburn 11836: my $docuhome = &Apache::lonnet::homeserver($docuname,$docudom);
11837: if (grep(/^\Q$docuhome\E$/,@ids)) {
11838: $prefix = &LONCAPA::propath($docudom,$docuname);
11839: $pathtocheck = "$dir_root/$destination";
11840: $dir = $dir_root;
11841: $ishome = 1;
11842: } else {
11843: $prefix = $Apache::lonnet::perlvar{'lonDocRoot'};
11844: $pathtocheck = "$dir_root/$docudom/$docuname/$destination";
11845: $dir = "$dir_root/$docudom/$docuname";
11846: }
11847: my $currdir = "$dir_root/$destination";
11848: (my $docstype,$mapinner{'0'}) = ($destination =~ m{^(docs|supplemental)/(\w+)/});
11849: if ($env{'form.folderpath'}) {
11850: my @items = split('&',$env{'form.folderpath'});
11851: $folders{'0'} = $items[-2];
1.1099 raeburn 11852: if ($env{'form.folderpath'} =~ /\:1$/) {
11853: $containers{'0'}='page';
11854: } else {
11855: $containers{'0'}='sequence';
11856: }
1.1055 raeburn 11857: }
11858: my @archdirs = &get_env_multiple('form.archive_directory');
11859: if ($numitems) {
11860: for (my $i=1; $i<=$numitems; $i++) {
11861: my $path = $env{'form.archive_content_'.$i};
11862: if ($path =~ m{^\Q$pathtocheck\E/([^/]+)$}) {
11863: my $item = $1;
11864: $toplevelitems{$item} = $i;
11865: if (grep(/^\Q$i\E$/,@archdirs)) {
11866: $is_dir{$item} = 1;
11867: }
11868: }
11869: }
11870: }
1.1067 raeburn 11871: my ($output,%children,%parent,%titles,%dirorder,$result);
1.1055 raeburn 11872: if (keys(%toplevelitems) > 0) {
11873: my @contents = sort(keys(%toplevelitems));
1.1056 raeburn 11874: (my $count,undef) = &get_extracted($docudom,$docuname,$currdir,\%is_dir,\%children,
11875: \%parent,\@contents,\%dirorder,\%titles);
1.1055 raeburn 11876: }
1.1066 raeburn 11877: my (%referrer,%orphaned,%todelete,%todeletedir,%newdest,%newseqid);
1.1055 raeburn 11878: if ($numitems) {
11879: for (my $i=1; $i<=$numitems; $i++) {
1.1086 raeburn 11880: next if ($env{'form.archive_'.$i} eq 'dependency');
1.1055 raeburn 11881: my $path = $env{'form.archive_content_'.$i};
11882: if ($path =~ /^\Q$pathtocheck\E/) {
11883: if ($env{'form.archive_'.$i} eq 'discard') {
11884: if ($prefix ne '' && $path ne '') {
11885: if (-e $prefix.$path) {
1.1066 raeburn 11886: if ((@archdirs > 0) &&
11887: (grep(/^\Q$i\E$/,@archdirs))) {
11888: $todeletedir{$prefix.$path} = 1;
11889: } else {
11890: $todelete{$prefix.$path} = 1;
11891: }
1.1055 raeburn 11892: }
11893: }
11894: } elsif ($env{'form.archive_'.$i} eq 'display') {
1.1059 raeburn 11895: my ($docstitle,$title,$url,$outer);
1.1055 raeburn 11896: ($title) = ($path =~ m{/([^/]+)$});
1.1059 raeburn 11897: $docstitle = $env{'form.archive_title_'.$i};
11898: if ($docstitle eq '') {
11899: $docstitle = $title;
11900: }
1.1055 raeburn 11901: $outer = 0;
1.1056 raeburn 11902: if (ref($dirorder{$i}) eq 'ARRAY') {
11903: if (@{$dirorder{$i}} > 0) {
11904: foreach my $item (reverse(@{$dirorder{$i}})) {
1.1055 raeburn 11905: if ($env{'form.archive_'.$item} eq 'display') {
11906: $outer = $item;
11907: last;
11908: }
11909: }
11910: }
11911: }
11912: my ($errtext,$fatal) =
11913: &LONCAPA::map::mapread('/uploaded/'.$docudom.'/'.$docuname.
11914: '/'.$folders{$outer}.'.'.
11915: $containers{$outer});
11916: next if ($fatal);
11917: if ((@archdirs > 0) && (grep(/^\Q$i\E$/,@archdirs))) {
11918: if ($context eq 'coursedocs') {
1.1056 raeburn 11919: $mapinner{$i} = time;
1.1055 raeburn 11920: $folders{$i} = 'default_'.$mapinner{$i};
11921: $containers{$i} = 'sequence';
11922: my $url = '/uploaded/'.$docudom.'/'.$docuname.'/'.
11923: $folders{$i}.'.'.$containers{$i};
11924: my $newidx = &LONCAPA::map::getresidx();
11925: $LONCAPA::map::resources[$newidx]=
1.1059 raeburn 11926: $docstitle.':'.$url.':false:normal:res';
1.1055 raeburn 11927: push(@LONCAPA::map::order,$newidx);
11928: my ($outtext,$errtext) =
11929: &LONCAPA::map::storemap('/uploaded/'.$docudom.'/'.
11930: $docuname.'/'.$folders{$outer}.
1.1087 raeburn 11931: '.'.$containers{$outer},1,1);
1.1056 raeburn 11932: $newseqid{$i} = $newidx;
1.1067 raeburn 11933: unless ($errtext) {
11934: $result .= '<li>'.&mt('Folder: [_1] added to course',$docstitle).'</li>'."\n";
11935: }
1.1055 raeburn 11936: }
11937: } else {
11938: if ($context eq 'coursedocs') {
11939: my $newidx=&LONCAPA::map::getresidx();
11940: my $url = '/uploaded/'.$docudom.'/'.$docuname.'/'.
11941: $docstype.'/'.$mapinner{$outer}.'/'.$newidx.'/'.
11942: $title;
11943: if (!-e "$prefix$dir/$docstype/$mapinner{$outer}") {
11944: mkdir("$prefix$dir/$docstype/$mapinner{$outer}",0755);
11945: }
11946: if (!-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx") {
11947: mkdir("$prefix$dir/$docstype/$mapinner{$outer}/$newidx");
11948: }
11949: if (-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx") {
11950: system("mv $prefix$path $prefix$dir/$docstype/$mapinner{$outer}/$newidx/$title");
1.1056 raeburn 11951: $newdest{$i} = "$prefix$dir/$docstype/$mapinner{$outer}/$newidx";
1.1067 raeburn 11952: unless ($ishome) {
11953: my $fetch = "$newdest{$i}/$title";
11954: $fetch =~ s/^\Q$prefix$dir\E//;
11955: $prompttofetch{$fetch} = 1;
11956: }
1.1055 raeburn 11957: }
11958: $LONCAPA::map::resources[$newidx]=
1.1059 raeburn 11959: $docstitle.':'.$url.':false:normal:res';
1.1055 raeburn 11960: push(@LONCAPA::map::order, $newidx);
11961: my ($outtext,$errtext)=
11962: &LONCAPA::map::storemap('/uploaded/'.$docudom.'/'.
11963: $docuname.'/'.$folders{$outer}.
1.1087 raeburn 11964: '.'.$containers{$outer},1,1);
1.1067 raeburn 11965: unless ($errtext) {
11966: if (-e "$prefix$dir/$docstype/$mapinner{$outer}/$newidx/$title") {
11967: $result .= '<li>'.&mt('File: [_1] added to course',$docstitle).'</li>'."\n";
11968: }
11969: }
1.1055 raeburn 11970: }
11971: }
1.1086 raeburn 11972: }
11973: } else {
11974: $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',$path).'<br />';
11975: }
11976: }
11977: for (my $i=1; $i<=$numitems; $i++) {
11978: next unless ($env{'form.archive_'.$i} eq 'dependency');
11979: my $path = $env{'form.archive_content_'.$i};
11980: if ($path =~ /^\Q$pathtocheck\E/) {
11981: my ($title) = ($path =~ m{/([^/]+)$});
11982: $referrer{$i} = $env{'form.archive_dependent_on_'.$i};
11983: if ($env{'form.archive_'.$referrer{$i}} eq 'display') {
11984: if (ref($dirorder{$i}) eq 'ARRAY') {
11985: my ($itemidx,$fullpath,$relpath);
11986: if (ref($dirorder{$referrer{$i}}) eq 'ARRAY') {
11987: my $container = $dirorder{$referrer{$i}}->[-1];
1.1056 raeburn 11988: for (my $j=0; $j<@{$dirorder{$i}}; $j++) {
1.1086 raeburn 11989: if ($dirorder{$i}->[$j] eq $container) {
11990: $itemidx = $j;
1.1056 raeburn 11991: }
11992: }
1.1086 raeburn 11993: }
11994: if ($itemidx eq '') {
11995: $itemidx = 0;
11996: }
11997: if (grep(/^\Q$referrer{$i}\E$/,@archdirs)) {
11998: if ($mapinner{$referrer{$i}}) {
11999: $fullpath = "$prefix$dir/$docstype/$mapinner{$referrer{$i}}";
12000: for (my $j=$itemidx; $j<@{$dirorder{$i}}; $j++) {
12001: if (grep(/^\Q$dirorder{$i}->[$j]\E$/,@archdirs)) {
12002: unless (defined($newseqid{$dirorder{$i}->[$j]})) {
12003: $fullpath .= '/'.$titles{$dirorder{$i}->[$j]};
12004: $relpath .= '/'.$titles{$dirorder{$i}->[$j]};
12005: if (!-e $fullpath) {
12006: mkdir($fullpath,0755);
1.1056 raeburn 12007: }
12008: }
1.1086 raeburn 12009: } else {
12010: last;
1.1056 raeburn 12011: }
1.1086 raeburn 12012: }
12013: }
12014: } elsif ($newdest{$referrer{$i}}) {
12015: $fullpath = $newdest{$referrer{$i}};
12016: for (my $j=$itemidx; $j<@{$dirorder{$i}}; $j++) {
12017: if ($env{'form.archive_'.$dirorder{$i}->[$j]} eq 'discard') {
12018: $orphaned{$i} = $env{'form.archive_'.$dirorder{$i}->[$j]};
12019: last;
12020: } elsif (grep(/^\Q$dirorder{$i}->[$j]\E$/,@archdirs)) {
12021: unless (defined($newseqid{$dirorder{$i}->[$j]})) {
12022: $fullpath .= '/'.$titles{$dirorder{$i}->[$j]};
12023: $relpath .= '/'.$titles{$dirorder{$i}->[$j]};
12024: if (!-e $fullpath) {
12025: mkdir($fullpath,0755);
1.1056 raeburn 12026: }
12027: }
1.1086 raeburn 12028: } else {
12029: last;
1.1056 raeburn 12030: }
1.1055 raeburn 12031: }
12032: }
1.1086 raeburn 12033: if ($fullpath ne '') {
12034: if (-e "$prefix$path") {
12035: system("mv $prefix$path $fullpath/$title");
12036: }
12037: if (-e "$fullpath/$title") {
12038: my $showpath;
12039: if ($relpath ne '') {
12040: $showpath = "$relpath/$title";
12041: } else {
12042: $showpath = "/$title";
12043: }
12044: $result .= '<li>'.&mt('[_1] included as a dependency',$showpath).'</li>'."\n";
12045: }
12046: unless ($ishome) {
12047: my $fetch = "$fullpath/$title";
12048: $fetch =~ s/^\Q$prefix$dir\E//;
12049: $prompttofetch{$fetch} = 1;
12050: }
12051: }
1.1055 raeburn 12052: }
1.1086 raeburn 12053: } elsif ($env{'form.archive_'.$referrer{$i}} eq 'discard') {
12054: $warning .= &mt('[_1] is a dependency of [_2], which was discarded.',
12055: $path,$env{'form.archive_content_'.$referrer{$i}}).'<br />';
1.1055 raeburn 12056: }
12057: } else {
12058: $warning .= &mt('Item extracted from archive: [_1] has unexpected path.',$path).'<br />';
12059: }
12060: }
12061: if (keys(%todelete)) {
12062: foreach my $key (keys(%todelete)) {
12063: unlink($key);
1.1066 raeburn 12064: }
12065: }
12066: if (keys(%todeletedir)) {
12067: foreach my $key (keys(%todeletedir)) {
12068: rmdir($key);
12069: }
12070: }
12071: foreach my $dir (sort(keys(%is_dir))) {
12072: if (($pathtocheck ne '') && ($dir ne '')) {
12073: &cleanup_empty_dirs($prefix."$pathtocheck/$dir");
1.1055 raeburn 12074: }
12075: }
1.1067 raeburn 12076: if ($result ne '') {
12077: $output .= '<ul>'."\n".
12078: $result."\n".
12079: '</ul>';
12080: }
12081: unless ($ishome) {
12082: my $replicationfail;
12083: foreach my $item (keys(%prompttofetch)) {
12084: my $fetchresult= &Apache::lonnet::reply('fetchuserfile:'.$item,$docuhome);
12085: unless ($fetchresult eq 'ok') {
12086: $replicationfail .= '<li>'.$item.'</li>'."\n";
12087: }
12088: }
12089: if ($replicationfail) {
12090: $output .= '<p class="LC_error">'.
12091: &mt('Course home server failed to retrieve:').'<ul>'.
12092: $replicationfail.
12093: '</ul></p>';
12094: }
12095: }
1.1055 raeburn 12096: } else {
12097: $warning = &mt('No items found in archive.');
12098: }
12099: if ($error) {
12100: $output .= '<p class="LC_error">'.&mt('Not extracted.').'<br />'.
12101: $error.'</p>'."\n";
12102: }
12103: if ($warning) {
12104: $output .= '<p class="LC_warning">'.$warning.'</p>'."\n";
12105: }
12106: return $output;
12107: }
12108:
1.1066 raeburn 12109: sub cleanup_empty_dirs {
12110: my ($path) = @_;
12111: if (($path ne '') && (-d $path)) {
12112: if (opendir(my $dirh,$path)) {
12113: my @dircontents = grep(!/^\./,readdir($dirh));
12114: my $numitems = 0;
12115: foreach my $item (@dircontents) {
12116: if (-d "$path/$item") {
1.1111 raeburn 12117: &cleanup_empty_dirs("$path/$item");
1.1066 raeburn 12118: if (-e "$path/$item") {
12119: $numitems ++;
12120: }
12121: } else {
12122: $numitems ++;
12123: }
12124: }
12125: if ($numitems == 0) {
12126: rmdir($path);
12127: }
12128: closedir($dirh);
12129: }
12130: }
12131: return;
12132: }
12133:
1.41 ng 12134: =pod
1.45 matthew 12135:
1.1162 raeburn 12136: =item * &get_folder_hierarchy()
1.1068 raeburn 12137:
12138: Provides hierarchy of names of folders/sub-folders containing the current
12139: item,
12140:
12141: Inputs: 3
12142: - $navmap - navmaps object
12143:
12144: - $map - url for map (either the trigger itself, or map containing
12145: the resource, which is the trigger).
12146:
12147: - $showitem - 1 => show title for map itself; 0 => do not show.
12148:
12149: Outputs: 1 @pathitems - array of folder/subfolder names.
12150:
12151: =cut
12152:
12153: sub get_folder_hierarchy {
12154: my ($navmap,$map,$showitem) = @_;
12155: my @pathitems;
12156: if (ref($navmap)) {
12157: my $mapres = $navmap->getResourceByUrl($map);
12158: if (ref($mapres)) {
12159: my $pcslist = $mapres->map_hierarchy();
12160: if ($pcslist ne '') {
12161: my @pcs = split(/,/,$pcslist);
12162: foreach my $pc (@pcs) {
12163: if ($pc == 1) {
1.1129 raeburn 12164: push(@pathitems,&mt('Main Content'));
1.1068 raeburn 12165: } else {
12166: my $res = $navmap->getByMapPc($pc);
12167: if (ref($res)) {
12168: my $title = $res->compTitle();
12169: $title =~ s/\W+/_/g;
12170: if ($title ne '') {
12171: push(@pathitems,$title);
12172: }
12173: }
12174: }
12175: }
12176: }
1.1071 raeburn 12177: if ($showitem) {
12178: if ($mapres->{ID} eq '0.0') {
1.1129 raeburn 12179: push(@pathitems,&mt('Main Content'));
1.1071 raeburn 12180: } else {
12181: my $maptitle = $mapres->compTitle();
12182: $maptitle =~ s/\W+/_/g;
12183: if ($maptitle ne '') {
12184: push(@pathitems,$maptitle);
12185: }
1.1068 raeburn 12186: }
12187: }
12188: }
12189: }
12190: return @pathitems;
12191: }
12192:
12193: =pod
12194:
1.1015 raeburn 12195: =item * &get_turnedin_filepath()
12196:
12197: Determines path in a user's portfolio file for storage of files uploaded
12198: to a specific essayresponse or dropbox item.
12199:
12200: Inputs: 3 required + 1 optional.
12201: $symb is symb for resource, $uname and $udom are for current user (required).
12202: $caller is optional (can be "submission", if routine is called when storing
12203: an upoaded file when "Submit Answer" button was pressed).
12204:
12205: Returns array containing $path and $multiresp.
12206: $path is path in portfolio. $multiresp is 1 if this resource contains more
12207: than one file upload item. Callers of routine should append partid as a
12208: subdirectory to $path in cases where $multiresp is 1.
12209:
12210: Called by: homework/essayresponse.pm and homework/structuretags.pm
12211:
12212: =cut
12213:
12214: sub get_turnedin_filepath {
12215: my ($symb,$uname,$udom,$caller) = @_;
12216: my ($map,$resid,$resurl)=&Apache::lonnet::decode_symb($symb);
12217: my $turnindir;
12218: my %userhash = &Apache::lonnet::userenvironment($udom,$uname,'turnindir');
12219: $turnindir = $userhash{'turnindir'};
12220: my ($path,$multiresp);
12221: if ($turnindir eq '') {
12222: if ($caller eq 'submission') {
12223: $turnindir = &mt('turned in');
12224: $turnindir =~ s/\W+/_/g;
12225: my %newhash = (
12226: 'turnindir' => $turnindir,
12227: );
12228: &Apache::lonnet::put('environment',\%newhash,$udom,$uname);
12229: }
12230: }
12231: if ($turnindir ne '') {
12232: $path = '/'.$turnindir.'/';
12233: my ($multipart,$turnin,@pathitems);
12234: my $navmap = Apache::lonnavmaps::navmap->new();
12235: if (defined($navmap)) {
12236: my $mapres = $navmap->getResourceByUrl($map);
12237: if (ref($mapres)) {
12238: my $pcslist = $mapres->map_hierarchy();
12239: if ($pcslist ne '') {
12240: foreach my $pc (split(/,/,$pcslist)) {
12241: my $res = $navmap->getByMapPc($pc);
12242: if (ref($res)) {
12243: my $title = $res->compTitle();
12244: $title =~ s/\W+/_/g;
12245: if ($title ne '') {
1.1149 raeburn 12246: if (($pc > 1) && (length($title) > 12)) {
12247: $title = substr($title,0,12);
12248: }
1.1015 raeburn 12249: push(@pathitems,$title);
12250: }
12251: }
12252: }
12253: }
12254: my $maptitle = $mapres->compTitle();
12255: $maptitle =~ s/\W+/_/g;
12256: if ($maptitle ne '') {
1.1149 raeburn 12257: if (length($maptitle) > 12) {
12258: $maptitle = substr($maptitle,0,12);
12259: }
1.1015 raeburn 12260: push(@pathitems,$maptitle);
12261: }
12262: unless ($env{'request.state'} eq 'construct') {
12263: my $res = $navmap->getBySymb($symb);
12264: if (ref($res)) {
12265: my $partlist = $res->parts();
12266: my $totaluploads = 0;
12267: if (ref($partlist) eq 'ARRAY') {
12268: foreach my $part (@{$partlist}) {
12269: my @types = $res->responseType($part);
12270: my @ids = $res->responseIds($part);
12271: for (my $i=0; $i < scalar(@ids); $i++) {
12272: if ($types[$i] eq 'essay') {
12273: my $partid = $part.'_'.$ids[$i];
12274: if (&Apache::lonnet::EXT("resource.$partid.uploadedfiletypes") ne '') {
12275: $totaluploads ++;
12276: }
12277: }
12278: }
12279: }
12280: if ($totaluploads > 1) {
12281: $multiresp = 1;
12282: }
12283: }
12284: }
12285: }
12286: } else {
12287: return;
12288: }
12289: } else {
12290: return;
12291: }
12292: my $restitle=&Apache::lonnet::gettitle($symb);
12293: $restitle =~ s/\W+/_/g;
12294: if ($restitle eq '') {
12295: $restitle = ($resurl =~ m{/[^/]+$});
12296: if ($restitle eq '') {
12297: $restitle = time;
12298: }
12299: }
1.1149 raeburn 12300: if (length($restitle) > 12) {
12301: $restitle = substr($restitle,0,12);
12302: }
1.1015 raeburn 12303: push(@pathitems,$restitle);
12304: $path .= join('/',@pathitems);
12305: }
12306: return ($path,$multiresp);
12307: }
12308:
12309: =pod
12310:
1.464 albertel 12311: =back
1.41 ng 12312:
1.112 bowersj2 12313: =head1 CSV Upload/Handling functions
1.38 albertel 12314:
1.41 ng 12315: =over 4
12316:
1.648 raeburn 12317: =item * &upfile_store($r)
1.41 ng 12318:
12319: Store uploaded file, $r should be the HTTP Request object,
1.258 albertel 12320: needs $env{'form.upfile'}
1.41 ng 12321: returns $datatoken to be put into hidden field
12322:
12323: =cut
1.31 albertel 12324:
12325: sub upfile_store {
12326: my $r=shift;
1.258 albertel 12327: $env{'form.upfile'}=~s/\r/\n/gs;
12328: $env{'form.upfile'}=~s/\f/\n/gs;
12329: $env{'form.upfile'}=~s/\n+/\n/gs;
12330: $env{'form.upfile'}=~s/\n+$//gs;
1.31 albertel 12331:
1.258 albertel 12332: my $datatoken=$env{'user.name'}.'_'.$env{'user.domain'}.
12333: '_enroll_'.$env{'request.course.id'}.'_'.time.'_'.$$;
1.31 albertel 12334: {
1.158 raeburn 12335: my $datafile = $r->dir_config('lonDaemons').
12336: '/tmp/'.$datatoken.'.tmp';
12337: if ( open(my $fh,">$datafile") ) {
1.258 albertel 12338: print $fh $env{'form.upfile'};
1.158 raeburn 12339: close($fh);
12340: }
1.31 albertel 12341: }
12342: return $datatoken;
12343: }
12344:
1.56 matthew 12345: =pod
12346:
1.648 raeburn 12347: =item * &load_tmp_file($r)
1.41 ng 12348:
12349: Load uploaded file from tmp, $r should be the HTTP Request object,
1.258 albertel 12350: needs $env{'form.datatoken'},
12351: sets $env{'form.upfile'} to the contents of the file
1.41 ng 12352:
12353: =cut
1.31 albertel 12354:
12355: sub load_tmp_file {
12356: my $r=shift;
12357: my @studentdata=();
12358: {
1.158 raeburn 12359: my $studentfile = $r->dir_config('lonDaemons').
1.258 albertel 12360: '/tmp/'.$env{'form.datatoken'}.'.tmp';
1.158 raeburn 12361: if ( open(my $fh,"<$studentfile") ) {
12362: @studentdata=<$fh>;
12363: close($fh);
12364: }
1.31 albertel 12365: }
1.258 albertel 12366: $env{'form.upfile'}=join('',@studentdata);
1.31 albertel 12367: }
12368:
1.56 matthew 12369: =pod
12370:
1.648 raeburn 12371: =item * &upfile_record_sep()
1.41 ng 12372:
12373: Separate uploaded file into records
12374: returns array of records,
1.258 albertel 12375: needs $env{'form.upfile'} and $env{'form.upfiletype'}
1.41 ng 12376:
12377: =cut
1.31 albertel 12378:
12379: sub upfile_record_sep {
1.258 albertel 12380: if ($env{'form.upfiletype'} eq 'xml') {
1.31 albertel 12381: } else {
1.248 albertel 12382: my @records;
1.258 albertel 12383: foreach my $line (split(/\n/,$env{'form.upfile'})) {
1.248 albertel 12384: if ($line=~/^\s*$/) { next; }
12385: push(@records,$line);
12386: }
12387: return @records;
1.31 albertel 12388: }
12389: }
12390:
1.56 matthew 12391: =pod
12392:
1.648 raeburn 12393: =item * &record_sep($record)
1.41 ng 12394:
1.258 albertel 12395: Separate a record into fields $record should be an item from the upfile_record_sep(), needs $env{'form.upfiletype'}
1.41 ng 12396:
12397: =cut
12398:
1.263 www 12399: sub takeleft {
12400: my $index=shift;
12401: return substr('0000'.$index,-4,4);
12402: }
12403:
1.31 albertel 12404: sub record_sep {
12405: my $record=shift;
12406: my %components=();
1.258 albertel 12407: if ($env{'form.upfiletype'} eq 'xml') {
12408: } elsif ($env{'form.upfiletype'} eq 'space') {
1.31 albertel 12409: my $i=0;
1.356 albertel 12410: foreach my $field (split(/\s+/,$record)) {
1.31 albertel 12411: $field=~s/^(\"|\')//;
12412: $field=~s/(\"|\')$//;
1.263 www 12413: $components{&takeleft($i)}=$field;
1.31 albertel 12414: $i++;
12415: }
1.258 albertel 12416: } elsif ($env{'form.upfiletype'} eq 'tab') {
1.31 albertel 12417: my $i=0;
1.356 albertel 12418: foreach my $field (split(/\t/,$record)) {
1.31 albertel 12419: $field=~s/^(\"|\')//;
12420: $field=~s/(\"|\')$//;
1.263 www 12421: $components{&takeleft($i)}=$field;
1.31 albertel 12422: $i++;
12423: }
12424: } else {
1.561 www 12425: my $separator=',';
1.480 banghart 12426: if ($env{'form.upfiletype'} eq 'semisv') {
1.561 www 12427: $separator=';';
1.480 banghart 12428: }
1.31 albertel 12429: my $i=0;
1.561 www 12430: # the character we are looking for to indicate the end of a quote or a record
12431: my $looking_for=$separator;
12432: # do not add the characters to the fields
12433: my $ignore=0;
12434: # we just encountered a separator (or the beginning of the record)
12435: my $just_found_separator=1;
12436: # store the field we are working on here
12437: my $field='';
12438: # work our way through all characters in record
12439: foreach my $character ($record=~/(.)/g) {
12440: if ($character eq $looking_for) {
12441: if ($character ne $separator) {
12442: # Found the end of a quote, again looking for separator
12443: $looking_for=$separator;
12444: $ignore=1;
12445: } else {
12446: # Found a separator, store away what we got
12447: $components{&takeleft($i)}=$field;
12448: $i++;
12449: $just_found_separator=1;
12450: $ignore=0;
12451: $field='';
12452: }
12453: next;
12454: }
12455: # single or double quotation marks after a separator indicate beginning of a quote
12456: # we are now looking for the end of the quote and need to ignore separators
12457: if ((($character eq '"') || ($character eq "'")) && ($just_found_separator)) {
12458: $looking_for=$character;
12459: next;
12460: }
12461: # ignore would be true after we reached the end of a quote
12462: if ($ignore) { next; }
12463: if (($just_found_separator) && ($character=~/\s/)) { next; }
12464: $field.=$character;
12465: $just_found_separator=0;
1.31 albertel 12466: }
1.561 www 12467: # catch the very last entry, since we never encountered the separator
12468: $components{&takeleft($i)}=$field;
1.31 albertel 12469: }
12470: return %components;
12471: }
12472:
1.144 matthew 12473: ######################################################
12474: ######################################################
12475:
1.56 matthew 12476: =pod
12477:
1.648 raeburn 12478: =item * &upfile_select_html()
1.41 ng 12479:
1.144 matthew 12480: Return HTML code to select a file from the users machine and specify
12481: the file type.
1.41 ng 12482:
12483: =cut
12484:
1.144 matthew 12485: ######################################################
12486: ######################################################
1.31 albertel 12487: sub upfile_select_html {
1.144 matthew 12488: my %Types = (
12489: csv => &mt('CSV (comma separated values, spreadsheet)'),
1.480 banghart 12490: semisv => &mt('Semicolon separated values'),
1.144 matthew 12491: space => &mt('Space separated'),
12492: tab => &mt('Tabulator separated'),
12493: # xml => &mt('HTML/XML'),
12494: );
12495: my $Str = '<input type="file" name="upfile" size="50" />'.
1.727 riegler 12496: '<br />'.&mt('Type').': <select name="upfiletype">';
1.144 matthew 12497: foreach my $type (sort(keys(%Types))) {
12498: $Str .= '<option value="'.$type.'" >'.$Types{$type}."</option>\n";
12499: }
12500: $Str .= "</select>\n";
12501: return $Str;
1.31 albertel 12502: }
12503:
1.301 albertel 12504: sub get_samples {
12505: my ($records,$toget) = @_;
12506: my @samples=({});
12507: my $got=0;
12508: foreach my $rec (@$records) {
12509: my %temp = &record_sep($rec);
12510: if (! grep(/\S/, values(%temp))) { next; }
12511: if (%temp) {
12512: $samples[$got]=\%temp;
12513: $got++;
12514: if ($got == $toget) { last; }
12515: }
12516: }
12517: return \@samples;
12518: }
12519:
1.144 matthew 12520: ######################################################
12521: ######################################################
12522:
1.56 matthew 12523: =pod
12524:
1.648 raeburn 12525: =item * &csv_print_samples($r,$records)
1.41 ng 12526:
12527: Prints a table of sample values from each column uploaded $r is an
12528: Apache Request ref, $records is an arrayref from
12529: &Apache::loncommon::upfile_record_sep
12530:
12531: =cut
12532:
1.144 matthew 12533: ######################################################
12534: ######################################################
1.31 albertel 12535: sub csv_print_samples {
12536: my ($r,$records) = @_;
1.662 bisitz 12537: my $samples = &get_samples($records,5);
1.301 albertel 12538:
1.594 raeburn 12539: $r->print(&mt('Samples').'<br />'.&start_data_table().
12540: &start_data_table_header_row());
1.356 albertel 12541: foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
1.845 bisitz 12542: $r->print('<th>'.&mt('Column [_1]',($sample+1)).'</th>'); }
1.594 raeburn 12543: $r->print(&end_data_table_header_row());
1.301 albertel 12544: foreach my $hash (@$samples) {
1.594 raeburn 12545: $r->print(&start_data_table_row());
1.356 albertel 12546: foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
1.31 albertel 12547: $r->print('<td>');
1.356 albertel 12548: if (defined($$hash{$sample})) { $r->print($$hash{$sample}); }
1.31 albertel 12549: $r->print('</td>');
12550: }
1.594 raeburn 12551: $r->print(&end_data_table_row());
1.31 albertel 12552: }
1.594 raeburn 12553: $r->print(&end_data_table().'<br />'."\n");
1.31 albertel 12554: }
12555:
1.144 matthew 12556: ######################################################
12557: ######################################################
12558:
1.56 matthew 12559: =pod
12560:
1.648 raeburn 12561: =item * &csv_print_select_table($r,$records,$d)
1.41 ng 12562:
12563: Prints a table to create associations between values and table columns.
1.144 matthew 12564:
1.41 ng 12565: $r is an Apache Request ref,
12566: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
1.174 matthew 12567: $d is an array of 2 element arrays (internal name, displayed name,defaultcol)
1.41 ng 12568:
12569: =cut
12570:
1.144 matthew 12571: ######################################################
12572: ######################################################
1.31 albertel 12573: sub csv_print_select_table {
12574: my ($r,$records,$d) = @_;
1.301 albertel 12575: my $i=0;
12576: my $samples = &get_samples($records,1);
1.144 matthew 12577: $r->print(&mt('Associate columns with student attributes.')."\n".
1.594 raeburn 12578: &start_data_table().&start_data_table_header_row().
1.144 matthew 12579: '<th>'.&mt('Attribute').'</th>'.
1.594 raeburn 12580: '<th>'.&mt('Column').'</th>'.
12581: &end_data_table_header_row()."\n");
1.356 albertel 12582: foreach my $array_ref (@$d) {
12583: my ($value,$display,$defaultcol)=@{ $array_ref };
1.729 raeburn 12584: $r->print(&start_data_table_row().'<td>'.$display.'</td>');
1.31 albertel 12585:
1.875 bisitz 12586: $r->print('<td><select name="f'.$i.'"'.
1.32 matthew 12587: ' onchange="javascript:flip(this.form,'.$i.');">');
1.31 albertel 12588: $r->print('<option value="none"></option>');
1.356 albertel 12589: foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) {
12590: $r->print('<option value="'.$sample.'"'.
12591: ($sample eq $defaultcol ? ' selected="selected" ' : '').
1.662 bisitz 12592: '>'.&mt('Column [_1]',($sample+1)).'</option>');
1.31 albertel 12593: }
1.594 raeburn 12594: $r->print('</select></td>'.&end_data_table_row()."\n");
1.31 albertel 12595: $i++;
12596: }
1.594 raeburn 12597: $r->print(&end_data_table());
1.31 albertel 12598: $i--;
12599: return $i;
12600: }
1.56 matthew 12601:
1.144 matthew 12602: ######################################################
12603: ######################################################
12604:
1.56 matthew 12605: =pod
1.31 albertel 12606:
1.648 raeburn 12607: =item * &csv_samples_select_table($r,$records,$d)
1.41 ng 12608:
12609: Prints a table of sample values from the upload and can make associate samples to internal names.
12610:
12611: $r is an Apache Request ref,
12612: $records is an arrayref from &Apache::loncommon::upfile_record_sep,
12613: $d is an array of 2 element arrays (internal name, displayed name)
12614:
12615: =cut
12616:
1.144 matthew 12617: ######################################################
12618: ######################################################
1.31 albertel 12619: sub csv_samples_select_table {
12620: my ($r,$records,$d) = @_;
12621: my $i=0;
1.144 matthew 12622: #
1.662 bisitz 12623: my $max_samples = 5;
12624: my $samples = &get_samples($records,$max_samples);
1.594 raeburn 12625: $r->print(&start_data_table().
12626: &start_data_table_header_row().'<th>'.
12627: &mt('Field').'</th><th>'.&mt('Samples').'</th>'.
12628: &end_data_table_header_row());
1.301 albertel 12629:
12630: foreach my $key (sort(keys(%{ $samples->[0] }))) {
1.594 raeburn 12631: $r->print(&start_data_table_row().'<td><select name="f'.$i.'"'.
1.32 matthew 12632: ' onchange="javascript:flip(this.form,'.$i.');">');
1.301 albertel 12633: foreach my $option (@$d) {
12634: my ($value,$display,$defaultcol)=@{ $option };
1.174 matthew 12635: $r->print('<option value="'.$value.'"'.
1.253 albertel 12636: ($i eq $defaultcol ? ' selected="selected" ':'').'>'.
1.174 matthew 12637: $display.'</option>');
1.31 albertel 12638: }
12639: $r->print('</select></td><td>');
1.662 bisitz 12640: foreach my $line (0..($max_samples-1)) {
1.301 albertel 12641: if (defined($samples->[$line]{$key})) {
12642: $r->print($samples->[$line]{$key}."<br />\n");
12643: }
12644: }
1.594 raeburn 12645: $r->print('</td>'.&end_data_table_row());
1.31 albertel 12646: $i++;
12647: }
1.594 raeburn 12648: $r->print(&end_data_table());
1.31 albertel 12649: $i--;
12650: return($i);
1.115 matthew 12651: }
12652:
1.144 matthew 12653: ######################################################
12654: ######################################################
12655:
1.115 matthew 12656: =pod
12657:
1.648 raeburn 12658: =item * &clean_excel_name($name)
1.115 matthew 12659:
12660: Returns a replacement for $name which does not contain any illegal characters.
12661:
12662: =cut
12663:
1.144 matthew 12664: ######################################################
12665: ######################################################
1.115 matthew 12666: sub clean_excel_name {
12667: my ($name) = @_;
12668: $name =~ s/[:\*\?\/\\]//g;
12669: if (length($name) > 31) {
12670: $name = substr($name,0,31);
12671: }
12672: return $name;
1.25 albertel 12673: }
1.84 albertel 12674:
1.85 albertel 12675: =pod
12676:
1.648 raeburn 12677: =item * &check_if_partid_hidden($id,$symb,$udom,$uname)
1.85 albertel 12678:
12679: Returns either 1 or undef
12680:
12681: 1 if the part is to be hidden, undef if it is to be shown
12682:
12683: Arguments are:
12684:
12685: $id the id of the part to be checked
12686: $symb, optional the symb of the resource to check
12687: $udom, optional the domain of the user to check for
12688: $uname, optional the username of the user to check for
12689:
12690: =cut
1.84 albertel 12691:
12692: sub check_if_partid_hidden {
12693: my ($id,$symb,$udom,$uname) = @_;
1.133 albertel 12694: my $hiddenparts=&Apache::lonnet::EXT('resource.0.hiddenparts',
1.84 albertel 12695: $symb,$udom,$uname);
1.141 albertel 12696: my $truth=1;
12697: #if the string starts with !, then the list is the list to show not hide
12698: if ($hiddenparts=~s/^\s*!//) { $truth=undef; }
1.84 albertel 12699: my @hiddenlist=split(/,/,$hiddenparts);
12700: foreach my $checkid (@hiddenlist) {
1.141 albertel 12701: if ($checkid =~ /^\s*\Q$id\E\s*$/) { return $truth; }
1.84 albertel 12702: }
1.141 albertel 12703: return !$truth;
1.84 albertel 12704: }
1.127 matthew 12705:
1.138 matthew 12706:
12707: ############################################################
12708: ############################################################
12709:
12710: =pod
12711:
1.157 matthew 12712: =back
12713:
1.138 matthew 12714: =head1 cgi-bin script and graphing routines
12715:
1.157 matthew 12716: =over 4
12717:
1.648 raeburn 12718: =item * &get_cgi_id()
1.138 matthew 12719:
12720: Inputs: none
12721:
12722: Returns an id which can be used to pass environment variables
12723: to various cgi-bin scripts. These environment variables will
12724: be removed from the users environment after a given time by
12725: the routine &Apache::lonnet::transfer_profile_to_env.
12726:
12727: =cut
12728:
12729: ############################################################
12730: ############################################################
1.152 albertel 12731: my $uniq=0;
1.136 matthew 12732: sub get_cgi_id {
1.154 albertel 12733: $uniq=($uniq+1)%100000;
1.280 albertel 12734: return (time.'_'.$$.'_'.$uniq);
1.136 matthew 12735: }
12736:
1.127 matthew 12737: ############################################################
12738: ############################################################
12739:
12740: =pod
12741:
1.648 raeburn 12742: =item * &DrawBarGraph()
1.127 matthew 12743:
1.138 matthew 12744: Facilitates the plotting of data in a (stacked) bar graph.
12745: Puts plot definition data into the users environment in order for
12746: graph.png to plot it. Returns an <img> tag for the plot.
12747: The bars on the plot are labeled '1','2',...,'n'.
12748:
12749: Inputs:
12750:
12751: =over 4
12752:
12753: =item $Title: string, the title of the plot
12754:
12755: =item $xlabel: string, text describing the X-axis of the plot
12756:
12757: =item $ylabel: string, text describing the Y-axis of the plot
12758:
12759: =item $Max: scalar, the maximum Y value to use in the plot
12760: If $Max is < any data point, the graph will not be rendered.
12761:
1.140 matthew 12762: =item $colors: array ref holding the colors to be used for the data sets when
1.138 matthew 12763: they are plotted. If undefined, default values will be used.
12764:
1.178 matthew 12765: =item $labels: array ref holding the labels to use on the x-axis for the bars.
12766:
1.138 matthew 12767: =item @Values: An array of array references. Each array reference holds data
12768: to be plotted in a stacked bar chart.
12769:
1.239 matthew 12770: =item If the final element of @Values is a hash reference the key/value
12771: pairs will be added to the graph definition.
12772:
1.138 matthew 12773: =back
12774:
12775: Returns:
12776:
12777: An <img> tag which references graph.png and the appropriate identifying
12778: information for the plot.
12779:
1.127 matthew 12780: =cut
12781:
12782: ############################################################
12783: ############################################################
1.134 matthew 12784: sub DrawBarGraph {
1.178 matthew 12785: my ($Title,$xlabel,$ylabel,$Max,$colors,$labels,@Values)=@_;
1.134 matthew 12786: #
12787: if (! defined($colors)) {
12788: $colors = ['#33ff00',
12789: '#0033cc', '#990000', '#aaaa66', '#663399', '#ff9933',
12790: '#66ccff', '#ff9999', '#cccc33', '#660000', '#33cc66',
12791: ];
12792: }
1.228 matthew 12793: my $extra_settings = {};
12794: if (ref($Values[-1]) eq 'HASH') {
12795: $extra_settings = pop(@Values);
12796: }
1.127 matthew 12797: #
1.136 matthew 12798: my $identifier = &get_cgi_id();
12799: my $id = 'cgi.'.$identifier;
1.129 matthew 12800: if (! @Values || ref($Values[0]) ne 'ARRAY') {
1.127 matthew 12801: return '';
12802: }
1.225 matthew 12803: #
12804: my @Labels;
12805: if (defined($labels)) {
12806: @Labels = @$labels;
12807: } else {
12808: for (my $i=0;$i<@{$Values[0]};$i++) {
12809: push (@Labels,$i+1);
12810: }
12811: }
12812: #
1.129 matthew 12813: my $NumBars = scalar(@{$Values[0]});
1.225 matthew 12814: if ($NumBars < scalar(@Labels)) { $NumBars = scalar(@Labels); }
1.129 matthew 12815: my %ValuesHash;
12816: my $NumSets=1;
12817: foreach my $array (@Values) {
12818: next if (! ref($array));
1.136 matthew 12819: $ValuesHash{$id.'.data.'.$NumSets++} =
1.132 matthew 12820: join(',',@$array);
1.129 matthew 12821: }
1.127 matthew 12822: #
1.136 matthew 12823: my ($height,$width,$xskip,$bar_width) = (200,120,1,15);
1.225 matthew 12824: if ($NumBars < 3) {
12825: $width = 120+$NumBars*32;
1.220 matthew 12826: $xskip = 1;
1.225 matthew 12827: $bar_width = 30;
12828: } elsif ($NumBars < 5) {
12829: $width = 120+$NumBars*20;
12830: $xskip = 1;
12831: $bar_width = 20;
1.220 matthew 12832: } elsif ($NumBars < 10) {
1.136 matthew 12833: $width = 120+$NumBars*15;
12834: $xskip = 1;
12835: $bar_width = 15;
12836: } elsif ($NumBars <= 25) {
12837: $width = 120+$NumBars*11;
12838: $xskip = 5;
12839: $bar_width = 8;
12840: } elsif ($NumBars <= 50) {
12841: $width = 120+$NumBars*8;
12842: $xskip = 5;
12843: $bar_width = 4;
12844: } else {
12845: $width = 120+$NumBars*8;
12846: $xskip = 5;
12847: $bar_width = 4;
12848: }
12849: #
1.137 matthew 12850: $Max = 1 if ($Max < 1);
12851: if ( int($Max) < $Max ) {
12852: $Max++;
12853: $Max = int($Max);
12854: }
1.127 matthew 12855: $Title = '' if (! defined($Title));
12856: $xlabel = '' if (! defined($xlabel));
12857: $ylabel = '' if (! defined($ylabel));
1.369 www 12858: $ValuesHash{$id.'.title'} = &escape($Title);
12859: $ValuesHash{$id.'.xlabel'} = &escape($xlabel);
12860: $ValuesHash{$id.'.ylabel'} = &escape($ylabel);
1.137 matthew 12861: $ValuesHash{$id.'.y_max_value'} = $Max;
1.136 matthew 12862: $ValuesHash{$id.'.NumBars'} = $NumBars;
12863: $ValuesHash{$id.'.NumSets'} = $NumSets;
12864: $ValuesHash{$id.'.PlotType'} = 'bar';
12865: $ValuesHash{$id.'.Colors'} = join(',',@{$colors});
12866: $ValuesHash{$id.'.height'} = $height;
12867: $ValuesHash{$id.'.width'} = $width;
12868: $ValuesHash{$id.'.xskip'} = $xskip;
12869: $ValuesHash{$id.'.bar_width'} = $bar_width;
12870: $ValuesHash{$id.'.labels'} = join(',',@Labels);
1.127 matthew 12871: #
1.228 matthew 12872: # Deal with other parameters
12873: while (my ($key,$value) = each(%$extra_settings)) {
12874: $ValuesHash{$id.'.'.$key} = $value;
12875: }
12876: #
1.646 raeburn 12877: &Apache::lonnet::appenv(\%ValuesHash);
1.137 matthew 12878: return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
12879: }
12880:
12881: ############################################################
12882: ############################################################
12883:
12884: =pod
12885:
1.648 raeburn 12886: =item * &DrawXYGraph()
1.137 matthew 12887:
1.138 matthew 12888: Facilitates the plotting of data in an XY graph.
12889: Puts plot definition data into the users environment in order for
12890: graph.png to plot it. Returns an <img> tag for the plot.
12891:
12892: Inputs:
12893:
12894: =over 4
12895:
12896: =item $Title: string, the title of the plot
12897:
12898: =item $xlabel: string, text describing the X-axis of the plot
12899:
12900: =item $ylabel: string, text describing the Y-axis of the plot
12901:
12902: =item $Max: scalar, the maximum Y value to use in the plot
12903: If $Max is < any data point, the graph will not be rendered.
12904:
12905: =item $colors: Array ref containing the hex color codes for the data to be
12906: plotted in. If undefined, default values will be used.
12907:
12908: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
12909:
12910: =item $Ydata: Array ref containing Array refs.
1.185 www 12911: Each of the contained arrays will be plotted as a separate curve.
1.138 matthew 12912:
12913: =item %Values: hash indicating or overriding any default values which are
12914: passed to graph.png.
12915: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
12916:
12917: =back
12918:
12919: Returns:
12920:
12921: An <img> tag which references graph.png and the appropriate identifying
12922: information for the plot.
12923:
1.137 matthew 12924: =cut
12925:
12926: ############################################################
12927: ############################################################
12928: sub DrawXYGraph {
12929: my ($Title,$xlabel,$ylabel,$Max,$colors,$Xlabels,$Ydata,%Values)=@_;
12930: #
12931: # Create the identifier for the graph
12932: my $identifier = &get_cgi_id();
12933: my $id = 'cgi.'.$identifier;
12934: #
12935: $Title = '' if (! defined($Title));
12936: $xlabel = '' if (! defined($xlabel));
12937: $ylabel = '' if (! defined($ylabel));
12938: my %ValuesHash =
12939: (
1.369 www 12940: $id.'.title' => &escape($Title),
12941: $id.'.xlabel' => &escape($xlabel),
12942: $id.'.ylabel' => &escape($ylabel),
1.137 matthew 12943: $id.'.y_max_value'=> $Max,
12944: $id.'.labels' => join(',',@$Xlabels),
12945: $id.'.PlotType' => 'XY',
12946: );
12947: #
12948: if (defined($colors) && ref($colors) eq 'ARRAY') {
12949: $ValuesHash{$id.'.Colors'} = join(',',@{$colors});
12950: }
12951: #
12952: if (! ref($Ydata) || ref($Ydata) ne 'ARRAY') {
12953: return '';
12954: }
12955: my $NumSets=1;
1.138 matthew 12956: foreach my $array (@{$Ydata}){
1.137 matthew 12957: next if (! ref($array));
12958: $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
12959: }
1.138 matthew 12960: $ValuesHash{$id.'.NumSets'} = $NumSets-1;
1.137 matthew 12961: #
12962: # Deal with other parameters
12963: while (my ($key,$value) = each(%Values)) {
12964: $ValuesHash{$id.'.'.$key} = $value;
1.127 matthew 12965: }
12966: #
1.646 raeburn 12967: &Apache::lonnet::appenv(\%ValuesHash);
1.136 matthew 12968: return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
12969: }
12970:
12971: ############################################################
12972: ############################################################
12973:
12974: =pod
12975:
1.648 raeburn 12976: =item * &DrawXYYGraph()
1.138 matthew 12977:
12978: Facilitates the plotting of data in an XY graph with two Y axes.
12979: Puts plot definition data into the users environment in order for
12980: graph.png to plot it. Returns an <img> tag for the plot.
12981:
12982: Inputs:
12983:
12984: =over 4
12985:
12986: =item $Title: string, the title of the plot
12987:
12988: =item $xlabel: string, text describing the X-axis of the plot
12989:
12990: =item $ylabel: string, text describing the Y-axis of the plot
12991:
12992: =item $colors: Array ref containing the hex color codes for the data to be
12993: plotted in. If undefined, default values will be used.
12994:
12995: =item $Xlabels: Array ref containing the labels to be used for the X-axis.
12996:
12997: =item $Ydata1: The first data set
12998:
12999: =item $Min1: The minimum value of the left Y-axis
13000:
13001: =item $Max1: The maximum value of the left Y-axis
13002:
13003: =item $Ydata2: The second data set
13004:
13005: =item $Min2: The minimum value of the right Y-axis
13006:
13007: =item $Max2: The maximum value of the left Y-axis
13008:
13009: =item %Values: hash indicating or overriding any default values which are
13010: passed to graph.png.
13011: Possible values are: width, xskip, x_ticks, x_tick_offset, among others.
13012:
13013: =back
13014:
13015: Returns:
13016:
13017: An <img> tag which references graph.png and the appropriate identifying
13018: information for the plot.
1.136 matthew 13019:
13020: =cut
13021:
13022: ############################################################
13023: ############################################################
1.137 matthew 13024: sub DrawXYYGraph {
13025: my ($Title,$xlabel,$ylabel,$colors,$Xlabels,$Ydata1,$Min1,$Max1,
13026: $Ydata2,$Min2,$Max2,%Values)=@_;
1.136 matthew 13027: #
13028: # Create the identifier for the graph
13029: my $identifier = &get_cgi_id();
13030: my $id = 'cgi.'.$identifier;
13031: #
13032: $Title = '' if (! defined($Title));
13033: $xlabel = '' if (! defined($xlabel));
13034: $ylabel = '' if (! defined($ylabel));
13035: my %ValuesHash =
13036: (
1.369 www 13037: $id.'.title' => &escape($Title),
13038: $id.'.xlabel' => &escape($xlabel),
13039: $id.'.ylabel' => &escape($ylabel),
1.136 matthew 13040: $id.'.labels' => join(',',@$Xlabels),
13041: $id.'.PlotType' => 'XY',
13042: $id.'.NumSets' => 2,
1.137 matthew 13043: $id.'.two_axes' => 1,
13044: $id.'.y1_max_value' => $Max1,
13045: $id.'.y1_min_value' => $Min1,
13046: $id.'.y2_max_value' => $Max2,
13047: $id.'.y2_min_value' => $Min2,
1.136 matthew 13048: );
13049: #
1.137 matthew 13050: if (defined($colors) && ref($colors) eq 'ARRAY') {
13051: $ValuesHash{$id.'.Colors'} = join(',',@{$colors});
13052: }
13053: #
13054: if (! ref($Ydata1) || ref($Ydata1) ne 'ARRAY' ||
13055: ! ref($Ydata2) || ref($Ydata2) ne 'ARRAY'){
1.136 matthew 13056: return '';
13057: }
13058: my $NumSets=1;
1.137 matthew 13059: foreach my $array ($Ydata1,$Ydata2){
1.136 matthew 13060: next if (! ref($array));
13061: $ValuesHash{$id.'.data.'.$NumSets++} = join(',',@$array);
1.137 matthew 13062: }
13063: #
13064: # Deal with other parameters
13065: while (my ($key,$value) = each(%Values)) {
13066: $ValuesHash{$id.'.'.$key} = $value;
1.136 matthew 13067: }
13068: #
1.646 raeburn 13069: &Apache::lonnet::appenv(\%ValuesHash);
1.130 albertel 13070: return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />';
1.139 matthew 13071: }
13072:
13073: ############################################################
13074: ############################################################
13075:
13076: =pod
13077:
1.157 matthew 13078: =back
13079:
1.139 matthew 13080: =head1 Statistics helper routines?
13081:
13082: Bad place for them but what the hell.
13083:
1.157 matthew 13084: =over 4
13085:
1.648 raeburn 13086: =item * &chartlink()
1.139 matthew 13087:
13088: Returns a link to the chart for a specific student.
13089:
13090: Inputs:
13091:
13092: =over 4
13093:
13094: =item $linktext: The text of the link
13095:
13096: =item $sname: The students username
13097:
13098: =item $sdomain: The students domain
13099:
13100: =back
13101:
1.157 matthew 13102: =back
13103:
1.139 matthew 13104: =cut
13105:
13106: ############################################################
13107: ############################################################
13108: sub chartlink {
13109: my ($linktext, $sname, $sdomain) = @_;
13110: my $link = '<a href="/adm/statistics?reportSelected=student_assessment'.
1.369 www 13111: '&SelectedStudent='.&escape($sname.':'.$sdomain).
1.219 albertel 13112: '&chartoutputmode='.HTML::Entities::encode('html, with all links').
1.139 matthew 13113: '">'.$linktext.'</a>';
1.153 matthew 13114: }
13115:
13116: #######################################################
13117: #######################################################
13118:
13119: =pod
13120:
13121: =head1 Course Environment Routines
1.157 matthew 13122:
13123: =over 4
1.153 matthew 13124:
1.648 raeburn 13125: =item * &restore_course_settings()
1.153 matthew 13126:
1.648 raeburn 13127: =item * &store_course_settings()
1.153 matthew 13128:
13129: Restores/Store indicated form parameters from the course environment.
13130: Will not overwrite existing values of the form parameters.
13131:
13132: Inputs:
13133: a scalar describing the data (e.g. 'chart', 'problem_analysis')
13134:
13135: a hash ref describing the data to be stored. For example:
13136:
13137: %Save_Parameters = ('Status' => 'scalar',
13138: 'chartoutputmode' => 'scalar',
13139: 'chartoutputdata' => 'scalar',
13140: 'Section' => 'array',
1.373 raeburn 13141: 'Group' => 'array',
1.153 matthew 13142: 'StudentData' => 'array',
13143: 'Maps' => 'array');
13144:
13145: Returns: both routines return nothing
13146:
1.631 raeburn 13147: =back
13148:
1.153 matthew 13149: =cut
13150:
13151: #######################################################
13152: #######################################################
13153: sub store_course_settings {
1.496 albertel 13154: return &store_settings($env{'request.course.id'},@_);
13155: }
13156:
13157: sub store_settings {
1.153 matthew 13158: # save to the environment
13159: # appenv the same items, just to be safe
1.300 albertel 13160: my $udom = $env{'user.domain'};
13161: my $uname = $env{'user.name'};
1.496 albertel 13162: my ($context,$prefix,$Settings) = @_;
1.153 matthew 13163: my %SaveHash;
13164: my %AppHash;
13165: while (my ($setting,$type) = each(%$Settings)) {
1.496 albertel 13166: my $basename = join('.','internal',$context,$prefix,$setting);
1.300 albertel 13167: my $envname = 'environment.'.$basename;
1.258 albertel 13168: if (exists($env{'form.'.$setting})) {
1.153 matthew 13169: # Save this value away
13170: if ($type eq 'scalar' &&
1.258 albertel 13171: (! exists($env{$envname}) ||
13172: $env{$envname} ne $env{'form.'.$setting})) {
13173: $SaveHash{$basename} = $env{'form.'.$setting};
13174: $AppHash{$envname} = $env{'form.'.$setting};
1.153 matthew 13175: } elsif ($type eq 'array') {
13176: my $stored_form;
1.258 albertel 13177: if (ref($env{'form.'.$setting})) {
1.153 matthew 13178: $stored_form = join(',',
13179: map {
1.369 www 13180: &escape($_);
1.258 albertel 13181: } sort(@{$env{'form.'.$setting}}));
1.153 matthew 13182: } else {
13183: $stored_form =
1.369 www 13184: &escape($env{'form.'.$setting});
1.153 matthew 13185: }
13186: # Determine if the array contents are the same.
1.258 albertel 13187: if ($stored_form ne $env{$envname}) {
1.153 matthew 13188: $SaveHash{$basename} = $stored_form;
13189: $AppHash{$envname} = $stored_form;
13190: }
13191: }
13192: }
13193: }
13194: my $put_result = &Apache::lonnet::put('environment',\%SaveHash,
1.300 albertel 13195: $udom,$uname);
1.153 matthew 13196: if ($put_result !~ /^(ok|delayed)/) {
13197: &Apache::lonnet::logthis('unable to save form parameters, '.
13198: 'got error:'.$put_result);
13199: }
13200: # Make sure these settings stick around in this session, too
1.646 raeburn 13201: &Apache::lonnet::appenv(\%AppHash);
1.153 matthew 13202: return;
13203: }
13204:
13205: sub restore_course_settings {
1.499 albertel 13206: return &restore_settings($env{'request.course.id'},@_);
1.496 albertel 13207: }
13208:
13209: sub restore_settings {
13210: my ($context,$prefix,$Settings) = @_;
1.153 matthew 13211: while (my ($setting,$type) = each(%$Settings)) {
1.258 albertel 13212: next if (exists($env{'form.'.$setting}));
1.496 albertel 13213: my $envname = 'environment.internal.'.$context.'.'.$prefix.
1.153 matthew 13214: '.'.$setting;
1.258 albertel 13215: if (exists($env{$envname})) {
1.153 matthew 13216: if ($type eq 'scalar') {
1.258 albertel 13217: $env{'form.'.$setting} = $env{$envname};
1.153 matthew 13218: } elsif ($type eq 'array') {
1.258 albertel 13219: $env{'form.'.$setting} = [
1.153 matthew 13220: map {
1.369 www 13221: &unescape($_);
1.258 albertel 13222: } split(',',$env{$envname})
1.153 matthew 13223: ];
13224: }
13225: }
13226: }
1.127 matthew 13227: }
13228:
1.618 raeburn 13229: #######################################################
13230: #######################################################
13231:
13232: =pod
13233:
13234: =head1 Domain E-mail Routines
13235:
13236: =over 4
13237:
1.648 raeburn 13238: =item * &build_recipient_list()
1.618 raeburn 13239:
1.1144 raeburn 13240: Build recipient lists for following types of e-mail:
1.766 raeburn 13241: (a) Error Reports, (b) Package Updates, (c) lonstatus warnings/errors
1.1144 raeburn 13242: (d) Help requests, (e) Course requests needing approval, (f) loncapa
13243: module change checking, student/employee ID conflict checks, as
13244: generated by lonerrorhandler.pm, CHECKRPMS, loncron,
13245: lonsupportreq.pm, loncoursequeueadmin.pm, searchcat.pl respectively.
1.618 raeburn 13246:
13247: Inputs:
1.619 raeburn 13248: defmail (scalar - email address of default recipient),
1.1144 raeburn 13249: mailing type (scalar: errormail, packagesmail, helpdeskmail,
13250: requestsmail, updatesmail, or idconflictsmail).
13251:
1.619 raeburn 13252: defdom (domain for which to retrieve configuration settings),
1.1144 raeburn 13253:
1.619 raeburn 13254: origmail (scalar - email address of recipient from loncapa.conf,
13255: i.e., predates configuration by DC via domainprefs.pm
1.618 raeburn 13256:
1.655 raeburn 13257: Returns: comma separated list of addresses to which to send e-mail.
13258:
13259: =back
1.618 raeburn 13260:
13261: =cut
13262:
13263: ############################################################
13264: ############################################################
13265: sub build_recipient_list {
1.619 raeburn 13266: my ($defmail,$mailing,$defdom,$origmail) = @_;
1.618 raeburn 13267: my @recipients;
13268: my $otheremails;
13269: my %domconfig =
13270: &Apache::lonnet::get_dom('configuration',['contacts'],$defdom);
13271: if (ref($domconfig{'contacts'}) eq 'HASH') {
1.766 raeburn 13272: if (exists($domconfig{'contacts'}{$mailing})) {
13273: if (ref($domconfig{'contacts'}{$mailing}) eq 'HASH') {
13274: my @contacts = ('adminemail','supportemail');
13275: foreach my $item (@contacts) {
13276: if ($domconfig{'contacts'}{$mailing}{$item}) {
13277: my $addr = $domconfig{'contacts'}{$item};
13278: if (!grep(/^\Q$addr\E$/,@recipients)) {
13279: push(@recipients,$addr);
13280: }
1.619 raeburn 13281: }
1.766 raeburn 13282: $otheremails = $domconfig{'contacts'}{$mailing}{'others'};
1.618 raeburn 13283: }
13284: }
1.766 raeburn 13285: } elsif ($origmail ne '') {
13286: push(@recipients,$origmail);
1.618 raeburn 13287: }
1.619 raeburn 13288: } elsif ($origmail ne '') {
13289: push(@recipients,$origmail);
1.618 raeburn 13290: }
1.688 raeburn 13291: if (defined($defmail)) {
13292: if ($defmail ne '') {
13293: push(@recipients,$defmail);
13294: }
1.618 raeburn 13295: }
13296: if ($otheremails) {
1.619 raeburn 13297: my @others;
13298: if ($otheremails =~ /,/) {
13299: @others = split(/,/,$otheremails);
1.618 raeburn 13300: } else {
1.619 raeburn 13301: push(@others,$otheremails);
13302: }
13303: foreach my $addr (@others) {
13304: if (!grep(/^\Q$addr\E$/,@recipients)) {
13305: push(@recipients,$addr);
13306: }
1.618 raeburn 13307: }
13308: }
1.619 raeburn 13309: my $recipientlist = join(',',@recipients);
1.618 raeburn 13310: return $recipientlist;
13311: }
13312:
1.127 matthew 13313: ############################################################
13314: ############################################################
1.154 albertel 13315:
1.655 raeburn 13316: =pod
13317:
13318: =head1 Course Catalog Routines
13319:
13320: =over 4
13321:
13322: =item * &gather_categories()
13323:
13324: Converts category definitions - keys of categories hash stored in
13325: coursecategories in configuration.db on the primary library server in a
13326: domain - to an array. Also generates javascript and idx hash used to
13327: generate Domain Coordinator interface for editing Course Categories.
13328:
13329: Inputs:
1.663 raeburn 13330:
1.655 raeburn 13331: categories (reference to hash of category definitions).
1.663 raeburn 13332:
1.655 raeburn 13333: cats (reference to array of arrays/hashes which encapsulates hierarchy of
13334: categories and subcategories).
1.663 raeburn 13335:
1.655 raeburn 13336: idx (reference to hash of counters used in Domain Coordinator interface for
13337: editing Course Categories).
1.663 raeburn 13338:
1.655 raeburn 13339: jsarray (reference to array of categories used to create Javascript arrays for
13340: Domain Coordinator interface for editing Course Categories).
13341:
13342: Returns: nothing
13343:
13344: Side effects: populates cats, idx and jsarray.
13345:
13346: =cut
13347:
13348: sub gather_categories {
13349: my ($categories,$cats,$idx,$jsarray) = @_;
13350: my %counters;
13351: my $num = 0;
13352: foreach my $item (keys(%{$categories})) {
13353: my ($cat,$container,$depth) = map { &unescape($_); } split(/:/,$item);
13354: if ($container eq '' && $depth == 0) {
13355: $cats->[$depth][$categories->{$item}] = $cat;
13356: } else {
13357: $cats->[$depth]{$container}[$categories->{$item}] = $cat;
13358: }
13359: my ($escitem,$tail) = split(/:/,$item,2);
13360: if ($counters{$tail} eq '') {
13361: $counters{$tail} = $num;
13362: $num ++;
13363: }
13364: if (ref($idx) eq 'HASH') {
13365: $idx->{$item} = $counters{$tail};
13366: }
13367: if (ref($jsarray) eq 'ARRAY') {
13368: push(@{$jsarray->[$counters{$tail}]},$item);
13369: }
13370: }
13371: return;
13372: }
13373:
13374: =pod
13375:
13376: =item * &extract_categories()
13377:
13378: Used to generate breadcrumb trails for course categories.
13379:
13380: Inputs:
1.663 raeburn 13381:
1.655 raeburn 13382: categories (reference to hash of category definitions).
1.663 raeburn 13383:
1.655 raeburn 13384: cats (reference to array of arrays/hashes which encapsulates hierarchy of
13385: categories and subcategories).
1.663 raeburn 13386:
1.655 raeburn 13387: trails (reference to array of breacrumb trails for each category).
1.663 raeburn 13388:
1.655 raeburn 13389: allitems (reference to hash - key is category key
13390: (format: escaped(name):escaped(parent category):depth in hierarchy).
1.663 raeburn 13391:
1.655 raeburn 13392: idx (reference to hash of counters used in Domain Coordinator interface for
13393: editing Course Categories).
1.663 raeburn 13394:
1.655 raeburn 13395: jsarray (reference to array of categories used to create Javascript arrays for
13396: Domain Coordinator interface for editing Course Categories).
13397:
1.665 raeburn 13398: subcats (reference to hash of arrays containing all subcategories within each
13399: category, -recursive)
13400:
1.655 raeburn 13401: Returns: nothing
13402:
13403: Side effects: populates trails and allitems hash references.
13404:
13405: =cut
13406:
13407: sub extract_categories {
1.665 raeburn 13408: my ($categories,$cats,$trails,$allitems,$idx,$jsarray,$subcats) = @_;
1.655 raeburn 13409: if (ref($categories) eq 'HASH') {
13410: &gather_categories($categories,$cats,$idx,$jsarray);
13411: if (ref($cats->[0]) eq 'ARRAY') {
13412: for (my $i=0; $i<@{$cats->[0]}; $i++) {
13413: my $name = $cats->[0][$i];
13414: my $item = &escape($name).'::0';
13415: my $trailstr;
13416: if ($name eq 'instcode') {
13417: $trailstr = &mt('Official courses (with institutional codes)');
1.919 raeburn 13418: } elsif ($name eq 'communities') {
13419: $trailstr = &mt('Communities');
1.655 raeburn 13420: } else {
13421: $trailstr = $name;
13422: }
13423: if ($allitems->{$item} eq '') {
13424: push(@{$trails},$trailstr);
13425: $allitems->{$item} = scalar(@{$trails})-1;
13426: }
13427: my @parents = ($name);
13428: if (ref($cats->[1]{$name}) eq 'ARRAY') {
13429: for (my $j=0; $j<@{$cats->[1]{$name}}; $j++) {
13430: my $category = $cats->[1]{$name}[$j];
1.665 raeburn 13431: if (ref($subcats) eq 'HASH') {
13432: push(@{$subcats->{$item}},&escape($category).':'.&escape($name).':1');
13433: }
13434: &recurse_categories($cats,2,$category,$trails,$allitems,\@parents,$subcats);
13435: }
13436: } else {
13437: if (ref($subcats) eq 'HASH') {
13438: $subcats->{$item} = [];
1.655 raeburn 13439: }
13440: }
13441: }
13442: }
13443: }
13444: return;
13445: }
13446:
13447: =pod
13448:
1.1162 raeburn 13449: =item * &recurse_categories()
1.655 raeburn 13450:
13451: Recursively used to generate breadcrumb trails for course categories.
13452:
13453: Inputs:
1.663 raeburn 13454:
1.655 raeburn 13455: cats (reference to array of arrays/hashes which encapsulates hierarchy of
13456: categories and subcategories).
1.663 raeburn 13457:
1.655 raeburn 13458: depth (current depth in hierarchy of categories and sub-categories - 0 indexed).
1.663 raeburn 13459:
13460: category (current course category, for which breadcrumb trail is being generated).
13461:
13462: trails (reference to array of breadcrumb trails for each category).
13463:
1.655 raeburn 13464: allitems (reference to hash - key is category key
13465: (format: escaped(name):escaped(parent category):depth in hierarchy).
1.663 raeburn 13466:
1.655 raeburn 13467: parents (array containing containers directories for current category,
13468: back to top level).
13469:
13470: Returns: nothing
13471:
13472: Side effects: populates trails and allitems hash references
13473:
13474: =cut
13475:
13476: sub recurse_categories {
1.665 raeburn 13477: my ($cats,$depth,$category,$trails,$allitems,$parents,$subcats) = @_;
1.655 raeburn 13478: my $shallower = $depth - 1;
13479: if (ref($cats->[$depth]{$category}) eq 'ARRAY') {
13480: for (my $k=0; $k<@{$cats->[$depth]{$category}}; $k++) {
13481: my $name = $cats->[$depth]{$category}[$k];
13482: my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
13483: my $trailstr = join(' -> ',(@{$parents},$category));
13484: if ($allitems->{$item} eq '') {
13485: push(@{$trails},$trailstr);
13486: $allitems->{$item} = scalar(@{$trails})-1;
13487: }
13488: my $deeper = $depth+1;
13489: push(@{$parents},$category);
1.665 raeburn 13490: if (ref($subcats) eq 'HASH') {
13491: my $subcat = &escape($name).':'.$category.':'.$depth;
13492: for (my $j=@{$parents}; $j>=0; $j--) {
13493: my $higher;
13494: if ($j > 0) {
13495: $higher = &escape($parents->[$j]).':'.
13496: &escape($parents->[$j-1]).':'.$j;
13497: } else {
13498: $higher = &escape($parents->[$j]).'::'.$j;
13499: }
13500: push(@{$subcats->{$higher}},$subcat);
13501: }
13502: }
13503: &recurse_categories($cats,$deeper,$name,$trails,$allitems,$parents,
13504: $subcats);
1.655 raeburn 13505: pop(@{$parents});
13506: }
13507: } else {
13508: my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower;
13509: my $trailstr = join(' -> ',(@{$parents},$category));
13510: if ($allitems->{$item} eq '') {
13511: push(@{$trails},$trailstr);
13512: $allitems->{$item} = scalar(@{$trails})-1;
13513: }
13514: }
13515: return;
13516: }
13517:
1.663 raeburn 13518: =pod
13519:
1.1162 raeburn 13520: =item * &assign_categories_table()
1.663 raeburn 13521:
13522: Create a datatable for display of hierarchical categories in a domain,
13523: with checkboxes to allow a course to be categorized.
13524:
13525: Inputs:
13526:
13527: cathash - reference to hash of categories defined for the domain (from
13528: configuration.db)
13529:
13530: currcat - scalar with an & separated list of categories assigned to a course.
13531:
1.919 raeburn 13532: type - scalar contains course type (Course or Community).
13533:
1.663 raeburn 13534: Returns: $output (markup to be displayed)
13535:
13536: =cut
13537:
13538: sub assign_categories_table {
1.919 raeburn 13539: my ($cathash,$currcat,$type) = @_;
1.663 raeburn 13540: my $output;
13541: if (ref($cathash) eq 'HASH') {
13542: my (@cats,@trails,%allitems,%idx,@jsarray,@path,$maxdepth);
13543: &extract_categories($cathash,\@cats,\@trails,\%allitems,\%idx,\@jsarray);
13544: $maxdepth = scalar(@cats);
13545: if (@cats > 0) {
13546: my $itemcount = 0;
13547: if (ref($cats[0]) eq 'ARRAY') {
13548: my @currcategories;
13549: if ($currcat ne '') {
13550: @currcategories = split('&',$currcat);
13551: }
1.919 raeburn 13552: my $table;
1.663 raeburn 13553: for (my $i=0; $i<@{$cats[0]}; $i++) {
13554: my $parent = $cats[0][$i];
1.919 raeburn 13555: next if ($parent eq 'instcode');
13556: if ($type eq 'Community') {
13557: next unless ($parent eq 'communities');
13558: } else {
13559: next if ($parent eq 'communities');
13560: }
1.663 raeburn 13561: my $css_class = $itemcount%2?' class="LC_odd_row"':'';
13562: my $item = &escape($parent).'::0';
13563: my $checked = '';
13564: if (@currcategories > 0) {
13565: if (grep(/^\Q$item\E$/,@currcategories)) {
1.772 bisitz 13566: $checked = ' checked="checked"';
1.663 raeburn 13567: }
13568: }
1.919 raeburn 13569: my $parent_title = $parent;
13570: if ($parent eq 'communities') {
13571: $parent_title = &mt('Communities');
13572: }
13573: $table .= '<tr '.$css_class.'><td><span class="LC_nobreak">'.
13574: '<input type="checkbox" name="usecategory" value="'.
13575: $item.'"'.$checked.' />'.$parent_title.'</span>'.
13576: '<input type="hidden" name="catname" value="'.$parent.'" /></td>';
1.663 raeburn 13577: my $depth = 1;
13578: push(@path,$parent);
1.919 raeburn 13579: $table .= &assign_category_rows($itemcount,\@cats,$depth,$parent,\@path,\@currcategories);
1.663 raeburn 13580: pop(@path);
1.919 raeburn 13581: $table .= '</tr><tr><td colspan="'.$maxdepth.'" class="LC_row_separator"></td></tr>';
1.663 raeburn 13582: $itemcount ++;
13583: }
1.919 raeburn 13584: if ($itemcount) {
13585: $output = &Apache::loncommon::start_data_table().
13586: $table.
13587: &Apache::loncommon::end_data_table();
13588: }
1.663 raeburn 13589: }
13590: }
13591: }
13592: return $output;
13593: }
13594:
13595: =pod
13596:
1.1162 raeburn 13597: =item * &assign_category_rows()
1.663 raeburn 13598:
13599: Create a datatable row for display of nested categories in a domain,
13600: with checkboxes to allow a course to be categorized,called recursively.
13601:
13602: Inputs:
13603:
13604: itemcount - track row number for alternating colors
13605:
13606: cats - reference to array of arrays/hashes which encapsulates hierarchy of
13607: categories and subcategories.
13608:
13609: depth - current depth in hierarchy of categories and sub-categories - 0 indexed.
13610:
13611: parent - parent of current category item
13612:
13613: path - Array containing all categories back up through the hierarchy from the
13614: current category to the top level.
13615:
13616: currcategories - reference to array of current categories assigned to the course
13617:
13618: Returns: $output (markup to be displayed).
13619:
13620: =cut
13621:
13622: sub assign_category_rows {
13623: my ($itemcount,$cats,$depth,$parent,$path,$currcategories) = @_;
13624: my ($text,$name,$item,$chgstr);
13625: if (ref($cats) eq 'ARRAY') {
13626: my $maxdepth = scalar(@{$cats});
13627: if (ref($cats->[$depth]) eq 'HASH') {
13628: if (ref($cats->[$depth]{$parent}) eq 'ARRAY') {
13629: my $numchildren = @{$cats->[$depth]{$parent}};
13630: my $css_class = $itemcount%2?' class="LC_odd_row"':'';
1.1145 raeburn 13631: $text .= '<td><table class="LC_data_table">';
1.663 raeburn 13632: for (my $j=0; $j<$numchildren; $j++) {
13633: $name = $cats->[$depth]{$parent}[$j];
13634: $item = &escape($name).':'.&escape($parent).':'.$depth;
13635: my $deeper = $depth+1;
13636: my $checked = '';
13637: if (ref($currcategories) eq 'ARRAY') {
13638: if (@{$currcategories} > 0) {
13639: if (grep(/^\Q$item\E$/,@{$currcategories})) {
1.772 bisitz 13640: $checked = ' checked="checked"';
1.663 raeburn 13641: }
13642: }
13643: }
1.664 raeburn 13644: $text .= '<tr><td><span class="LC_nobreak"><label>'.
13645: '<input type="checkbox" name="usecategory" value="'.
1.675 raeburn 13646: $item.'"'.$checked.' />'.$name.'</label></span>'.
13647: '<input type="hidden" name="catname" value="'.$name.'" />'.
13648: '</td><td>';
1.663 raeburn 13649: if (ref($path) eq 'ARRAY') {
13650: push(@{$path},$name);
13651: $text .= &assign_category_rows($itemcount,$cats,$deeper,$name,$path,$currcategories);
13652: pop(@{$path});
13653: }
13654: $text .= '</td></tr>';
13655: }
13656: $text .= '</table></td>';
13657: }
13658: }
13659: }
13660: return $text;
13661: }
13662:
1.655 raeburn 13663: ############################################################
13664: ############################################################
13665:
13666:
1.443 albertel 13667: sub commit_customrole {
1.664 raeburn 13668: my ($udom,$uname,$url,$three,$four,$five,$start,$end,$context) = @_;
1.630 raeburn 13669: my $output = &mt('Assigning custom role').' "'.$five.'" by '.$four.':'.$three.' in '.$url.
1.443 albertel 13670: ($start?', '.&mt('starting').' '.localtime($start):'').
13671: ($end?', ending '.localtime($end):'').': <b>'.
13672: &Apache::lonnet::assigncustomrole(
1.664 raeburn 13673: $udom,$uname,$url,$three,$four,$five,$end,$start,undef,undef,$context).
1.443 albertel 13674: '</b><br />';
13675: return $output;
13676: }
13677:
13678: sub commit_standardrole {
1.1116 raeburn 13679: my ($udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context,$credits) = @_;
1.541 raeburn 13680: my ($output,$logmsg,$linefeed);
13681: if ($context eq 'auto') {
13682: $linefeed = "\n";
13683: } else {
13684: $linefeed = "<br />\n";
13685: }
1.443 albertel 13686: if ($three eq 'st') {
1.541 raeburn 13687: my $result = &commit_studentrole(\$logmsg,$udom,$uname,$url,$three,$start,$end,
1.1116 raeburn 13688: $one,$two,$sec,$context,$credits);
1.541 raeburn 13689: if (($result =~ /^error/) || ($result eq 'not_in_class') ||
1.626 raeburn 13690: ($result eq 'unknown_course') || ($result eq 'refused')) {
13691: $output = $logmsg.' '.&mt('Error: ').$result."\n";
1.443 albertel 13692: } else {
1.541 raeburn 13693: $output = $logmsg.$linefeed.&mt('Assigning').' '.$three.' in '.$url.
1.443 albertel 13694: ($start?', '.&mt('starting').' '.localtime($start):'').
1.541 raeburn 13695: ($end?', '.&mt('ending').' '.localtime($end):'').': ';
13696: if ($context eq 'auto') {
13697: $output .= $result.$linefeed.&mt('Add to classlist').': ok';
13698: } else {
13699: $output .= '<b>'.$result.'</b>'.$linefeed.
13700: &mt('Add to classlist').': <b>ok</b>';
13701: }
13702: $output .= $linefeed;
1.443 albertel 13703: }
13704: } else {
13705: $output = &mt('Assigning').' '.$three.' in '.$url.
13706: ($start?', '.&mt('starting').' '.localtime($start):'').
1.541 raeburn 13707: ($end?', '.&mt('ending').' '.localtime($end):'').': ';
1.652 raeburn 13708: my $result = &Apache::lonnet::assignrole($udom,$uname,$url,$three,$end,$start,'','',$context);
1.541 raeburn 13709: if ($context eq 'auto') {
13710: $output .= $result.$linefeed;
13711: } else {
13712: $output .= '<b>'.$result.'</b>'.$linefeed;
13713: }
1.443 albertel 13714: }
13715: return $output;
13716: }
13717:
13718: sub commit_studentrole {
1.1116 raeburn 13719: my ($logmsg,$udom,$uname,$url,$three,$start,$end,$one,$two,$sec,$context,
13720: $credits) = @_;
1.626 raeburn 13721: my ($result,$linefeed,$oldsecurl,$newsecurl);
1.541 raeburn 13722: if ($context eq 'auto') {
13723: $linefeed = "\n";
13724: } else {
13725: $linefeed = '<br />'."\n";
13726: }
1.443 albertel 13727: if (defined($one) && defined($two)) {
13728: my $cid=$one.'_'.$two;
13729: my $oldsec=&Apache::lonnet::getsection($udom,$uname,$cid);
13730: my $secchange = 0;
13731: my $expire_role_result;
13732: my $modify_section_result;
1.628 raeburn 13733: if ($oldsec ne '-1') {
13734: if ($oldsec ne $sec) {
1.443 albertel 13735: $secchange = 1;
1.628 raeburn 13736: my $now = time;
1.443 albertel 13737: my $uurl='/'.$cid;
13738: $uurl=~s/\_/\//g;
13739: if ($oldsec) {
13740: $uurl.='/'.$oldsec;
13741: }
1.626 raeburn 13742: $oldsecurl = $uurl;
1.628 raeburn 13743: $expire_role_result =
1.652 raeburn 13744: &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',$now,'','',$context);
1.628 raeburn 13745: if ($env{'request.course.sec'} ne '') {
13746: if ($expire_role_result eq 'refused') {
13747: my @roles = ('st');
13748: my @statuses = ('previous');
13749: my @roledoms = ($one);
13750: my $withsec = 1;
13751: my %roleshash =
13752: &Apache::lonnet::get_my_roles($uname,$udom,'userroles',
13753: \@statuses,\@roles,\@roledoms,$withsec);
13754: if (defined ($roleshash{$two.':'.$one.':st:'.$oldsec})) {
13755: my ($oldstart,$oldend) =
13756: split(':',$roleshash{$two.':'.$one.':st:'.$oldsec});
13757: if ($oldend > 0 && $oldend <= $now) {
13758: $expire_role_result = 'ok';
13759: }
13760: }
13761: }
13762: }
1.443 albertel 13763: $result = $expire_role_result;
13764: }
13765: }
13766: if (($expire_role_result eq 'ok') || ($secchange == 0)) {
1.1116 raeburn 13767: $modify_section_result =
13768: &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,
13769: undef,undef,undef,$sec,
13770: $end,$start,'','',$cid,
13771: '',$context,$credits);
1.443 albertel 13772: if ($modify_section_result =~ /^ok/) {
13773: if ($secchange == 1) {
1.628 raeburn 13774: if ($sec eq '') {
13775: $$logmsg .= &mt('Section for [_1] switched from (possibly expired) old section: [_2] to student role without a section.',$uname,$oldsec).$linefeed;
13776: } else {
13777: $$logmsg .= &mt('Section for [_1] switched from (possibly expired) old section: [_2] to new section: [_3].',$uname,$oldsec,$sec).$linefeed;
13778: }
1.443 albertel 13779: } elsif ($oldsec eq '-1') {
1.628 raeburn 13780: if ($sec eq '') {
13781: $$logmsg .= &mt('New student role without a section for [_1] in course [_2].',$uname,$cid).$linefeed;
13782: } else {
13783: $$logmsg .= &mt('New student role for [_1] in section [_2] in course [_3].',$uname,$sec,$cid).$linefeed;
13784: }
1.443 albertel 13785: } else {
1.628 raeburn 13786: if ($sec eq '') {
13787: $$logmsg .= &mt('Student [_1] assigned to course [_2] without a section.',$uname,$cid).$linefeed;
13788: } else {
13789: $$logmsg .= &mt('Student [_1] assigned to section [_2] in course [_3].',$uname,$sec,$cid).$linefeed;
13790: }
1.443 albertel 13791: }
13792: } else {
1.1115 raeburn 13793: if ($secchange) {
1.628 raeburn 13794: $$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;
13795: } else {
13796: $$logmsg .= &mt('Error when attempting to modify role for [_1] for section: "[_2]" in course [_3] -error:',$uname,$sec,$cid).' '.$modify_section_result.$linefeed;
13797: }
1.443 albertel 13798: }
13799: $result = $modify_section_result;
13800: } elsif ($secchange == 1) {
1.628 raeburn 13801: if ($oldsec eq '') {
1.1103 raeburn 13802: $$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 13803: } else {
13804: $$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;
13805: }
1.626 raeburn 13806: if ($expire_role_result eq 'refused') {
13807: my $newsecurl = '/'.$cid;
13808: $newsecurl =~ s/\_/\//g;
13809: if ($sec ne '') {
13810: $newsecurl.='/'.$sec;
13811: }
13812: if (&Apache::lonnet::allowed('cst',$newsecurl) && !(&Apache::lonnet::allowed('cst',$oldsecurl))) {
13813: if ($sec eq '') {
13814: $$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;
13815: } else {
13816: $$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;
13817: }
13818: }
13819: }
1.443 albertel 13820: }
13821: } else {
1.626 raeburn 13822: $$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 13823: $result = "error: incomplete course id\n";
13824: }
13825: return $result;
13826: }
13827:
1.1108 raeburn 13828: sub show_role_extent {
13829: my ($scope,$context,$role) = @_;
13830: $scope =~ s{^/}{};
13831: my @courseroles = &Apache::lonuserutils::roles_by_context('course',1);
13832: push(@courseroles,'co');
13833: my @authorroles = &Apache::lonuserutils::roles_by_context('author');
13834: if (($context eq 'course') || (grep(/^\Q$role\E/,@courseroles))) {
13835: $scope =~ s{/}{_};
13836: return '<span class="LC_cusr_emph">'.$env{'course.'.$scope.'.description'}.'</span>';
13837: } elsif (($context eq 'author') || (grep(/^\Q$role\E/,@authorroles))) {
13838: my ($audom,$auname) = split(/\//,$scope);
13839: return &mt('[_1] Author Space','<span class="LC_cusr_emph">'.
13840: &Apache::loncommon::plainname($auname,$audom).'</span>');
13841: } else {
13842: $scope =~ s{/$}{};
13843: return &mt('Domain: [_1]','<span class="LC_cusr_emph">'.
13844: &Apache::lonnet::domain($scope,'description').'</span>');
13845: }
13846: }
13847:
1.443 albertel 13848: ############################################################
13849: ############################################################
13850:
1.566 albertel 13851: sub check_clone {
1.578 raeburn 13852: my ($args,$linefeed) = @_;
1.566 albertel 13853: my $cloneid='/'.$args->{'clonedomain'}.'/'.$args->{'clonecourse'};
13854: my ($clonecrsudom,$clonecrsunum)= &LONCAPA::split_courseid($cloneid);
13855: my $clonehome=&Apache::lonnet::homeserver($clonecrsunum,$clonecrsudom);
13856: my $clonemsg;
13857: my $can_clone = 0;
1.944 raeburn 13858: my $lctype = lc($args->{'crstype'});
1.908 raeburn 13859: if ($lctype ne 'community') {
13860: $lctype = 'course';
13861: }
1.566 albertel 13862: if ($clonehome eq 'no_host') {
1.944 raeburn 13863: if ($args->{'crstype'} eq 'Community') {
1.908 raeburn 13864: $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'});
13865: } else {
13866: $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'});
13867: }
1.566 albertel 13868: } else {
13869: my %clonedesc = &Apache::lonnet::coursedescription($cloneid,{'one_time' => 1});
1.944 raeburn 13870: if ($args->{'crstype'} eq 'Community') {
1.908 raeburn 13871: if ($clonedesc{'type'} ne 'Community') {
13872: $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'});
13873: return ($can_clone, $clonemsg, $cloneid, $clonehome);
13874: }
13875: }
1.882 raeburn 13876: if (($env{'request.role.domain'} eq $args->{'clonedomain'}) &&
13877: (&Apache::lonnet::allowed('ccc',$env{'request.role.domain'}))) {
1.566 albertel 13878: $can_clone = 1;
13879: } else {
13880: my %clonehash = &Apache::lonnet::get('environment',['cloners'],
13881: $args->{'clonedomain'},$args->{'clonecourse'});
13882: my @cloners = split(/,/,$clonehash{'cloners'});
1.578 raeburn 13883: if (grep(/^\*$/,@cloners)) {
13884: $can_clone = 1;
13885: } elsif (grep(/^\*\:\Q$args->{'ccdomain'}\E$/,@cloners)) {
13886: $can_clone = 1;
13887: } else {
1.908 raeburn 13888: my $ccrole = 'cc';
1.944 raeburn 13889: if ($args->{'crstype'} eq 'Community') {
1.908 raeburn 13890: $ccrole = 'co';
13891: }
1.578 raeburn 13892: my %roleshash =
13893: &Apache::lonnet::get_my_roles($args->{'ccuname'},
13894: $args->{'ccdomain'},
1.908 raeburn 13895: 'userroles',['active'],[$ccrole],
1.578 raeburn 13896: [$args->{'clonedomain'}]);
1.908 raeburn 13897: if (($roleshash{$args->{'clonecourse'}.':'.$args->{'clonedomain'}.':'.$ccrole}) || (grep(/^\Q$args->{'ccuname'}\E:\Q$args->{'ccdomain'}\E$/,@cloners))) {
1.942 raeburn 13898: $can_clone = 1;
13899: } elsif (&Apache::lonnet::is_course_owner($args->{'clonedomain'},$args->{'clonecourse'},$args->{'ccuname'},$args->{'ccdomain'})) {
13900: $can_clone = 1;
13901: } else {
1.944 raeburn 13902: if ($args->{'crstype'} eq 'Community') {
1.908 raeburn 13903: $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'});
13904: } else {
13905: $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'});
13906: }
1.578 raeburn 13907: }
1.566 albertel 13908: }
1.578 raeburn 13909: }
1.566 albertel 13910: }
13911: return ($can_clone, $clonemsg, $cloneid, $clonehome);
13912: }
13913:
1.444 albertel 13914: sub construct_course {
1.1166 raeburn 13915: my ($args,$logmsg,$courseid,$crsudom,$crsunum,$udom,$uname,$context,$cnum,$category,$coderef) = @_;
1.444 albertel 13916: my $outcome;
1.541 raeburn 13917: my $linefeed = '<br />'."\n";
13918: if ($context eq 'auto') {
13919: $linefeed = "\n";
13920: }
1.566 albertel 13921:
13922: #
13923: # Are we cloning?
13924: #
13925: my ($can_clone, $clonemsg, $cloneid, $clonehome);
13926: if (($args->{'clonecourse'}) && ($args->{'clonedomain'})) {
1.578 raeburn 13927: ($can_clone, $clonemsg, $cloneid, $clonehome) = &check_clone($args,$linefeed);
1.566 albertel 13928: if ($context ne 'auto') {
1.578 raeburn 13929: if ($clonemsg ne '') {
13930: $clonemsg = '<span class="LC_error">'.$clonemsg.'</span>';
13931: }
1.566 albertel 13932: }
13933: $outcome .= $clonemsg.$linefeed;
13934:
13935: if (!$can_clone) {
13936: return (0,$outcome);
13937: }
13938: }
13939:
1.444 albertel 13940: #
13941: # Open course
13942: #
13943: my $crstype = lc($args->{'crstype'});
13944: my %cenv=();
13945: $$courseid=&Apache::lonnet::createcourse($args->{'course_domain'},
13946: $args->{'cdescr'},
13947: $args->{'curl'},
13948: $args->{'course_home'},
13949: $args->{'nonstandard'},
13950: $args->{'crscode'},
13951: $args->{'ccuname'}.':'.
13952: $args->{'ccdomain'},
1.882 raeburn 13953: $args->{'crstype'},
1.885 raeburn 13954: $cnum,$context,$category);
1.444 albertel 13955:
13956: # Note: The testing routines depend on this being output; see
13957: # Utils::Course. This needs to at least be output as a comment
13958: # if anyone ever decides to not show this, and Utils::Course::new
13959: # will need to be suitably modified.
1.541 raeburn 13960: $outcome .= &mt('New LON-CAPA [_1] ID: [_2]',$crstype,$$courseid).$linefeed;
1.943 raeburn 13961: if ($$courseid =~ /^error:/) {
13962: return (0,$outcome);
13963: }
13964:
1.444 albertel 13965: #
13966: # Check if created correctly
13967: #
1.479 albertel 13968: ($$crsudom,$$crsunum)= &LONCAPA::split_courseid($$courseid);
1.444 albertel 13969: my $crsuhome=&Apache::lonnet::homeserver($$crsunum,$$crsudom);
1.943 raeburn 13970: if ($crsuhome eq 'no_host') {
13971: $outcome .= &mt('Course creation failed, unrecognized course home server.').$linefeed;
13972: return (0,$outcome);
13973: }
1.541 raeburn 13974: $outcome .= &mt('Created on').': '.$crsuhome.$linefeed;
1.566 albertel 13975:
1.444 albertel 13976: #
1.566 albertel 13977: # Do the cloning
13978: #
13979: if ($can_clone && $cloneid) {
13980: $clonemsg = &mt('Cloning [_1] from [_2]',$crstype,$clonehome);
13981: if ($context ne 'auto') {
13982: $clonemsg = '<span class="LC_success">'.$clonemsg.'</span>';
13983: }
13984: $outcome .= $clonemsg.$linefeed;
13985: my %oldcenv=&Apache::lonnet::dump('environment',$$crsudom,$$crsunum);
1.444 albertel 13986: # Copy all files
1.637 www 13987: &Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid,$args->{'datemode'},$args->{'dateshift'});
1.444 albertel 13988: # Restore URL
1.566 albertel 13989: $cenv{'url'}=$oldcenv{'url'};
1.444 albertel 13990: # Restore title
1.566 albertel 13991: $cenv{'description'}=$oldcenv{'description'};
1.955 raeburn 13992: # Restore creation date, creator and creation context.
13993: $cenv{'internal.created'}=$oldcenv{'internal.created'};
13994: $cenv{'internal.creator'}=$oldcenv{'internal.creator'};
13995: $cenv{'internal.creationcontext'}=$oldcenv{'internal.creationcontext'};
1.444 albertel 13996: # Mark as cloned
1.566 albertel 13997: $cenv{'clonedfrom'}=$cloneid;
1.638 www 13998: # Need to clone grading mode
13999: my %newenv=&Apache::lonnet::get('environment',['grading'],$$crsudom,$$crsunum);
14000: $cenv{'grading'}=$newenv{'grading'};
14001: # Do not clone these environment entries
14002: &Apache::lonnet::del('environment',
14003: ['default_enrollment_start_date',
14004: 'default_enrollment_end_date',
14005: 'question.email',
14006: 'policy.email',
14007: 'comment.email',
14008: 'pch.users.denied',
1.725 raeburn 14009: 'plc.users.denied',
14010: 'hidefromcat',
1.1121 raeburn 14011: 'checkforpriv',
1.1166 raeburn 14012: 'categories',
14013: 'internal.uniquecode'],
1.638 www 14014: $$crsudom,$$crsunum);
1.1170 raeburn 14015: if ($args->{'textbook'}) {
14016: $cenv{'internal.textbook'} = $args->{'textbook'};
14017: }
1.444 albertel 14018: }
1.566 albertel 14019:
1.444 albertel 14020: #
14021: # Set environment (will override cloned, if existing)
14022: #
14023: my @sections = ();
14024: my @xlists = ();
14025: if ($args->{'crstype'}) {
14026: $cenv{'type'}=$args->{'crstype'};
14027: }
14028: if ($args->{'crsid'}) {
14029: $cenv{'courseid'}=$args->{'crsid'};
14030: }
14031: if ($args->{'crscode'}) {
14032: $cenv{'internal.coursecode'}=$args->{'crscode'};
14033: }
14034: if ($args->{'crsquota'} ne '') {
14035: $cenv{'internal.coursequota'}=$args->{'crsquota'};
14036: } else {
14037: $cenv{'internal.coursequota'}=$args->{'crsquota'} = 20;
14038: }
14039: if ($args->{'ccuname'}) {
14040: $cenv{'internal.courseowner'} = $args->{'ccuname'}.
14041: ':'.$args->{'ccdomain'};
14042: } else {
14043: $cenv{'internal.courseowner'} = $args->{'curruser'};
14044: }
1.1116 raeburn 14045: if ($args->{'defaultcredits'}) {
14046: $cenv{'internal.defaultcredits'} = $args->{'defaultcredits'};
14047: }
1.444 albertel 14048: my @badclasses = (); # Used to accumulate sections/crosslistings that did not pass classlist access check for course owner.
14049: if ($args->{'crssections'}) {
14050: $cenv{'internal.sectionnums'} = '';
14051: if ($args->{'crssections'} =~ m/,/) {
14052: @sections = split/,/,$args->{'crssections'};
14053: } else {
14054: $sections[0] = $args->{'crssections'};
14055: }
14056: if (@sections > 0) {
14057: foreach my $item (@sections) {
14058: my ($sec,$gp) = split/:/,$item;
14059: my $class = $args->{'crscode'}.$sec;
14060: my $addcheck = &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$class,$cenv{'internal.courseowner'});
14061: $cenv{'internal.sectionnums'} .= $item.',';
14062: unless ($addcheck eq 'ok') {
14063: push @badclasses, $class;
14064: }
14065: }
14066: $cenv{'internal.sectionnums'} =~ s/,$//;
14067: }
14068: }
14069: # do not hide course coordinator from staff listing,
14070: # even if privileged
14071: $cenv{'nothideprivileged'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
1.1121 raeburn 14072: # add course coordinator's domain to domains to check for privileged users
14073: # if different to course domain
14074: if ($$crsudom ne $args->{'ccdomain'}) {
14075: $cenv{'checkforpriv'} = $args->{'ccdomain'};
14076: }
1.444 albertel 14077: # add crosslistings
14078: if ($args->{'crsxlist'}) {
14079: $cenv{'internal.crosslistings'}='';
14080: if ($args->{'crsxlist'} =~ m/,/) {
14081: @xlists = split/,/,$args->{'crsxlist'};
14082: } else {
14083: $xlists[0] = $args->{'crsxlist'};
14084: }
14085: if (@xlists > 0) {
14086: foreach my $item (@xlists) {
14087: my ($xl,$gp) = split/:/,$item;
14088: my $addcheck = &Apache::lonnet::auto_new_course($$crsunum,$$crsudom,$xl,$cenv{'internal.courseowner'});
14089: $cenv{'internal.crosslistings'} .= $item.',';
14090: unless ($addcheck eq 'ok') {
14091: push @badclasses, $xl;
14092: }
14093: }
14094: $cenv{'internal.crosslistings'} =~ s/,$//;
14095: }
14096: }
14097: if ($args->{'autoadds'}) {
14098: $cenv{'internal.autoadds'}=$args->{'autoadds'};
14099: }
14100: if ($args->{'autodrops'}) {
14101: $cenv{'internal.autodrops'}=$args->{'autodrops'};
14102: }
14103: # check for notification of enrollment changes
14104: my @notified = ();
14105: if ($args->{'notify_owner'}) {
14106: if ($args->{'ccuname'} ne '') {
14107: push(@notified,$args->{'ccuname'}.':'.$args->{'ccdomain'});
14108: }
14109: }
14110: if ($args->{'notify_dc'}) {
14111: if ($uname ne '') {
1.630 raeburn 14112: push(@notified,$uname.':'.$udom);
1.444 albertel 14113: }
14114: }
14115: if (@notified > 0) {
14116: my $notifylist;
14117: if (@notified > 1) {
14118: $notifylist = join(',',@notified);
14119: } else {
14120: $notifylist = $notified[0];
14121: }
14122: $cenv{'internal.notifylist'} = $notifylist;
14123: }
14124: if (@badclasses > 0) {
14125: my %lt=&Apache::lonlocal::texthash(
14126: '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',
14127: 'dnhr' => 'does not have rights to access enrollment in these classes',
14128: 'adby' => 'as determined by the policies of your institution on access to official classlists'
14129: );
1.541 raeburn 14130: my $badclass_msg = $cenv{'internal.courseowner'}.') - '.$lt{'dnhr'}.
14131: ' ('.$lt{'adby'}.')';
14132: if ($context eq 'auto') {
14133: $outcome .= $badclass_msg.$linefeed;
1.566 albertel 14134: $outcome .= '<div class="LC_warning">'.$badclass_msg.$linefeed.'<ul>'."\n";
1.541 raeburn 14135: foreach my $item (@badclasses) {
14136: if ($context eq 'auto') {
14137: $outcome .= " - $item\n";
14138: } else {
14139: $outcome .= "<li>$item</li>\n";
14140: }
14141: }
14142: if ($context eq 'auto') {
14143: $outcome .= $linefeed;
14144: } else {
1.566 albertel 14145: $outcome .= "</ul><br /><br /></div>\n";
1.541 raeburn 14146: }
14147: }
1.444 albertel 14148: }
14149: if ($args->{'no_end_date'}) {
14150: $args->{'endaccess'} = 0;
14151: }
14152: $cenv{'internal.autostart'}=$args->{'enrollstart'};
14153: $cenv{'internal.autoend'}=$args->{'enrollend'};
14154: $cenv{'default_enrollment_start_date'}=$args->{'startaccess'};
14155: $cenv{'default_enrollment_end_date'}=$args->{'endaccess'};
14156: if ($args->{'showphotos'}) {
14157: $cenv{'internal.showphotos'}=$args->{'showphotos'};
14158: }
14159: $cenv{'internal.authtype'} = $args->{'authtype'};
14160: $cenv{'internal.autharg'} = $args->{'autharg'};
14161: if ( ($cenv{'internal.authtype'} =~ /^krb/) && ($cenv{'internal.autoadds'} == 1)) {
14162: if (! defined($cenv{'internal.autharg'}) || $cenv{'internal.autharg'} eq '') {
1.541 raeburn 14163: 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');
14164: if ($context eq 'auto') {
14165: $outcome .= $krb_msg;
14166: } else {
1.566 albertel 14167: $outcome .= '<span class="LC_error">'.$krb_msg.'</span>';
1.541 raeburn 14168: }
14169: $outcome .= $linefeed;
1.444 albertel 14170: }
14171: }
14172: if (($args->{'ccdomain'}) && ($args->{'ccuname'})) {
14173: if ($args->{'setpolicy'}) {
14174: $cenv{'policy.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
14175: }
14176: if ($args->{'setcontent'}) {
14177: $cenv{'question.email'}=$args->{'ccuname'}.':'.$args->{'ccdomain'};
14178: }
14179: }
14180: if ($args->{'reshome'}) {
14181: $cenv{'reshome'}=$args->{'reshome'}.'/';
14182: $cenv{'reshome'}=~s/\/+$/\//;
14183: }
14184: #
14185: # course has keyed access
14186: #
14187: if ($args->{'setkeys'}) {
14188: $cenv{'keyaccess'}='yes';
14189: }
14190: # if specified, key authority is not course, but user
14191: # only active if keyaccess is yes
14192: if ($args->{'keyauth'}) {
1.487 albertel 14193: my ($user,$domain) = split(':',$args->{'keyauth'});
14194: $user = &LONCAPA::clean_username($user);
14195: $domain = &LONCAPA::clean_username($domain);
1.488 foxr 14196: if ($user ne '' && $domain ne '') {
1.487 albertel 14197: $cenv{'keyauth'}=$user.':'.$domain;
1.444 albertel 14198: }
14199: }
14200:
1.1166 raeburn 14201: #
1.1167 raeburn 14202: # generate and store uniquecode (available to course requester), if course should have one.
1.1166 raeburn 14203: #
14204: if ($args->{'uniquecode'}) {
14205: my ($code,$error) = &make_unique_code($$crsudom,$$crsunum);
14206: if ($code) {
14207: $cenv{'internal.uniquecode'} = $code;
1.1167 raeburn 14208: my %crsinfo =
14209: &Apache::lonnet::courseiddump($$crsudom,'.',1,'.','.',$$crsunum,undef,undef,'.');
14210: if (ref($crsinfo{$$crsudom.'_'.$$crsunum}) eq 'HASH') {
14211: $crsinfo{$$crsudom.'_'.$$crsunum}{'uniquecode'} = $code;
14212: my $putres = &Apache::lonnet::courseidput($$crsudom,\%crsinfo,$crsuhome,'notime');
14213: }
1.1166 raeburn 14214: if (ref($coderef)) {
14215: $$coderef = $code;
14216: }
14217: }
14218: }
14219:
1.444 albertel 14220: if ($args->{'disresdis'}) {
14221: $cenv{'pch.roles.denied'}='st';
14222: }
14223: if ($args->{'disablechat'}) {
14224: $cenv{'plc.roles.denied'}='st';
14225: }
14226:
14227: # Record we've not yet viewed the Course Initialization Helper for this
14228: # course
14229: $cenv{'course.helper.not.run'} = 1;
14230: #
14231: # Use new Randomseed
14232: #
14233: $cenv{'rndseed'}=&Apache::lonnet::latest_rnd_algorithm_id();;
14234: $cenv{'receiptalg'}=&Apache::lonnet::latest_receipt_algorithm_id();;
14235: #
14236: # The encryption code and receipt prefix for this course
14237: #
14238: $cenv{'internal.encseed'}=$Apache::lonnet::perlvar{'lonReceipt'}.$$.time.int(rand(9999));
14239: $cenv{'internal.encpref'}=100+int(9*rand(99));
14240: #
14241: # By default, use standard grading
14242: if (!defined($cenv{'grading'})) { $cenv{'grading'} = 'standard'; }
14243:
1.541 raeburn 14244: $outcome .= $linefeed.&mt('Setting environment').': '.
14245: &Apache::lonnet::put('environment',\%cenv,$$crsudom,$$crsunum).$linefeed;
1.444 albertel 14246: #
14247: # Open all assignments
14248: #
14249: if ($args->{'openall'}) {
14250: my $storeunder=$$crsudom.'_'.$$crsunum.'.0.opendate';
14251: my %storecontent = ($storeunder => time,
14252: $storeunder.'.type' => 'date_start');
14253:
14254: $outcome .= &mt('Opening all assignments').': '.&Apache::lonnet::cput
1.541 raeburn 14255: ('resourcedata',\%storecontent,$$crsudom,$$crsunum).$linefeed;
1.444 albertel 14256: }
14257: #
14258: # Set first page
14259: #
14260: unless (($args->{'nonstandard'}) || ($args->{'firstres'} eq 'blank')
14261: || ($cloneid)) {
1.445 albertel 14262: use LONCAPA::map;
1.444 albertel 14263: $outcome .= &mt('Setting first resource').': ';
1.445 albertel 14264:
14265: my $map = '/uploaded/'.$$crsudom.'/'.$$crsunum.'/default.sequence';
14266: my ($errtext,$fatal)=&LONCAPA::map::mapread($map);
14267:
1.444 albertel 14268: $outcome .= ($fatal?$errtext:'read ok').' - ';
14269: my $title; my $url;
14270: if ($args->{'firstres'} eq 'syl') {
1.690 bisitz 14271: $title=&mt('Syllabus');
1.444 albertel 14272: $url='/public/'.$$crsudom.'/'.$$crsunum.'/syllabus';
14273: } else {
1.963 raeburn 14274: $title=&mt('Table of Contents');
1.444 albertel 14275: $url='/adm/navmaps';
14276: }
1.445 albertel 14277:
14278: $LONCAPA::map::resources[1]=$title.':'.$url.':false:start:res';
14279: (my $outtext,$errtext) = &LONCAPA::map::storemap($map,1);
14280:
14281: if ($errtext) { $fatal=2; }
1.541 raeburn 14282: $outcome .= ($fatal?$errtext:'write ok').$linefeed;
1.444 albertel 14283: }
1.566 albertel 14284:
14285: return (1,$outcome);
1.444 albertel 14286: }
14287:
1.1166 raeburn 14288: sub make_unique_code {
14289: my ($cdom,$cnum) = @_;
14290: # get lock on uniquecodes db
14291: my $lockhash = {
14292: $cnum."\0".'uniquecodes' => $env{'user.name'}.
14293: ':'.$env{'user.domain'},
14294: };
14295: my $tries = 0;
14296: my $gotlock = &Apache::lonnet::newput_dom('uniquecodes',$lockhash,$cdom);
14297: my ($code,$error);
14298:
14299: while (($gotlock ne 'ok') && ($tries<3)) {
14300: $tries ++;
14301: sleep 1;
14302: $gotlock = &Apache::lonnet::newput_dom('uniquecodes',$lockhash,$cdom);
14303: }
14304: if ($gotlock eq 'ok') {
14305: my %currcodes = &Apache::lonnet::dump_dom('uniquecodes',$cdom);
14306: my $gotcode;
14307: my $attempts = 0;
14308: while ((!$gotcode) && ($attempts < 100)) {
14309: $code = &generate_code();
14310: if (!exists($currcodes{$code})) {
14311: $gotcode = 1;
14312: unless (&Apache::lonnet::newput_dom('uniquecodes',{ $code => $cnum },$cdom) eq 'ok') {
14313: $error = 'nostore';
14314: }
14315: }
14316: $attempts ++;
14317: }
14318: my @del_lock = ($cnum."\0".'uniquecodes');
14319: my $dellockoutcome = &Apache::lonnet::del_dom('uniquecodes',\@del_lock,$cdom);
14320: } else {
14321: $error = 'nolock';
14322: }
14323: return ($code,$error);
14324: }
14325:
14326: sub generate_code {
14327: my $code;
14328: my @letts = qw(B C D G H J K M N P Q R S T V W X Z);
14329: for (my $i=0; $i<6; $i++) {
14330: my $lettnum = int (rand 2);
14331: my $item = '';
14332: if ($lettnum) {
14333: $item = $letts[int( rand(18) )];
14334: } else {
14335: $item = 1+int( rand(8) );
14336: }
14337: $code .= $item;
14338: }
14339: return $code;
14340: }
14341:
1.444 albertel 14342: ############################################################
14343: ############################################################
14344:
1.953 droeschl 14345: #SD
14346: # only Community and Course, or anything else?
1.378 raeburn 14347: sub course_type {
14348: my ($cid) = @_;
14349: if (!defined($cid)) {
14350: $cid = $env{'request.course.id'};
14351: }
1.404 albertel 14352: if (defined($env{'course.'.$cid.'.type'})) {
14353: return $env{'course.'.$cid.'.type'};
1.378 raeburn 14354: } else {
14355: return 'Course';
1.377 raeburn 14356: }
14357: }
1.156 albertel 14358:
1.406 raeburn 14359: sub group_term {
14360: my $crstype = &course_type();
14361: my %names = (
14362: 'Course' => 'group',
1.865 raeburn 14363: 'Community' => 'group',
1.406 raeburn 14364: );
14365: return $names{$crstype};
14366: }
14367:
1.902 raeburn 14368: sub course_types {
1.1165 raeburn 14369: my @types = ('official','unofficial','community','textbook');
1.902 raeburn 14370: my %typename = (
14371: official => 'Official course',
14372: unofficial => 'Unofficial course',
14373: community => 'Community',
1.1165 raeburn 14374: textbook => 'Textbook course',
1.902 raeburn 14375: );
14376: return (\@types,\%typename);
14377: }
14378:
1.156 albertel 14379: sub icon {
14380: my ($file)=@_;
1.505 albertel 14381: my $curfext = lc((split(/\./,$file))[-1]);
1.168 albertel 14382: my $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/unknown.gif';
1.156 albertel 14383: my $embstyle = &Apache::loncommon::fileembstyle($curfext);
1.168 albertel 14384: if (!(!defined($embstyle) || $embstyle eq 'unk' || $embstyle eq 'hdn')) {
14385: if (-e $Apache::lonnet::perlvar{'lonDocRoot'}.'/'.
14386: $Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
14387: $curfext.".gif") {
14388: $iconname=$Apache::lonnet::perlvar{'lonIconsURL'}.'/'.
14389: $curfext.".gif";
14390: }
14391: }
1.249 albertel 14392: return &lonhttpdurl($iconname);
1.154 albertel 14393: }
1.84 albertel 14394:
1.575 albertel 14395: sub lonhttpdurl {
1.692 www 14396: #
14397: # Had been used for "small fry" static images on separate port 8080.
14398: # Modify here if lightweight http functionality desired again.
14399: # Currently eliminated due to increasing firewall issues.
14400: #
1.575 albertel 14401: my ($url)=@_;
1.692 www 14402: return $url;
1.215 albertel 14403: }
14404:
1.213 albertel 14405: sub connection_aborted {
14406: my ($r)=@_;
14407: $r->print(" ");$r->rflush();
14408: my $c = $r->connection;
14409: return $c->aborted();
14410: }
14411:
1.221 foxr 14412: # Escapes strings that may have embedded 's that will be put into
1.222 foxr 14413: # strings as 'strings'.
14414: sub escape_single {
1.221 foxr 14415: my ($input) = @_;
1.223 albertel 14416: $input =~ s/\\/\\\\/g; # Escape the \'s..(must be first)>
1.221 foxr 14417: $input =~ s/\'/\\\'/g; # Esacpe the 's....
14418: return $input;
14419: }
1.223 albertel 14420:
1.222 foxr 14421: # Same as escape_single, but escape's "'s This
14422: # can be used for "strings"
14423: sub escape_double {
14424: my ($input) = @_;
14425: $input =~ s/\\/\\\\/g; # Escape the /'s..(must be first)>
14426: $input =~ s/\"/\\\"/g; # Esacpe the "s....
14427: return $input;
14428: }
1.223 albertel 14429:
1.222 foxr 14430: # Escapes the last element of a full URL.
14431: sub escape_url {
14432: my ($url) = @_;
1.238 raeburn 14433: my @urlslices = split(/\//, $url,-1);
1.369 www 14434: my $lastitem = &escape(pop(@urlslices));
1.223 albertel 14435: return join('/',@urlslices).'/'.$lastitem;
1.222 foxr 14436: }
1.462 albertel 14437:
1.820 raeburn 14438: sub compare_arrays {
14439: my ($arrayref1,$arrayref2) = @_;
14440: my (@difference,%count);
14441: @difference = ();
14442: %count = ();
14443: if ((ref($arrayref1) eq 'ARRAY') && (ref($arrayref2) eq 'ARRAY')) {
14444: foreach my $element (@{$arrayref1}, @{$arrayref2}) { $count{$element}++; }
14445: foreach my $element (keys(%count)) {
14446: if ($count{$element} == 1) {
14447: push(@difference,$element);
14448: }
14449: }
14450: }
14451: return @difference;
14452: }
14453:
1.817 bisitz 14454: # -------------------------------------------------------- Initialize user login
1.462 albertel 14455: sub init_user_environment {
1.463 albertel 14456: my ($r, $username, $domain, $authhost, $form, $args) = @_;
1.462 albertel 14457: my $lonids=$Apache::lonnet::perlvar{'lonIDsDir'};
14458:
14459: my $public=($username eq 'public' && $domain eq 'public');
14460:
14461: # See if old ID present, if so, remove
14462:
1.1062 raeburn 14463: my ($filename,$cookie,$userroles,$firstaccenv,$timerintenv);
1.462 albertel 14464: my $now=time;
14465:
14466: if ($public) {
14467: my $max_public=100;
14468: my $oldest;
14469: my $oldest_time=0;
14470: for(my $next=1;$next<=$max_public;$next++) {
14471: if (-e $lonids."/publicuser_$next.id") {
14472: my $mtime=(stat($lonids."/publicuser_$next.id"))[9];
14473: if ($mtime<$oldest_time || !$oldest_time) {
14474: $oldest_time=$mtime;
14475: $oldest=$next;
14476: }
14477: } else {
14478: $cookie="publicuser_$next";
14479: last;
14480: }
14481: }
14482: if (!$cookie) { $cookie="publicuser_$oldest"; }
14483: } else {
1.463 albertel 14484: # if this isn't a robot, kill any existing non-robot sessions
14485: if (!$args->{'robot'}) {
14486: opendir(DIR,$lonids);
14487: while ($filename=readdir(DIR)) {
14488: if ($filename=~/^$username\_\d+\_$domain\_$authhost\.id$/) {
14489: unlink($lonids.'/'.$filename);
14490: }
1.462 albertel 14491: }
1.463 albertel 14492: closedir(DIR);
1.462 albertel 14493: }
14494: # Give them a new cookie
1.463 albertel 14495: my $id = ($args->{'robot'} ? 'robot'.$args->{'robot'}
1.684 www 14496: : $now.$$.int(rand(10000)));
1.463 albertel 14497: $cookie="$username\_$id\_$domain\_$authhost";
1.462 albertel 14498:
14499: # Initialize roles
14500:
1.1062 raeburn 14501: ($userroles,$firstaccenv,$timerintenv) =
14502: &Apache::lonnet::rolesinit($domain,$username,$authhost);
1.462 albertel 14503: }
14504: # ------------------------------------ Check browser type and MathML capability
14505:
14506: my ($httpbrowser,$clientbrowser,$clientversion,$clientmathml,
1.1141 raeburn 14507: $clientunicode,$clientos,$clientmobile,$clientinfo) = &decode_user_agent($r);
1.462 albertel 14508:
14509: # ------------------------------------------------------------- Get environment
14510:
14511: my %userenv = &Apache::lonnet::dump('environment',$domain,$username);
14512: my ($tmp) = keys(%userenv);
14513: if ($tmp !~ /^(con_lost|error|no_such_host)/i) {
14514: } else {
14515: undef(%userenv);
14516: }
14517: if (($userenv{'interface'}) && (!$form->{'interface'})) {
14518: $form->{'interface'}=$userenv{'interface'};
14519: }
14520: if ($userenv{'texengine'} eq 'ttm') { $clientmathml=1; }
14521:
14522: # --------------- Do not trust query string to be put directly into environment
1.817 bisitz 14523: foreach my $option ('interface','localpath','localres') {
14524: $form->{$option}=~s/[\n\r\=]//gs;
1.462 albertel 14525: }
14526: # --------------------------------------------------------- Write first profile
14527:
14528: {
14529: my %initial_env =
14530: ("user.name" => $username,
14531: "user.domain" => $domain,
14532: "user.home" => $authhost,
14533: "browser.type" => $clientbrowser,
14534: "browser.version" => $clientversion,
14535: "browser.mathml" => $clientmathml,
14536: "browser.unicode" => $clientunicode,
14537: "browser.os" => $clientos,
1.1137 raeburn 14538: "browser.mobile" => $clientmobile,
1.1141 raeburn 14539: "browser.info" => $clientinfo,
1.462 albertel 14540: "server.domain" => $Apache::lonnet::perlvar{'lonDefDomain'},
14541: "request.course.fn" => '',
14542: "request.course.uri" => '',
14543: "request.course.sec" => '',
14544: "request.role" => 'cm',
14545: "request.role.adv" => $env{'user.adv'},
14546: "request.host" => $ENV{'REMOTE_ADDR'},);
14547:
14548: if ($form->{'localpath'}) {
14549: $initial_env{"browser.localpath"} = $form->{'localpath'};
14550: $initial_env{"browser.localres"} = $form->{'localres'};
14551: }
14552:
14553: if ($form->{'interface'}) {
14554: $form->{'interface'}=~s/\W//gs;
14555: $initial_env{"browser.interface"} = $form->{'interface'};
14556: $env{'browser.interface'}=$form->{'interface'};
14557: }
14558:
1.1157 raeburn 14559: if ($form->{'iptoken'}) {
14560: my $lonhost = $r->dir_config('lonHostID');
14561: $initial_env{"user.noloadbalance"} = $lonhost;
14562: $env{'user.noloadbalance'} = $lonhost;
14563: }
14564:
1.981 raeburn 14565: my %is_adv = ( is_adv => $env{'user.adv'} );
1.1016 raeburn 14566: my %domdef;
14567: unless ($domain eq 'public') {
14568: %domdef = &Apache::lonnet::get_domain_defaults($domain);
14569: }
1.980 raeburn 14570:
1.1081 raeburn 14571: foreach my $tool ('aboutme','blog','webdav','portfolio') {
1.724 raeburn 14572: $userenv{'availabletools.'.$tool} =
1.980 raeburn 14573: &Apache::lonnet::usertools_access($username,$domain,$tool,'reload',
14574: undef,\%userenv,\%domdef,\%is_adv);
1.724 raeburn 14575: }
14576:
1.1165 raeburn 14577: foreach my $crstype ('official','unofficial','community','textbook') {
1.765 raeburn 14578: $userenv{'canrequest.'.$crstype} =
14579: &Apache::lonnet::usertools_access($username,$domain,$crstype,
1.980 raeburn 14580: 'reload','requestcourses',
14581: \%userenv,\%domdef,\%is_adv);
1.765 raeburn 14582: }
14583:
1.1092 raeburn 14584: $userenv{'canrequest.author'} =
14585: &Apache::lonnet::usertools_access($username,$domain,'requestauthor',
14586: 'reload','requestauthor',
14587: \%userenv,\%domdef,\%is_adv);
14588: my %reqauthor = &Apache::lonnet::get('requestauthor',['author_status','author'],
14589: $domain,$username);
14590: my $reqstatus = $reqauthor{'author_status'};
14591: if ($reqstatus eq 'approval' || $reqstatus eq 'approved') {
14592: if (ref($reqauthor{'author'}) eq 'HASH') {
14593: $userenv{'requestauthorqueued'} = $reqstatus.':'.
14594: $reqauthor{'author'}{'timestamp'};
14595: }
14596: }
14597:
1.462 albertel 14598: $env{'user.environment'} = "$lonids/$cookie.id";
1.1062 raeburn 14599:
1.462 albertel 14600: if (tie(my %disk_env,'GDBM_File',"$lonids/$cookie.id",
14601: &GDBM_WRCREAT(),0640)) {
14602: &_add_to_env(\%disk_env,\%initial_env);
14603: &_add_to_env(\%disk_env,\%userenv,'environment.');
14604: &_add_to_env(\%disk_env,$userroles);
1.1062 raeburn 14605: if (ref($firstaccenv) eq 'HASH') {
14606: &_add_to_env(\%disk_env,$firstaccenv);
14607: }
14608: if (ref($timerintenv) eq 'HASH') {
14609: &_add_to_env(\%disk_env,$timerintenv);
14610: }
1.463 albertel 14611: if (ref($args->{'extra_env'})) {
14612: &_add_to_env(\%disk_env,$args->{'extra_env'});
14613: }
1.462 albertel 14614: untie(%disk_env);
14615: } else {
1.705 tempelho 14616: &Apache::lonnet::logthis("<span style=\"color:blue;\">WARNING: ".
14617: 'Could not create environment storage in lonauth: '.$!.'</span>');
1.462 albertel 14618: return 'error: '.$!;
14619: }
14620: }
14621: $env{'request.role'}='cm';
14622: $env{'request.role.adv'}=$env{'user.adv'};
14623: $env{'browser.type'}=$clientbrowser;
14624:
14625: return $cookie;
14626:
14627: }
14628:
14629: sub _add_to_env {
14630: my ($idf,$env_data,$prefix) = @_;
1.676 raeburn 14631: if (ref($env_data) eq 'HASH') {
14632: while (my ($key,$value) = each(%$env_data)) {
14633: $idf->{$prefix.$key} = $value;
14634: $env{$prefix.$key} = $value;
14635: }
1.462 albertel 14636: }
14637: }
14638:
1.685 tempelho 14639: # --- Get the symbolic name of a problem and the url
14640: sub get_symb {
14641: my ($request,$silent) = @_;
1.726 raeburn 14642: (my $url=$env{'form.url'}) =~ s-^https?\://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--;
1.685 tempelho 14643: my $symb=($env{'form.symb'} ne '' ? $env{'form.symb'} : (&Apache::lonnet::symbread($url)));
14644: if ($symb eq '') {
14645: if (!$silent) {
1.1071 raeburn 14646: if (ref($request)) {
14647: $request->print("Unable to handle ambiguous references:$url:.");
14648: }
1.685 tempelho 14649: return ();
14650: }
14651: }
14652: &Apache::lonenc::check_decrypt(\$symb);
14653: return ($symb);
14654: }
14655:
14656: # --------------------------------------------------------------Get annotation
14657:
14658: sub get_annotation {
14659: my ($symb,$enc) = @_;
14660:
14661: my $key = $symb;
14662: if (!$enc) {
14663: $key =
14664: &Apache::lonnet::clutter((&Apache::lonnet::decode_symb($symb))[2]);
14665: }
14666: my %annotation=&Apache::lonnet::get('nohist_annotations',[$key]);
14667: return $annotation{$key};
14668: }
14669:
14670: sub clean_symb {
1.731 raeburn 14671: my ($symb,$delete_enc) = @_;
1.685 tempelho 14672:
14673: &Apache::lonenc::check_decrypt(\$symb);
14674: my $enc = $env{'request.enc'};
1.731 raeburn 14675: if ($delete_enc) {
1.730 raeburn 14676: delete($env{'request.enc'});
14677: }
1.685 tempelho 14678:
14679: return ($symb,$enc);
14680: }
1.462 albertel 14681:
1.990 raeburn 14682: sub build_release_hashes {
14683: my ($checkparms,$checkresponsetypes,$checkcrstypes,$anonsurvey,$randomizetry) = @_;
14684: return unless((ref($checkparms) eq 'HASH') && (ref($checkresponsetypes) eq 'HASH') &&
14685: (ref($checkcrstypes) eq 'HASH') && (ref($anonsurvey) eq 'HASH') &&
14686: (ref($randomizetry) eq 'HASH'));
14687: foreach my $key (keys(%Apache::lonnet::needsrelease)) {
14688: my ($item,$name,$value) = split(/:/,$key);
14689: if ($item eq 'parameter') {
14690: if (ref($checkparms->{$name}) eq 'ARRAY') {
14691: unless(grep(/^\Q$name\E$/,@{$checkparms->{$name}})) {
14692: push(@{$checkparms->{$name}},$value);
14693: }
14694: } else {
14695: push(@{$checkparms->{$name}},$value);
14696: }
14697: } elsif ($item eq 'resourcetag') {
14698: if ($name eq 'responsetype') {
14699: $checkresponsetypes->{$value} = $Apache::lonnet::needsrelease{$key}
14700: }
14701: } elsif ($item eq 'course') {
14702: if ($name eq 'crstype') {
14703: $checkcrstypes->{$value} = $Apache::lonnet::needsrelease{$key};
14704: }
14705: }
14706: }
14707: ($anonsurvey->{major},$anonsurvey->{minor}) = split(/\./,$Apache::lonnet::needsrelease{'parameter:type:anonsurvey'});
14708: ($randomizetry->{major},$randomizetry->{minor}) = split(/\./,$Apache::lonnet::needsrelease{'parameter:type:randomizetry'});
14709: return;
14710: }
14711:
1.1083 raeburn 14712: sub update_content_constraints {
14713: my ($cdom,$cnum,$chome,$cid) = @_;
14714: my %curr_reqd_hash = &Apache::lonnet::userenvironment($cdom,$cnum,'internal.releaserequired');
14715: my ($reqdmajor,$reqdminor) = split(/\./,$curr_reqd_hash{'internal.releaserequired'});
14716: my %checkresponsetypes;
14717: foreach my $key (keys(%Apache::lonnet::needsrelease)) {
14718: my ($item,$name,$value) = split(/:/,$key);
14719: if ($item eq 'resourcetag') {
14720: if ($name eq 'responsetype') {
14721: $checkresponsetypes{$value} = $Apache::lonnet::needsrelease{$key}
14722: }
14723: }
14724: }
14725: my $navmap = Apache::lonnavmaps::navmap->new();
14726: if (defined($navmap)) {
14727: my %allresponses;
14728: foreach my $res ($navmap->retrieveResources(undef,sub { $_[0]->is_problem() },1,0)) {
14729: my %responses = $res->responseTypes();
14730: foreach my $key (keys(%responses)) {
14731: next unless(exists($checkresponsetypes{$key}));
14732: $allresponses{$key} += $responses{$key};
14733: }
14734: }
14735: foreach my $key (keys(%allresponses)) {
14736: my ($major,$minor) = split(/\./,$checkresponsetypes{$key});
14737: if (($major > $reqdmajor) || ($major == $reqdmajor && $minor > $reqdminor)) {
14738: ($reqdmajor,$reqdminor) = ($major,$minor);
14739: }
14740: }
14741: undef($navmap);
14742: }
14743: unless (($reqdmajor eq '') && ($reqdminor eq '')) {
14744: &Apache::lonnet::update_released_required($reqdmajor.'.'.$reqdminor,$cdom,$cnum,$chome,$cid);
14745: }
14746: return;
14747: }
14748:
1.1110 raeburn 14749: sub allmaps_incourse {
14750: my ($cdom,$cnum,$chome,$cid) = @_;
14751: if ($cdom eq '' || $cnum eq '' || $chome eq '' || $cid eq '') {
14752: $cid = $env{'request.course.id'};
14753: $cdom = $env{'course.'.$cid.'.domain'};
14754: $cnum = $env{'course.'.$cid.'.num'};
14755: $chome = $env{'course.'.$cid.'.home'};
14756: }
14757: my %allmaps = ();
14758: my $lastchange =
14759: &Apache::lonnet::get_coursechange($cdom,$cnum);
14760: if ($lastchange > $env{'request.course.tied'}) {
14761: my ($furl,$ferr) = &Apache::lonuserstate::readmap("$cdom/$cnum");
14762: unless ($ferr) {
14763: &update_content_constraints($cdom,$cnum,$chome,$cid);
14764: }
14765: }
14766: my $navmap = Apache::lonnavmaps::navmap->new();
14767: if (defined($navmap)) {
14768: foreach my $res ($navmap->retrieveResources(undef,sub { $_[0]->is_map() },1,0,1)) {
14769: $allmaps{$res->src()} = 1;
14770: }
14771: }
14772: return \%allmaps;
14773: }
14774:
1.1083 raeburn 14775: sub parse_supplemental_title {
14776: my ($title) = @_;
14777:
14778: my ($foldertitle,$renametitle);
14779: if ($title =~ /&&&/) {
14780: $title = &HTML::Entites::decode($title);
14781: }
14782: if ($title =~ m/^(\d+)___&&&___($match_username)___&&&___($match_domain)___&&&___(.*)$/) {
14783: $renametitle=$4;
14784: my ($time,$uname,$udom) = ($1,$2,$3);
14785: $foldertitle=&Apache::lontexconvert::msgtexconverted($4);
14786: my $name = &plainname($uname,$udom);
14787: $name = &HTML::Entities::encode($name,'"<>&\'');
14788: $renametitle = &HTML::Entities::encode($renametitle,'"<>&\'');
14789: $title='<i>'.&Apache::lonlocal::locallocaltime($time).'</i> '.
14790: $name.': <br />'.$foldertitle;
14791: }
14792: if (wantarray) {
14793: return ($title,$foldertitle,$renametitle);
14794: }
14795: return $title;
14796: }
14797:
1.1143 raeburn 14798: sub recurse_supplemental {
14799: my ($cnum,$cdom,$suppmap,$numfiles,$errors) = @_;
14800: if ($suppmap) {
14801: my ($errtext,$fatal) = &LONCAPA::map::mapread('/uploaded/'.$cdom.'/'.$cnum.'/'.$suppmap);
14802: if ($fatal) {
14803: $errors ++;
14804: } else {
14805: if ($#LONCAPA::map::resources > 0) {
14806: foreach my $res (@LONCAPA::map::resources) {
14807: my ($title,$src,$ext,$type,$status)=split(/\:/,$res);
14808: if (($src ne '') && ($status eq 'res')) {
1.1146 raeburn 14809: if ($src =~ m{^\Q/uploaded/$cdom/$cnum/\E(supplemental_\d+\.sequence)$}) {
14810: ($numfiles,$errors) = &recurse_supplemental($cnum,$cdom,$1,$numfiles,$errors);
1.1143 raeburn 14811: } else {
14812: $numfiles ++;
14813: }
14814: }
14815: }
14816: }
14817: }
14818: }
14819: return ($numfiles,$errors);
14820: }
14821:
1.1101 raeburn 14822: sub symb_to_docspath {
14823: my ($symb) = @_;
14824: return unless ($symb);
14825: my ($mapurl,$id,$resurl) = &Apache::lonnet::decode_symb($symb);
14826: if ($resurl=~/\.(sequence|page)$/) {
14827: $mapurl=$resurl;
14828: } elsif ($resurl eq 'adm/navmaps') {
14829: $mapurl=$env{'course.'.$env{'request.course.id'}.'.url'};
14830: }
14831: my $mapresobj;
14832: my $navmap = Apache::lonnavmaps::navmap->new();
14833: if (ref($navmap)) {
14834: $mapresobj = $navmap->getResourceByUrl($mapurl);
14835: }
14836: $mapurl=~s{^.*/([^/]+)\.(\w+)$}{$1};
14837: my $type=$2;
14838: my $path;
14839: if (ref($mapresobj)) {
14840: my $pcslist = $mapresobj->map_hierarchy();
14841: if ($pcslist ne '') {
14842: foreach my $pc (split(/,/,$pcslist)) {
14843: next if ($pc <= 1);
14844: my $res = $navmap->getByMapPc($pc);
14845: if (ref($res)) {
14846: my $thisurl = $res->src();
14847: $thisurl=~s{^.*/([^/]+)\.\w+$}{$1};
14848: my $thistitle = $res->title();
14849: $path .= '&'.
14850: &Apache::lonhtmlcommon::entity_encode($thisurl).'&'.
1.1146 raeburn 14851: &escape($thistitle).
1.1101 raeburn 14852: ':'.$res->randompick().
14853: ':'.$res->randomout().
14854: ':'.$res->encrypted().
14855: ':'.$res->randomorder().
14856: ':'.$res->is_page();
14857: }
14858: }
14859: }
14860: $path =~ s/^\&//;
14861: my $maptitle = $mapresobj->title();
14862: if ($mapurl eq 'default') {
1.1129 raeburn 14863: $maptitle = 'Main Content';
1.1101 raeburn 14864: }
14865: $path .= (($path ne '')? '&' : '').
14866: &Apache::lonhtmlcommon::entity_encode($mapurl).'&'.
1.1146 raeburn 14867: &escape($maptitle).
1.1101 raeburn 14868: ':'.$mapresobj->randompick().
14869: ':'.$mapresobj->randomout().
14870: ':'.$mapresobj->encrypted().
14871: ':'.$mapresobj->randomorder().
14872: ':'.$mapresobj->is_page();
14873: } else {
14874: my $maptitle = &Apache::lonnet::gettitle($mapurl);
14875: my $ispage = (($type eq 'page')? 1 : '');
14876: if ($mapurl eq 'default') {
1.1129 raeburn 14877: $maptitle = 'Main Content';
1.1101 raeburn 14878: }
14879: $path = &Apache::lonhtmlcommon::entity_encode($mapurl).'&'.
1.1146 raeburn 14880: &escape($maptitle).':::::'.$ispage;
1.1101 raeburn 14881: }
14882: unless ($mapurl eq 'default') {
14883: $path = 'default&'.
1.1146 raeburn 14884: &escape('Main Content').
1.1101 raeburn 14885: ':::::&'.$path;
14886: }
14887: return $path;
14888: }
14889:
1.1094 raeburn 14890: sub captcha_display {
14891: my ($context,$lonhost) = @_;
14892: my ($output,$error);
14893: my ($captcha,$pubkey,$privkey) = &get_captcha_config($context,$lonhost);
1.1095 raeburn 14894: if ($captcha eq 'original') {
1.1094 raeburn 14895: $output = &create_captcha();
14896: unless ($output) {
1.1172 raeburn 14897: $error = 'captcha';
1.1094 raeburn 14898: }
14899: } elsif ($captcha eq 'recaptcha') {
14900: $output = &create_recaptcha($pubkey);
14901: unless ($output) {
1.1172 raeburn 14902: $error = 'recaptcha';
1.1094 raeburn 14903: }
14904: }
14905: return ($output,$error);
14906: }
14907:
14908: sub captcha_response {
14909: my ($context,$lonhost) = @_;
14910: my ($captcha_chk,$captcha_error);
14911: my ($captcha,$pubkey,$privkey) = &get_captcha_config($context,$lonhost);
1.1095 raeburn 14912: if ($captcha eq 'original') {
1.1094 raeburn 14913: ($captcha_chk,$captcha_error) = &check_captcha();
14914: } elsif ($captcha eq 'recaptcha') {
14915: $captcha_chk = &check_recaptcha($privkey);
14916: } else {
14917: $captcha_chk = 1;
14918: }
14919: return ($captcha_chk,$captcha_error);
14920: }
14921:
14922: sub get_captcha_config {
14923: my ($context,$lonhost) = @_;
1.1095 raeburn 14924: my ($captcha,$pubkey,$privkey,$hashtocheck);
1.1094 raeburn 14925: my $hostname = &Apache::lonnet::hostname($lonhost);
14926: my $serverhomeID = &Apache::lonnet::get_server_homeID($hostname);
14927: my $serverhomedom = &Apache::lonnet::host_domain($serverhomeID);
1.1095 raeburn 14928: if ($context eq 'usercreation') {
14929: my %domconfig = &Apache::lonnet::get_dom('configuration',[$context],$serverhomedom);
14930: if (ref($domconfig{$context}) eq 'HASH') {
14931: $hashtocheck = $domconfig{$context}{'cancreate'};
14932: if (ref($hashtocheck) eq 'HASH') {
14933: if ($hashtocheck->{'captcha'} eq 'recaptcha') {
14934: if (ref($hashtocheck->{'recaptchakeys'}) eq 'HASH') {
14935: $pubkey = $hashtocheck->{'recaptchakeys'}{'public'};
14936: $privkey = $hashtocheck->{'recaptchakeys'}{'private'};
14937: }
14938: if ($privkey && $pubkey) {
14939: $captcha = 'recaptcha';
14940: } else {
14941: $captcha = 'original';
14942: }
14943: } elsif ($hashtocheck->{'captcha'} ne 'notused') {
14944: $captcha = 'original';
14945: }
1.1094 raeburn 14946: }
1.1095 raeburn 14947: } else {
14948: $captcha = 'captcha';
14949: }
14950: } elsif ($context eq 'login') {
14951: my %domconfhash = &Apache::loncommon::get_domainconf($serverhomedom);
14952: if ($domconfhash{$serverhomedom.'.login.captcha'} eq 'recaptcha') {
14953: $pubkey = $domconfhash{$serverhomedom.'.login.recaptchakeys_public'};
14954: $privkey = $domconfhash{$serverhomedom.'.login.recaptchakeys_private'};
1.1094 raeburn 14955: if ($privkey && $pubkey) {
14956: $captcha = 'recaptcha';
1.1095 raeburn 14957: } else {
14958: $captcha = 'original';
1.1094 raeburn 14959: }
1.1095 raeburn 14960: } elsif ($domconfhash{$serverhomedom.'.login.captcha'} eq 'original') {
14961: $captcha = 'original';
1.1094 raeburn 14962: }
14963: }
14964: return ($captcha,$pubkey,$privkey);
14965: }
14966:
14967: sub create_captcha {
14968: my %captcha_params = &captcha_settings();
14969: my ($output,$maxtries,$tries) = ('',10,0);
14970: while ($tries < $maxtries) {
14971: $tries ++;
14972: my $captcha = Authen::Captcha->new (
14973: output_folder => $captcha_params{'output_dir'},
14974: data_folder => $captcha_params{'db_dir'},
14975: );
14976: my $md5sum = $captcha->generate_code($captcha_params{'numchars'});
14977:
14978: if (-e $Apache::lonnet::perlvar{'lonCaptchaDir'}.'/'.$md5sum.'.png') {
14979: $output = '<input type="hidden" name="crypt" value="'.$md5sum.'" />'."\n".
14980: &mt('Type in the letters/numbers shown below').' '.
14981: '<input type="text" size="5" name="code" value="" /><br />'.
1.1172 raeburn 14982: '<img src="'.$captcha_params{'www_output_dir'}.'/'.$md5sum.'.png" alt="captcha" />';
1.1094 raeburn 14983: last;
14984: }
14985: }
14986: return $output;
14987: }
14988:
14989: sub captcha_settings {
14990: my %captcha_params = (
14991: output_dir => $Apache::lonnet::perlvar{'lonCaptchaDir'},
14992: www_output_dir => "/captchaspool",
14993: db_dir => $Apache::lonnet::perlvar{'lonCaptchaDb'},
14994: numchars => '5',
14995: );
14996: return %captcha_params;
14997: }
14998:
14999: sub check_captcha {
15000: my ($captcha_chk,$captcha_error);
15001: my $code = $env{'form.code'};
15002: my $md5sum = $env{'form.crypt'};
15003: my %captcha_params = &captcha_settings();
15004: my $captcha = Authen::Captcha->new(
15005: output_folder => $captcha_params{'output_dir'},
15006: data_folder => $captcha_params{'db_dir'},
15007: );
1.1109 raeburn 15008: $captcha_chk = $captcha->check_code($code,$md5sum);
1.1094 raeburn 15009: my %captcha_hash = (
15010: 0 => 'Code not checked (file error)',
15011: -1 => 'Failed: code expired',
15012: -2 => 'Failed: invalid code (not in database)',
15013: -3 => 'Failed: invalid code (code does not match crypt)',
15014: );
15015: if ($captcha_chk != 1) {
15016: $captcha_error = $captcha_hash{$captcha_chk}
15017: }
15018: return ($captcha_chk,$captcha_error);
15019: }
15020:
15021: sub create_recaptcha {
15022: my ($pubkey) = @_;
1.1153 raeburn 15023: my $use_ssl;
15024: if ($ENV{'SERVER_PORT'} == 443) {
15025: $use_ssl = 1;
15026: }
1.1094 raeburn 15027: my $captcha = Captcha::reCAPTCHA->new;
15028: return $captcha->get_options_setter({theme => 'white'})."\n".
1.1153 raeburn 15029: $captcha->get_html($pubkey,undef,$use_ssl).
1.1094 raeburn 15030: &mt('If either word is hard to read, [_1] will replace them.',
1.1133 raeburn 15031: '<img src="/res/adm/pages/refresh.gif" alt="reCAPTCHA refresh" />').
1.1094 raeburn 15032: '<br /><br />';
15033: }
15034:
15035: sub check_recaptcha {
15036: my ($privkey) = @_;
15037: my $captcha_chk;
15038: my $captcha = Captcha::reCAPTCHA->new;
15039: my $captcha_result =
15040: $captcha->check_answer(
15041: $privkey,
15042: $ENV{'REMOTE_ADDR'},
15043: $env{'form.recaptcha_challenge_field'},
15044: $env{'form.recaptcha_response_field'},
15045: );
15046: if ($captcha_result->{is_valid}) {
15047: $captcha_chk = 1;
15048: }
15049: return $captcha_chk;
15050: }
15051:
1.1174 raeburn 15052: sub emailusername_info {
15053: my @fields = ('lastname','firstname','institution','web','location','officialemail');
15054: my %titles = &Apache::lonlocal::texthash (
15055: lastname => 'Last Name',
15056: firstname => 'First Name',
15057: institution => 'School/college/university',
15058: location => "School's city, state/province, country",
15059: web => "School's web address",
15060: officialemail => 'E-mail address at institution (if different)',
15061: );
15062: return (\@fields,\%titles);
15063: }
15064:
1.1161 raeburn 15065: sub cleanup_html {
15066: my ($incoming) = @_;
15067: my $outgoing;
15068: if ($incoming ne '') {
15069: $outgoing = $incoming;
15070: $outgoing =~ s/;/;/g;
15071: $outgoing =~ s/\#/#/g;
15072: $outgoing =~ s/\&/&/g;
15073: $outgoing =~ s/</</g;
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: }
15084: return $outgoing;
15085: }
15086:
1.1174 raeburn 15087: # Use:
15088: # my $answer=reply("encrypt:passwd:$udom:$uname:$upass",$tryserver);
15089: #
15090: ##################################################
15091: # password associated functions #
15092: ##################################################
15093: sub des_keys {
15094: # Make a new key for DES encryption.
15095: # Each key has two parts which are returned separately.
15096: # Please note: Each key must be passed through the &hex function
15097: # before it is output to the web browser. The hex versions cannot
15098: # be used to decrypt.
15099: my @hexstr=('0','1','2','3','4','5','6','7',
15100: '8','9','a','b','c','d','e','f');
15101: my $lkey='';
15102: for (0..7) {
15103: $lkey.=$hexstr[rand(15)];
15104: }
15105: my $ukey='';
15106: for (0..7) {
15107: $ukey.=$hexstr[rand(15)];
15108: }
15109: return ($lkey,$ukey);
15110: }
15111:
15112: sub des_decrypt {
15113: my ($key,$cyphertext) = @_;
15114: my $keybin=pack("H16",$key);
15115: my $cypher;
15116: if ($Crypt::DES::VERSION>=2.03) {
15117: $cypher=new Crypt::DES $keybin;
15118: } else {
15119: $cypher=new DES $keybin;
15120: }
15121: my $plaintext=
15122: $cypher->decrypt(unpack("a8",pack("H16",substr($cyphertext,0,16))));
15123: $plaintext.=
15124: $cypher->decrypt(unpack("a8",pack("H16",substr($cyphertext,16,16))));
15125: $plaintext=substr($plaintext,1,ord(substr($plaintext,0,1)) );
15126: return $plaintext;
15127: }
15128:
1.41 ng 15129: =pod
15130:
15131: =back
15132:
1.112 bowersj2 15133: =cut
1.41 ng 15134:
1.112 bowersj2 15135: 1;
15136: __END__;
1.41 ng 15137:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>