--- loncom/interface/loncommon.pm 2013/12/18 01:32:16 1.1163
+++ loncom/interface/loncommon.pm 2014/02/19 15:50:56 1.1176
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# a pile of common routines
#
-# $Id: loncommon.pm,v 1.1163 2013/12/18 01:32:16 raeburn Exp $
+# $Id: loncommon.pm,v 1.1176 2014/02/19 15:50:56 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -75,6 +75,8 @@ use DateTime::Locale::Catalog;
use Text::Aspell;
use Authen::Captcha;
use Captcha::reCAPTCHA;
+use Crypt::DES;
+use DynaLoader; # for Crypt::DES version
# ---------------------------------------------- Designs
use vars qw(%defaultdesign);
@@ -1380,16 +1382,21 @@ sub top_nav_help {
$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 ($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
END
+ } else {
+ return ' '.$text.' ';
+ }
}
sub help_menu_js {
@@ -1406,7 +1413,7 @@ sub help_menu_js {
'js_ready' => 1,
'use_absolute' => $httphost,
'add_entries' => {
- 'border' => '0',
+ 'border' => '0',
'rows' => "110,*",},});
my $end_page =
&Apache::loncommon::end_page({'frameset' => 1,
@@ -1436,9 +1443,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 -->
// ]]>
@@ -3884,7 +3892,7 @@ sub get_previous_attempt {
if ($key =~/$regexp$/ && (defined &$gradesub)) {
$value = &$gradesub($value);
}
- $prevattempts.='
'.$value.' | ';
+ $prevattempts.=''. $value.' | ';
} else {
$prevattempts.=' | ';
}
@@ -3900,7 +3908,7 @@ sub get_previous_attempt {
if ($key =~/$regexp$/ && (defined &$gradesub)) {
$value = &$gradesub($value);
}
- $prevattempts.=''.$value.' | ';
+ $prevattempts.=''.$value.' | ';
}
}
$prevattempts.= &end_data_table_row().&end_data_table();
@@ -3921,11 +3929,13 @@ sub get_previous_attempt {
sub format_previous_attempt_value {
my ($key,$value) = @_;
if (($key =~ /timestamp/) || ($key=~/duedate/)) {
- $value = &Apache::lonlocal::locallocaltime($value);
+ $value = &Apache::lonlocal::locallocaltime($value);
} elsif (ref($value) eq 'ARRAY') {
- $value = '('.join(', ', @{ $value }).')';
+ $value = &HTML::Entities::encode('('.join(', ', @{ $value }).')','"<>&');
} elsif ($key =~ /answerstring$/) {
my %answers = &Apache::lonnet::str2hash($value);
+ my @answer = %answers;
+ %answers = map {&HTML::Entities::encode($_, '"<>&')} @answer;
my @anskeys = sort(keys(%answers));
if (@anskeys == 1) {
my $answer = $answers{$anskeys[0]};
@@ -3948,7 +3958,7 @@ sub format_previous_attempt_value {
}
}
} else {
- $value = &unescape($value);
+ $value = &HTML::Entities::encode(&unescape($value), '"<>&');
}
return $value;
}
@@ -5222,6 +5232,10 @@ 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($httphost);
@@ -7330,7 +7344,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'}) {
@@ -7356,8 +7374,12 @@ sub font_settings {
my $headerstring='';
if ((!$env{'browser.mathml'} && $env{'browser.unicode'}) ||
((ref($args) eq 'HASH') && ($args->{'browser.unicode'}))) {
- $headerstring.=
- ''."\n";
+ $headerstring.=
+ '{'frameset'}) {
+ $headerstring.= ' /';
+ }
+ $headerstring .= '>'."\n";
}
return $headerstring;
}
@@ -7449,6 +7471,7 @@ Inputs: none
=cut
sub xml_begin {
+ my ($is_frameset) = @_;
my $output='';
if ($env{'browser.mathml'}) {
@@ -7460,9 +7483,12 @@ sub xml_begin {
.''
.'';
+ } elsif ($is_frameset) {
+ $output=''."\n".
+ ''."\n";
} else {
- $output=''."\n"
- .''."\n";
+ $output=''."\n".
+ ''."\n";
}
return $output;
}
@@ -7529,7 +7555,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'}) ) {
@@ -7630,9 +7656,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');
}
@@ -8693,7 +8721,7 @@ 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:
@@ -8767,7 +8795,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 '') {
@@ -8908,13 +8937,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.
@@ -8924,9 +8954,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);
@@ -11005,16 +11035,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;
@@ -11026,7 +11083,7 @@ sub decompress_form {
function camtasiaToggle() {
for (var i=0; i'.
''.$lt{'proa'}.'
'.
@@ -11322,6 +11379,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'} = [];
@@ -11340,12 +11398,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';
@@ -13851,7 +13912,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') {
@@ -13948,8 +14009,12 @@ sub construct_course {
'plc.users.denied',
'hidefromcat',
'checkforpriv',
- 'categories'],
+ 'categories',
+ 'internal.uniquecode'],
$$crsudom,$$crsunum);
+ if ($args->{'textbook'}) {
+ $cenv{'internal.textbook'} = $args->{'textbook'};
+ }
}
#
@@ -14133,6 +14198,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';
}
@@ -14201,6 +14285,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;
+}
+
############################################################
############################################################
@@ -14228,11 +14366,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);
}
@@ -14435,7 +14574,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',
@@ -14755,15 +14894,15 @@ sub captcha_display {
if ($captcha eq 'original') {
$output = &create_captcha();
unless ($output) {
- $error = 'captcha';
+ $error = 'captcha';
}
} elsif ($captcha eq 'recaptcha') {
$output = &create_recaptcha($pubkey);
unless ($output) {
- $error = 'recaptcha';
+ $error = 'recaptcha';
}
}
- return ($output,$error);
+ return ($output,$error,$captcha);
}
sub captcha_response {
@@ -14839,8 +14978,9 @@ sub create_captcha {
if (-e $Apache::lonnet::perlvar{'lonCaptchaDir'}.'/'.$md5sum.'.png') {
$output = ''."\n".
&mt('Type in the letters/numbers shown below').' '.
- '
'.
- '
';
+ ''.
+ '
'.
+ '
';
last;
}
}
@@ -14910,6 +15050,19 @@ sub check_recaptcha {
return $captcha_chk;
}
+sub emailusername_info {
+ my @fields = ('lastname','firstname','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;
@@ -14932,6 +15085,48 @@ sub cleanup_html {
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