--- loncom/interface/loncommon.pm 2013/08/18 17:32:57 1.1075.2.46 +++ loncom/interface/loncommon.pm 2014/02/22 00:02:13 1.1075.2.67 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # a pile of common routines # -# $Id: loncommon.pm,v 1.1075.2.46 2013/08/18 17:32:57 raeburn Exp $ +# $Id: loncommon.pm,v 1.1075.2.67 2014/02/22 00:02:13 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -74,6 +74,8 @@ use DateTime::TimeZone; use DateTime::Locale::Catalog; use Authen::Captcha; use Captcha::reCAPTCHA; +use Crypt::DES; +use DynaLoader; # for Crypt::DES version # ---------------------------------------------- Designs use vars qw(%defaultdesign); @@ -1234,7 +1236,11 @@ sub help_open_topic { $topic=~s/\W/\_/g; if (!$stayOnPage) { - $link = "javascript:openMyModal('/adm/help/${filename}.hlp',$width,$height,'yes');"; + if ($env{'browser.mobile'}) { + $link = "javascript:openMyModal('/adm/help/${filename}.hlp',$width,$height,'yes');"; + } else { + $link = "javascript:void(open('/adm/help/${filename}.hlp', 'Help_for_$topic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))"; + } } elsif ($stayOnPage eq 'popup') { $link = "javascript:void(open('/adm/help/${filename}.hlp', 'Help_for_$topic', 'menubar=0,toolbar=1,scrollbars=1,width=$width,height=$height,resizable=yes'))"; } else { @@ -1348,32 +1354,40 @@ sub help_open_menu { sub top_nav_help { my ($text) = @_; $text = &mt($text); - my $stay_on_page = 1; - - my $link = ($stay_on_page) ? "javascript:helpMenu('display')" - : "javascript:helpMenu('open')"; - my $banner_link = &update_help_link(undef,undef,undef,undef,$stay_on_page); - + my $stay_on_page; + unless ($env{'environment.remote'} eq 'on') { + $stay_on_page = 1; + } + my ($link,$banner_link); + unless ($env{'request.noversionuri'} =~ m{^/adm/helpmenu}) { + $link = ($stay_on_page) ? "javascript:helpMenu('display')" + : "javascript:helpMenu('open')"; + $banner_link = &update_help_link(undef,undef,undef,undef,$stay_on_page); + } my $title = &mt('Get help'); - - return <<"END"; + if ($link) { + return <<"END"; $banner_link - $text +$text END + } else { + return ' '.$text.' '; + } } sub help_menu_js { - my ($text) = @_; + my ($httphost) = @_; my $stayOnPage = 1; my $width = 620; my $height = 600; my $helptopic=&general_help(); - my $details_link = '/adm/help/'.$helptopic.'.hlp'; + my $details_link = $httphost.'/adm/help/'.$helptopic.'.hlp'; my $nothing=&Apache::lonhtmlcommon::javascript_nothing(); my $start_page = &Apache::loncommon::start_page('Help Menu', undef, {'frameset' => 1, 'js_ready' => 1, + 'use_absolute' => $httphost, 'add_entries' => { 'border' => '0', 'rows' => "110,*",},}); @@ -1405,9 +1419,10 @@ function helpMenu(target) { return; } function writeHelp(caller) { - caller.document.writeln('$start_page\\n\\n\\n$end_page') - caller.document.close() - caller.focus() + caller.document.writeln('$start_page\\n\\n'); + caller.document.writeln('\\n$end_page'); + caller.document.close(); + caller.focus(); } // END LON-CAPA Internal --> // ]]> @@ -1719,8 +1734,6 @@ RESIZE =head1 Excel and CSV file utility routines -=over 4 - =cut ############################################################### @@ -1728,6 +1741,8 @@ RESIZE =pod +=over 4 + =item * &csv_translate($text) Translate $text to allow it to be output as a 'comma separated values' @@ -5037,6 +5052,7 @@ sub bodytag { $public = 1; } if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); } + my $httphost = $args->{'use_absolute'}; $function = &get_users_function() if (!$function); my $img = &designparm($function.'.img',$domain); @@ -5108,7 +5124,7 @@ sub bodytag { my $funclist; if (($env{'environment.remote'} eq 'on') && ($env{'request.state'} ne 'construct')) { - $bodytag .= Apache::lonhtmlcommon::scripttag(Apache::lonmenu::utilityfunctions(), 'start')."\n". + $bodytag .= Apache::lonhtmlcommon::scripttag(Apache::lonmenu::utilityfunctions($httphost), 'start')."\n". Apache::lonmenu::serverform(); my $forbodytag; &Apache::lonmenu::prepare_functions($env{'request.noversionuri'}, @@ -5125,7 +5141,7 @@ sub bodytag { # } $bodytag .= Apache::lonhtmlcommon::scripttag( - Apache::lonmenu::utilityfunctions(), 'start'); + Apache::lonmenu::utilityfunctions($httphost), 'start'); my ($left,$right) = Apache::lonmenu::primary_menu(); @@ -5149,9 +5165,13 @@ sub bodytag { } $bodytag .= qq|
$realm $dc_info
|; + #if directed to not display the secondary menu, don't. + if ($args->{'no_secondary_menu'}) { + return $bodytag; + } #don't show menus for public users if (!$public){ - $bodytag .= Apache::lonmenu::secondary_menu(); + $bodytag .= Apache::lonmenu::secondary_menu($httphost); $bodytag .= Apache::lonmenu::serverform(); $bodytag .= Apache::lonhtmlcommon::scripttag('', 'end'); if ($env{'request.state'} eq 'construct') { @@ -5190,6 +5210,9 @@ sub bodytag { } my $upperleft=''.$function.''; + my $help=($no_inline_link?'' + :&Apache::loncommon::top_nav_help('Help')); + # Explicit link to get inline menu my $menu= ($no_inline_link?'' :''.&mt('Switch to Inline Menu Mode').''); @@ -5207,6 +5230,7 @@ sub bodytag { unless ($env{'form.inhibitmenu'}) { $bodytag .= qq|
$name $role
    +
  1. $help
  2. $menu
