--- loncom/interface/loncommon.pm 2008/01/04 18:08:11 1.632 +++ loncom/interface/loncommon.pm 2008/12/08 22:43:52 1.711 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # a pile of common routines # -# $Id: loncommon.pm,v 1.632 2008/01/04 18:08:11 raeburn Exp $ +# $Id: loncommon.pm,v 1.711 2008/12/08 22:43:52 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -61,12 +61,15 @@ use POSIX qw(strftime mktime); use Apache::lonmenu(); use Apache::lonenc(); use Apache::lonlocal; +use Apache::lonnet(); use HTML::Entities; use Apache::lonhtmlcommon(); use Apache::loncoursedata(); use Apache::lontexconvert(); use Apache::lonclonecourse(); use LONCAPA qw(:DEFAULT :match); +use DateTime::TimeZone; +use DateTime::Locale::Catalog; # ---------------------------------------------- Designs use vars qw(%defaultdesign); @@ -78,6 +81,76 @@ my $readit; ## Global Variables ## + +# ----------------------------------------------- SSI with retries: +# + +=pod + +=head1 Server Side include with retries: + +=over 4 + +=item * &ssi_with_retries(resource,retries form) + +Performs an ssi with some number of retries. Retries continue either +until the result is ok or until the retry count supplied by the +caller is exhausted. + +Inputs: + +=over 4 + +resource - Identifies the resource to insert. + +retries - Count of the number of retries allowed. + +form - Hash that identifies the rendering options. + +=back + +Returns: + +=over 4 + +content - The content of the response. If retries were exhausted this is empty. + +response - The response from the last attempt (which may or may not have been successful. + +=back + +=back + +=cut + +sub ssi_with_retries { + my ($resource, $retries, %form) = @_; + + + my $ok = 0; # True if we got a good response. + my $content; + my $response; + + # Try to get the ssi done. within the retries count: + + do { + ($content, $response) = &Apache::lonnet::ssi($resource, %form); + $ok = $response->is_success; + if (!$ok) { + &Apache::lonnet::logthis("Failed ssi_with_retries on $resource: ".$response->is_success.', '.$response->code.', '.$response->message); + } + $retries--; + } while (!$ok && ($retries > 0)); + + if (!$ok) { + $content = ''; # On error return an empty content. + } + return ($content, $response); + +} + + + # ----------------------------------------------- Filetypes/Languages/Copyright my %language; my %supported_language; @@ -198,7 +271,7 @@ BEGIN { } } &Apache::lonnet::logthis( - "<font color=yellow>INFO: Read file types</font>"); + "<span style='color:yellow;'>INFO: Read file types</span>"); $readit=1; } # end of unless($readit) @@ -214,14 +287,14 @@ BEGIN { =over 4 -=item * browser_and_searcher_javascript () +=item * &browser_and_searcher_javascript() X<browsing, javascript>X<searching, javascript>Returns a string containing javascript with two functions, C<openbrowser> and C<opensearcher>. Returned string does not contain E<lt>scriptE<gt> tags. -=item * openbrowser(formname,elementname,only,omit) [javascript] +=item * &openbrowser(formname,elementname,only,omit) [javascript] inputs: formname, elementname, only, omit @@ -234,7 +307,7 @@ with the given extension. Can be a comm Specifying 'omit' will restrict the browser to NOT displaying files with the given extension. Can be a comma separated list. -=item * opensearcher(formname, elementname) [javascript] +=item * &opensearcher(formname,elementname) [javascript] Inputs: formname, elementname @@ -319,7 +392,7 @@ sub storeresurl { unless ($resurl=~/^\/res/) { return 0; } $resurl=~s/\/$//; &Apache::lonnet::put('environment',{'lastresurl' => $resurl}); - &Apache::lonnet::appenv('environment.lastresurl' => $resurl); + &Apache::lonnet::appenv({'environment.lastresurl' => $resurl}); return 1; } @@ -377,6 +450,25 @@ sub selectstudent_link { return ''; } +sub authorbrowser_javascript { + return <<"ENDAUTHORBRW"; +<script type="text/javascript"> +var stdeditbrowser; + +function openauthorbrowser(formname,udom) { + var url = '/adm/pickauthor?'; + url += 'form='+formname+'&roledom='+udom; + var title = 'Author_Browser'; + var options = 'scrollbars=1,resizable=1,menubar=0'; + options += ',width=700,height=600'; + stdeditbrowser = open(url,title,options,'1'); + stdeditbrowser.focus(); +} + +</script> +ENDAUTHORBRW +} + sub coursebrowser_javascript { my ($domainfilter,$sec_element,$formname)=@_; my $crs_or_grp_alert = &mt('Please select the type of LON-CAPA entity - Course or Group - for which you wish to add/modify a user role'); @@ -514,6 +606,12 @@ sub selectcourse_link { '","'.$udomele.'","'.$desc.'","'.$extra_element.'","'.$multflag.'","'.$selecttype.'");'."'>".&mt('Select Course')."</a>"; } +sub selectauthor_link { + my ($form,$udom)=@_; + return '<a href="javascript:openauthorbrowser('."'$form','$udom'".');">'. + &mt('Select Author').'</a>'; +} + sub check_uncheck_jscript { my $jscript = <<"ENDSCRT"; function checkAll(field) { @@ -539,10 +637,82 @@ ENDSCRT return $jscript; } +sub select_timezone { + my ($name,$selected,$onchange,$includeempty)=@_; + my $output='<select name="'.$name.'" '.$onchange.'>'."\n"; + if ($includeempty) { + $output .= '<option value=""'; + if (($selected eq '') || ($selected eq 'local')) { + $output .= ' selected="selected" '; + } + $output .= '> </option>'; + } + my @timezones = DateTime::TimeZone->all_names; + foreach my $tzone (@timezones) { + $output.= '<option value="'.$tzone.'"'; + if ($tzone eq $selected) { + $output.=' selected="selected"'; + } + $output.=">$tzone</option>\n"; + } + $output.="</select>"; + return $output; +} + +sub select_datelocale { + my ($name,$selected,$onchange,$includeempty)=@_; + my $output='<select name="'.$name.'" '.$onchange.'>'."\n"; + if ($includeempty) { + $output .= '<option value=""'; + if ($selected eq '') { + $output .= ' selected="selected" '; + } + $output .= '> </option>'; + } + my (@possibles,%locale_names); + my @locales = DateTime::Locale::Catalog::Locales; + foreach my $locale (@locales) { + if (ref($locale) eq 'HASH') { + my $id = $locale->{'id'}; + if ($id ne '') { + my $en_terr = $locale->{'en_territory'}; + my $native_terr = $locale->{'native_territory'}; + my @languages = &Apache::lonlocal::preferred_languages(); + if (grep(/^en$/,@languages) || !@languages) { + if ($en_terr ne '') { + $locale_names{$id} = '('.$en_terr.')'; + } elsif ($native_terr ne '') { + $locale_names{$id} = $native_terr; + } + } else { + if ($native_terr ne '') { + $locale_names{$id} = $native_terr.' '; + } elsif ($en_terr ne '') { + $locale_names{$id} = '('.$en_terr.')'; + } + } + push (@possibles,$id); + } + } + } + foreach my $item (sort(@possibles)) { + $output.= '<option value="'.$item.'"'; + if ($item eq $selected) { + $output.=' selected="selected"'; + } + $output.=">$item"; + if ($locale_names{$item} ne '') { + $output.=" $locale_names{$item}</option>\n"; + } + $output.="</option>\n"; + } + $output.="</select>"; + return $output; +} =pod -=item * linked_select_forms(...) +=item * &linked_select_forms(...) linked_select_forms returns a string containing a <script></script> block and html for two <select> menus. The select menus will be linked in that @@ -707,7 +877,7 @@ END =pod -=item * help_open_topic($topic, $text, $stayOnPage, $width, $height) +=item * &help_open_topic($topic,$text,$stayOnPage,$width,$height) Returns a string corresponding to an HTML link to the given help $topic, where $topic corresponds to the name of a .tex file in @@ -756,12 +926,12 @@ sub help_open_topic { if ($text ne "") { $template .= "<table bgcolor='#3333AA' cellspacing='1' cellpadding='1' border='0'><tr>". - "<td bgcolor='#5555FF'><a target=\"_top\" href=\"$link\"><font color='#FFFFFF' size='2'>$text</font></a>"; + "<td bgcolor='#5555FF'><a target=\"_top\" href=\"$link\"><span style=\"color:#FFFFFF;font-size:10pt;\">$text</span></a>"; } # Add the graphic my $title = &mt('Online Help'); - my $helpicon=&lonhttpdurl("/adm/help/gif/smallHelp.gif"); + my $helpicon=&lonhttpdurl("/adm/help/help.png"); $template .= <<"ENDTEMPLATE"; <a target="_top" href="$link" title="$title"><img src="$helpicon" border="0" alt="(Help: $topic)" /></a> ENDTEMPLATE @@ -782,11 +952,14 @@ sub helpLatexCheatsheet { } return '<table><tr><td>'. $addOther . - &Apache::loncommon::help_open_topic("Greek_Symbols",'Greek Symbols', + &Apache::loncommon::help_open_topic("Greek_Symbols",&mt('Greek Symbols'), undef,undef,600) .'</td><td>'. - &Apache::loncommon::help_open_topic("Other_Symbols",'Other Symbols', + &Apache::loncommon::help_open_topic("Other_Symbols",&mt('Other Symbols'), undef,undef,600) + .'</td><td>'. + &Apache::loncommon::help_open_topic("Authoring_Output_Tags",&mt('Output Tags'), + undef,undef,600) .'</td></tr></table>'; } @@ -796,6 +969,8 @@ sub general_help { $helptopic='Authoring_Intro'; } elsif ($env{'request.role'}=~/^cc/) { $helptopic='Course_Coordination_Intro'; + } elsif ($env{'request.role'}=~/^dc/) { + $helptopic='Domain_Coordination_Intro'; } return $helptopic; } @@ -953,7 +1128,7 @@ sub help_open_bug { { $template .= "<table bgcolor='#AA3333' cellspacing='1' cellpadding='1' border='0'><tr>". - "<td bgcolor='#FF5555'><a target=\"_top\" href=\"$link\"><font color='#FFFFFF' size='2'>$text</font></a>"; + "<td bgcolor='#FF5555'><a target=\"_top\" href=\"$link\"><span style=\"color:#FFFFFF;font-size:10pt;\">$text</span></a>"; } # Add the graphic @@ -998,7 +1173,7 @@ sub help_open_faq { { $template .= "<table bgcolor='#337733' cellspacing='1' cellpadding='1' border='0'><tr>". - "<td bgcolor='#448844'><a target=\"_top\" href=\"$link\"><font color='#FFFFFF' size='2'>$text</font></a>"; + "<td bgcolor='#448844'><a target=\"_top\" href=\"$link\"><span style=\"color:#FFFFFF; font-size:10pt;\">$text</span></a>"; } # Add the graphic @@ -1017,7 +1192,7 @@ ENDTEMPLATE =pod -=item * change_content_javascript(): +=item * &change_content_javascript(): This and the next function allow you to create small sections of an otherwise static HTML page that you can update on the fly with @@ -1072,7 +1247,7 @@ DOMBASED =pod -=item * changable_area($name, $origContent): +=item * &changable_area($name,$origContent): This provides a "changable area" that can be modified on the fly via the Javascript code provided in C<change_content_javascript>. $name is @@ -1096,7 +1271,7 @@ sub changable_area { =pod -=item * viewport_geometry_js { +=item * &viewport_geometry_js Provides javascript object (Geometry) which can provide information about the viewport geometry for the client browser. @@ -1143,7 +1318,7 @@ GEOMETRY =pod -=item * viewport_size_js { +=item * &viewport_size_js() 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. @@ -1167,7 +1342,7 @@ DIMS =pod -=item * resize_textarea_js +=item * &resize_textarea_js() emits the needed javascript to resize a textarea to be as big as possible @@ -1176,6 +1351,7 @@ the id of the element to resize, second surrounds everything that comes after the textarea, this routine needs to be attached to the <body> for the onload and onresize events. +=back =cut @@ -1228,8 +1404,6 @@ RESIZE =pod -=back - =head1 Excel and CSV file utility routines =over 4 @@ -1241,7 +1415,7 @@ RESIZE =pod -=item * csv_translate($text) +=item * &csv_translate($text) Translate $text to allow it to be output as a 'comma separated values' format. @@ -1262,7 +1436,7 @@ sub csv_translate { =pod -=item * define_excel_formats +=item * &define_excel_formats() Define some commonly used Excel cell formats. @@ -1318,7 +1492,7 @@ sub define_excel_formats { =pod -=item * create_workbook +=item * &create_workbook() Create an Excel worksheet. If it fails, output message on the request object and return undefs. @@ -1361,7 +1535,7 @@ sub create_workbook { =pod -=item * create_text_file +=item * &create_text_file() Create a file to write to and eventually make available to the user. If file creation fails, outputs an error message on the request object and @@ -1386,9 +1560,9 @@ sub create_text_file { $fh = Apache::File->new('>/home/httpd'.$filename); if (! defined($fh)) { $r->log_error("Couldn't open $filename for output $!"); - $r->print("Problems occured in creating the output file. ". - "This error has been logged. ". - "Please alert your LON-CAPA administrator."); + $r->print(&mt('Problems occurred in creating the output file. ' + .'This error has been logged. ' + .'Please alert your LON-CAPA administrator.')); } return ($fh,$filename) } @@ -1429,7 +1603,7 @@ sub domain_select { =over 4 -=item * multiple_select_form($name,$value,$size,$hash,$order) +=item * &multiple_select_form($name,$value,$size,$hash,$order) Returns a string containing a <select> element int multiple mode @@ -1479,7 +1653,7 @@ sub multiple_select_form { =pod -=item * select_form($defdom,$name,%hash) +=item * &select_form($defdom,$name,%hash) Returns a string containing a <select name='$name' size='1'> form to allow a user to select options from a hash option_name => displayed text. @@ -1566,7 +1740,7 @@ sub select_level_form { =pod -=item * select_dom_form($defdom,$name,$includeempty,$showdomdesc) +=item * &select_dom_form($defdom,$name,$includeempty,$showdomdesc) Returns a string containing a <select name='$name' size='1'> form to allow a user to select the domain to preform an operation in. @@ -1606,7 +1780,7 @@ sub select_dom_form { =pod -=item * home_server_form_item($domain,$name,$defaultflag) +=item * &home_server_form_item($domain,$name,$defaultflag) input: 4 arguments (two required, two optional) - $domain - domain of new user @@ -1766,14 +1940,12 @@ sub decode_user_agent { =over 4 -=item * authform_xxxxxx +=item * &authform_xxxxxx() The authform_xxxxxx subroutines provide javascript and html forms which handle some of the conveniences required for authentication forms. This is not an optimal method, but it works. -See loncreateuser.pm for invocation and use examples. - =over 4 =item * authform_header @@ -1790,7 +1962,7 @@ See loncreateuser.pm for invocation and =back -=back +See loncreateuser.pm for invocation and use examples. =cut @@ -2242,42 +2414,6 @@ sub get_assignable_auth { } ############################################################### -## Get Authentication Defaults for Domain ## -############################################################### - -=pod - -=head1 Domains and Authentication - -Returns default authentication type and an associated argument as -listed in file 'domain.tab'. - -=over 4 - -=item * get_auth_defaults - -get_auth_defaults($target_domain) returns the default authentication -type and an associated argument (initial password or a kerberos domain). -These values are stored in lonTabs/domain.tab - -($def_auth, $def_arg) = &get_auth_defaults($target_domain); - -If target_domain is not found in domain.tab, returns nothing (''). - -=cut - -#------------------------------------------- -sub get_auth_defaults { - my $domain=shift; - return (&Apache::lonnet::domain($domain,'auth_def'), - &Apache::lonnet::domain($domain,'auth_arg_def')); - -} -############################################################### -## End Get Authentication Defaults for Domain ## -############################################################### - -############################################################### ## Get Kerberos Defaults for Domain ## ############################################################### ## @@ -2289,22 +2425,31 @@ sub get_auth_defaults { =pod -=item * get_kerberos_defaults +=item * &get_kerberos_defaults() get_kerberos_defaults($target_domain) returns the default kerberos -version and domain. If not found in domain.tabs, it defaults to -version 4 and the domain of the server. +version and domain. If not found, it defaults to version 4 and the +domain of the server. + +=over 4 ($def_version, $def_krb_domain) = &get_kerberos_defaults($target_domain); +=back + +=back + =cut #------------------------------------------- sub get_kerberos_defaults { my $domain=shift; - my ($krbdef,$krbdefdom) = - &Apache::loncommon::get_auth_defaults($domain); - unless ($krbdef =~/^krb/ && $krbdefdom) { + my ($krbdef,$krbdefdom); + my %domdefaults = &Apache::lonnet::get_domain_defaults($domain); + if (($domdefaults{'auth_def'} =~/^krb(4|5)$/) && ($domdefaults{'auth_arg_def'} ne '')) { + $krbdef = $domdefaults{'auth_def'}; + $krbdefdom = $domdefaults{'auth_arg_def'}; + } else { $ENV{'SERVER_NAME'}=~/(\w+\.\w+)$/; my $krbdefdom=$1; $krbdefdom=~tr/a-z/A-Z/; @@ -2313,11 +2458,6 @@ sub get_kerberos_defaults { return ($krbdef,$krbdefdom); } -=pod - -=back - -=cut ############################################################### ## Thesaurus Functions ## @@ -2329,7 +2469,7 @@ sub get_kerberos_defaults { =over 4 -=item * initialize_keywords +=item * &initialize_keywords() Initializes the package variable %Keywords if it is empty. Uses the package variable $thesaurus_db_file. @@ -2374,7 +2514,7 @@ sub initialize_keywords { =pod -=item * keyword($word) +=item * &keyword($word) Returns true if $word is a keyword. A keyword is a word that appears more than the average number of times in the thesaurus database. Calls @@ -2395,7 +2535,7 @@ sub keyword { =pod -=item * get_related_words +=item * &get_related_words() Look up a word in the thesaurus. Takes a scalar argument and returns an array of words. If the keyword is not in the thesaurus, an empty array @@ -2453,7 +2593,7 @@ sub get_related_words { =over 4 -=item * plainname($uname,$udom,$first) +=item * &plainname($uname,$udom,$first) Takes a users logon name and returns it as a string in "first middle last generation" form @@ -2482,7 +2622,7 @@ sub plainname { # -------------------------------------------------------------------- Nickname =pod -=item * nickname($uname,$udom) +=item * &nickname($uname,$udom) Gets a users name and returns it as a string as @@ -2532,18 +2672,21 @@ sub getnames { } # -------------------------------------------------------------------- getemails + =pod -=item * getemails($uname,$udom) +=item * &getemails($uname,$udom) Gets a user's email information and returns it as a hash with keys: notification, critnotification, permanentemail For notification and critnotification, values are comma-separated lists -of e-mail address(es); for permanentemail, value is a single e-mail address. +of e-mail addresses; for permanentemail, value is a single e-mail address. + =cut + sub getemails { my ($uname,$udom)=@_; if ($udom eq 'public' && $uname eq 'public') { @@ -2578,7 +2721,7 @@ sub flush_email_cache { =pod -=item * screenname($uname,$udom) +=item * &screenname($uname,$udom) Gets a users screenname and returns it as a string @@ -2625,10 +2768,7 @@ sub aboutmewrapper { sub syllabuswrapper { - my ($linktext,$coursedir,$domain,$fontcolor)=@_; - if ($fontcolor) { - $linktext='<font color="'.$fontcolor.'">'.$linktext.'</font>'; - } + my ($linktext,$coursedir,$domain)=@_; return qq{<a href="/public/$domain/$coursedir/syllabus">$linktext</a>}; } @@ -2674,7 +2814,7 @@ sub student_image_tag { =over 4 -=item * languageids() +=item * &languageids() returns list of all language ids @@ -2686,7 +2826,7 @@ sub languageids { =pod -=item * languagedescription() +=item * &languagedescription() returns description of a specified language id @@ -2711,7 +2851,7 @@ sub supportedlanguagecode { =pod -=item * copyrightids() +=item * ©rightids() returns list of all copyrights @@ -2723,7 +2863,7 @@ sub copyrightids { =pod -=item * copyrightdescription() +=item * ©rightdescription() returns description of a specified copyright id @@ -2735,7 +2875,7 @@ sub copyrightdescription { =pod -=item * source_copyrightids() +=item * &source_copyrightids() returns list of all source copyrights @@ -2747,7 +2887,7 @@ sub source_copyrightids { =pod -=item * source_copyrightdescription() +=item * &source_copyrightdescription() returns description of a specified source copyright id @@ -2759,7 +2899,7 @@ sub source_copyrightdescription { =pod -=item * filecategories() +=item * &filecategories() returns list of all file categories @@ -2771,7 +2911,7 @@ sub filecategories { =pod -=item * filecategorytypes() +=item * &filecategorytypes() returns list of file types belonging to a given file category @@ -2785,7 +2925,7 @@ sub filecategorytypes { =pod -=item * fileembstyle() +=item * &fileembstyle() returns embedding style for a specified file type @@ -2809,7 +2949,7 @@ sub filecategoryselect { =pod -=item * filedescription() +=item * &filedescription() returns description for a specified file type @@ -2823,7 +2963,7 @@ sub filedescription { =pod -=item * filedescriptionex() +=item * &filedescriptionex() returns description for a specified file type with extra formatting @@ -2855,7 +2995,7 @@ sub fileextensions { sub display_languages { my %languages=(); - foreach my $lang (&preferred_languages()) { + foreach my $lang (&Apache::lonlocal::preferred_languages()) { $languages{$lang}=1; } &get_unprocessed_cgi($ENV{'QUERY_STRING'},['displaylanguage']); @@ -2867,56 +3007,9 @@ sub display_languages { return %languages; } -sub preferred_languages { - my @languages=(); - if ($env{'course.'.$env{'request.course.id'}.'.languages'}) { - @languages=(@languages,split(/\s*(\,|\;|\:)\s*/, - $env{'course.'.$env{'request.course.id'}.'.languages'})); - } - if ($env{'environment.languages'}) { - @languages=(@languages, - split(/\s*(\,|\;|\:)\s*/,$env{'environment.languages'})); - } - my $browser=$ENV{'HTTP_ACCEPT_LANGUAGE'}; - if ($browser) { - my @browser = - map { (split(/\s*;\s*/,$_))[0] } (split(/\s*,\s*/,$browser)); - push(@languages,@browser); - } - if (&Apache::lonnet::domain($env{'user.domain'},'lang_def')) { - @languages=(@languages, - &Apache::lonnet::domain($env{'user.domain'}, - 'lang_def')); - } - if (&Apache::lonnet::domain($env{'request.role.domain'},'lang_def')) { - @languages=(@languages, - &Apache::lonnet::domain($env{'request.role.domain'}, - 'lang_def')); - } - if (&Apache::lonnet::domain($Apache::lonnet::perlvar{'lonDefDomain'}, - 'lang_def')) { - @languages=(@languages, - &Apache::lonnet::domain($Apache::lonnet::perlvar{'lonDefDomain'}, - 'lang_def')); - } -# turn "en-ca" into "en-ca,en" - my @genlanguages; - foreach my $lang (@languages) { - unless ($lang=~/\w/) { next; } - push(@genlanguages,$lang); - if ($lang=~/(\-|\_)/) { - push(@genlanguages,(split(/(\-|\_)/,$lang))[0]); - } - } - #uniqueify the languages list - my %count; - @genlanguages = map { $count{$_}++ == 0 ? $_ : () } @genlanguages; - return @genlanguages; -} - sub languages { my ($possible_langs) = @_; - my @preferred_langs = &preferred_languages(); + my @preferred_langs = &Apache::lonlocal::preferred_languages(); if (!ref($possible_langs)) { if( wantarray ) { return @preferred_langs; @@ -2947,7 +3040,7 @@ sub languages { =over 4 -=item * get_previous_attempt($symb, $username, $domain, $course, +=item * &get_previous_attempt($symb, $username, $domain, $course, $getattempt, $regexp, $gradesub) Return string with previous attempt on problem. Arguments: @@ -3091,7 +3184,7 @@ sub relative_to_absolute { =pod -=item * get_student_view +=item * &get_student_view() show a snapshot of what student was looking at @@ -3110,7 +3203,7 @@ sub get_student_view { } if (defined($target)) { $form{'grade_target'} = $target; } $feedurl=&Apache::lonnet::clutter($feedurl); - my $userview=&Apache::lonnet::ssi_body($feedurl,%form); + my ($userview,$response)=&Apache::lonnet::ssi_body($feedurl,%form); $userview=~s/\<body[^\>]*\>//gi; $userview=~s/\<\/body\>//gi; $userview=~s/\<html\>//gi; @@ -3119,12 +3212,44 @@ sub get_student_view { $userview=~s/\<\/head\>//gi; $userview=~s/action\s*\=/would_be_action\=/gi; $userview=&relative_to_absolute($feedurl,$userview); - return $userview; + if (wantarray) { + return ($userview,$response); + } else { + return $userview; + } +} + +sub get_student_view_with_retries { + my ($symb,$retries,$username,$domain,$courseid,$target,$moreenv) = @_; + + my $ok = 0; # True if we got a good response. + my $content; + my $response; + + # Try to get the student_view done. within the retries count: + + do { + ($content, $response) = &get_student_view($symb,$username,$domain,$courseid,$target,$moreenv); + $ok = $response->is_success; + if (!$ok) { + &Apache::lonnet::logthis("Failed get_student_view_with_retries on $symb: ".$response->is_success.', '.$response->code.', '.$response->message); + } + $retries--; + } while (!$ok && ($retries > 0)); + + if (!$ok) { + $content = ''; # On error return an empty content. + } + if (wantarray) { + return ($content, $response); + } else { + return $content; + } } =pod -=item * get_student_answers() +=item * &get_student_answers() show a snapshot of how student was answering problem @@ -3228,16 +3353,21 @@ sub pprmlink { sub timehash { - my @ltime=localtime(shift); - return ( 'seconds' => $ltime[0], - 'minutes' => $ltime[1], - 'hours' => $ltime[2], - 'day' => $ltime[3], - 'month' => $ltime[4]+1, - 'year' => $ltime[5]+1900, - 'weekday' => $ltime[6], - 'dayyear' => $ltime[7]+1, - 'dlsav' => $ltime[8] ); + my ($thistime) = @_; + my $timezone = &Apache::lonlocal::gettimezone(); + my $dt = DateTime->from_epoch(epoch => $thistime) + ->set_time_zone($timezone); + my $wday = $dt->day_of_week(); + if ($wday == 7) { $wday = 0; } + return ( 'second' => $dt->second(), + 'minute' => $dt->minute(), + 'hour' => $dt->hour(), + 'day' => $dt->day_of_month(), + 'month' => $dt->month(), + 'year' => $dt->year(), + 'weekday' => $wday, + 'dayyear' => $dt->day_of_year(), + 'dlsav' => $dt->is_dst() ); } sub utc_string { @@ -3247,6 +3377,24 @@ sub utc_string { sub maketime { my %th=@_; + my ($epoch_time,$timezone,$dt); + $timezone = &Apache::lonlocal::gettimezone(); + eval { + $dt = DateTime->new( year => $th{'year'}, + month => $th{'month'}, + day => $th{'day'}, + hour => $th{'hour'}, + minute => $th{'minute'}, + second => $th{'second'}, + time_zone => $timezone, + ); + }; + if (!$@) { + $epoch_time = $dt->epoch; + if ($epoch_time) { + return $epoch_time; + } + } return POSIX::mktime( ($th{'seconds'},$th{'minutes'},$th{'hours'}, $th{'day'},$th{'month'}-1,$th{'year'}-1900,0,0,-1)); @@ -3627,6 +3775,60 @@ sub blocking_status { ############################################### +sub check_ip_acc { + my ($acc)=@_; + &Apache::lonxml::debug("acc is $acc"); + if (!defined($acc) || $acc =~ /^\s*$/ || $acc =~/^\s*no\s*$/i) { + return 1; + } + my $allowed=0; + my $ip=$env{'request.host'} || $ENV{'REMOTE_ADDR'}; + + my $name; + foreach my $pattern (split(',',$acc)) { + $pattern =~ s/^\s*//; + $pattern =~ s/\s*$//; + if ($pattern =~ /\*$/) { + #35.8.* + $pattern=~s/\*//; + if ($ip =~ /^\Q$pattern\E/) { $allowed=1; } + } elsif ($pattern =~ /(\d+\.\d+\.\d+)\.\[(\d+)-(\d+)\]$/) { + #35.8.3.[34-56] + my $low=$2; + my $high=$3; + $pattern=$1; + if ($ip =~ /^\Q$pattern\E/) { + my $last=(split(/\./,$ip))[3]; + if ($last <=$high && $last >=$low) { $allowed=1; } + } + } elsif ($pattern =~ /^\*/) { + #*.msu.edu + $pattern=~s/\*//; + if (!defined($name)) { + use Socket; + my $netaddr=inet_aton($ip); + ($name)=gethostbyaddr($netaddr,AF_INET); + } + if ($name =~ /\Q$pattern\E$/i) { $allowed=1; } + } elsif ($pattern =~ /\d+\.\d+\.\d+\.\d+/) { + #127.0.0.1 + if ($ip =~ /^\Q$pattern\E/) { $allowed=1; } + } else { + #some.name.com + if (!defined($name)) { + use Socket; + my $netaddr=inet_aton($ip); + ($name)=gethostbyaddr($netaddr,AF_INET); + } + if ($name =~ /\Q$pattern\E$/i) { $allowed=1; } + } + if ($allowed) { last; } + } + return $allowed; +} + +############################################### + =pod =head1 Domain Template Functions @@ -3675,7 +3877,14 @@ sub get_domainconf { if (ref($domconfig{'login'}) eq 'HASH') { if (keys(%{$domconfig{'login'}})) { foreach my $key (keys(%{$domconfig{'login'}})) { - $designhash{$udom.'.login.'.$key}=$domconfig{'login'}{$key}; + if (ref($domconfig{'login'}{$key}) eq 'HASH') { + foreach my $img (keys(%{$domconfig{'login'}{$key}})) { + $designhash{$udom.'.login.'.$key.'_'.$img} = + $domconfig{'login'}{$key}{$img}; + } + } else { + $designhash{$udom.'.login.'.$key}=$domconfig{'login'}{$key}; + } } } else { $legacy{'login'} = 1; @@ -3791,10 +4000,10 @@ Returns: value of designparamter $which sub designparm { my ($which,$domain)=@_; if ($env{'browser.blackwhite'} eq 'on') { - if ($which=~/\.(font|alink|vlink|link)$/) { + if ($which=~/\.(font|alink|vlink|link|textcol)$/) { return '#000000'; } - if ($which=~/\.(pgbg|sidebg)$/) { + if ($which=~/\.(pgbg|sidebg|bgcol)$/) { return '#FFFFFF'; } if ($which=~/\.tabbg$/) { @@ -3813,7 +4022,7 @@ sub designparm { $output = $defaultdesign{$which}; } if (($which =~ /^(student|coordinator|author|admin)\.img$/) || - ($which =~ /login\.(img|logo|domlogo)/)) { + ($which =~ /login\.(img|logo|domlogo|login)/)) { if ($output =~ m{^/(adm|res)/}) { if ($output =~ m{^/res/}) { my $local_name = &Apache::lonnet::filelocation('',$output); @@ -3981,7 +4190,7 @@ ENDROLE $dc_info = '('.$dc_info.')'; } - if ($env{'environment.remote'} eq 'off') { + if (($env{'environment.remote'} eq 'off') || ($args->{'suppress_header_logos'})) { # No Remote if ($env{'request.state'} eq 'construct') { $forcereg=1; @@ -4004,11 +4213,11 @@ ENDROLE $lastitem = $thisdisfn; } $titleinfo = - &Apache::loncommon::help_open_menu('','',3,'Authoring'). - '<b>Construction Space</b>: '. - '<form name="dirs" method="post" action="'.$formaction + &Apache::loncommon::help_open_menu('','',3,'Authoring') + .'<b>'.&mt('Construction Space').'</b>: ' + .'<form name="dirs" method="post" action="'.$formaction .'" target="_top"><tt><b>' - .&Apache::lonhtmlcommon::crumbs($uname.'/'.$parentpath,'_top','/priv','','+1',1)."<font size=\"+1\">$lastitem</font></b></tt><br />" + .&Apache::lonhtmlcommon::crumbs($uname.'/'.$parentpath,'_top','/priv','','+1',1)."<span class=\"LC_fontsize_big\">$lastitem</span></b></tt><br />" .&Apache::lonhtmlcommon::select_recent('construct','recent','this.form.action=this.form.recent.value;this.form.submit()') .'</form>' .&Apache::lonmenu::constspaceform(); @@ -4130,18 +4339,25 @@ sub make_attr_string { Returns a uniform footer for LON-CAPA web pages. -Inputs: none +Inputs: 1 - optional reference to an args hash +If in the hash, key for noredirectlink has a value which evaluates to true, +a 'Continue' link is not displayed if the page contains an +internal redirect in the <head></head> section, +i.e., $env{'internal.head.redirect'} exists =cut sub endbodytag { + my ($args) = @_; my $endbodytag='</body>'; $endbodytag=&Apache::lontexconvert::jsMath_process()."\n".$endbodytag; if ( exists( $env{'internal.head.redirect'} ) ) { - $endbodytag= - "<br /><a href=\"$env{'internal.head.redirect'}\">". - &mt('Continue').'</a>'. - $endbodytag; + if (!(ref($args) eq 'HASH' && $args->{'noredirectlink'})) { + $endbodytag= + "<br /><a href=\"$env{'internal.head.redirect'}\">". + &mt('Continue').'</a>'. + $endbodytag; + } } return $endbodytag; } @@ -4175,6 +4391,8 @@ sub standard_css { my $vlink = &designparm($function.'.vlink', $domain); my $link = &designparm($function.'.link', $domain); + my $loginbg = &designparm('login.sidebg',$domain); + my $sans = 'Verdana,Arial,Helvetica,sans-serif'; my $mono = 'monospace'; my $data_table_head = $tabbg; @@ -4192,6 +4410,7 @@ sub standard_css { my $mail_other_hover = '#669999'; my $table_header = '#DDDDDD'; my $feedback_link_bg = '#BBBBBB'; + my $lg_border_color = '#C8C8C8'; my $border = ($env{'browser.type'} eq 'explorer' || $env{'browser.type'} eq 'safari' ) ? '0px 2px 0px 2px' @@ -4199,22 +4418,35 @@ sub standard_css { return <<END; -h1, h2, h3, th { font-family: $sans } +body{ + font-family: $sans; + line-height:130%; + font-size:0.83em; + color:$font; + } +a:link, a:visited { font-size:100%; } + a:focus { color: red; background: yellow } table.thinborder, - table.thinborder tr th { border-style: solid; border-width: 1px; + border-color: $lg_border_color; background: $tabbg; } table.thinborder tr td { border-style: solid; - border-width: 1px + border-width: 1px; + border-color: $lg_border_color; } form, .inline { display: inline; } .center { text-align: center; } +.left { text-align:left; } +.right {text-align:right;} +.middle {vertical-align:middle;} +.top {vertical-align:top;} +.bottom {vertical-align:bottom;} .LC_filename {font-family: $mono; white-space:pre;} .LC_error { color: red; @@ -4276,7 +4508,6 @@ table#LC_title_bar.LC_with_remote { border-collapse: collapse; padding: 0px; } - table.LC_docs_path { width: 100%; border: 0; @@ -4311,21 +4542,15 @@ table#LC_title_bar td.LC_title_bar_role_ padding: 0px; } -table#LC_menubuttons_mainmenu { - width: 100%; - border: 0px; - border-spacing: 1px; - padding: 0px 1px; - margin: 0px; - border-collapse: separate; -} -table#LC_menubuttons img, table#LC_menubuttons_mainmenu img { +table#LC_menubuttons img{ border: 0px; } table#LC_top_nav td { background: $tabbg; border: 0px; font-size: small; + vertical-align:top; + padding:2px 5px 2px 5px; } table#LC_top_nav td a, div#LC_top_nav a { color: $font; @@ -4366,21 +4591,41 @@ table.LC_docs_path td.LC_docs_path_compo td.LC_table_cell_checkbox { text-align: center; } - table#LC_mainmenu td.LC_mainmenu_column { vertical-align: top; } +.LC_fontsize_small +{ + font-size: 70%; +} + +.LC_fontsize_medium +{ + font-size: 85%; +} + +.LC_fontsize_large +{ + font-size: 120%; +} + +.LC_fontcolor_red +{ + color: #FF0000; +} + .LC_menubuttons_inline_text { color: $font; font-family: $sans; - font-size: smaller; + font-size: 90%; + padding-left:3px; } .LC_menubuttons_link { text-decoration: none; } - +/*2008--9-5: new menu style sheet.Changed category*/ .LC_menubuttons_category { color: $font; background: $pgbg; @@ -4390,13 +4635,10 @@ table#LC_mainmenu td.LC_mainmenu_column } td.LC_menubuttons_text { - width: 90%; - color: $font; - font-family: $sans; + color: $font; } -td.LC_menubuttons_img { -} + .LC_current_location { font-family: $sans; @@ -4404,37 +4646,10 @@ td.LC_menubuttons_img { } .LC_new_mail { font-family: $sans; + background: $tabbg; font-weight: bold; } -.LC_rolesmenu_is { - font-family: $sans; -} - -.LC_rolesmenu_selected { - font-family: $sans; -} - -.LC_rolesmenu_future { - font-family: $sans; -} - - -.LC_rolesmenu_will { - font-family: $sans; -} - -.LC_rolesmenu_will_not { - font-family: $sans; -} - -.LC_rolesmenu_expired { - font-family: $sans; -} - -.LC_rolesinfo { - font-family: $sans; -} .LC_dropadd_labeltext { font-family: $sans; @@ -4446,6 +4661,10 @@ td.LC_menubuttons_img { text-align: right; } +.LC_roleslog_note { + font-size: small; +} + table.LC_aboutme_port { border: 0px; border-collapse: collapse; @@ -4476,20 +4695,32 @@ table.LC_data_table tr th, table.LC_cale table.LC_prior_tries tr th { font-weight: bold; background-color: $data_table_head; - font-size: smaller; + font-size:90%; +} +table.LC_data_table tr.LC_info_row > td { + background-color: #CCC; + font-weight: bold; + text-align: left; } table.LC_data_table tr.LC_odd_row > td, +table.LC_pick_box tr > td.LC_odd_row, table.LC_aboutme_port tr td { background-color: $data_table_light; padding: 2px; } table.LC_data_table tr.LC_even_row > td, +table.LC_pick_box tr > td.LC_even_row, table.LC_aboutme_port tr.LC_even_row td { background-color: $data_table_dark; + padding: 2px; } table.LC_data_table tr.LC_data_table_highlight td { background-color: $data_table_darker; } +table.LC_data_table tr td.LC_leftcol_header { + background-color: $data_table_head; + font-weight: bold; +} table.LC_data_table tr.LC_empty_row td, table.LC_nested tr.LC_empty_row td { background-color: #FFFFFF; @@ -4504,7 +4735,7 @@ table.LC_nested tr.LC_empty_row td { table.LC_nested_outer tr th { font-weight: bold; background-color: $data_table_head; - font-size: smaller; + font-size: small; border-bottom: 1px solid #000000; } table.LC_nested_outer tr td.LC_subheader { @@ -4543,7 +4774,7 @@ table.LC_createuser { } table.LC_createuser tr.LC_section_row td { - font-size: smaller; + font-size: small; } table.LC_createuser tr.LC_info_row td { @@ -4599,71 +4830,46 @@ table.LC_mail_list tr.LC_mail_even { table.LC_mail_list tr.LC_mail_odd { } - -table#LC_portfolio_actions { - width: auto; - background: $pgbg; - border: 0px; - border-spacing: 2px 2px; - padding: 0px; - margin: 0px; - border-collapse: separate; -} -table#LC_portfolio_actions td.LC_label { - background: $tabbg; - text-align: right; +table.LC_data_table tr > td.LC_browser_file, +table.LC_data_table tr > td.LC_browser_file_published { + background: #CCFF88; } -table#LC_portfolio_actions td.LC_value { - background: $tabbg; +table.LC_data_table tr > td.LC_browser_file_locked, +table.LC_data_table tr > td.LC_browser_file_unpublished { + background: #FFAA99; } - -table#LC_cstr_controls { - width: 100%; - border-collapse: collapse; +table.LC_data_table tr > td.LC_browser_file_obsolete { + background: #AAAAAA; } -table#LC_cstr_controls tr td { - border: 4px solid $pgbg; - padding: 4px; - text-align: center; - background: $tabbg; +table.LC_data_table tr > td.LC_browser_file_modified, +table.LC_data_table tr > td.LC_browser_file_metamodified { + background: #FFFF77; } -table#LC_cstr_controls tr th { - border: 4px solid $pgbg; - background: $table_header; - text-align: center; - font-family: $sans; - font-size: smaller; +table.LC_data_table tr.LC_browser_folder > td { + background: #CCCCFF; } -table#LC_browser { - -} -table#LC_browser tr th { - background: $table_header; +table.LC_data_table tr > td.LC_roles_is { +/* background: #77FF77; */ } -table#LC_browser tr td { - padding: 2px; -} -table#LC_browser tr.LC_browser_file, -table#LC_browser tr.LC_browser_file_published { - background: #CCFF88; +table.LC_data_table tr > td.LC_roles_future { + background: #FFFF77; } -table#LC_browser tr.LC_browser_file_locked, -table#LC_browser tr.LC_browser_file_unpublished { - background: #FFAA99; +table.LC_data_table tr > td.LC_roles_will { + background: #FFAA77; } -table#LC_browser tr.LC_browser_file_obsolete { - background: #AAAAAA; +table.LC_data_table tr > td.LC_roles_expired { + background: #FF7777; } -table#LC_browser tr.LC_browser_file_modified, -table#LC_browser tr.LC_browser_file_metamodified { - background: #FFFF77; +table.LC_data_table tr > td.LC_roles_will_not { + background: #AAFF77; } -table#LC_browser tr.LC_browser_folder { - background: #CCCCFF; +table.LC_data_table tr > td.LC_roles_selected { + background: #11CC55; } + span.LC_current_location { - font-size: x-large; + font-size:larger; background: $pgbg; } @@ -4765,6 +4971,14 @@ table.LC_pick_box td.LC_pick_box_title { width: 184px; padding: 8px; } +table.LC_pick_box td.LC_selfenroll_pick_box_title { + background: $tabbg; + font-weight: bold; + text-align: right; + width: 350px; + padding: 8px; +} + table.LC_pick_box td.LC_pick_box_value { text-align: left; padding: 8px; @@ -4979,6 +5193,11 @@ span.LC_cusr_emph { font-style: italic; } +span.LC_cusr_subheading { + font-weight: normal; + font-size: 85%; +} + table.LC_docs_documents { background: #BBBBBB; border-width: 0px; @@ -5094,23 +5313,6 @@ div.LC_clear_float_footer { } -div.LC_grade_select_mode { - font-family: $sans; -} -div.LC_grade_select_mode div div { - margin: 5px; -} -div.LC_grade_select_mode_selector { - margin: 5px; - float: left; -} -div.LC_grade_select_mode_selector_header { - font: bold medium $sans; -} -div.LC_grade_select_mode_type { - clear: left; -} - div.LC_grade_show_user { margin-top: 20px; border: 1px solid black; @@ -5118,7 +5320,8 @@ div.LC_grade_show_user { div.LC_grade_user_name { background: #DDDDEE; border-bottom: 1px solid black; - font: bold large $sans; + font-weight: bold; + font-size: large; } div.LC_grade_show_user_odd_row div.LC_grade_user_name { background: #DDEEDD; @@ -5137,7 +5340,8 @@ div.LC_grade_show_problem_header, div.LC_grade_submissions_header, div.LC_grade_message_center_header, div.LC_grade_assign_header { - font: bold large $sans; + font-weight: bold; + font-size: large; } div.LC_grade_show_problem_problem, div.LC_grade_submissions_body, @@ -5148,7 +5352,8 @@ div.LC_grade_assign_body { background: #FFFFFF; } span.LC_grade_check_note { - font: normal medium $sans; + font-weight: normal; + font-size: medium; display: inline; position: absolute; right: 1em; @@ -5158,12 +5363,13 @@ table.LC_scantron_action { width: 100%; } table.LC_scantron_action tr th { - font: normal bold $sans; + font-weight:bold; + font-style:normal; } - -div.LC_edit_problem_header, +.LC_edit_problem_header, div.LC_edit_problem_footer { - font: normal medium $sans; + font-weight: normal; + font-size: medium; margin: 2px; } div.LC_edit_problem_header, @@ -5180,12 +5386,14 @@ div.LC_edit_problem_header_edit_row { margin-bottom: 5px; } div.LC_edit_problem_header_title { - font: larger bold $sans; + font-weight: bold; + font-size: larger; background: $tabbg; padding: 3px; } table.LC_edit_problem_header_title { - font: larger bold $sans; + font-size: larger; + font-weight: bold; width: 100%; border-color: $pgbg; border-style: solid; @@ -5211,6 +5419,326 @@ hr.LC_edit_problem_divide { height: 3px; border: 0px; } +img.stift{ + border-width:0; + vertical-align:middle; +} + +table#LC_mainmenu{ + margin-top:10px; + width:80%; + +} + +table#LC_mainmenu td.LC_mainmenu_col_fieldset{ + vertical-align: top; + width: 45%; +} +.LC_mainmenu_fieldset_category { + color: $font; + background: $pgbg; + font-family: $sans; + font-size: small; + font-weight: bold; +} + +/* ---- Remove when done ---- +# The following styles is part of the redesign of LON-CAPA and are +# subject to change during this project. +# Don't rely on their current functionality as they might be +# changed or removed. +# --------------------------*/ + +a:hover, +ol.smallMenu a:hover, +ol#MenuBreadcrumbs a:hover, +ul#TabMainMenuContent a:hover, +.FormSectionClearButton input:hover{ + color:#BF2317; + text-decoration:none; +} + +h1 { + padding:5px 10px 5px 0px; + line-height:130%; +} + +h2,h3,h4,h5,h6 +{ +margin:5px 0px 5px 0px; +line-height:130%; +} +.hcell{ + padding:3px 15px 3px 15px; + margin:0px; + background-color:$tabbg; + border-bottom:solid 1px $lg_border_color; +} +.noBorder { + border:0px; +} +/* +.bgLightGrey { background:URL(images/TabMenuBG.png) repeat-x left top; } +.bgLightGreyYellow {background-color:#EFECE0;} +*/ + + +/* Main Header with discription of Person, Course, etc. */ +.HeadRight { + text-align: right; + float: right; + margin: 0px; + padding: 0px; + right:0; + position:absolute; + overflow:hidden; +} + +p { + padding: 10px; + +} +.FormSectionClearButton input { + background-color:transparent; + border:0px; + cursor:pointer; + text-decoration:underline; +} + + +dl,ul,div,fieldset { + margin: 10px 10px 10px 0px; + overflow:hidden; +} +ol.smallMenu { + margin: 0px; +} + +ol.smallMenu li { + display: inline; + padding: 5px 5px 0px 10px; + vertical-align: top; +} + +ol.smallMenu li img { + vertical-align: bottom; +} + +ol.smallMenu a { + font-size: 90%; + color: RGB(80, 80, 80); + text-decoration: none; +} + +ol#TabMainMenuContent { + + margin: 0px 0px 10px 0px; + padding: 0px; +} + +ol#TabMainMenuContent li { + display: inline; + vertical-align: bottom; + border-bottom: solid 1px RGB(175, 175, 175); + border-right: solid 1px RGB(175, 175, 175); + padding: 5px 15px 5px 15px; + margin-right:4px; + line-height: 140%; + font-weight: bold; + overflow:hidden; +/* background: RGB(211, 206, 205) URL(images/TabMenuBG.png) repeat-x left top;*/ +} + +ol#TabMainMenuContent li a{ + color: RGB(47, 47, 47); + text-decoration: none; +} + +ol#TabMainMenuContent div.columnSection { + margin-bottom: 0px; +} + +ol#MenuBreadcrumbs, ol#PathBreadcrumbs { + border-top: solid 1px RGB(255, 255, 255); + height: 20px; + line-height: 20px; + vertical-align: bottom; + margin: 0px 0px 30px 0px; + padding-left: 10px; + list-style-position: inside; +/* background: RGB(211, 206, 205) URL(images/TabMenuBG.png) repeat-x left + top;*/ +} + +ol#MenuBreadcrumbs li, ol#PathBreadcrumbs li { +/* background: url(images/pfeil_white.png) no-repeat left center;*/ + display: inline; + padding: 0px 0px 0px 10px; + vertical-align: bottom; + overflow:hidden; +} + +ol#MenuBreadcrumbs li a { + text-decoration: none; + font-size:90%; +} +ol#PathBreadcrumbs li a{ + text-decoration:none; + font-size:100%; + font-weight:bold; +} + +.ContentBoxSpecial +{ + border: solid 1px $lg_border_color; +} +.ContentBox { + padding:10px; +} +.PopUp +{ + padding:10px; + border-left:solid 1px $lg_border_color; + border-top:solid 1px $lg_border_color; + border-bottom:outset 1px $lg_border_color; + border-right:outset 1px $lg_border_color; + display:none; + position:absolute; + right:0; + background-color:white; + z-index:5; +} + +dl.ListStyleClean dt { + padding-right: 5px; + display: table-header-group; +} + +dl.ListStyleClean dd { + display: table-row; +} + +.ListStyleClean, +.ListStyleSimple, +.ListStyleNormal, +.ListStyleNormal_Border, +.ListStyleSpecial + { + /*display:block; */ + list-style-position: inside; + list-style-type: none; + overflow: hidden; + padding: 0px; +} + +.ListStyleSimple li, +.ListStyleSimple dd, +.ListStyleNormal li, +.ListStyleNormal dd, +.ListStyleSpecial li, +.ListStyleSpecial dd + { + margin: 0px; + padding: 5px 5px 5px 10px; + clear: both; +} + +.ListStyleClean li, +.ListStyleClean dd { + padding-top: 0px; + padding-bottom: 0px; +} + +.ListStyleSimple dd, +.ListStyleSimple li{ + border-bottom: solid 1px $lg_border_color; +} + +.ListStyleSpecial li, +.ListStyleSpecial dd { + list-style-type: none; + background-color: RGB(220, 220, 220); + margin-bottom: 4px; +} + +table.SimpleTable { + margin:5px; + border:solid 1px $lg_border_color; + } + +table.SimpleTable tr { + padding:0px; + border:solid 1px $lg_border_color; +} +table.SimpleTable thead{ + background:rgb(220,220,220); +} + +div.columnSection { + display: block; + clear: both; + overflow: hidden; + margin:0px; +} + +div.columnSection>* { + float: left; + margin: 10px 20px 10px 0px; + overflow:hidden; +} +div.columnSection > .ContentBox, +div.columnSection > .ContentBoxSpecial + { + width: 400px; + +} + +.LC_loginpage_container { + text-align:left; + margin : 0 auto; + width:65%; + padding: 10px; + height: auto; +# background-color:#FFFFFF; + background-color:$loginbg; + border:1px solid #CCCCCC; +} + + +.LC_loginpage_loginContainer { + float:left; + width:60%; +} + +.LC_loginpage_loginInfo { + margin-top:20px; + margin-left:20px; + float:left; + width:30%; + border:1px solid #CCCCCC; + padding:10px; +} + +.LC_loginpage_space { + clear:both; + margin-bottom:20px; + border-bottom: 1px solid #CCCCCC; +} + +.LC_loginpage_fieldset{ + border: 1px solid #CCCCCC; + margin: 0 auto; +} + +.LC_loginpage_legend{ + padding: 2px; + margin: 0px; + font-size:14px; + font-weight:bold; +} + + + END } @@ -5304,10 +5832,7 @@ Inputs: none sub font_settings { my $headerstring=''; - if (($env{'browser.os'} eq 'mac') && (!$env{'browser.mathml'})) { - $headerstring.= - '<meta Content-Type="text/html; charset=x-mac-roman" />'; - } elsif (!$env{'browser.mathml'} && $env{'browser.unicode'}) { + if (!$env{'browser.mathml'} && $env{'browser.unicode'}) { $headerstring.= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />'; } @@ -5366,8 +5891,15 @@ sub endheadtag { Returns a uniform complete <head>..</head> section for LON-CAPA web pages. -Inputs: $title - optional title for the page - $head_extra - optional extra HTML to put inside the <head> +Inputs: + +=over 4 + +$title - optional title for the page + +$head_extra - optional extra HTML to put inside the <head> + +=back =cut @@ -5382,44 +5914,54 @@ sub head { Returns a complete <html> .. <body> section for LON-CAPA web pages. -Inputs: $title - optional title for the page - $head_extra - optional extra HTML to incude inside the <head> - $args - additional optional args supported are: - only_body -> is true will set &bodytag() onlybodytag +Inputs: + +=over 4 + +$title - optional title for the page + +$head_extra - optional extra HTML to incude inside the <head> + +$args - additional optional args supported are: + +=over 8 + + only_body -> is true will set &bodytag() onlybodytag arg on - no_nav_bar -> is true will set &bodytag() notopbar arg on - add_entries -> additional attributes to add to the <body> - domain -> force to color decorate a page for a + no_nav_bar -> is true will set &bodytag() notopbar arg on + add_entries -> additional attributes to add to the <body> + domain -> force to color decorate a page for a specific domain - function -> force usage of a specific rolish color + function -> force usage of a specific rolish color scheme - redirect -> see &headtag() - bgcolor -> override the default page bg color - js_ready -> return a string ready for being used in + redirect -> see &headtag() + bgcolor -> override the default page bg color + js_ready -> return a string ready for being used in a javascript writeln - html_encode -> return a string ready for being used in + html_encode -> return a string ready for being used in a html attribute - force_register -> if is true will turn on the &bodytag() + force_register -> if is true will turn on the &bodytag() $forcereg arg - body_title -> alternate text to use instead of $title + body_title -> alternate text to use instead of $title in the title box that appears, this text is not auto translated like the $title is - frameset -> if true will start with a <frameset> + frameset -> if true will start with a <frameset> rather than <body> - no_title -> if true the title bar won't be shown - skip_phases -> hash ref of + no_title -> if true the title bar won't be shown + skip_phases -> hash ref of head -> skip the <html><head> generation body -> skip all <body> generation - - no_inline_link -> if true and in remote mode, don't show the + no_inline_link -> if true and in remote mode, don't show the 'Switch To Inline Menu' link - - no_auto_mt_title -> prevent &mt()ing the title arg - - inherit_jsmath -> when creating popup window in a page, + no_auto_mt_title -> prevent &mt()ing the title arg + inherit_jsmath -> when creating popup window in a page, should it have jsmath forced on by the current page +=back + +=back + =cut sub start_page { @@ -5506,7 +6048,7 @@ sub end_page { if ($args->{'frameset'}) { $result .= '</frameset>'; } else { - $result .= &endbodytag(); + $result .= &endbodytag($args); } $result .= "\n</html>"; @@ -5611,7 +6153,7 @@ sub simple_error_page { } sub start_data_table_empty_row { - $row_count[0]++; +# $row_count[0]++; return '<tr class="LC_empty_row" >'."\n";; } @@ -6227,19 +6769,35 @@ sub default_quota { if ($inststatus ne '') { my @statuses = split(/:/,$inststatus); foreach my $item (@statuses) { - if ($quotahash{'quotas'}{$item} ne '') { - if ($defquota eq '') { - $defquota = $quotahash{'quotas'}{$item}; - $settingstatus = $item; - } elsif ($quotahash{'quotas'}{$item} > $defquota) { - $defquota = $quotahash{'quotas'}{$item}; - $settingstatus = $item; + if (ref($quotahash{'quotas'}{'defaultquota'}) eq 'HASH') { + if ($quotahash{'quotas'}{'defaultquota'}{$item} ne '') { + if ($defquota eq '') { + $defquota = $quotahash{'quotas'}{'defaultquota'}{$item}; + $settingstatus = $item; + } elsif ($quotahash{'quotas'}{'defaultquota'}{$item} > $defquota) { + $defquota = $quotahash{'quotas'}{'defaultquota'}{$item}; + $settingstatus = $item; + } + } + } else { + if ($quotahash{'quotas'}{$item} ne '') { + if ($defquota eq '') { + $defquota = $quotahash{'quotas'}{$item}; + $settingstatus = $item; + } elsif ($quotahash{'quotas'}{$item} > $defquota) { + $defquota = $quotahash{'quotas'}{$item}; + $settingstatus = $item; + } } } } } if ($defquota eq '') { - $defquota = $quotahash{'quotas'}{'default'}; + if (ref($quotahash{'quotas'}{'defaultquota'}) eq 'HASH') { + $defquota = $quotahash{'quotas'}{'defaultquota'}{'default'}; + } else { + $defquota = $quotahash{'quotas'}{'default'}; + } $settingstatus = 'default'; } } else { @@ -6645,12 +7203,16 @@ sub instrule_disallow_msg { $text{'action'} = 'IDs'; } } - $response = &mt("The $text{'item'} you chose $text{'match'} the format of $text{'items'} defined for <span class=\"LC_cusr_emph\">[_1]</span>, but the $text{'item'} $text{'do'} not exist in the institutional directory.",$domdesc).'<br />'; + $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 />'; if ($mode eq 'upload') { if ($checkitem eq 'username') { $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'}."); } elsif ($checkitem eq 'id') { - $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 ID/Student Number field."); + $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."); + } + } elsif ($mode eq 'selfcreate') { + if ($checkitem eq 'id') { + $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."); } } else { if ($checkitem eq 'username') { @@ -6675,6 +7237,69 @@ sub personal_data_fieldtitles { return %fieldtitles; } +sub sorted_inst_types { + my ($dom) = @_; + my ($usertypes,$order) = &Apache::lonnet::retrieve_inst_usertypes($dom); + my $othertitle = &mt('All users'); + if ($env{'request.course.id'}) { + $othertitle = &mt('Any users'); + } + my @types; + if (ref($order) eq 'ARRAY') { + @types = @{$order}; + } + if (@types == 0) { + if (ref($usertypes) eq 'HASH') { + @types = sort(keys(%{$usertypes})); + } + } + if (keys(%{$usertypes}) > 0) { + $othertitle = &mt('Other users'); + } + return ($othertitle,$usertypes,\@types); +} + +sub get_institutional_codes { + my ($settings,$allcourses,$LC_code) = @_; +# Get complete list of course sections to update + my @currsections = (); + my @currxlists = (); + my $coursecode = $$settings{'internal.coursecode'}; + + if ($$settings{'internal.sectionnums'} ne '') { + @currsections = split(/,/,$$settings{'internal.sectionnums'}); + } + + if ($$settings{'internal.crosslistings'} ne '') { + @currxlists = split(/,/,$$settings{'internal.crosslistings'}); + } + + if (@currxlists > 0) { + foreach (@currxlists) { + if (m/^([^:]+):(\w*)$/) { + unless (grep/^$1$/,@{$allcourses}) { + push @{$allcourses},$1; + $$LC_code{$1} = $2; + } + } + } + } + + if (@currsections > 0) { + foreach (@currsections) { + if (m/^(\w+):(\w*)$/) { + my $sec = $coursecode.$1; + my $lc_sec = $2; + unless (grep/^$sec$/,@{$allcourses}) { + push @{$allcourses},$sec; + $$LC_code{$sec} = $lc_sec; + } + } + } + } + return; +} + =pod =back @@ -6683,7 +7308,7 @@ sub personal_data_fieldtitles { =over 4 -=item * get_unprocessed_cgi($query,$possible_names) +=item * &get_unprocessed_cgi($query,$possible_names) Modify the %env hash to contain unprocessed CGI form parameters held in $query. The parameters listed in $possible_names (an array reference), @@ -6712,7 +7337,7 @@ sub get_unprocessed_cgi { =pod -=item * cacheheader() +=item * &cacheheader() returns cache-controlling header code @@ -6729,7 +7354,7 @@ sub cacheheader { =pod -=item * no_cache($r) +=item * &no_cache($r) specifies header code to not have cache @@ -6765,7 +7390,7 @@ sub content_type { =pod -=item * add_to_env($name,$value) +=item * &add_to_env($name,$value) adds $name to the %env hash with value $value, if $name already exists, the entry is converted to an array @@ -6792,7 +7417,7 @@ sub add_to_env { =pod -=item * get_env_multiple($name) +=item * &get_env_multiple($name) gets $name from the %env hash, it seemlessly handles the cases where multiple values may be defined and end up as an array ref. @@ -6815,6 +7440,232 @@ sub get_env_multiple { return(@values); } +sub ask_for_embedded_content { + my ($actionurl,$state,$allfiles,$codebase,$args)=@_; + my $upload_output = ' + <form name="upload_embedded" action="'.$actionurl.'" + method="post" enctype="multipart/form-data">'; + $upload_output .= $state; + $upload_output .= '<b>Upload embedded files</b>:<br />'.&start_data_table(); + + my $num = 0; + foreach my $embed_file (sort {lc($a) cmp lc($b)} keys(%{$allfiles})) { + $upload_output .= &start_data_table_row(). + '<td>'.$embed_file.'</td><td>'; + if ($args->{'ignore_remote_references'} + && $embed_file =~ m{^\w+://}) { + $upload_output.='<span class="LC_warning">'.&mt("URL points to other server.").'</span>'; + } elsif ($args->{'error_on_invalid_names'} + && $embed_file ne &Apache::lonnet::clean_filename($embed_file,{'keep_path' => 1,})) { + + $upload_output.='<span class="LC_warning">'.&mt("Invalid characters").'</span>'; + + } else { + $upload_output .=' + <input name="embedded_item_'.$num.'" type="file" value="" /> + <input name="embedded_orig_'.$num.'" type="hidden" value="'.&escape($embed_file).'" />'; + my $attrib = join(':',@{$$allfiles{$embed_file}}); + $upload_output .= + "\n\t\t". + '<input name="embedded_attrib_'.$num.'" type="hidden" value="'. + $attrib.'" />'; + if (exists($$codebase{$embed_file})) { + $upload_output .= + "\n\t\t". + '<input name="codebase_'.$num.'" type="hidden" value="'. + &escape($$codebase{$embed_file}).'" />'; + } + } + $upload_output .= '</td>'.&Apache::loncommon::end_data_table_row(); + $num++; + } + $upload_output .= &Apache::loncommon::end_data_table().'<br /> + <input type ="hidden" name="number_embedded_items" value="'.$num.'" /> + <input type ="submit" value="'.&mt('Upload Listed Files').'" /> + '.&mt('(only files for which a location has been provided will be uploaded)').' + </form>'; + return $upload_output; +} + +sub upload_embedded { + my ($context,$dirpath,$uname,$udom,$dir_root,$url_root,$group,$disk_quota, + $current_disk_usage) = @_; + my $output; + for (my $i=0; $i<$env{'form.number_embedded_items'}; $i++) { + next if (!exists($env{'form.embedded_item_'.$i.'.filename'})); + my $orig_uploaded_filename = + $env{'form.embedded_item_'.$i.'.filename'}; + + $env{'form.embedded_orig_'.$i} = + &unescape($env{'form.embedded_orig_'.$i}); + my ($path,$fname) = + ($env{'form.embedded_orig_'.$i} =~ m{(.*/)([^/]*)}); + # no path, whole string is fname + if (!$fname) { $fname = $env{'form.embedded_orig_'.$i} }; + + $path = $env{'form.currentpath'}.$path; + $fname = &Apache::lonnet::clean_filename($fname); + # See if there is anything left + next if ($fname eq ''); + + # Check if file already exists as a file or directory. + my ($state,$msg); + if ($context eq 'portfolio') { + my $port_path = $dirpath; + if ($group ne '') { + $port_path = "groups/$group/$port_path"; + } + ($state,$msg) = &check_for_upload($path,$fname,$group,'embedded_item_'.$i, + $dir_root,$port_path,$disk_quota, + $current_disk_usage,$uname,$udom); + if ($state eq 'will_exceed_quota' + || $state eq 'file_locked' + || $state eq 'file_exists' ) { + $output .= $msg; + next; + } + } elsif (($context eq 'author') || ($context eq 'testbank')) { + ($state,$msg) = &check_for_existing($path,$fname,'embedded_item_'.$i); + if ($state eq 'exists') { + $output .= $msg; + next; + } + } + # Check if extension is valid + if (($fname =~ /\.(\w+)$/) && + (&Apache::loncommon::fileembstyle($1) eq 'hdn')) { + $output .= &mt('Invalid file extension ([_1]) - reserved for LONCAPA use - rename the file with a different extension and re-upload. ',$1); + next; + } elsif (($fname =~ /\.(\w+)$/) && + (!defined(&Apache::loncommon::fileembstyle($1)))) { + $output .= &mt('Unrecognized file extension ([_1]) - rename the file with a proper extension and re-upload.',$1); + next; + } elsif ($fname=~/\.(\d+)\.(\w+)$/) { + $output .= &mt('File name not allowed - rename the file to remove the number immediately before the file extension([_1]) and re-upload.',$2); + next; + } + + $env{'form.embedded_item_'.$i.'.filename'}=$fname; + if ($context eq 'portfolio') { + my $result= + &Apache::lonnet::userfileupload('embedded_item_'.$i,'', + $dirpath.$path); + if ($result !~ m|^/uploaded/|) { + $output .= '<span class="LC_error">' + .&mt('An error occurred ([_1]) while trying to upload [_2] for embedded element [_3].' + ,$result,$orig_uploaded_filename,$env{'form.embedded_orig_'.$i}) + .'</span><br />'; + next; + } else { + $output .= '<p>'.&mt('Uploaded [_1]','<span class="LC_filename">'. + $path.$fname.'</span>').'</p>'; + } + } else { +# Save the file + my $target = $env{'form.embedded_item_'.$i}; + my $fullpath = $dir_root.$dirpath.'/'.$path; + my $dest = $fullpath.$fname; + my $url = $url_root.$dirpath.'/'.$path.$fname; + my @parts=split(/\//,$fullpath); + my $count; + my $filepath = $dir_root; + for ($count=4;$count<=$#parts;$count++) { + $filepath .= "/$parts[$count]"; + if ((-e $filepath)!=1) { + mkdir($filepath,0770); + } + } + my $fh; + if (!open($fh,'>'.$dest)) { + &Apache::lonnet::logthis('Failed to create '.$dest); + $output .= '<span class="LC_error">'. + &mt('An error occurred while trying to upload [_1] for embedded element [_2].',$orig_uploaded_filename,$env{'form.embedded_orig_'.$i}). + '</span><br />'; + } else { + if (!print $fh $env{'form.embedded_item_'.$i}) { + &Apache::lonnet::logthis('Failed to write to '.$dest); + $output .= '<span class="LC_error">'. + &mt('An error occurred while writing the file [_1] for embedded element [_2].',$orig_uploaded_filename,$env{'form.embedded_orig_'.$i}). + '</span><br />'; + } else { + if ($context eq 'testbank') { + $output .= &mt('Embedded file uploaded successfully:'). + ' <a href="'.$url.'">'. + $orig_uploaded_filename.'</a><br />'; + } else { + $output .= '<span class=\"LC_fontsize_large\">'. + &mt('View embedded file: [_1]','<a href="'.$url.'">'. + $orig_uploaded_filename.'</a>').'</span><br />'; + } + } + close($fh); + } + } + } + return $output; +} + +sub check_for_existing { + my ($path,$fname,$element) = @_; + my ($state,$msg); + if (-d $path.'/'.$fname) { + $state = 'exists'; + $msg = &mt('Unable to upload [_1]. A directory by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$path); + } elsif (-e $path.'/'.$fname) { + $state = 'exists'; + $msg = &mt('Unable to upload [_1]. A file by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$path); + } + if ($state eq 'exists') { + $msg = '<span class="LC_error">'.$msg.'</span><br />'; + } + return ($state,$msg); +} + +sub check_for_upload { + my ($path,$fname,$group,$element,$portfolio_root,$port_path, + $disk_quota,$current_disk_usage,$uname,$udom) = @_; + my $filesize = (length($env{'form.'.$element})) / 1000; #express in k (1024?) + my $getpropath = 1; + my @dir_list = &Apache::lonnet::dirlist($portfolio_root.$path,$udom,$uname, + $getpropath); + my $found_file = 0; + my $locked_file = 0; + foreach my $line (@dir_list) { + my ($file_name)=split(/\&/,$line,2); + if ($file_name eq $fname){ + $file_name = $path.$file_name; + if ($group ne '') { + $file_name = $group.$file_name; + } + $found_file = 1; + if (&Apache::lonnet::is_locked($file_name,$udom,$uname) eq 'true') { + $locked_file = 1; + } + } + } + if (($current_disk_usage + $filesize) > $disk_quota){ + my $msg = '<span class="LC_error">'. + &mt('Unable to upload [_1]. (size = [_2] kilobytes). Disk quota will be exceeded.','<span class="LC_filename">'.$fname.'</span>',$filesize).'</span>'. + '<br />'.&mt('Disk quota is [_1] kilobytes. Your current disk usage is [_2] kilobytes.',$disk_quota,$current_disk_usage); + return ('will_exceed_quota',$msg); + } elsif ($found_file) { + if ($locked_file) { + my $msg = '<span class="LC_error">'; + $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>'); + $msg .= '</span><br />'; + $msg .= &mt('You will be able to rename or delete existing [_1] after a grade has been assigned.','<span class="LC_filename">'.$fname.'</span>'); + return ('file_locked',$msg); + } else { + my $msg = '<span class="LC_error">'; + $msg .= &mt('Unable to upload [_1]. A file by that name was found in [_2].','<span class="LC_filename">'.$fname.'</span>',$port_path.$env{'form.currentpath'}); + $msg .= '</span>'; + $msg .= '<br />'; + $msg .= &mt('To upload, rename or delete existing [_1] in [_2].','<span class="LC_filename">'.$fname.'</span>', $port_path.$env{'form.currentpath'}); + return ('file_exists',$msg); + } + } +} + =pod @@ -6824,7 +7675,7 @@ sub get_env_multiple { =over 4 -=item * upfile_store($r) +=item * &upfile_store($r) Store uploaded file, $r should be the HTTP Request object, needs $env{'form.upfile'} @@ -6854,7 +7705,7 @@ sub upfile_store { =pod -=item * load_tmp_file($r) +=item * &load_tmp_file($r) Load uploaded file from tmp, $r should be the HTTP Request object, needs $env{'form.datatoken'}, @@ -6878,7 +7729,7 @@ sub load_tmp_file { =pod -=item * upfile_record_sep() +=item * &upfile_record_sep() Separate uploaded file into records returns array of records, @@ -6900,7 +7751,7 @@ sub upfile_record_sep { =pod -=item * record_sep($record) +=item * &record_sep($record) Separate a record into fields $record should be an item from the upfile_record_sep(), needs $env{'form.upfiletype'} @@ -6985,7 +7836,7 @@ sub record_sep { =pod -=item * upfile_select_html() +=item * &upfile_select_html() Return HTML code to select a file from the users machine and specify the file type. @@ -7032,7 +7883,7 @@ sub get_samples { =pod -=item * csv_print_samples($r,$records) +=item * &csv_print_samples($r,$records) Prints a table of sample values from each column uploaded $r is an Apache Request ref, $records is an arrayref from @@ -7044,7 +7895,7 @@ Apache Request ref, $records is an array ###################################################### sub csv_print_samples { my ($r,$records) = @_; - my $samples = &get_samples($records,3); + my $samples = &get_samples($records,5); $r->print(&mt('Samples').'<br />'.&start_data_table(). &start_data_table_header_row()); @@ -7068,7 +7919,7 @@ sub csv_print_samples { =pod -=item * csv_print_select_table($r,$records,$d) +=item * &csv_print_select_table($r,$records,$d) Prints a table to create associations between values and table columns. @@ -7099,7 +7950,7 @@ sub csv_print_select_table { foreach my $sample (sort({$a <=> $b} keys(%{ $samples->[0] }))) { $r->print('<option value="'.$sample.'"'. ($sample eq $defaultcol ? ' selected="selected" ' : ''). - '>Column '.($sample+1).'</option>'); + '>'.&mt('Column [_1]',($sample+1)).'</option>'); } $r->print('</select></td>'.&end_data_table_row()."\n"); $i++; @@ -7114,7 +7965,7 @@ sub csv_print_select_table { =pod -=item * csv_samples_select_table($r,$records,$d) +=item * &csv_samples_select_table($r,$records,$d) Prints a table of sample values from the upload and can make associate samples to internal names. @@ -7130,7 +7981,8 @@ sub csv_samples_select_table { my ($r,$records,$d) = @_; my $i=0; # - my $samples = &get_samples($records,3); + my $max_samples = 5; + my $samples = &get_samples($records,$max_samples); $r->print(&start_data_table(). &start_data_table_header_row().'<th>'. &mt('Field').'</th><th>'.&mt('Samples').'</th>'. @@ -7146,7 +7998,7 @@ sub csv_samples_select_table { $display.'</option>'); } $r->print('</select></td><td>'); - foreach my $line (0..2) { + foreach my $line (0..($max_samples-1)) { if (defined($samples->[$line]{$key})) { $r->print($samples->[$line]{$key}."<br />\n"); } @@ -7164,7 +8016,7 @@ sub csv_samples_select_table { =pod -=item clean_excel_name($name) +=item * &clean_excel_name($name) Returns a replacement for $name which does not contain any illegal characters. @@ -7183,7 +8035,7 @@ sub clean_excel_name { =pod -=item * check_if_partid_hidden($id,$symb,$udom,$uname) +=item * &check_if_partid_hidden($id,$symb,$udom,$uname) Returns either 1 or undef @@ -7224,7 +8076,7 @@ sub check_if_partid_hidden { =over 4 -=item get_cgi_id +=item * &get_cgi_id() Inputs: none @@ -7248,7 +8100,7 @@ sub get_cgi_id { =pod -=item DrawBarGraph +=item * &DrawBarGraph() Facilitates the plotting of data in a (stacked) bar graph. Puts plot definition data into the users environment in order for @@ -7383,7 +8235,7 @@ sub DrawBarGraph { $ValuesHash{$id.'.'.$key} = $value; } # - &Apache::lonnet::appenv(%ValuesHash); + &Apache::lonnet::appenv(\%ValuesHash); return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />'; } @@ -7392,7 +8244,7 @@ sub DrawBarGraph { =pod -=item DrawXYGraph +=item * &DrawXYGraph() Facilitates the plotting of data in an XY graph. Puts plot definition data into the users environment in order for @@ -7473,7 +8325,7 @@ sub DrawXYGraph { $ValuesHash{$id.'.'.$key} = $value; } # - &Apache::lonnet::appenv(%ValuesHash); + &Apache::lonnet::appenv(\%ValuesHash); return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />'; } @@ -7482,7 +8334,7 @@ sub DrawXYGraph { =pod -=item DrawXYYGraph +=item * &DrawXYYGraph() Facilitates the plotting of data in an XY graph with two Y axes. Puts plot definition data into the users environment in order for @@ -7575,7 +8427,7 @@ sub DrawXYYGraph { $ValuesHash{$id.'.'.$key} = $value; } # - &Apache::lonnet::appenv(%ValuesHash); + &Apache::lonnet::appenv(\%ValuesHash); return '<img src="/cgi-bin/graph.png?'.$identifier.'" border="1" />'; } @@ -7592,7 +8444,7 @@ Bad place for them but what the hell. =over 4 -=item &chartlink +=item * &chartlink() Returns a link to the chart for a specific student. @@ -7631,9 +8483,9 @@ sub chartlink { =over 4 -=item &restore_course_settings +=item * &restore_course_settings() -=item &store_course_settings +=item * &store_course_settings() Restores/Store indicated form parameters from the course environment. Will not overwrite existing values of the form parameters. @@ -7707,7 +8559,7 @@ sub store_settings { 'got error:'.$put_result); } # Make sure these settings stick around in this session, too - &Apache::lonnet::appenv(%AppHash); + &Apache::lonnet::appenv(\%AppHash); return; } @@ -7744,7 +8596,7 @@ sub restore_settings { =over 4 -=item &build_recipient_list +=item * &build_recipient_list() Build recipient lists for three types of e-mail: (a) Error Reports, (b) Package Updates, (c) Help requests, generated by @@ -7757,7 +8609,9 @@ defdom (domain for which to retrieve con origmail (scalar - email address of recipient from loncapa.conf, i.e., predates configuration by DC via domainprefs.pm -Returns: comma separated list of addresses to which to send e-mail. +Returns: comma separated list of addresses to which to send e-mail. + +=back =cut @@ -7785,8 +8639,10 @@ sub build_recipient_list { } elsif ($origmail ne '') { push(@recipients,$origmail); } - if ($defmail ne '') { - push(@recipients,$defmail); + if (defined($defmail)) { + if ($defmail ne '') { + push(@recipients,$defmail); + } } if ($otheremails) { my @others; @@ -7808,13 +8664,347 @@ sub build_recipient_list { ############################################################ ############################################################ +=pod + +=head1 Course Catalog Routines + +=over 4 + +=item * &gather_categories() + +Converts category definitions - keys of categories hash stored in +coursecategories in configuration.db on the primary library server in a +domain - to an array. Also generates javascript and idx hash used to +generate Domain Coordinator interface for editing Course Categories. + +Inputs: + +categories (reference to hash of category definitions). + +cats (reference to array of arrays/hashes which encapsulates hierarchy of + categories and subcategories). + +idx (reference to hash of counters used in Domain Coordinator interface for + editing Course Categories). + +jsarray (reference to array of categories used to create Javascript arrays for + Domain Coordinator interface for editing Course Categories). + +Returns: nothing + +Side effects: populates cats, idx and jsarray. + +=cut + +sub gather_categories { + my ($categories,$cats,$idx,$jsarray) = @_; + my %counters; + my $num = 0; + foreach my $item (keys(%{$categories})) { + my ($cat,$container,$depth) = map { &unescape($_); } split(/:/,$item); + if ($container eq '' && $depth == 0) { + $cats->[$depth][$categories->{$item}] = $cat; + } else { + $cats->[$depth]{$container}[$categories->{$item}] = $cat; + } + my ($escitem,$tail) = split(/:/,$item,2); + if ($counters{$tail} eq '') { + $counters{$tail} = $num; + $num ++; + } + if (ref($idx) eq 'HASH') { + $idx->{$item} = $counters{$tail}; + } + if (ref($jsarray) eq 'ARRAY') { + push(@{$jsarray->[$counters{$tail}]},$item); + } + } + return; +} + +=pod + +=item * &extract_categories() + +Used to generate breadcrumb trails for course categories. + +Inputs: + +categories (reference to hash of category definitions). + +cats (reference to array of arrays/hashes which encapsulates hierarchy of + categories and subcategories). + +trails (reference to array of breacrumb trails for each category). + +allitems (reference to hash - key is category key + (format: escaped(name):escaped(parent category):depth in hierarchy). + +idx (reference to hash of counters used in Domain Coordinator interface for + editing Course Categories). + +jsarray (reference to array of categories used to create Javascript arrays for + Domain Coordinator interface for editing Course Categories). + +subcats (reference to hash of arrays containing all subcategories within each + category, -recursive) + +Returns: nothing + +Side effects: populates trails and allitems hash references. + +=cut + +sub extract_categories { + my ($categories,$cats,$trails,$allitems,$idx,$jsarray,$subcats) = @_; + if (ref($categories) eq 'HASH') { + &gather_categories($categories,$cats,$idx,$jsarray); + if (ref($cats->[0]) eq 'ARRAY') { + for (my $i=0; $i<@{$cats->[0]}; $i++) { + my $name = $cats->[0][$i]; + my $item = &escape($name).'::0'; + my $trailstr; + if ($name eq 'instcode') { + $trailstr = &mt('Official courses (with institutional codes)'); + } else { + $trailstr = $name; + } + if ($allitems->{$item} eq '') { + push(@{$trails},$trailstr); + $allitems->{$item} = scalar(@{$trails})-1; + } + my @parents = ($name); + if (ref($cats->[1]{$name}) eq 'ARRAY') { + for (my $j=0; $j<@{$cats->[1]{$name}}; $j++) { + my $category = $cats->[1]{$name}[$j]; + if (ref($subcats) eq 'HASH') { + push(@{$subcats->{$item}},&escape($category).':'.&escape($name).':1'); + } + &recurse_categories($cats,2,$category,$trails,$allitems,\@parents,$subcats); + } + } else { + if (ref($subcats) eq 'HASH') { + $subcats->{$item} = []; + } + } + } + } + } + return; +} + +=pod + +=item *&recurse_categories() + +Recursively used to generate breadcrumb trails for course categories. + +Inputs: + +cats (reference to array of arrays/hashes which encapsulates hierarchy of + categories and subcategories). + +depth (current depth in hierarchy of categories and sub-categories - 0 indexed). + +category (current course category, for which breadcrumb trail is being generated). + +trails (reference to array of breadcrumb trails for each category). + +allitems (reference to hash - key is category key + (format: escaped(name):escaped(parent category):depth in hierarchy). + +parents (array containing containers directories for current category, + back to top level). + +Returns: nothing + +Side effects: populates trails and allitems hash references + +=cut + +sub recurse_categories { + my ($cats,$depth,$category,$trails,$allitems,$parents,$subcats) = @_; + my $shallower = $depth - 1; + if (ref($cats->[$depth]{$category}) eq 'ARRAY') { + for (my $k=0; $k<@{$cats->[$depth]{$category}}; $k++) { + my $name = $cats->[$depth]{$category}[$k]; + my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower; + my $trailstr = join(' -> ',(@{$parents},$category)); + if ($allitems->{$item} eq '') { + push(@{$trails},$trailstr); + $allitems->{$item} = scalar(@{$trails})-1; + } + my $deeper = $depth+1; + push(@{$parents},$category); + if (ref($subcats) eq 'HASH') { + my $subcat = &escape($name).':'.$category.':'.$depth; + for (my $j=@{$parents}; $j>=0; $j--) { + my $higher; + if ($j > 0) { + $higher = &escape($parents->[$j]).':'. + &escape($parents->[$j-1]).':'.$j; + } else { + $higher = &escape($parents->[$j]).'::'.$j; + } + push(@{$subcats->{$higher}},$subcat); + } + } + &recurse_categories($cats,$deeper,$name,$trails,$allitems,$parents, + $subcats); + pop(@{$parents}); + } + } else { + my $item = &escape($category).':'.&escape($parents->[-1]).':'.$shallower; + my $trailstr = join(' -> ',(@{$parents},$category)); + if ($allitems->{$item} eq '') { + push(@{$trails},$trailstr); + $allitems->{$item} = scalar(@{$trails})-1; + } + } + return; +} + +=pod + +=item *&assign_categories_table() + +Create a datatable for display of hierarchical categories in a domain, +with checkboxes to allow a course to be categorized. + +Inputs: + +cathash - reference to hash of categories defined for the domain (from + configuration.db) + +currcat - scalar with an & separated list of categories assigned to a course. + +Returns: $output (markup to be displayed) + +=cut + +sub assign_categories_table { + my ($cathash,$currcat) = @_; + my $output; + if (ref($cathash) eq 'HASH') { + my (@cats,@trails,%allitems,%idx,@jsarray,@path,$maxdepth); + &extract_categories($cathash,\@cats,\@trails,\%allitems,\%idx,\@jsarray); + $maxdepth = scalar(@cats); + if (@cats > 0) { + my $itemcount = 0; + if (ref($cats[0]) eq 'ARRAY') { + $output = &Apache::loncommon::start_data_table(); + my @currcategories; + if ($currcat ne '') { + @currcategories = split('&',$currcat); + } + for (my $i=0; $i<@{$cats[0]}; $i++) { + my $parent = $cats[0][$i]; + my $css_class = $itemcount%2?' class="LC_odd_row"':''; + next if ($parent eq 'instcode'); + my $item = &escape($parent).'::0'; + my $checked = ''; + if (@currcategories > 0) { + if (grep(/^\Q$item\E$/,@currcategories)) { + $checked = ' checked="checked" '; + } + } + $output .= '<tr '.$css_class.'><td><span class="LC_nobreak">'. + '<input type="checkbox" name="usecategory" value="'. + $item.'"'.$checked.' />'.$parent.'</span>'. + '<input type="hidden" name="catname" value="'.$parent.'" /></td>'; + my $depth = 1; + push(@path,$parent); + $output .= &assign_category_rows($itemcount,\@cats,$depth,$parent,\@path,\@currcategories); + pop(@path); + $output .= '</tr><tr><td colspan="'.$maxdepth.'" class="LC_row_separator"></td></tr>'; + $itemcount ++; + } + $output .= &Apache::loncommon::end_data_table(); + } + } + } + return $output; +} + +=pod + +=item *&assign_category_rows() + +Create a datatable row for display of nested categories in a domain, +with checkboxes to allow a course to be categorized,called recursively. + +Inputs: + +itemcount - track row number for alternating colors + +cats - reference to array of arrays/hashes which encapsulates hierarchy of + categories and subcategories. + +depth - current depth in hierarchy of categories and sub-categories - 0 indexed. + +parent - parent of current category item + +path - Array containing all categories back up through the hierarchy from the + current category to the top level. + +currcategories - reference to array of current categories assigned to the course + +Returns: $output (markup to be displayed). + +=cut + +sub assign_category_rows { + my ($itemcount,$cats,$depth,$parent,$path,$currcategories) = @_; + my ($text,$name,$item,$chgstr); + if (ref($cats) eq 'ARRAY') { + my $maxdepth = scalar(@{$cats}); + if (ref($cats->[$depth]) eq 'HASH') { + if (ref($cats->[$depth]{$parent}) eq 'ARRAY') { + my $numchildren = @{$cats->[$depth]{$parent}}; + my $css_class = $itemcount%2?' class="LC_odd_row"':''; + $text .= '<td><table class="LC_datatable">'; + for (my $j=0; $j<$numchildren; $j++) { + $name = $cats->[$depth]{$parent}[$j]; + $item = &escape($name).':'.&escape($parent).':'.$depth; + my $deeper = $depth+1; + my $checked = ''; + if (ref($currcategories) eq 'ARRAY') { + if (@{$currcategories} > 0) { + if (grep(/^\Q$item\E$/,@{$currcategories})) { + $checked = ' checked="checked" '; + } + } + } + $text .= '<tr><td><span class="LC_nobreak"><label>'. + '<input type="checkbox" name="usecategory" value="'. + $item.'"'.$checked.' />'.$name.'</label></span>'. + '<input type="hidden" name="catname" value="'.$name.'" />'. + '</td><td>'; + if (ref($path) eq 'ARRAY') { + push(@{$path},$name); + $text .= &assign_category_rows($itemcount,$cats,$deeper,$name,$path,$currcategories); + pop(@{$path}); + } + $text .= '</td></tr>'; + } + $text .= '</table></td>'; + } + } + } + return $text; +} + +############################################################ +############################################################ + + sub commit_customrole { - my ($udom,$uname,$url,$three,$four,$five,$start,$end) = @_; + my ($udom,$uname,$url,$three,$four,$five,$start,$end,$context) = @_; my $output = &mt('Assigning custom role').' "'.$five.'" by '.$four.':'.$three.' in '.$url. ($start?', '.&mt('starting').' '.localtime($start):''). ($end?', ending '.localtime($end):'').': <b>'. &Apache::lonnet::assigncustomrole( - $udom,$uname,$url,$three,$four,$five,$end,$start). + $udom,$uname,$url,$three,$four,$five,$end,$start,undef,undef,$context). '</b><br />'; return $output; } @@ -7849,7 +9039,7 @@ sub commit_standardrole { $output = &mt('Assigning').' '.$three.' in '.$url. ($start?', '.&mt('starting').' '.localtime($start):''). ($end?', '.&mt('ending').' '.localtime($end):'').': '; - my $result = &Apache::lonnet::assignrole($udom,$uname,$url,$three,$end,$start); + my $result = &Apache::lonnet::assignrole($udom,$uname,$url,$three,$end,$start,'','',$context); if ($context eq 'auto') { $output .= $result.$linefeed; } else { @@ -7884,7 +9074,7 @@ sub commit_studentrole { } $oldsecurl = $uurl; $expire_role_result = - &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',$now); + &Apache::lonnet::assignrole($udom,$uname,$uurl,'st',$now,'','',$context); if ($env{'request.course.sec'} ne '') { if ($expire_role_result eq 'refused') { my @roles = ('st'); @@ -7907,7 +9097,7 @@ sub commit_studentrole { } } if (($expire_role_result eq 'ok') || ($secchange == 0)) { - $modify_section_result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,'','',$cid); + $modify_section_result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,'','',$cid,'',$context); if ($modify_section_result =~ /^ok/) { if ($secchange == 1) { if ($sec eq '') { @@ -8070,19 +9260,26 @@ sub construct_course { $outcome .= $clonemsg.$linefeed; my %oldcenv=&Apache::lonnet::dump('environment',$$crsudom,$$crsunum); # Copy all files - &Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid); + &Apache::lonclonecourse::copycoursefiles($cloneid,$$courseid,$args->{'datemode'},$args->{'dateshift'}); # Restore URL $cenv{'url'}=$oldcenv{'url'}; # Restore title $cenv{'description'}=$oldcenv{'description'}; -# restore grading mode - if (defined($oldcenv{'grading'})) { - $cenv{'grading'}=$oldcenv{'grading'}; - } # Mark as cloned $cenv{'clonedfrom'}=$cloneid; - delete($cenv{'default_enrollment_start_date'}); - delete($cenv{'default_enrollment_end_date'}); +# Need to clone grading mode + my %newenv=&Apache::lonnet::get('environment',['grading'],$$crsudom,$$crsunum); + $cenv{'grading'}=$newenv{'grading'}; +# Do not clone these environment entries + &Apache::lonnet::del('environment', + ['default_enrollment_start_date', + 'default_enrollment_end_date', + 'question.email', + 'policy.email', + 'comment.email', + 'pch.users.denied', + 'plc.users.denied'], + $$crsudom,$$crsunum); } # @@ -8110,7 +9307,6 @@ sub construct_course { } else { $cenv{'internal.courseowner'} = $args->{'curruser'}; } - my @badclasses = (); # Used to accumulate sections/crosslistings that did not pass classlist access check for course owner. if ($args->{'crssections'}) { $cenv{'internal.sectionnums'} = ''; @@ -8310,10 +9506,10 @@ sub construct_course { $outcome .= ($fatal?$errtext:'read ok').' - '; my $title; my $url; if ($args->{'firstres'} eq 'syl') { - $title='Syllabus'; + $title=&mt('Syllabus'); $url='/public/'.$$crsudom.'/'.$$crsunum.'/syllabus'; } else { - $title='Navigate Contents'; + $title=&mt('Navigate Contents'); $url='/adm/navmaps'; } @@ -8367,28 +9563,14 @@ sub icon { return &lonhttpdurl($iconname); } -sub lonhttpd_port { - my $lonhttpd_port=$Apache::lonnet::perlvar{'lonhttpdPort'}; - if (!defined($lonhttpd_port)) { $lonhttpd_port='8080'; } - # IE doesn't like a secure page getting images from a non-secure - # port (when logging we haven't parsed the browser type so default - # back to secure - if ((!exists($env{'browser.type'}) || $env{'browser.type'} eq 'explorer') - && $ENV{'SERVER_PORT'} == 443) { - return 443; - } - return $lonhttpd_port; - -} - sub lonhttpdurl { +# +# Had been used for "small fry" static images on separate port 8080. +# Modify here if lightweight http functionality desired again. +# Currently eliminated due to increasing firewall issues. +# my ($url)=@_; - - my $lonhttpd_port = &lonhttpd_port(); - if ($lonhttpd_port == 443) { - return 'https://'.$ENV{'SERVER_NAME'}.$url; - } - return 'http://'.$ENV{'SERVER_NAME'}.':'.$lonhttpd_port.$url; + return $url; } sub connection_aborted { @@ -8466,7 +9648,7 @@ sub init_user_environment { } # Give them a new cookie my $id = ($args->{'robot'} ? 'robot'.$args->{'robot'} - : $now); + : $now.$$.int(rand(10000))); $cookie="$username\_$id\_$domain\_$authhost"; # Initialize roles @@ -8566,8 +9748,8 @@ sub init_user_environment { } untie(%disk_env); } else { - &Apache::lonnet::logthis("<font color=\"blue\">WARNING: ". - 'Could not create environment storage in lonauth: '.$!.'</font>'); + &Apache::lonnet::logthis("<span style=\"color:blue;\">WARNING: ". + 'Could not create environment storage in lonauth: '.$!.'</span>'); return 'error: '.$!; } } @@ -8581,12 +9763,52 @@ sub init_user_environment { sub _add_to_env { my ($idf,$env_data,$prefix) = @_; - while (my ($key,$value) = each(%$env_data)) { - $idf->{$prefix.$key} = $value; - $env{$prefix.$key} = $value; + if (ref($env_data) eq 'HASH') { + while (my ($key,$value) = each(%$env_data)) { + $idf->{$prefix.$key} = $value; + $env{$prefix.$key} = $value; + } } } +# --- Get the symbolic name of a problem and the url +sub get_symb { + my ($request,$silent) = @_; + (my $url=$env{'form.url'}) =~ s-^http://($ENV{'SERVER_NAME'}|$ENV{'HTTP_HOST'})--; + my $symb=($env{'form.symb'} ne '' ? $env{'form.symb'} : (&Apache::lonnet::symbread($url))); + if ($symb eq '') { + if (!$silent) { + $request->print("Unable to handle ambiguous references:$url:."); + return (); + } + } + &Apache::lonenc::check_decrypt(\$symb); + return ($symb); +} + +# --------------------------------------------------------------Get annotation + +sub get_annotation { + my ($symb,$enc) = @_; + + my $key = $symb; + if (!$enc) { + $key = + &Apache::lonnet::clutter((&Apache::lonnet::decode_symb($symb))[2]); + } + my %annotation=&Apache::lonnet::get('nohist_annotations',[$key]); + return $annotation{$key}; +} + +sub clean_symb { + my ($symb) = @_; + + &Apache::lonenc::check_decrypt(\$symb); + my $enc = $env{'request.enc'}; + delete($env{'request.enc'}); + + return ($symb,$enc); +} =pod