$realm $dc_info
|; } @@ -5214,7 +5238,7 @@ sub bodytag { if (!$public){ if ($env{'request.state'} eq 'construct') { $funclist = &Apache::lonhtmlcommon::scripttag( - &Apache::lonmenu::utilityfunctions(), 'start'). + &Apache::lonmenu::utilityfunctions($httphost), 'start'). &Apache::lonhtmlcommon::scripttag('','end'). &Apache::lonmenu::innerregister($forcereg, $args->{'bread_crumbs'}); @@ -5265,7 +5289,7 @@ sub make_attr_string { } my $attr_string; - foreach my $attr (keys(%$attr_ref)) { + foreach my $attr (sort(keys(%$attr_ref))) { $attr_string .= " $attr=\"".$attr_ref->{$attr}.'" '; } return $attr_string; @@ -6479,6 +6503,14 @@ div.LC_edit_problem_saves { white-space: nowrap; } +.LC_edit_problem_latexhelper{ + text-align: right; +} + +#LC_edit_problem_colorful div{ + margin-left: 40px; +} + img.stift { border-width: 0; vertical-align: middle; @@ -7245,6 +7277,7 @@ sub headtag { my $function = $args->{'function'} || &get_users_function(); my $domain = $args->{'domain'} || &determinedomain(); my $bgcolor = $args->{'bgcolor'} || &designparm($function.'.pgbg',$domain); + my $httphost = $args->{'use_absolute'}; my $url = join(':',$env{'user.name'},$env{'user.domain'}, $Apache::lonnet::perlvar{'lonVersion'}, #time(), @@ -7255,7 +7288,7 @@ sub headtag { my $result = ''. - &font_settings(); + &font_settings($args); my $inhibitprint = &print_suppression(); @@ -7268,7 +7301,7 @@ sub headtag { if (!$args->{'no_nav_bar'} && !$args->{'only_body'} && !$args->{'frameset'}) { - $result .= &help_menu_js(); + $result .= &help_menu_js($httphost); $result.=&modal_window(); $result.=&togglebox_script(); $result.=&wishlist_window(); @@ -7303,7 +7336,11 @@ ADDMETA } if (!$args->{'no_auto_mt_title'}) { $title = &mt($title); } $result .= ' LON-CAPA '.$title.'' - .'' + .'{'frameset'}) { + $result .= ' /'; + } + $result .= '>' .$inhibitprint .$head_extra; if ($env{'browser.mobile'}) { @@ -7320,15 +7357,21 @@ ADDMETA Returns neccessary to set the proper encoding -Inputs: none +Inputs: optional reference to HASH -- $args passed to &headtag() =cut sub font_settings { + my ($args) = @_; my $headerstring=''; - if (!$env{'browser.mathml'} && $env{'browser.unicode'}) { + if ((!$env{'browser.mathml'} && $env{'browser.unicode'}) || + ((ref($args) eq 'HASH') && ($args->{'browser.unicode'}))) { $headerstring.= - ''; + '{'frameset'}) { + $headerstring.= ' /'; + } + $headerstring .= '>'."\n"; } return $headerstring; } @@ -7420,6 +7463,7 @@ Inputs: none =cut sub xml_begin { + my ($is_frameset) = @_; my $output=''; if ($env{'browser.mathml'}) { @@ -7431,9 +7475,12 @@ sub xml_begin { .'' .''; + } elsif ($is_frameset) { + $output=''."\n". + ''."\n"; } else { - $output='' - .''; + $output=''."\n". + ''."\n"; } return $output; } @@ -7502,7 +7549,7 @@ sub start_page { my ($result,@advtools); if (! exists($args->{'skip_phases'}{'head'}) ) { - $result .= &xml_begin() . &headtag($title, $head_extra, $args); + $result .= &xml_begin($args->{'frameset'}) . &headtag($title, $head_extra, $args); } if (! exists($args->{'skip_phases'}{'body'}) ) { @@ -7608,9 +7655,11 @@ function set_wishlistlink(title, path) { title = document.title; title = title.replace(/^LON-CAPA /,''); } + title = encodeURIComponent(title); if (!path) { path = location.pathname; } + path = encodeURIComponent(path); Win = window.open('/adm/wishlist?mode=newLink&setTitle='+title+'&setPath='+path, 'wishlistNewLink','width=560,height=350,scrollbars=0'); } @@ -7925,7 +7974,7 @@ sub validate_page { sub start_scrollbox { - my ($outerwidth,$width,$height,$id,$bgcolor,$cursor,$needjsready)=@_; + my ($outerwidth,$width,$height,$id,$bgcolor,$cursor,$needjsready) = @_; unless ($outerwidth) { $outerwidth='520px'; } unless ($width) { $width='500px'; } unless ($height) { $height='200px'; } @@ -7945,7 +7994,7 @@ sub start_scrollbox { $nicescroll_js
-
+
END } @@ -8035,10 +8084,16 @@ function expand_div(caller) { } sub simple_error_page { - my ($r,$title,$msg) = @_; + my ($r,$title,$msg,$args) = @_; + if (ref($args) eq 'HASH') { + if (!$args->{'no_auto_mt_msg'}) { $msg = &mt($msg); } + } else { + $msg = &mt($msg); + } + my $page = &Apache::loncommon::start_page($title). - '

'.&mt($msg).'

'. + '

'.$msg.'

'. &Apache::loncommon::end_page(); if (ref($r)) { $r->print($page); @@ -8665,11 +8720,11 @@ Incoming parameters: 2. user's domain 3. quota name - portfolio, author, or course (if no quota name provided, defaults to portfolio). -4. crstype - official, unofficial or community, if quota name is +4. crstype - official, unofficial, textbook or community, if quota name is course Returns: -1. Disk quota (in Mb) assigned to student. +1. Disk quota (in MB) assigned to student. 2. (Optional) Type of setting: custom or default (individually assigned or default for user's institutional status). @@ -8739,7 +8794,8 @@ sub get_user_quota { if ($quota eq '' || wantarray) { if ($quotaname eq 'course') { my %domdefs = &Apache::lonnet::get_domain_defaults($udom); - if (($crstype eq 'official') || ($crstype eq 'unofficial') || ($crstype eq 'community')) { + if (($crstype eq 'official') || ($crstype eq 'unofficial') || + ($crstype eq 'community') || ($crstype eq 'textbook')) { $defquota = $domdefs{$crstype.'quota'}; } if ($defquota eq '') { @@ -8785,14 +8841,14 @@ Incoming parameters: Returns: -1. Default disk quota (in Mb) for user portfolios in the domain. +1. Default disk quota (in MB) for user portfolios in the domain. 2. (Optional) institutional type which determined the value of the default quota. If a value has been stored in the domain's configuration db, it will return that, otherwise it returns 20 (for backwards compatibility with domains which have not set up a configuration -db file; the original statically defined portfolio quota was 20 Mb). +db file; the original statically defined portfolio quota was 20 MB). If the user's status includes multiple types (e.g., staff and student), the largest default quota which applies to the user determines the @@ -8880,13 +8936,14 @@ space to be exceeded. Same, if upload of a file directly to a course/community via Course Editor will cause quota for uploaded content for the course to be exceeded. -Inputs: 6 +Inputs: 7 1. username or coursenum 2. domain 3. context ('author' or 'course') 4. filename of file for which action is being requested 5. filesize (kB) of file 6. action being taken: copy or upload. +7. quotatype (in course context -- official, unofficial, community or textbook). Returns: 1 scalar: HTML to display containing warning if quota would be exceeded, otherwise return null. @@ -8896,9 +8953,9 @@ Returns: 1 scalar: HTML to display conta =cut sub excess_filesize_warning { - my ($uname,$udom,$context,$filename,$filesize,$action) = @_; + my ($uname,$udom,$context,$filename,$filesize,$action,$quotatype) = @_; my $current_disk_usage = 0; - my $disk_quota = &get_user_quota($uname,$udom,$context); #expressed in MB + my $disk_quota = &get_user_quota($uname,$udom,$context,$quotatype); #expressed in MB if ($context eq 'author') { my $authorspace = $Apache::lonnet::perlvar{'lonDocRoot'}."/priv/$udom/$uname"; $current_disk_usage = &Apache::lonnet::diskusage($udom,$uname,$authorspace); @@ -9761,11 +9818,10 @@ sub ask_for_embedded_content { my $numexisting = 0; my $numunused = 0; my ($output,$upload_output,$toplevel,$url,$udom,$uname,$getpropath,$cdom,$cnum, - $fileloc,$filename,$delete_output,$modify_output,$title,$symb,$path); + $fileloc,$filename,$delete_output,$modify_output,$title,$symb,$path,$navmap); my $heading = &mt('Upload embedded files'); my $buttontext = &mt('Upload'); - my ($navmap,$cdom,$cnum); if ($env{'request.course.id'}) { if ($actionurl eq '/adm/dependencies') { $navmap = Apache::lonnavmaps::navmap->new(); @@ -9849,17 +9905,18 @@ sub ask_for_embedded_content { } else { $embed_file = $file; } - my $absolutepath; + my ($absolutepath,$cleaned_file); if ($embed_file =~ m{^\w+://}) { - $newfiles{$embed_file} = 1; - $mapping{$embed_file} = $embed_file; + $cleaned_file = $embed_file; + $newfiles{$cleaned_file} = 1; + $mapping{$cleaned_file} = $embed_file; } else { + $cleaned_file = &clean_path($embed_file); if ($embed_file =~ m{^/}) { $absolutepath = $embed_file; - $embed_file =~ s{^(/+)}{}; } - if ($embed_file =~ m{/}) { - my ($path,$fname) = ($embed_file =~ m{^(.+)/([^/]*)$}); + if ($cleaned_file =~ m{/}) { + my ($path,$fname) = ($cleaned_file =~ m{^(.+)/([^/]*)$}); $path = &check_for_traversal($path,$url,$toplevel); my $item = $fname; if ($path ne '') { @@ -9876,9 +9933,9 @@ sub ask_for_embedded_content { } else { $dependencies{$embed_file} = 1; if ($absolutepath) { - $mapping{$embed_file} = $absolutepath; + $mapping{$cleaned_file} = $absolutepath; } else { - $mapping{$embed_file} = $embed_file; + $mapping{$cleaned_file} = $embed_file; } } } @@ -10252,6 +10309,46 @@ sub ask_for_embedded_content { return ($output,$counter,$numpathchg); } +=pod + +=item * clean_path($name) + +Performs clean-up of directories, subdirectories and filename in an +embedded object, referenced in an HTML file which is being uploaded +to a course or portfolio, where +"Upload embedded images/multimedia files if HTML file" checkbox was +checked. + +Clean-up is similar to replacements in lonnet::clean_filename() +except each / between sub-directory and next level is preserved. + +=cut + +sub clean_path { + my ($embed_file) = @_; + $embed_file =~s{^/+}{}; + my @contents; + if ($embed_file =~ m{/}) { + @contents = split(/\//,$embed_file); + } else { + @contents = ($embed_file); + } + my $lastidx = scalar(@contents)-1; + for (my $i=0; $i<=$lastidx; $i++) { + $contents[$i]=~s{\\}{/}g; + $contents[$i]=~s/\s+/\_/g; + $contents[$i]=~s{[^/\w\.\-]}{}g; + if ($i == $lastidx) { + $contents[$i]=~s/\.(\d+)(?=\.)/_$1/g; + } + } + if ($lastidx > 0) { + return join('/',@contents); + } else { + return $contents[0]; + } +} + sub embedded_file_element { my ($context,$num,$embed_file,$mapping,$allfiles,$codebase,$type) = @_; return unless ((ref($mapping) eq 'HASH') && (ref($allfiles) eq 'HASH') && @@ -10376,7 +10473,8 @@ sub upload_embedded { # 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).'
'; + $output .= &mt('Invalid file extension ([_1]) - reserved for internal use.',$1) + .' '.&mt('Rename the file with a different extension and re-upload.').'
'; next; } elsif (($fname =~ /\.(\w+)$/) && (!defined(&Apache::loncommon::fileembstyle($1)))) { @@ -10640,6 +10738,7 @@ sub modify_html_refs { my $numchg = ($content =~ s{($attrib_regexp\s*=\s*['"]?)\Q$ref\E(['"]?)}{$1$newname$2}gi); $count += $numchg; $allfiles{$newname} = $allfiles{$ref}; + delete($allfiles{$ref}); } if ($env{'form.embedded_codebase_'.$i} ne '') { $codebase = &unescape($env{'form.embedded_codebase_'.$i}); @@ -10933,16 +11032,43 @@ sub decompress_form { } } if ($mimetype =~ m{^application/(x\-)?(compressed|zip)}) { - my @camtasia = ("$topdir/","$topdir/index.html", + my @camtasia6 = ("$topdir/","$topdir/index.html", "$topdir/media/", "$topdir/media/$topdir.mp4", "$topdir/media/FirstFrame.png", "$topdir/media/player.swf", "$topdir/media/swfobject.js", "$topdir/media/expressInstall.swf"); - my @diffs = &compare_arrays(\@paths,\@camtasia); + my @camtasia8 = ("$topdir/","$topdir/$topdir.html", + "$topdir/$topdir.mp4", + "$topdir/$topdir\_config.xml", + "$topdir/$topdir\_controller.swf", + "$topdir/$topdir\_embed.css", + "$topdir/$topdir\_First_Frame.png", + "$topdir/$topdir\_player.html", + "$topdir/$topdir\_Thumbnails.png", + "$topdir/playerProductInstall.swf", + "$topdir/scripts/", + "$topdir/scripts/config_xml.js", + "$topdir/scripts/handlebars.js", + "$topdir/scripts/jquery-1.7.1.min.js", + "$topdir/scripts/jquery-ui-1.8.15.custom.min.js", + "$topdir/scripts/modernizr.js", + "$topdir/scripts/player-min.js", + "$topdir/scripts/swfobject.js", + "$topdir/skins/", + "$topdir/skins/configuration_express.xml", + "$topdir/skins/express_show/", + "$topdir/skins/express_show/player-min.css", + "$topdir/skins/express_show/spritesheet.png"); + my @diffs = &compare_arrays(\@paths,\@camtasia6); if (@diffs == 0) { - $is_camtasia = 1; + $is_camtasia = 6; + } else { + @diffs = &compare_arrays(\@paths,\@camtasia8); + if (@diffs == 0) { + $is_camtasia = 8; + } } } my $output; @@ -10954,8 +11080,7 @@ sub decompress_form { function camtasiaToggle() { for (var i=0; i'. ''.$lt{'proa'}.' 
'. @@ -11250,6 +11375,7 @@ sub process_decompression { \%titles,\%children); } if ($env{'form.autoextract_camtasia'}) { + my $version = $env{'form.autoextract_camtasia'}; my %displayed; my $total = 1; $env{'form.archive_directory'} = []; @@ -11268,12 +11394,15 @@ sub process_decompression { $env{'form.archive_'.$i} = 'display'; $env{'form.archive_title_'.$i} = $env{'form.camtasia_foldername'}; $displayed{'folder'} = $i; - } elsif ($item eq "$contents[0]/index.html") { + } elsif ((($item eq "$contents[0]/index.html") && ($version == 6)) || + (($item eq "$contents[0]/$contents[0]".'.html') && ($version == 8))) { $env{'form.archive_'.$i} = 'display'; $env{'form.archive_title_'.$i} = $env{'form.camtasia_moviename'}; $displayed{'web'} = $i; } else { - if ($item eq "$contents[0]/media") { + if ((($item eq "$contents[0]/media") && ($version == 6)) || + ((($item eq "$contents[0]/scripts") || ($item eq "$contents[0]/skins") || + ($item eq "$contents[0]/skins/express_show")) && ($version == 8))) { push(@{$env{'form.archive_directory'}},$i); } $env{'form.archive_'.$i} = 'dependency'; @@ -12000,7 +12129,7 @@ sub cleanup_empty_dirs { =pod -=item &get_folder_hierarchy() +=item * &get_folder_hierarchy() Provides hierarchy of names of folders/sub-folders containing the current item, @@ -12110,6 +12239,9 @@ sub get_turnedin_filepath { my $title = $res->compTitle(); $title =~ s/\W+/_/g; if ($title ne '') { + if (($pc > 1) && (length($title) > 12)) { + $title = substr($title,0,12); + } push(@pathitems,$title); } } @@ -12118,6 +12250,9 @@ sub get_turnedin_filepath { my $maptitle = $mapres->compTitle(); $maptitle =~ s/\W+/_/g; if ($maptitle ne '') { + if (length($maptitle) > 12) { + $maptitle = substr($maptitle,0,12); + } push(@pathitems,$maptitle); } unless ($env{'request.state'} eq 'construct') { @@ -12158,6 +12293,9 @@ sub get_turnedin_filepath { $restitle = time; } } + if (length($restitle) > 12) { + $restitle = substr($restitle,0,12); + } push(@pathitems,$restitle); $path .= join('/',@pathitems); } @@ -13304,7 +13442,7 @@ sub extract_categories { =pod -=item *&recurse_categories() +=item * &recurse_categories() Recursively used to generate breadcrumb trails for course categories. @@ -13375,7 +13513,7 @@ sub recurse_categories { =pod -=item *&assign_categories_table() +=item * &assign_categories_table() Create a datatable for display of hierarchical categories in a domain, with checkboxes to allow a course to be categorized. @@ -13452,7 +13590,7 @@ sub assign_categories_table { =pod -=item *&assign_category_rows() +=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. @@ -13770,7 +13908,7 @@ sub check_clone { } sub construct_course { - my ($args,$logmsg,$courseid,$crsudom,$crsunum,$udom,$uname,$context,$cnum,$category) = @_; + my ($args,$logmsg,$courseid,$crsudom,$crsunum,$udom,$uname,$context,$cnum,$category,$coderef) = @_; my $outcome; my $linefeed = '
'."\n"; if ($context eq 'auto') { @@ -13867,8 +14005,12 @@ sub construct_course { 'plc.users.denied', 'hidefromcat', 'checkforpriv', - 'categories'], + 'categories', + 'internal.uniquecode'], $$crsudom,$$crsunum); + if ($args->{'textbook'}) { + $cenv{'internal.textbook'} = $args->{'textbook'}; + } } # @@ -14052,6 +14194,25 @@ sub construct_course { } } +# +# generate and store uniquecode (available to course requester), if course should have one. +# + if ($args->{'uniquecode'}) { + my ($code,$error) = &make_unique_code($$crsudom,$$crsunum); + if ($code) { + $cenv{'internal.uniquecode'} = $code; + my %crsinfo = + &Apache::lonnet::courseiddump($$crsudom,'.',1,'.','.',$$crsunum,undef,undef,'.'); + if (ref($crsinfo{$$crsudom.'_'.$$crsunum}) eq 'HASH') { + $crsinfo{$$crsudom.'_'.$$crsunum}{'uniquecode'} = $code; + my $putres = &Apache::lonnet::courseidput($$crsudom,\%crsinfo,$crsuhome,'notime'); + } + if (ref($coderef)) { + $$coderef = $code; + } + } + } + if ($args->{'disresdis'}) { $cenv{'pch.roles.denied'}='st'; } @@ -14120,6 +14281,60 @@ sub construct_course { return (1,$outcome); } +sub make_unique_code { + my ($cdom,$cnum) = @_; + # get lock on uniquecodes db + my $lockhash = { + $cnum."\0".'uniquecodes' => $env{'user.name'}. + ':'.$env{'user.domain'}, + }; + my $tries = 0; + my $gotlock = &Apache::lonnet::newput_dom('uniquecodes',$lockhash,$cdom); + my ($code,$error); + + while (($gotlock ne 'ok') && ($tries<3)) { + $tries ++; + sleep 1; + $gotlock = &Apache::lonnet::newput_dom('uniquecodes',$lockhash,$cdom); + } + if ($gotlock eq 'ok') { + my %currcodes = &Apache::lonnet::dump_dom('uniquecodes',$cdom); + my $gotcode; + my $attempts = 0; + while ((!$gotcode) && ($attempts < 100)) { + $code = &generate_code(); + if (!exists($currcodes{$code})) { + $gotcode = 1; + unless (&Apache::lonnet::newput_dom('uniquecodes',{ $code => $cnum },$cdom) eq 'ok') { + $error = 'nostore'; + } + } + $attempts ++; + } + my @del_lock = ($cnum."\0".'uniquecodes'); + my $dellockoutcome = &Apache::lonnet::del_dom('uniquecodes',\@del_lock,$cdom); + } else { + $error = 'nolock'; + } + return ($code,$error); +} + +sub generate_code { + my $code; + my @letts = qw(B C D G H J K M N P Q R S T V W X Z); + for (my $i=0; $i<6; $i++) { + my $lettnum = int (rand 2); + my $item = ''; + if ($lettnum) { + $item = $letts[int( rand(18) )]; + } else { + $item = 1+int( rand(8) ); + } + $code .= $item; + } + return $code; +} + ############################################################ ############################################################ @@ -14147,11 +14362,12 @@ sub group_term { } sub course_types { - my @types = ('official','unofficial','community'); + my @types = ('official','unofficial','community','textbook'); my %typename = ( official => 'Official course', unofficial => 'Unofficial course', community => 'Community', + textbook => 'Textbook course', ); return (\@types,\%typename); } @@ -14336,6 +14552,12 @@ sub init_user_environment { $env{'browser.interface'}=$form->{'interface'}; } + if ($form->{'iptoken'}) { + my $lonhost = $r->dir_config('lonHostID'); + $initial_env{"user.noloadbalance"} = $lonhost; + $env{'user.noloadbalance'} = $lonhost; + } + my %is_adv = ( is_adv => $env{'user.adv'} ); my %domdef; unless ($domain eq 'public') { @@ -14348,7 +14570,7 @@ sub init_user_environment { undef,\%userenv,\%domdef,\%is_adv); } - foreach my $crstype ('official','unofficial','community') { + foreach my $crstype ('official','unofficial','community','textbook') { $userenv{'canrequest.'.$crstype} = &Apache::lonnet::usertools_access($username,$domain,$crstype, 'reload','requestcourses', @@ -14676,7 +14898,7 @@ sub captcha_display { $error = 'recaptcha'; } } - return ($output,$error); + return ($output,$error,$captcha); } sub captcha_response { @@ -14752,8 +14974,9 @@ sub create_captcha { if (-e $Apache::lonnet::perlvar{'lonCaptchaDir'}.'/'.$md5sum.'.png') { $output = ''."\n". &mt('Type in the letters/numbers shown below').' '. - '
'. - ''; + ''. + '
'. + 'captcha'; last; } } @@ -14794,9 +15017,13 @@ sub check_captcha { sub create_recaptcha { my ($pubkey) = @_; + my $use_ssl; + if ($ENV{'SERVER_PORT'} == 443) { + $use_ssl = 1; + } my $captcha = Captcha::reCAPTCHA->new; return $captcha->get_options_setter({theme => 'white'})."\n". - $captcha->get_html($pubkey). + $captcha->get_html($pubkey,undef,$use_ssl). &mt('If either word is hard to read, [_1] will replace them.', 'reCAPTCHA refresh'). '

'; @@ -14819,6 +15046,83 @@ sub check_recaptcha { return $captcha_chk; } +sub emailusername_info { + my @fields = ('firstname','lastname','institution','web','location','officialemail'); + my %titles = &Apache::lonlocal::texthash ( + lastname => 'Last Name', + firstname => 'First Name', + institution => 'School/college/university', + location => "School's city, state/province, country", + web => "School's web address", + officialemail => 'E-mail address at institution (if different)', + ); + return (\@fields,\%titles); +} + +sub cleanup_html { + my ($incoming) = @_; + my $outgoing; + if ($incoming ne '') { + $outgoing = $incoming; + $outgoing =~ s/;/;/g; + $outgoing =~ s/\#/#/g; + $outgoing =~ s/\&/&/g; + $outgoing =~ s//>/g; + $outgoing =~ s/\(/(/g; + $outgoing =~ s/\)/)/g; + $outgoing =~ s/"/"/g; + $outgoing =~ s/'/'/g; + $outgoing =~ s/\$/$/g; + $outgoing =~ s{/}{/}g; + $outgoing =~ s/=/=/g; + $outgoing =~ s/\\/\/g + } + return $outgoing; +} + +# Use: +# my $answer=reply("encrypt:passwd:$udom:$uname:$upass",$tryserver); +# +################################################## +# password associated functions # +################################################## +sub des_keys { + # Make a new key for DES encryption. + # Each key has two parts which are returned separately. + # Please note: Each key must be passed through the &hex function + # before it is output to the web browser. The hex versions cannot + # be used to decrypt. + my @hexstr=('0','1','2','3','4','5','6','7', + '8','9','a','b','c','d','e','f'); + my $lkey=''; + for (0..7) { + $lkey.=$hexstr[rand(15)]; + } + my $ukey=''; + for (0..7) { + $ukey.=$hexstr[rand(15)]; + } + return ($lkey,$ukey); +} + +sub des_decrypt { + my ($key,$cyphertext) = @_; + my $keybin=pack("H16",$key); + my $cypher; + if ($Crypt::DES::VERSION>=2.03) { + $cypher=new Crypt::DES $keybin; + } else { + $cypher=new DES $keybin; + } + my $plaintext= + $cypher->decrypt(unpack("a8",pack("H16",substr($cyphertext,0,16)))); + $plaintext.= + $cypher->decrypt(unpack("a8",pack("H16",substr($cyphertext,16,16)))); + $plaintext=substr($plaintext,1,ord(substr($plaintext,0,1)) ); + return $plaintext; +} + =pod =back