--- loncom/interface/domainprefs.pm 2017/08/15 21:01:06 1.307 +++ loncom/interface/domainprefs.pm 2018/01/01 01:29:38 1.324 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Handler to set domain-wide configuration settings # -# $Id: domainprefs.pm,v 1.307 2017/08/15 21:01:06 raeburn Exp $ +# $Id: domainprefs.pm,v 1.324 2018/01/01 01:29:38 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -219,13 +219,14 @@ sub handler { 'serverstatuses','requestcourses','helpsettings', 'coursedefaults','usersessions','loadbalancing', 'requestauthor','selfenrollment','inststatus', - 'ltitools','ssl','trust'],$dom); + 'ltitools','ssl','trust','lti'],$dom); + my %encconfig = + &Apache::lonnet::get_dom('encconfig',['ltitools','lti'],$dom); if (ref($domconfig{'ltitools'}) eq 'HASH') { - my %encconfig = - &Apache::lonnet::get_dom('encconfig',['ltitools'],$dom); if (ref($encconfig{'ltitools'}) eq 'HASH') { foreach my $id (keys(%{$domconfig{'ltitools'}})) { - if (ref($domconfig{'ltitools'}{$id}) eq 'HASH') { + if ((ref($domconfig{'ltitools'}{$id}) eq 'HASH') && + (ref($encconfig{'ltitools'}{$id}) eq 'HASH')) { foreach my $item ('key','secret') { $domconfig{'ltitools'}{$id}{$item} = $encconfig{'ltitools'}{$id}{$item}; } @@ -233,12 +234,24 @@ sub handler { } } } + if (ref($domconfig{'lti'}) eq 'HASH') { + if (ref($encconfig{'lti'}) eq 'HASH') { + foreach my $id (keys(%{$domconfig{'lti'}})) { + if ((ref($domconfig{'lti'}{$id}) eq 'HASH') && + (ref($encconfig{'lti'}{$id}) eq 'HASH')) { + foreach my $item ('key','secret') { + $domconfig{'lti'}{$id}{$item} = $encconfig{'lti'}{$id}{$item}; + } + } + } + } + } my @prefs_order = ('rolecolors','login','defaults','quotas','autoenroll', 'autoupdate','autocreate','directorysrch','contacts', 'usercreation','selfcreation','usermodification','scantron', 'requestcourses','requestauthor','coursecategories', 'serverstatuses','helpsettings','coursedefaults', - 'ltitools','selfenrollment','usersessions','ssl','trust'); + 'ltitools','selfenrollment','usersessions','ssl','trust','lti'); my %existing; if (ref($domconfig{'loadbalancing'}) eq 'HASH') { %existing = %{$domconfig{'loadbalancing'}}; @@ -545,6 +558,14 @@ sub handler { print => \&print_trust, modify => \&modify_trust, }, + 'lti' => + {text => 'LTI Provider', + help => 'Domain_Configuration_LTI_Provider', + header => [{col1 => 'Setting', + col2 => 'Value',}], + print => \&print_lti, + modify => \&modify_lti, + }, ); if (keys(%servers) > 1) { $prefs{'login'} = { text => 'Log-in page options', @@ -730,6 +751,8 @@ sub process_changes { $output = &modify_ssl($dom,$lastactref,%domconfig); } elsif ($action eq 'trust') { $output = &modify_trust($dom,$lastactref,%domconfig); + } elsif ($action eq 'lti') { + $output = &modify_lti($r,$dom,$action,$lastactref,%domconfig); } return $output; } @@ -1065,7 +1088,7 @@ sub print_config_box { $output .= &print_quotas($dom,$settings,\$rowtotal,$action); } elsif (($action eq 'autoenroll') || ($action eq 'autocreate') || ($action eq 'serverstatuses') || ($action eq 'loadbalancing') || - ($action eq 'ltitools')) { + ($action eq 'ltitools') || ($action eq 'lti')) { $output .= $item->{'print'}->($dom,$settings,\$rowtotal); } elsif ($action eq 'scantron') { $output .= &print_scantronformat($r,$dom,$confname,$settings,\$rowtotal); @@ -1656,7 +1679,8 @@ sub display_color_options { if ($fullwidth ne '' && $fullheight ne '') { if ($fullwidth > $width && $fullheight > $height) { my $size = $width.'x'.$height; - system("convert -sample $size $input $output"); + my @args = ('convert','-sample',$size,$input,$output); + system({$args[0]} @args); $showfile = "/$imgdir/tn-".$filename; } } @@ -2585,7 +2609,10 @@ ENDSCRIPT sub ltitools_javascript { my ($settings) = @_; - return unless(ref($settings) eq 'HASH'); + my $togglejs = <itools_toggle_js(); + unless (ref($settings) eq 'HASH') { + return $togglejs; + } my (%ordered,$total,%jstext); $total = 0; foreach my $item (keys(%{$settings})) { @@ -2603,7 +2630,7 @@ sub ltitools_javascript { return <<"ENDSCRIPT"; +$togglejs + +ENDSCRIPT +} + +sub ltitools_toggle_js { + return <<"ENDSCRIPT"; + + +ENDSCRIPT +} + +sub lti_javascript { + my ($settings) = @_; + my $togglejs = <i_toggle_js(); + unless (ref($settings) eq 'HASH') { + return $togglejs; + } + my (%ordered,$total,%jstext); + $total = 0; + foreach my $item (keys(%{$settings})) { + if (ref($settings->{$item}) eq 'HASH') { + my $num = $settings->{$item}{'order'}; + $ordered{$num} = $item; + } + } + $total = scalar(keys(%{$settings})); + my @jsarray = (); + foreach my $item (sort {$a <=> $b } (keys(%ordered))) { + push(@jsarray,$ordered{$item}); + } + my $jstext = ' var lti = Array('."'".join("','",@jsarray)."'".');'."\n"; + return <<"ENDSCRIPT"; + + +$togglejs + +ENDSCRIPT +} + +sub lti_toggle_js { + return <<"ENDSCRIPT"; + + ENDSCRIPT } @@ -3124,7 +3365,54 @@ sub print_contacts { $to{$item}.'" />'; $rownum ++; } - } else { + } elsif ($position eq 'bottom') { + $css_class = $rownum%2?' class="LC_odd_row"':''; + $datatable .= ''. + ''.&mt('Extra helpdesk form fields:').'
'. + &mt('(e-mail, subject, and description always shown)'). + ''; + if ((ref($fields) eq 'ARRAY') && (ref($fieldtitles) eq 'HASH') && + (ref($fieldoptions) eq 'HASH') && (ref($possoptions) eq 'HASH')) { + $datatable .= ''; + foreach my $field (@{$fields}) { + $datatable .= ''. + ''; + } + $datatable .= '
'.&mt('Field').''.&mt('Status').'
'.$fieldtitles->{$field}; + if (($field eq 'screenshot') || ($field eq 'cc')) { + $datatable .= ' '.&mt('(logged-in users)'); + } + $datatable .=''; + my $clickaction; + if ($field eq 'screenshot') { + $clickaction = ' onclick="screenshotSize(this);"'; + } + if (ref($possoptions->{$field}) eq 'ARRAY') { + foreach my $option (@{$possoptions->{$field}}) { + my $checked; + if ($currfield{$field} eq $option) { + $checked = ' checked="checked"'; + } + $datatable .= ''.(' 'x2); + } + } + if ($field eq 'screenshot') { + my $display; + if ($currfield{$field} eq 'no') { + $display = ' style="display:none"'; + } + $datatable .= '
'.&mt('Maximum size for upload (MB)').''. + ''; + } + $datatable .= '
'; + } + $datatable .= ''."\n"; + $rownum ++; + } + unless ($position eq 'top') { foreach my $type (@mailings) { $css_class = $rownum%2?' class="LC_odd_row"':''; $datatable .= ''. @@ -3184,56 +3472,138 @@ sub print_contacts { \%choices,$rownum); $datatable .= $reports; } elsif ($position eq 'bottom') { - $css_class = $rownum%2?' class="LC_odd_row"':''; - $datatable .= ''. - ''.&mt('Extra helpdesk form fields:').'
'. - &mt('(e-mail, subject, and description always shown)'). - ''; - if ((ref($fields) eq 'ARRAY') && (ref($fieldtitles) eq 'HASH') && - (ref($fieldoptions) eq 'HASH') && (ref($possoptions) eq 'HASH')) { - $datatable .= ''; - foreach my $field (@{$fields}) { - $datatable .= ''. - ''; } - $datatable .= '
'.&mt('Field').''.&mt('Status').'
'.$fieldtitles->{$field}; - if (($field eq 'screenshot') || ($field eq 'cc')) { - $datatable .= ' '.&mt('(logged-in users)'); - } - $datatable .=''; - my $clickaction; - if ($field eq 'screenshot') { - $clickaction = ' onclick="screenshotSize(this);"'; - } - if (ref($possoptions->{$field}) eq 'ARRAY') { - foreach my $option (@{$possoptions->{$field}}) { - my $checked; - if ($currfield{$field} eq $option) { - $checked = ' checked="checked"'; + my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom); + my (@posstypes,%usertypeshash); + if (ref($types) eq 'ARRAY') { + @posstypes = @{$types}; + } + if (@posstypes) { + if (ref($usertypes) eq 'HASH') { + %usertypeshash = %{$usertypes}; + } + my @overridden; + my $numinrow = 4; + if (ref($settings) eq 'HASH') { + if (ref($settings->{'overrides'}) eq 'HASH') { + foreach my $key (sort(keys(%{$settings->{'overrides'}}))) { + if (ref($settings->{'overrides'}{$key}) eq 'HASH') { + push(@overridden,$key); + foreach my $item (@contacts) { + if ($settings->{'overrides'}{$key}{$item}) { + $checked{'override_'.$key}{$item} = ' checked="checked" '; + } + } + $otheremails{'override_'.$key} = $settings->{'overrides'}{$key}{'others'}; + $bccemails{'override_'.$key} = $settings->{'overrides'}{$key}{'bcc'}; + $includeloc{'override_'.$key} = ''; + $includestr{'override_'.$key} = ''; + if ($settings->{'overrides'}{$key}{'include'} ne '') { + ($includeloc{'override_'.$key},$includestr{'override_'.$key}) = + split(/:/,$settings->{'overrides'}{$key}{'include'},2); + $includestr{'override_'.$key} = &unescape($includestr{'override_'.$key}); + } } - $datatable .= ''.(' 'x2); } } - if ($field eq 'screenshot') { - my $display; - if ($currfield{$field} eq 'no') { - $display = ' style="display:none"'; - } - $datatable .= '
'.&mt('Maximum size for upload (MB)').''. - ''; + } + my $customclass = 'LC_helpdesk_override'; + my $optionsprefix = 'LC_options_helpdesk_'; + + my $onclicktypes = "toggleHelpdeskRow(this.form,'overrides','$customclass','$optionsprefix');"; + + $datatable .= &insttypes_row($settings,$types,$usertypes,$dom, + $numinrow,$othertitle,'overrides', + \$rownum,$onclicktypes,$customclass); + $rownum ++; + $usertypeshash{'default'} = $othertitle; + foreach my $status (@posstypes) { + my $css_class; + if ($rownum%2) { + $css_class = 'LC_odd_row '; + } + $css_class .= $customclass; + my $rowid = $optionsprefix.$status; + my $hidden = 1; + my $currstyle = 'display:none'; + if (grep(/^\Q$status\E$/,@overridden)) { + $currstyle = 'display:table-row'; + $hidden = 0; + } + my $key = 'override_'.$status; + $datatable .= &overridden_helpdesk($checked{$key},$otheremails{$key},$bccemails{$key}, + $includeloc{$key},$includestr{$key},$status,$rowid, + $usertypeshash{$status},$css_class,$currstyle, + \@contacts,$short_titles); + unless ($hidden) { + $rownum ++; } - $datatable .= '
'; } - $datatable .= ''."\n"; - $rownum ++; } $$rowtotal += $rownum; return $datatable; } +sub overridden_helpdesk { + my ($checked,$otheremails,$bccemails,$includeloc,$includestr,$type,$rowid, + $typetitle,$css_class,$rowstyle,$contacts,$short_titles) = @_; + my $class = 'LC_left_item'; + if ($css_class) { + $css_class = ' class="'.$css_class.'"'; + } + if ($rowid) { + $rowid = ' id="'.$rowid.'"'; + } + if ($rowstyle) { + $rowstyle = ' style="'.$rowstyle.'"'; + } + my ($output,$description); + $description = &mt('Helpdesk requests from: [_1] in this domain (overrides default)',"$typetitle"); + $output = ''. + "$description\n". + ''. + '
'.&mt('E-mail recipient(s)').''. + ''; + if (ref($contacts) eq 'ARRAY') { + foreach my $item (@{$contacts}) { + my $check; + if (ref($checked) eq 'HASH') { + $check = $checked->{$item}; + } + my $title; + if (ref($short_titles) eq 'HASH') { + $title = $short_titles->{$item}; + } + $output .= ' '; + } + } + $output .= '
'.&mt('Others').':  '. + ''; + my %locchecked; + foreach my $loc ('s','b') { + if ($includeloc eq $loc) { + $locchecked{$loc} = ' checked="checked"'; + last; + } + } + $output .= '
'.&mt('Bcc:').(' 'x6). + '
'. + '
'.&mt('Optional added text').''. + &mt('Text automatically added to e-mail:').' '. + '
'. + ''.&mt('Location:').' '. + ''. + (' 'x2). + ''. + '
'. + ''."\n"; + return $output; +} + sub contacts_javascript { return <<"ENDSCRIPT"; @@ -3251,6 +3621,37 @@ function screenshotSize(field) { return; } +function toggleHelpdeskRow(form,checkbox,target,prefix,docount) { + if (form.elements[checkbox].length != undefined) { + var count = 0; + if (docount) { + for (var i=0; i @@ -3718,24 +4119,30 @@ sub print_ltitools { my %lt = <itools_names(); my @courseroles = ('cc','in','ta','ep','st'); my @ltiroles = qw(Instructor ContentDeveloper TeachingAssistant Learner); - my @fields = ('fullname','firstname','lastname','email','user','roles'); + my @fields = ('fullname','firstname','lastname','email','roles','user'); if (keys(%ordered)) { my @items = sort { $a <=> $b } keys(%ordered); for (my $i=0; $i<@items; $i++) { $css_class = $itemcount%2?' class="LC_odd_row"':''; my $item = $ordered{$items[$i]}; - my ($title,$key,$secret,$url,$imgsrc,$version); + my ($title,$key,$secret,$url,$lifetime,$imgsrc,%sigsel); if (ref($settings->{$item}) eq 'HASH') { $title = $settings->{$item}->{'title'}; $url = $settings->{$item}->{'url'}; $key = $settings->{$item}->{'key'}; $secret = $settings->{$item}->{'secret'}; + $lifetime = $settings->{$item}->{'lifetime'}; my $image = $settings->{$item}->{'image'}; if ($image ne '') { $imgsrc = ''.&mt('Tool Provider icon').''; } + if ($settings->{$item}->{'sigmethod'} eq 'HMAC-256') { + $sigsel{'HMAC-256'} = ' selected="selected"'; + } else { + $sigsel{'HMAC-SHA1'} = ' selected="selected"'; + } } - my $chgstr = ' onchange="javascript:reorderLTI(this.form,'."'ltitools_".$item."'".');"'; + my $chgstr = ' onchange="javascript:reorderLTITools(this.form,'."'ltitools_".$item."'".');"'; $datatable .= '' .' '. + ''.$lt{'title'}.': '. (' 'x2). ''.$lt{'version'}.': '. (' 'x2). ''.$lt{'msgtype'}.': '. + (' 'x2). + ''.$lt{'sigmethod'}.':'. '

'. - ''.$lt{'url'}.':'.$lt{'url'}.':'. (' 'x2). - ''.$lt{'key'}. + ''.$lt{'key'}.':'. ' '. (' 'x2). + ''.$lt{'lifetime'}.':'. + ' '. + (' 'x2). ''.$lt{'secret'}.':'. ''. ''. @@ -3808,21 +4222,36 @@ sub print_ltitools { '
'.$lt{'explanation'}.'
'. '

'; - $datatable .= '
'; + my %units = ( + 'passback' => 'days', + 'roster' => 'seconds', + ); foreach my $extra ('passback','roster') { + my $validsty = 'none'; + my $currvalid; my $checkedon = ''; my $checkedoff = ' checked="checked"'; if ($settings->{$item}->{$extra}) { $checkedon = $checkedoff; $checkedoff = ''; - } - $datatable .= $lt{$extra}.' '. - ''.(' 'x2). - ''.(' 'x4); + $validsty = 'inline-block'; + if ($settings->{$item}->{$extra.'valid'} =~ /^\d+\.?\d*$/) { + $currvalid = $settings->{$item}->{$extra.'valid'}; + } + } + my $onclick = ' onclick="toggleLTITools(this.form,'."'$extra','$i'".');"'; + $datatable .= '
'.$lt{$extra}.' '. + ''.(' 'x2). + '
'. + '
'. + ''. + &mt("at least [_1] $units{$extra} after launch", + ''). + '
'; } - $datatable .= '

'.$lt{'icon'}.': '; + $datatable .= ''.$lt{'icon'}.': '; if ($imgsrc) { $datatable .= $imgsrc. ''; - my (%checkedfields,%rolemaps); + my (%checkedfields,%rolemaps,$userincdom); if (ref($settings->{$item}) eq 'HASH') { if (ref($settings->{$item}->{'fields'}) eq 'HASH') { %checkedfields = %{$settings->{$item}->{'fields'}}; } + $userincdom = $settings->{$item}->{'incdom'}; if (ref($settings->{$item}->{'roles'}) eq 'HASH') { %rolemaps = %{$settings->{$item}->{'roles'}}; $checkedfields{'roles'} = 1; @@ -3849,16 +4279,40 @@ sub print_ltitools { } $datatable .= '
'.&mt('User data sent on launch').''. ''; + my $userfieldstyle = 'display:none;'; + my $seluserdom = ''; + my $unseluserdom = ' selected="selected"'; foreach my $field (@fields) { - my $checked; + my ($checked,$onclick,$id,$spacer); if ($checkedfields{$field}) { $checked = ' checked="checked"'; } + if ($field eq 'user') { + $id = ' id="ltitools_user_field_'.$i.'"'; + $onclick = ' onclick="toggleLTITools(this.form,'."'$field','$i'".')"'; + if ($checked) { + $userfieldstyle = 'display:inline-block'; + if ($userincdom) { + $seluserdom = $unseluserdom; + $unseluserdom = ''; + } + } + } else { + $spacer = (' ' x2); + } $datatable .= ''.(' ' x2); + ''. + $lt{$field}.''.$spacer; } - $datatable .= '
'. + $datatable .= '
'; + $datatable .= '
'. + ' : '. + '
'; + $datatable .= ''. '
'.&mt('Role mapping').''; foreach my $role (@courseroles) { my ($selected,$selectnone); @@ -3889,7 +4343,7 @@ sub print_ltitools { } } $datatable .= '
'.&mt('Configurable in course').''; - foreach my $item ('label','title','target','linktext','explanation') { + foreach my $item ('label','title','target','linktext','explanation','append') { my $checked; if ($courseconfig{$item}) { $checked = ' checked="checked"'; @@ -3922,7 +4376,7 @@ sub print_ltitools { } } $css_class = $itemcount%2?' class="LC_odd_row"':''; - my $chgstr = ' onchange="javascript:reorderLTI(this.form,'."'ltitools_add_pos'".');"'; + my $chgstr = ' onchange="javascript:reorderLTITools(this.form,'."'ltitools_add_pos'".');"'; $datatable .= '
'."\n". ''. + ''; + $itemcount ++; + } + } + $css_class = $itemcount%2?' class="LC_odd_row"':''; + my $chgstr = ' onchange="javascript:reorderLTI(this.form,'."'lti_pos_add'".');"'; + $datatable .= ''."\n". + ''."\n". + ''."\n"; + $$rowtotal ++; + return $datatable;; +} + +sub lti_names { + my %lt = &Apache::lonlocal::texthash( + 'version' => 'LTI Version', + 'url' => 'URL', + 'key' => 'Key', + 'lifetime' => 'Nonce lifetime (s)', + 'consumer' => 'LTI Consumer', + 'secret' => 'Secret', + 'email' => 'Email address', + 'sourcedid' => 'User ID', + 'other' => 'Other', + 'passback' => 'Can return grades to Consumer:', + 'roster' => 'Can retrieve roster from Consumer:', ); return %lt; } +sub lti_options { + my ($num,$current,%lt) = @_; + my (%checked,%rolemaps,$crssecsrc,$userfield,$cidfield); + $checked{'mapuser'}{'sourcedid'} = ' checked="checked"'; + $checked{'mapcrs'}{'course_offering_sourcedid'} = ' checked="checked"'; + $checked{'makecrs'}{'N'} = ' checked="checked"'; + $checked{'mapcrstype'} = {}; + $checked{'makeuser'} = {}; + $checked{'selfenroll'} = {}; + $checked{'crssec'} = {}; + $checked{'crssecsrc'} = {}; + + my $userfieldsty = 'none'; + my $crsfieldsty = 'none'; + my $crssecfieldsty = 'none'; + my $secsrcfieldsty = 'none'; + + if (ref($current) eq 'HASH') { + if (($current->{'mapuser'} ne '') && ($current->{'mapuser'} ne 'lis_person_sourcedid')) { + $checked{'mapuser'}{'sourcedid'} = ''; + if ($current->{'mapuser'} eq 'lis_person_contact_email_primary') { + $checked{'mapuser'}{'email'} = ' checked="checked"'; + } else { + $checked{'mapuser'}{'other'} = ' checked="checked"'; + $userfield = $current->{'mapuser'}; + $userfieldsty = 'inline-block'; + } + } + if (($current->{'mapcrs'} ne '') && ($current->{'mapcrs'} ne 'course_offering_sourcedid')) { + $checked{'mapcrs'}{'course_offering_sourcedid'} = ''; + if ($current->{'mapcrs'} eq 'context_id') { + $checked{'mapcrs'}{'context_id'} = ' checked="checked"'; + } else { + $checked{'mapcrs'}{'other'} = ' checked="checked"'; + $cidfield = $current->{'mapcrs'}; + $crsfieldsty = 'inline-block'; + } + } + if (ref($current->{'mapcrstype'}) eq 'ARRAY') { + foreach my $type (@{$current->{'mapcrstype'}}) { + $checked{'mapcrstype'}{$type} = ' checked="checked"'; + } + } + if ($current->{'makecrs'}) { + $checked{'makecrs'}{'Y'} = ' checked="checked"'; + } + if (ref($current->{'makeuser'}) eq 'ARRAY') { + foreach my $role (@{$current->{'makeuser'}}) { + $checked{'makeuser'}{$role} = ' checked="checked"'; + } + } + if (ref($current->{'selfenroll'}) eq 'ARRAY') { + foreach my $role (@{$current->{'selfenroll'}}) { + $checked{'selfenroll'}{$role} = ' checked="checked"'; + } + } + if (ref($current->{'maproles'}) eq 'HASH') { + %rolemaps = %{$current->{'maproles'}}; + } + if ($current->{'section'} ne '') { + $checked{'crssec'}{'Y'} = ' checked="checked"'; + $crssecfieldsty = 'inline-block'; + if ($current->{'section'} eq 'course_section_sourcedid') { + $checked{'crssecsrc'}{'sourcedid'} = ' checked="checked"'; + } else { + $checked{'crssecsrc'}{'other'} = ' checked="checked"'; + $crssecsrc = $current->{'section'}; + $secsrcfieldsty = 'inline-block'; + } + } else { + $checked{'crssec'}{'N'} = ' checked="checked"'; + } + } else { + $checked{'makecrs'}{'N'} = ' checked="checked"'; + $checked{'crssec'}{'N'} = ' checked="checked"'; + } + my @coursetypes = ('official','unofficial','community','textbook','placement'); + my %coursetypetitles = &Apache::lonlocal::texthash ( + official => 'Official', + unofficial => 'Unofficial', + community => 'Community', + textbook => 'Textbook', + placement => 'Placement Test', + ); + my @ltiroles = qw(Learner Instructor ContentDeveloper TeachingAssistant Mentor Member Manager Administrator); + my @lticourseroles = qw(Learner Instructor TeachingAssistant Mentor); + my @courseroles = ('cc','in','ta','ep','st'); + my $onclickuser = ' onclick="toggleLTI(this.form,'."'user','$num'".');"'; + my $onclickcrs = ' onclick="toggleLTI(this.form,'."'crs','$num'".');"'; + my $onclicksec = ' onclick="toggleLTI(this.form,'."'sec','$num'".');"'; + my $onclicksecsrc = ' onclick="toggleLTI(this.form,'."'secsrc','$num'".')"'; + my $output = '
'.&mt('Mapping users').''. + '
'.&mt('LON-CAPA username').': '; + foreach my $option ('sourcedid','email','other') { + $output .= ''. + ($option eq 'other' ? '' : (' 'x2) ); + } + $output .= '
'. + '
'. + '
'. + '
'.&mt('Mapping course roles').'
'."\n". ''."\n". ''.&mt('Add').''. '
'.&mt('Required settings').''. - ''.$lt{'title'}.': '."\n". + ''.$lt{'title'}.': '."\n". (' 'x2). ''.$lt{'version'}.': '."\n". (' 'x2). ''.$lt{'msgtype'}.': '. + ''.$lt{'sigmethod'}.':'. '
'. - ''.$lt{'url'}.': '."\n". + ''.$lt{'url'}.': '."\n". (' 'x2). ''.$lt{'key'}.': '."\n". (' 'x2). + ''.$lt{'lifetime'}.': '."\n". + (' 'x2). ''.$lt{'secret'}.':'. ' '."\n". '
'. @@ -3973,14 +4432,28 @@ sub print_ltitools { '
'.$lt{'explanation'}.'
'. ''. '

'; + my %units = ( + 'passback' => 'days', + 'roster' => 'seconds', + ); + my %defaulttimes = ( + 'passback' => '7', + 'roster' => '300', + ); foreach my $extra ('passback','roster') { - $datatable .= $lt{$extra}.' '. - ''.(' 'x2). - ''.(' 'x4); + my $onclick = ' onclick="toggleLTITools(this.form,'."'$extra','add'".');"'; + $datatable .= '
'.$lt{$extra}.' '. + ''.(' 'x2).''. + '
'. + '
'; } - $datatable .= '

'.$lt{'icon'}.': '. + $datatable .= ''.$lt{'icon'}.': '. '('.&mt('if larger than 21x21 pixels, image will be scaled').') '; if ($switchserver) { $datatable .= &mt('Upload to library server: [_1]',$switchserver); @@ -3991,12 +4464,26 @@ sub print_ltitools { '
'.&mt('User data sent on launch').''. ''; foreach my $field (@fields) { + my ($id,$onclick,$spacer); + if ($field eq 'user') { + $id = ' id="ltitools_user_field_add"'; + $onclick = ' onclick="toggleLTITools(this.form,'."'$field','add'".')"'; + } else { + $spacer = (' ' x2); + } $datatable .= ''.(' ' x2); + ''. + $lt{$field}.''.$spacer; } - $datatable .= '
'. - '
'.&mt('Role mapping').''; + $datatable .= ''. + ''; + $datatable .= '
'.&mt('Role mapping').'
'; foreach my $role (@courseroles) { my ($checked,$checkednone); $datatable .= '
'. @@ -4010,7 +4497,7 @@ sub print_ltitools { } $datatable .= '
'. '
'.&mt('Configurable in course').''; - foreach my $item ('label','title','target','linktext','explanation') { + foreach my $item ('label','title','target','linktext','explanation','append') { $datatable .= ''.(' ' x2)."\n"; @@ -4034,11 +4521,13 @@ sub ltitools_names { 'title' => 'Title', 'version' => 'Version', 'msgtype' => 'Message Type', + 'sigmethod' => 'Signature Method', 'url' => 'URL', 'key' => 'Key', + 'lifetime' => 'Nonce lifetime (s)', 'secret' => 'Secret', 'icon' => 'Icon', - 'user' => 'Username:domain', + 'user' => 'User', 'fullname' => 'Full Name', 'firstname' => 'First Name', 'lastname' => 'Last Name', @@ -4058,10 +4547,332 @@ sub ltitools_names { 'crstitle' => 'Course title', 'crslinktext' => 'Link Text', 'crsexplanation' => 'Explanation', + 'crsappend' => 'Provider URL', + ); + return %lt; +} + +sub print_lti { + my ($dom,$settings,$rowtotal) = @_; + my $itemcount = 1; + my $maxnum = 0; + my $css_class; + my %ordered; + if (ref($settings) eq 'HASH') { + foreach my $item (keys(%{$settings})) { + if (ref($settings->{$item}) eq 'HASH') { + my $num = $settings->{$item}{'order'}; + $ordered{$num} = $item; + } + } + } + my $maxnum = scalar(keys(%ordered)); + my $datatable = <i_javascript($settings); + my %lt = <i_names(); + if (keys(%ordered)) { + my @items = sort { $a <=> $b } keys(%ordered); + for (my $i=0; $i<@items; $i++) { + $css_class = $itemcount%2?' class="LC_odd_row"':''; + my $item = $ordered{$items[$i]}; + my ($key,$secret,$lifetime,$consumer,$current); + if (ref($settings->{$item}) eq 'HASH') { + $key = $settings->{$item}->{'key'}; + $secret = $settings->{$item}->{'secret'}; + $lifetime = $settings->{$item}->{'lifetime'}; + $consumer = $settings->{$item}->{'consumer'}; + $current = $settings->{$item}; + } + my $chgstr = ' onchange="javascript:reorderLTI(this.form,'."'lti_pos_".$item."'".');"'; + $datatable .= '
' + .''.(' 'x2). + ''. + '
'.&mt('Required settings').''. + ''.$lt{'consumer'}. + ': '. + (' 'x2). + ''.$lt{'version'}.': '. + (' 'x2). + ''.$lt{'lifetime'}.':'. + '

'. + ''.$lt{'key'}. + ': '. + (' 'x2). + ''.$lt{'secret'}.':'. + ''. + ''. + ''. + '
'.<i_options($i,$current,%lt).'
'."\n". + ''."\n". + ' '."\n". + ''.&mt('Add').''. + '
'.&mt('Required settings').''. + ''.$lt{'consumer'}. + ': '."\n". + (' 'x2). + ''.$lt{'version'}.': '."\n". + (' 'x2). + ''.$lt{'lifetime'}.': '."\n". + '

'. + ''.$lt{'key'}.': '."\n". + (' 'x2). + ''.$lt{'secret'}.':'. + ' '."\n". + '
'.<i_options('add',undef,%lt). + '
'; + foreach my $ltirole (@lticourseroles) { + my ($selected,$selectnone); + if ($rolemaps{$ltirole} eq '') { + $selectnone = ' selected="selected"'; + } + $output .= ''; + } + $output .= '
'.$ltirole.'
'. + '
'. + '
'.&mt('Roles which may create user accounts').''; + foreach my $ltirole (@ltiroles) { + $output .= '  '; + } + $output .= '
'. + '
'.&mt('Mapping courses').''. + '
'. + &mt('Unique course identifier').': '; + foreach my $option ('course_offering_sourcedid','context_id','other') { + $output .= ''. + ($option eq 'other' ? '' : (' 'x2) ); + } + $output .= '
'. + ''. + '
'. + ''.&mt('LON-CAPA course type(s)').': '; + foreach my $type (@coursetypes) { + $output .= ''. + (' 'x2); + } + $output .= '
'. + '
'.&mt('Creating courses').''. + ''.&mt('Course created (if absent) on Instructor access').': '. + ''.(' 'x2). + ''. + '
'. + '
'.&mt('Roles which may self-enroll').''; + foreach my $lticrsrole (@lticourseroles) { + $output .= '  '; + } + $output .= '
'. + '
'.&mt('Course options').''. + '
'.&mt('Assign users to sections').': '. + ''.(' 'x2). + '
'. + '
'. + ''.&mt('From').':'.(' 'x2). + '
'. + ''. + '
'; + foreach my $extra ('passback','roster') { + my $checkedon = ''; + my $checkedoff = ' checked="checked"'; + if (ref($current) eq 'HASH') { + if (($current->{$extra})) { + $checkedon = $checkedoff; + $checkedoff = ''; + } + } + $output .= $lt{$extra}.' '. + ''.(' 'x2). + '
'; + } + $output .= '
'; +# '
'.&mt('Assigning author roles').''; +# +# $output .= '
'. +# '
'.&mt('Assigning domain roles').''; + return $output; +} + sub print_coursedefaults { my ($position,$dom,$settings,$rowtotal) = @_; my ($css_class,$datatable,%checkedon,%checkedoff,%defaultchecked,@toggles); @@ -4073,11 +4884,13 @@ sub print_coursedefaults { coursecredits => 'Credits can be specified for courses', uselcmath => 'Math preview uses LON-CAPA previewer (javascript) in place of DragMath (Java)', usejsme => 'Molecule editor uses JSME (HTML5) in place of JME (Java)', + texengine => 'Default method to display mathematics', postsubmit => 'Disable submit button/keypress following student submission', canclone => "People who may clone a course (besides course's owner and coordinators)", mysqltables => 'Lifetime (s) of "Temporary" MySQL tables (student performance data) on homeserver', ); my %staticdefaults = ( + texengine => 'MathJax', anonsurvey_threshold => 10, uploadquota => 500, postsubmit => 60, @@ -4091,8 +4904,36 @@ sub print_coursedefaults { 'canclone' => 'none', ); @toggles = ('canuse_pdfforms','uselcmath','usejsme'); + my $deftex = $staticdefaults{'texengine'}; + if (ref($settings) eq 'HASH') { + if ($settings->{'texengine'}) { + if ($settings->{'texengine'} =~ /^(MathJax|mimetex|tth)$/) { + $deftex = $settings->{'texengine'}; + } + } + } + $css_class = $itemcount%2?' class="LC_odd_row"':''; + my $mathdisp = ''. + ''.$choices{'texengine'}. + ''. + ''."\n"; + $itemcount ++; ($datatable,$itemcount) = &radiobutton_prefs($settings,\@toggles,\%defaultchecked, \%choices,$itemcount); + $datatable = $mathdisp.$datatable; $css_class = $itemcount%2?' class="LC_odd_row"':''; $datatable .= ''. @@ -4139,7 +4980,7 @@ sub print_coursedefaults { if ($checked) { $show = 'block'; } - $additional = '
'. + $additional = '
'. &mt('Institutional codes for new and cloned course have identical:'). '
'; foreach my $item (@code_order) { @@ -5454,7 +6295,7 @@ sub loadbalance_rule_row { } my $space; if ($islast && $num == 1) { - $space = '
 
'; + $space = '
 
'; } my $output = ''.$space. @@ -5540,8 +6381,8 @@ sub contact_titles { 'adminemail' => 'Default Server Admin E-mail address', 'errormail' => 'Error reports to be e-mailed to', 'packagesmail' => 'Package update alerts to be e-mailed to', - 'helpdeskmail' => "Helpdesk requests for this domain's users", - 'otherdomsmail' => 'Helpdesk requests for other (unconfigured) domains', + 'helpdeskmail' => "Helpdesk requests from all users in this domain", + 'otherdomsmail' => 'Helpdesk requests from users in other (unconfigured) domains', 'lonstatusmail' => 'E-mail from nightly status check (warnings/errors)', 'requestsmail' => 'E-mail from course requests requiring approval', 'updatesmail' => 'E-mail from nightly check of LON-CAPA module integrity/updates', @@ -6402,7 +7243,7 @@ sub user_formats_row { } $output .= ''. ''; } @@ -7643,7 +8484,6 @@ sub modifiable_userdata_row { if ($rowid) { $rowid = ' id="'.$rowid.'"'; } - $output = ''. ''.$rolename.''. ''; @@ -7747,6 +8587,7 @@ sub insttypes_row { statustocreate => 'Institutional affiliation(s) able to create own account (login/SSO)', lockablenames => 'User preference to lock name', selfassign => 'Self-reportable affiliations', + overrides => "Override domain's helpdesk settings based on requester's affiliation", ); my $showdom; if ($context eq 'cansearch') { @@ -7793,6 +8634,10 @@ sub insttypes_row { if (grep(/^\Q$types->[$i]\E$/,@{$settings->{$context}})) { $check = ' checked="checked" '; } + } elsif (ref($settings->{$context}) eq 'HASH') { + if (ref($settings->{$context}->{$types->[$i]}) eq 'HASH') { + $check = ' checked="checked" '; + } } elsif ($context eq 'statustocreate') { $check = ' checked="checked" '; } @@ -7807,29 +8652,38 @@ sub insttypes_row { $rem = @{$types}%($numinrow); } my $colsleft = $numinrow - $rem; - if (($rem == 0) && (@{$types} > 0)) { - $output .= ''; - } - if ($colsleft > 1) { - $output .= ''; + } + if ($colsleft > 1) { + $output .= ''. - '
'; + if ($context eq 'overrides') { + if ($colsleft > 1) { + $output .= ''; + } else { + $output .= ''; + } + $output .= ' '; } else { - $output .= ''; - } - my $defcheck = ' '; - if (ref($settings) eq 'HASH') { - if (ref($settings->{$context}) eq 'ARRAY') { - if (grep(/^default$/,@{$settings->{$context}})) { + if (($rem == 0) && (@{$types} > 0)) { + $output .= '
'; + } else { + $output .= ''; + } + my $defcheck = ' '; + if (ref($settings) eq 'HASH') { + if (ref($settings->{$context}) eq 'ARRAY') { + if (grep(/^default$/,@{$settings->{$context}})) { + $defcheck = ' checked="checked" '; + } + } elsif ($context eq 'statustocreate') { $defcheck = ' checked="checked" '; } - } elsif ($context eq 'statustocreate') { - $defcheck = ' checked="checked" '; } + $output .= ''; } - $output .= '
'; + $output .= ''; return $output; } @@ -8863,14 +9717,14 @@ sub publishlogo { } else { my $source = $filepath.'/'.$file; my $logfile; - if (!open($logfile,">>$source".'.log')) { + if (!open($logfile,">>",$source.'.log')) { return (&mt('No write permission to Authoring Space')); } print $logfile "\n================= Publish ".localtime()." ================\n". $env{'user.name'}.':'.$env{'user.domain'}."\n"; # Save the file - if (!open(FH,'>'.$source)) { + if (!open(FH,">",$source)) { &Apache::lonnet::logthis('Failed to create '.$source); return (&mt('Failed to create file')); } @@ -8931,7 +9785,8 @@ $env{'user.name'}.':'.$env{'user.domain' if ($fullwidth ne '' && $fullheight ne '') { if ($fullwidth > $thumbwidth && $fullheight > $thumbheight) { my $thumbsize = $thumbwidth.'x'.$thumbheight; - system("convert -sample $thumbsize $inputfile $outfile"); + my @args = ('convert','-sample',$thumbsize,$inputfile,$outfile); + system({$args[0]} @args); chmod(0660, $filepath.'/tn-'.$file); if (-e $outfile) { my $copyfile=$targetdir.'/tn-'.$file; @@ -9011,7 +9866,7 @@ sub write_metadata { { print $logfile "\nWrite metadata file for ".$targetdir.'/'.$file; my $mfh; - if (open($mfh,'>'.$targetdir.'/'.$file.'.meta')) { + if (open($mfh,">",$targetdir.'/'.$file.'.meta')) { foreach (sort(keys(%metadatafields))) { unless ($_=~/\./) { my $unikey=$_; @@ -9045,7 +9900,7 @@ sub notifysubscribed { next unless (ref($targetsource) eq 'ARRAY'); my ($target,$source)=@{$targetsource}; if ($source ne '') { - if (open(my $logfh,'>>'.$source.'.log')) { + if (open(my $logfh,">>",$source.'.log')) { print $logfh "\nCleanup phase: Notifications\n"; my @subscribed=&subscribed_hosts($target); foreach my $subhost (@subscribed) { @@ -9071,7 +9926,7 @@ sub notifysubscribed { sub subscribed_hosts { my ($target) = @_; my @subscribed; - if (open(my $fh,"<$target.subscription")) { + if (open(my $fh,"<","$target.subscription")) { while (my $subline=<$fh>) { if ($subline =~ /^($match_lonid):/) { my $host = $1; @@ -9270,17 +10125,21 @@ sub modify_quotas { #FIXME need to obsolete item in RES space } elsif ($env{'form.'.$type.'_image_'.$i.'.filename'}) { my ($cdom,$cnum) = split(/_/,$key); - my ($imgurl,$error) = &process_textbook_image($r,$dom,$confname,$type.'_image_'.$i, - $cdom,$cnum,$type,$configuserok, - $switchserver,$author_ok); - if ($imgurl) { - $confhash{$type}{$key}{'image'} = $imgurl; - $changes{$type}{$key} = 1; + if (&Apache::lonnet::homeserver($cnum,$cdom) eq 'no_host') { + $errors .= '
  • '.&mt('Image not saved: could not find textbook course').'
  • '; + } else { + my ($imgurl,$error) = &process_textbook_image($r,$dom,$confname,$type.'_image_'.$i, + $cdom,$cnum,$type,$configuserok, + $switchserver,$author_ok); + if ($imgurl) { + $confhash{$type}{$key}{'image'} = $imgurl; + $changes{$type}{$key} = 1; + } + if ($error) { + &Apache::lonnet::logthis($error); + $errors .= '
  • '.$error.'
  • '; + } } - if ($error) { - &Apache::lonnet::logthis($error); - $errors .= '
  • '.$error.'
  • '; - } } elsif ($domconfig{$action}{$type}{$key}{'image'}) { $confhash{$type}{$key}{'image'} = $domconfig{$action}{$type}{$key}{'image'}; @@ -9313,15 +10172,19 @@ sub modify_quotas { if ($type eq 'textbooks') { if ($env{'form.'.$type.'_addbook_image.filename'} ne '') { my ($cdom,$cnum) = split(/_/,$newbook{$type}); - my ($imageurl,$error) = - &process_textbook_image($r,$dom,$confname,$type.'_addbook_image',$cdom,$cnum,$type, - $configuserok,$switchserver,$author_ok); - if ($imageurl) { - $confhash{$type}{$newbook{$type}}{'image'} = $imageurl; - } - if ($error) { - &Apache::lonnet::logthis($error); - $errors .= '
  • '.$error.'
  • '; + if (&Apache::lonnet::homeserver($cnum,$cdom) eq 'no_host') { + $errors .= '
  • '.&mt('Image not saved: could not find textbook course').'
  • '; + } else { + my ($imageurl,$error) = + &process_textbook_image($r,$dom,$confname,$type.'_addbook_image',$cdom,$cnum,$type, + $configuserok,$switchserver,$author_ok); + if ($imageurl) { + $confhash{$type}{$newbook{$type}}{'image'} = $imageurl; + } + if ($error) { + &Apache::lonnet::logthis($error); + $errors .= '
  • '.$error.'
  • '; + } } } } @@ -9813,7 +10676,7 @@ sub process_textbook_image { } elsif ($author_ok eq 'ok') { my ($result,$imageurl) = &publishlogo($r,'upload',$caller,$dom,$confname, - "$type/$dom/$cnum/cover",$width,$height); + "$type/$cdom/$cnum/cover",$width,$height); if ($result eq 'ok') { $url = $imageurl; } else { @@ -9853,8 +10716,11 @@ sub modify_ltitools { $allpos[$position] = $newid; } $changes{$newid} = 1; - foreach my $item ('title','url','key','secret') { + foreach my $item ('title','url','key','secret','lifetime') { $env{'form.ltitools_add_'.$item} =~ s/(`)/'/g; + if ($item eq 'lifetime') { + $env{'form.ltitools_add_'.$item} =~ s/[^\d.]//g; + } if ($env{'form.ltitools_add_'.$item}) { if (($item eq 'key') || ($item eq 'secret')) { $encconfig{$newid}{$item} = $env{'form.ltitools_add_'.$item}; @@ -9869,6 +10735,11 @@ sub modify_ltitools { if ($env{'form.ltitools_add_msgtype'} eq 'basic-lti-launch-request') { $confhash{$newid}{'msgtype'} = $env{'form.ltitools_add_msgtype'}; } + if ($env{'form.ltitools_add_sigmethod'} eq 'HMAC-SHA256') { + $confhash{$newid}{'sigmethod'} = $env{'form.ltitools_add_sigmethod'}; + } else { + $confhash{$newid}{'sigmethod'} = 'HMAC-SHA1'; + } foreach my $item ('width','height','linktext','explanation') { $env{'form.ltitools_add_'.$item} =~ s/^\s+//; $env{'form.ltitools_add_'.$item} =~ s/\s+$//; @@ -9890,8 +10761,15 @@ sub modify_ltitools { $confhash{$newid}{'display'}{'target'} = 'iframe'; } foreach my $item ('passback','roster') { - if ($env{'form.ltitools_add_'.$item}) { + if ($env{'form.ltitools_'.$item.'_add'}) { $confhash{$newid}{$item} = 1; + if ($env{'form.ltitools_'.$item.'valid_add'} ne '') { + my $lifetime = $env{'form.ltitools_'.$item.'valid_add'}; + $lifetime =~ s/^\s+|\s+$//g; + if ($lifetime =~ /^\d+\.?\d*$/) { + $confhash{$newid}{$item.'valid'} = $lifetime; + } + } } } if ($env{'form.ltitools_add_image.filename'} ne '') { @@ -9924,6 +10802,13 @@ sub modify_ltitools { } } } + if (ref($confhash{$newid}{'fields'}) eq 'HASH') { + if ($confhash{$newid}{'fields'}{'user'}) { + if ($env{'form.ltitools_userincdom_add'}) { + $confhash{$newid}{'incdom'} = 1; + } + } + } my @courseconfig = &Apache::loncommon::get_env_multiple('form.ltitools_courseconfig'); foreach my $item (@courseconfig) { $confhash{$newid}{'crsconf'}{$item} = 1; @@ -9959,6 +10844,7 @@ sub modify_ltitools { my $maxnum = $env{'form.ltitools_maxnum'}; for (my $i=0; $i<=$maxnum; $i++) { my $itemid = $env{'form.ltitools_id_'.$i}; + $itemid =~ s/\D+//g; if (ref($domconfig{$action}{$itemid}) eq 'HASH') { if ($deletions{$itemid}) { if ($domconfig{$action}{$itemid}{'image'}) { @@ -9969,7 +10855,7 @@ sub modify_ltitools { } else { my $newpos = $env{'form.ltitools_'.$itemid}; $newpos =~ s/\D+//g; - foreach my $item ('title','url') { + foreach my $item ('title','url','lifetime') { $confhash{$itemid}{$item} = $env{'form.ltitools_'.$item.'_'.$i}; if ($domconfig{$action}{$itemid}{$item} ne $confhash{$itemid}{$item}) { $changes{$itemid} = 1; @@ -9987,6 +10873,18 @@ sub modify_ltitools { if ($env{'form.ltitools_msgtype_'.$i} eq 'basic-lti-launch-request') { $confhash{$itemid}{'msgtype'} = $env{'form.ltitools_msgtype_'.$i}; } + if ($env{'form.ltitools_sigmethod_'.$i} eq 'HMAC-SHA256') { + $confhash{$itemid}{'sigmethod'} = $env{'form.ltitools_sigmethod_'.$i}; + } else { + $confhash{$itemid}{'sigmethod'} = 'HMAC-SHA1'; + } + if ($domconfig{$action}{$itemid}{'sigmethod'} eq '') { + if ($confhash{$itemid}{'sigmethod'} ne 'HMAC-SHA1') { + $changes{$itemid} = 1; + } + } elsif ($domconfig{$action}{$itemid}{'sigmethod'} ne $confhash{$itemid}{'sigmethod'}) { + $changes{$itemid} = 1; + } foreach my $size ('width','height') { $env{'form.ltitools_'.$size.'_'.$i} =~ s/^\s+//; $env{'form.ltitools_'.$size.'_'.$i} =~ s/\s+$//; @@ -10040,13 +10938,23 @@ sub modify_ltitools { foreach my $extra ('passback','roster') { if ($env{'form.ltitools_'.$extra.'_'.$i}) { $confhash{$itemid}{$extra} = 1; + if ($env{'form.ltitools_'.$extra.'valid_'.$i} ne '') { + my $lifetime = $env{'form.ltitools_'.$extra.'valid_add'}; + $lifetime =~ s/^\s+|\s+$//g; + if ($lifetime =~ /^\d+\.?\d*$/) { + $confhash{$itemid}{$extra.'valid'} = $lifetime; + } + } } if ($domconfig{$action}{$itemid}{$extra} ne $confhash{$itemid}{$extra}) { $changes{$itemid} = 1; } + if ($domconfig{$action}{$itemid}{$extra.'valid'} ne $confhash{$itemid}{$extra.'valid'}) { + $changes{$itemid} = 1; + } } my @courseconfig = &Apache::loncommon::get_env_multiple('form.ltitools_courseconfig_'.$i); - foreach my $item ('label','title','target','linktext','explanation') { + foreach my $item ('label','title','target','linktext','explanation','append') { if (grep(/^\Q$item\E$/,@courseconfig)) { $confhash{$itemid}{'crsconf'}{$item} = 1; if (ref($domconfig{$action}{$itemid}{'crsconf'}) eq 'HASH') { @@ -10090,6 +10998,16 @@ sub modify_ltitools { } } } + if (ref($confhash{$itemid}{'fields'}) eq 'HASH') { + if ($confhash{$itemid}{'fields'}{'user'}) { + if ($env{'form.ltitools_userincdom_'.$i}) { + $confhash{$itemid}{'incdom'} = 1; + } + if ($domconfig{$action}{$itemid}{'incdom'} ne $confhash{$itemid}{'incdom'}) { + $changes{$itemid} = 1; + } + } + } $allpos[$newpos] = $itemid; } if ($imgdeletions{$itemid}) { @@ -10223,7 +11141,7 @@ sub modify_ltitools { $resulttext .= '
      '; my $position = $pos + 1; $resulttext .= '
    • '.&mt('Order: [_1]',$position).'
    • '; - foreach my $item ('version','msgtype','url') { + foreach my $item ('version','msgtype','sigmethod','url','lifetime') { if ($confhash{$itemid}{$item} ne '') { $resulttext .= '
    • '.$lt{$item}.': '.$confhash{$itemid}{$item}.'
    • '; } @@ -10237,7 +11155,7 @@ sub modify_ltitools { $resulttext .= ('*'x$num).''; } $resulttext .= '
    • '.&mt('Configurable in course:'); - my @possconfig = ('label','title','target','linktext','explanation'); + my @possconfig = ('label','title','target','linktext','explanation','append'); my $numconfig = 0; if (ref($confhash{$itemid}{'crsconf'}) eq 'HASH') { foreach my $item (@possconfig) { @@ -10255,6 +11173,15 @@ sub modify_ltitools { $resulttext .= '
    • '.$lt{$item}.' '; if ($confhash{$itemid}{$item}) { $resulttext .= &mt('Yes'); + if ($confhash{$itemid}{$item.'valid'}) { + if ($item eq 'passback') { + $resulttext .= ' '.&mt('valid for at least [quant,_1,day] after launch', + $confhash{$itemid}{$item.'valid'}); + } else { + $resulttext .= ' '.&mt('valid for at least [quant,_1,second] after launch', + $confhash{$itemid}{$item.'valid'}); + } + } } else { $resulttext .= &mt('No'); } @@ -10291,6 +11218,13 @@ sub modify_ltitools { } if ($fieldlist) { $fieldlist =~ s/,$//; + if ($confhash{$itemid}{'fields'}{'user'}) { + if ($confhash{$itemid}{'incdom'}) { + $fieldlist .= ' ('.&mt('username:domain').')'; + } else { + $fieldlist .= ' ('.&mt('username').')'; + } + } $resulttext .= '
    • '.&mt('Data sent').':'.$fieldlist.'
    • '; } } @@ -10315,7 +11249,7 @@ sub modify_ltitools { } } if ($customlist) { - $resulttext .= '
    • '.&mt('Custom items').':'.$customlist.'
    • '; + $resulttext .= '
    • '.&mt('Custom items').': '.$customlist.'
    • '; } } $resulttext .= '
    '; @@ -10411,6 +11345,422 @@ sub get_ltitools_id { return ($id,$error); } +sub modify_lti { + my ($r,$dom,$action,$lastactref,%domconfig) = @_; + my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1); + my ($newid,@allpos,%changes,%confhash,%encconfig,$errors,$resulttext); + my (%posslti,%posslticrs,%posscrstype); + my @courseroles = ('cc','in','ta','ep','st'); + my @ltiroles = qw(Learner Instructor ContentDeveloper TeachingAssistant Mentor Member Manager Administrator); + my @lticourseroles = qw(Instructor TeachingAssistant Mentor Learner); + my @coursetypes = ('official','unofficial','community','textbook','placement'); + my %coursetypetitles = &Apache::lonlocal::texthash ( + official => 'Official', + unofficial => 'Unofficial', + community => 'Community', + textbook => 'Textbook', + placement => 'Placement Test', + ); + my %lt = <i_names(); + map { $posslti{$_} = 1; } @ltiroles; + map { $posslticrs{$_} = 1; } @lticourseroles; + map { $posscrstype{$_} = 1; } @coursetypes; + + my (@items,%deletions,%itemids); + if ($env{'form.lti_add'}) { + my $consumer = $env{'form.lti_consumer_add'}; + $consumer =~ s/(`)/'/g; + ($newid,my $error) = &get_lti_id($dom,$consumer); + if ($newid) { + $itemids{'add'} = $newid; + push(@items,'add'); + $changes{$newid} = 1; + } else { + my $error = &mt('Failed to acquire unique ID for new LTI configuration'); + $errors .= '
  • '.$error.'
  • '; + } + } + if (ref($domconfig{$action}) eq 'HASH') { + my @todelete = &Apache::loncommon::get_env_multiple('form.lti_del'); + if (@todelete) { + map { $deletions{$_} = 1; } @todelete; + } + my $maxnum = $env{'form.lti_maxnum'}; + for (my $i=0; $i<=$maxnum; $i++) { + my $itemid = $env{'form.lti_id_'.$i}; + $itemid =~ s/\D+//g; + if (ref($domconfig{$action}{$itemid}) eq 'HASH') { + if ($deletions{$itemid}) { + $changes{$itemid} = $domconfig{$action}{$itemid}{'consumer'}; + } else { + push(@items,$i); + $itemids{$i} = $itemid; + } + } + } + } + foreach my $idx (@items) { + my $itemid = $itemids{$idx}; + next unless ($itemid); + my $position = $env{'form.lti_pos_'.$idx}; + $position =~ s/\D+//g; + if ($position ne '') { + $allpos[$position] = $itemid; + } + foreach my $item ('consumer','key','secret','lifetime') { + my $formitem = 'form.lti_'.$item.'_'.$idx; + $env{$formitem} =~ s/(`)/'/g; + if ($item eq 'lifetime') { + $env{$formitem} =~ s/[^\d.]//g; + } + if ($env{$formitem} ne '') { + if (($item eq 'key') || ($item eq 'secret')) { + $encconfig{$itemid}{$item} = $env{$formitem}; + } else { + $confhash{$itemid}{$item} = $env{$formitem}; + unless (($idx eq 'add') || ($changes{$itemid})) { + if ($domconfig{$action}{$itemid}{$item} ne $confhash{$itemid}{$item}) { + $changes{$itemid} = 1; + } + } + } + } + } + if ($env{'form.lti_version_'.$idx} eq 'LTI-1p0') { + $confhash{$itemid}{'version'} = $env{'form.lti_version_'.$idx}; + } + if ($env{'form.lti_mapuser_'.$idx} eq 'sourcedid') { + $confhash{$itemid}{'mapuser'} = 'lis_person_sourcedid'; + } elsif ($env{'form.lti_mapuser_'.$idx} eq 'email') { + $confhash{$itemid}{'mapuser'} = 'lis_person_contact_email_primary'; + } elsif ($env{'form.lti_mapuser_'.$idx} eq 'other') { + my $mapuser = $env{'form.lti_customuser_'.$idx}; + $mapuser =~ s/(`)/'/g; + $mapuser =~ s/^\s+|\s+$//g; + $confhash{$itemid}{'mapuser'} = $mapuser; + } + foreach my $ltirole (@lticourseroles) { + my $possrole = $env{'form.lti_maprole_'.$ltirole.'_'.$idx}; + if (grep(/^\Q$possrole\E$/,@courseroles)) { + $confhash{$itemid}{'maproles'}{$ltirole} = $possrole; + } + } + my @possmakeuser = &Apache::loncommon::get_env_multiple('form.lti_makeuser_'.$idx); + my @makeuser; + foreach my $ltirole (sort(@possmakeuser)) { + if ($posslti{$ltirole}) { + push(@makeuser,$ltirole); + } + } + $confhash{$itemid}{'makeuser'} = \@makeuser; + if (($env{'form.lti_mapcrs_'.$idx} eq 'course_offering_sourcedid') || + ($env{'form.lti_mapcrs_'.$idx} eq 'context_id')) { + $confhash{$itemid}{'mapcrs'} = $env{'form.lti_mapcrs_'.$idx}; + } elsif ($env{'form.lti_mapcrs_'.$idx} eq 'other') { + my $mapcrs = $env{'form.lti_mapcrsfield_'.$idx}; + $mapcrs =~ s/(`)/'/g; + $mapcrs =~ s/^\s+|\s+$//g; + $confhash{$itemid}{'mapcrs'} = $mapcrs; + } + my @posstypes = &Apache::loncommon::get_env_multiple('form.lti_mapcrstype_'.$idx); + my @crstypes; + foreach my $type (sort(@posstypes)) { + if ($posscrstype{$type}) { + push(@crstypes,$type); + } + } + $confhash{$itemid}{'mapcrstype'} = \@crstypes; + if ($env{'form.lti_makecrs_'.$idx}) { + $confhash{$itemid}{'makecrs'} = 1; + } + my @possenroll = &Apache::loncommon::get_env_multiple('form.lti_selfenroll_'.$idx); + my @selfenroll; + foreach my $type (sort(@possenroll)) { + if ($posslticrs{$type}) { + push(@selfenroll,$type); + } + } + $confhash{$itemid}{'selfenroll'} = \@selfenroll; + if ($env{'form.lti_crssec_'.$idx}) { + if ($env{'form.lti_crssecsrc_'.$idx} eq 'course_section_sourcedid') { + $confhash{$itemid}{'section'} = $env{'form.lti_crssecsrc_'.$idx}; + } elsif ($env{'form.lti_crssecsrc_'.$idx} eq 'other') { + my $section = $env{'form.lti_customsection_'.$idx}; + $section =~ s/(`)/'/g; + $section =~ s/^\s+|\s+$//g; + if ($section ne '') { + $confhash{$itemid}{'section'} = $section; + } + } + } + foreach my $field ('passback','roster') { + if ($env{'form.lti_'.$field.'_'.$idx}) { + $confhash{$itemid}{$field} = 1; + } + } + unless (($idx eq 'add') || ($changes{$itemid})) { + foreach my $field ('mapuser','mapcrs','section','passback','roster') { + if ($domconfig{$action}{$itemid}{$field} ne $confhash{$itemid}{$field}) { + $changes{$itemid} = 1; + } + } + foreach my $field ('makeuser','mapcrstype','selfenroll') { + unless ($changes{$itemid}) { + if (ref($domconfig{$action}{$itemid}{$field}) eq 'ARRAY') { + if (ref($confhash{$itemid}{$field}) eq 'ARRAY') { + my @diffs = &Apache::loncommon::compare_arrays($domconfig{$action}{$itemid}{$field}, + $confhash{$itemid}{$field}); + if (@diffs) { + $changes{$itemid} = 1; + } + } elsif (@{$domconfig{$action}{$itemid}{$field}} > 0) { + $changes{$itemid} = 1; + } + } elsif (ref($confhash{$itemid}{$field}) eq 'ARRAY') { + if (@{$confhash{$itemid}{$field}} > 0) { + $changes{$itemid} = 1; + } + } + } + } + unless ($changes{$itemid}) { + if (ref($domconfig{$action}{$itemid}{'maproles'}) eq 'HASH') { + if (ref($confhash{$itemid}{'maproles'}) eq 'HASH') { + foreach my $ltirole (keys(%{$domconfig{$action}{$itemid}{'maproles'}})) { + if ($domconfig{$action}{$itemid}{'maproles'}{$ltirole} ne + $confhash{$itemid}{'maproles'}{$ltirole}) { + $changes{$itemid} = 1; + last; + } + } + unless ($changes{$itemid}) { + foreach my $ltirole (keys(%{$confhash{$itemid}{'maproles'}})) { + if ($confhash{$itemid}{'maproles'}{$ltirole} ne + $domconfig{$action}{$itemid}{'maproles'}{$ltirole}) { + $changes{$itemid} = 1; + last; + } + } + } + } elsif (keys(%{$domconfig{$action}{$itemid}{'maproles'}}) > 0) { + $changes{$itemid} = 1; + } + } elsif (ref($confhash{$itemid}{'maproles'}) eq 'HASH') { + unless ($changes{$itemid}) { + if (keys(%{$confhash{$itemid}{'maproles'}}) > 0) { + $changes{$itemid} = 1; + } + } + } + } + } + } + if (@allpos > 0) { + my $idx = 0; + foreach my $itemid (@allpos) { + if ($itemid ne '') { + $confhash{$itemid}{'order'} = $idx; + if (ref($domconfig{$action}) eq 'HASH') { + if (ref($domconfig{$action}{$itemid}) eq 'HASH') { + if ($domconfig{$action}{$itemid}{'order'} ne $idx) { + $changes{$itemid} = 1; + } + } + } + $idx ++; + } + } + } + my %ltihash = ( + $action => { %confhash } + ); + my $putresult = &Apache::lonnet::put_dom('configuration',\%ltihash, + $dom); + if ($putresult eq 'ok') { + my %ltienchash = ( + $action => { %encconfig } + ); + &Apache::lonnet::put_dom('encconfig',\%ltienchash,$dom); + if (keys(%changes) > 0) { + my $cachetime = 24*60*60; + my %ltiall = %confhash; + foreach my $id (keys(%ltiall)) { + if (ref($encconfig{$id}) eq 'HASH') { + foreach my $item ('key','secret') { + $ltiall{$id}{$item} = $encconfig{$id}{$item}; + } + } + } + &Apache::lonnet::do_cache_new('lti',$dom,\%ltiall,$cachetime); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'lti'} = 1; + } + $resulttext = &mt('Changes made:').'
      '; + my %bynum; + foreach my $itemid (sort(keys(%changes))) { + my $position = $confhash{$itemid}{'order'}; + $bynum{$position} = $itemid; + } + foreach my $pos (sort { $a <=> $b } keys(%bynum)) { + my $itemid = $bynum{$pos}; + if (ref($confhash{$itemid}) ne 'HASH') { + $resulttext .= '
    • '.&mt('Deleted: [_1]',$changes{$itemid}).'
    • '; + } else { + $resulttext .= '
    • '.$confhash{$itemid}{'consumer'}.'
      • '; + my $position = $pos + 1; + $resulttext .= '
      • '.&mt('Order: [_1]',$position).'
      • '; + foreach my $item ('version','lifetime') { + if ($confhash{$itemid}{$item} ne '') { + $resulttext .= '
      • '.$lt{$item}.': '.$confhash{$itemid}{$item}.'
      • '; + } + } + if ($encconfig{$itemid}{'key'} ne '') { + $resulttext .= '
      • '.$lt{'key'}.': '.$encconfig{$itemid}{'key'}.'
      • '; + } + if ($encconfig{$itemid}{'secret'} ne '') { + $resulttext .= '
      • '.$lt{'secret'}.': '; + my $num = length($encconfig{$itemid}{'secret'}); + $resulttext .= ('*'x$num).'
      • '; + } + if ($confhash{$itemid}{'mapuser'}) { + my $shownmapuser; + if ($confhash{$itemid}{'mapuser'} eq 'lis_person_sourcedid') { + $shownmapuser = $lt{'sourcedid'}.' (lis_person_sourcedid)'; + } elsif ($confhash{$itemid}{'mapuser'} eq 'lis_person_contact_email_primary') { + $shownmapuser = $lt{'email'}.' (lis_person_contact_email_primary)'; + } else { + $shownmapuser = &mt('Other').' ('.$confhash{$itemid}{'mapuser'}.')'; + } + $resulttext .= '
      • '.&mt('LON-CAPA username').': '.$shownmapuser.'
      • '; + } + if (ref($confhash{$itemid}{'maproles'}) eq 'HASH') { + my $rolemaps; + foreach my $role (@ltiroles) { + if ($confhash{$itemid}{'maproles'}{$role}) { + $rolemaps .= (' 'x2).$role.'='. + &Apache::lonnet::plaintext($confhash{$itemid}{'maproles'}{$role}, + 'Course').','; + } + } + if ($rolemaps) { + $rolemaps =~ s/,$//; + $resulttext .= '
      • '.&mt('Role mapping:').$rolemaps.'
      • '; + } + } + if (ref($confhash{$itemid}{'makeuser'}) eq 'ARRAY') { + if (@{$confhash{$itemid}{'makeuser'}} > 0) { + $resulttext .= '
      • '.&mt('Following roles may create user accounts: [_1]', + join(', ',@{$confhash{$itemid}{'makeuser'}})).'
      • '; + } else { + $resulttext .= '
      • '.&mt('User account creation not permitted.').'
      • '; + } + } + if ($confhash{$itemid}{'mapcrs'}) { + $resulttext .= '
      • '.&mt('Unique course identifier').': '.$confhash{$itemid}{'mapcrs'}.'
      • '; + } + if (ref($confhash{$itemid}{'mapcrstype'}) eq 'ARRAY') { + if (@{$confhash{$itemid}{'mapcrstype'}} > 0) { + $resulttext .= '
      • '.&mt('Mapping for the following LON-CAPA course types: [_1]', + join(', ',map { $coursetypetitles{$_}; } @coursetypes)). + '
      • '; + } else { + $resulttext .= '
      • '.&mt('No mapping to LON-CAPA courses').'
      • '; + } + } + if ($confhash{$itemid}{'makecrs'}) { + $resulttext .= '
      • '.&mt('Instructor may create course (if absent).').'
      • '; + } else { + $resulttext .= '
      • '.&mt('Instructor may not create course (if absent).').'
      • '; + } + if (ref($confhash{$itemid}{'selfenroll'}) eq 'ARRAY') { + if (@{$confhash{$itemid}{'selfenroll'}} > 0) { + $resulttext .= '
      • '.&mt('Self-enrollment for following roles: [_1]', + join(', ',@{$confhash{$itemid}{'selfenroll'}})). + '
      • '; + } else { + $resulttext .= '
      • '.&mt('Self-enrollment not permitted').'
      • '; + } + } + if ($confhash{$itemid}{'section'}) { + if ($confhash{$itemid}{'section'} eq 'course_section_sourcedid') { + $resulttext .= '
      • '.&mt('User section from standard field:'). + ' (course_section_sourcedid)'.'
      • '; + } else { + $resulttext .= '
      • '.&mt('User section from:').' '. + $confhash{$itemid}{'section'}.'
      • '; + } + } else { + $resulttext .= '
      • '.&mt('No section assignment').'
      • '; + } + foreach my $item ('passback','roster') { + $resulttext .= '
      • '.$lt{$item}.' '; + if ($confhash{$itemid}{$item}) { + $resulttext .= &mt('Yes'); + } else { + $resulttext .= &mt('No'); + } + $resulttext .= '
      • '; + } + $resulttext .= '
      '; + } + } + $resulttext .= '
    '; + } else { + $resulttext = &mt('No changes made.'); + } + } else { + $errors .= '
  • '.&mt('Failed to save changes').'
  • '; + } + if ($errors) { + $resulttext .= &mt('The following errors occurred: ').'
      '. + $errors.'
    '; + } + return $resulttext; +} + +sub get_lti_id { + my ($domain,$consumer) = @_; + # get lock on lti db + my $lockhash = { + lock => $env{'user.name'}. + ':'.$env{'user.domain'}, + }; + my $tries = 0; + my $gotlock = &Apache::lonnet::newput_dom('lti',$lockhash,$domain); + my ($id,$error); + + while (($gotlock ne 'ok') && ($tries<10)) { + $tries ++; + sleep (0.1); + $gotlock = &Apache::lonnet::newput_dom('lti',$lockhash,$domain); + } + if ($gotlock eq 'ok') { + my %currids = &Apache::lonnet::dump_dom('lti',$domain); + if ($currids{'lock'}) { + delete($currids{'lock'}); + if (keys(%currids)) { + my @curr = sort { $a <=> $b } keys(%currids); + if ($curr[-1] =~ /^\d+$/) { + $id = 1 + $curr[-1]; + } + } else { + $id = 1; + } + if ($id) { + unless (&Apache::lonnet::newput_dom('lti',{ $id => $consumer },$domain) eq 'ok') { + $error = 'nostore'; + } + } else { + $error = 'nonumber'; + } + } + my $dellockoutcome = &Apache::lonnet::del_dom('lti',['lock'],$domain); + } else { + $error = 'nolock'; + } + return ($id,$error); +} + sub modify_autoenroll { my ($dom,$lastactref,%domconfig) = @_; my ($resulttext,%changes); @@ -11036,17 +12386,57 @@ sub modify_contacts { my $value = $env{'form.helpform_'.$field}; $value =~ s/^\s+|\s+$//g; if (grep(/^\Q$value\E$/,@{$possoptions->{$field}})) { - $contacts_hash{contacts}{'helpform'}{$field} = $value; + $contacts_hash{'contacts'}{'helpform'}{$field} = $value; if ($field eq 'screenshot') { $env{'form.helpform_maxsize'} =~ s/^\s+|\s+$//g; if ($env{'form.helpform_maxsize'} =~ /^\d+\.?\d*$/) { - $contacts_hash{contacts}{'helpform'}{'maxsize'} = $env{'form.helpform_maxsize'}; + $contacts_hash{'contacts'}{'helpform'}{'maxsize'} = $env{'form.helpform_maxsize'}; } } } } } } + my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom); + my (@statuses,%usertypeshash,@overrides); + if ((ref($types) eq 'ARRAY') && (@{$types} > 0)) { + @statuses = @{$types}; + if (ref($usertypes) eq 'HASH') { + %usertypeshash = %{$usertypes}; + } + } + if (@statuses) { + my @possoverrides = &Apache::loncommon::get_env_multiple('form.overrides'); + foreach my $type (@possoverrides) { + if (($type ne '') && (grep(/^\Q$type\E$/,@statuses))) { + push(@overrides,$type); + } + } + if (@overrides) { + foreach my $type (@overrides) { + my @standard = &Apache::loncommon::get_env_multiple('form.override_'.$type); + foreach my $item (@contacts) { + if (grep(/^\Q$item\E$/,@standard)) { + $contacts_hash{'contacts'}{'overrides'}{$type}{$item} = 1; + $newsetting{'override_'.$type}{$item} = 1; + } else { + $contacts_hash{'contacts'}{'overrides'}{$type}{$item} = 0; + $newsetting{'override_'.$type}{$item} = 0; + } + } + $contacts_hash{'contacts'}{'overrides'}{$type}{'others'} = $env{'form.override_'.$type.'_others'}; + $contacts_hash{'contacts'}{'overrides'}{$type}{'bcc'} = $env{'form.override_'.$type.'_bcc'}; + $newsetting{'override_'.$type}{'others'} = $env{'form.override_'.$type.'_others'}; + $newsetting{'override_'.$type}{'bcc'} = $env{'form.override_'.$type.'_bcc'}; + if (($env{'form.override_'.$type.'_includestr'} ne '') && ($env{'form.override_'.$type.'_includeloc'} =~ /^s|b$/)) { + $includestr{$type} = $env{'form.override_'.$type.'_includestr'}; + $includeloc{$type} = $env{'form.override_'.$type.'_includeloc'}; + $contacts_hash{'contacts'}{'overrides'}{$type}{'include'} = $includeloc{$type}.':'.&escape($includestr{$type}); + $newsetting{'override_'.$type}{'include'} = $contacts_hash{'contacts'}{'overrides'}{$type}{'include'}; + } + } + } + } if (keys(%currsetting) > 0) { foreach my $item (@contacts) { if ($to{$item} ne $currsetting{$item}) { @@ -11101,6 +12491,33 @@ sub modify_contacts { } } } + if (@statuses) { + if (ref($currsetting{'overrides'}) eq 'HASH') { + foreach my $key (keys(%{$currsetting{'overrides'}})) { + if (ref($currsetting{'overrides'}{$key}) eq 'HASH') { + if (ref($newsetting{'override_'.$key}) eq 'HASH') { + foreach my $item (@contacts,'bcc','others','include') { + if ($currsetting{'overrides'}{$key}{$item} ne $newsetting{'override_'.$key}{$item}) { + push(@{$changes{'overrides'}},$key); + last; + } + } + } else { + push(@{$changes{'overrides'}},$key); + } + } + } + foreach my $key (@overrides) { + unless (exists($currsetting{'overrides'}{$key})) { + push(@{$changes{'overrides'}},$key); + } + } + } else { + foreach my $key (@overrides) { + push(@{$changes{'overrides'}},$key); + } + } + } } else { my %default; $default{'supportemail'} = $Apache::lonnet::perlvar{'lonSupportEMail'}; @@ -11215,6 +12632,60 @@ sub modify_contacts { $resulttext .= ''; } } + if (ref($changes{'overrides'}) eq 'ARRAY') { + my @deletions; + foreach my $type (@{$changes{'overrides'}}) { + if ($usertypeshash{$type}) { + if (grep(/^\Q$type\E/,@overrides)) { + $resulttext .= '
  • '.&mt("Overrides based on requester's affiliation set for [_1]", + $usertypeshash{$type}).'
    • '; + if (ref($newsetting{'override_'.$type}) eq 'HASH') { + my @text; + foreach my $item (@contacts) { + if ($newsetting{'override_'.$type}{$item}) { + push(@text,$short_titles->{$item}); + } + } + if ($newsetting{'override_'.$type}{'others'} ne '') { + push(@text,$newsetting{'override_'.$type}{'others'}); + } + + if (@text) { + $resulttext .= &mt('Helpdesk e-mail sent to: [_1]', + ''.join(', ',@text).''); + } + if ($newsetting{'override_'.$type}{'bcc'} ne '') { + my $bcctext; + if (@text) { + $bcctext = ' '.&mt('with Bcc to'); + } else { + $bcctext = '(Bcc)'; + } + $resulttext .= $bcctext.': '.$newsetting{'override_'.$type}{'bcc'}.''; + } elsif (!@text) { + $resulttext .= &mt('Helpdesk e-mail sent to no one'); + } + $resulttext .= '
    • '; + if ($newsetting{'override_'.$type}{'include'} ne '') { + my ($loc,$str) = split(/:/,$newsetting{'override_'.$type}{'include'}); + if ($loc eq 'b') { + $resulttext .= '
    • '.&mt('Text automatically added to e-mail body:').' '.&unescape($str).'
    • '; + } elsif ($loc eq 's') { + $resulttext .= '
    • '.&mt('Text automatically added to e-mail subject:').' '.&unescape($str).'
    • '; + } + } + } + $resulttext .= '
  • '; + } else { + push(@deletions,$usertypeshash{$type}); + } + } + } + if (@deletions) { + $resulttext .= '
  • '.&mt("Overrides based on requester's affiliation discontinued for: [_1]", + join(', ',@deletions)).'
  • '; + } + } my @offon = ('off','on'); if ($changes{'reporterrors'}) { $resulttext .= '
  • '. @@ -11270,7 +12741,6 @@ sub modify_contacts { &mt('Max size for file uploaded to help form by logged-in user set to [_1] MB.', $contacts_hash{'contacts'}{'helpform'}{'maxsize'}). '
  • '; - } } $resulttext .= ''; @@ -12072,7 +13542,7 @@ sub modify_selfcreation { if (@types) { if (@statuses) { $chgtext .= &mt('Processing of requests to create account with e-mail verification set as follows:'). - '
      '; + '
        '; foreach my $status (@statuses) { if ($status eq 'default') { $chgtext .= '
      • '.$othertitle.' -- '.$choices{$cancreate{'selfcreateprocessing'}{$status}}.'
      • '; @@ -12273,8 +13743,8 @@ sub modify_selfcreation { } } $chgtext .= '
      '; + my $typename; if (@types) { - my $typename; if ($type eq 'default') { $typename = $othertitle; } else { @@ -12290,10 +13760,9 @@ sub modify_selfcreation { ''; } else { $resulttext .= '
    • '. - &mt('There are now no restrictions on e-mail addresses which may be used for verification when a user requests an account.', - $usertypes{$type}). + &mt('There are now no restrictions on e-mail addresses which may be used for verification when a user requests an account.'). '
    • '. - &mt('(Affiliation: [_1])',$type); + &mt('(Affiliation: [_1])',$typename); } } } @@ -13656,7 +15125,11 @@ sub modify_coursedefaults { postsubmit => 60, mysqltables => 172800, ); - + my %texoptions = ( + MathJax => 'MathJax', + mimetex => &mt('Convert to Images'), + tth => &mt('TeX to HTML'), + ); $defaultshash{'coursedefaults'} = {}; if (ref($domconfig{'coursedefaults'}) ne 'HASH') { @@ -13716,6 +15189,20 @@ sub modify_coursedefaults { } } } + my $texengine; + if ($env{'form.texengine'} =~ /^(MathJax|mimetex|tth)$/) { + $texengine = $env{'form.texengine'}; + if ($defaultshash{'coursedefaults'}{'texengine'} eq '') { + unless ($texengine eq 'MathJax') { + $changes{'texengine'} = 1; + } + } elsif ($defaultshash{'coursedefaults'}{'texengine'} ne $texengine) { + $changes{'texengine'} = 1; + } + } + if ($texengine ne '') { + $defaultshash{'coursedefaults'}{'texengine'} = $texengine; + } my $currclone = $domconfig{'coursedefaults'}{'canclone'}; my @currclonecode; if (ref($currclone) eq 'HASH') { @@ -13836,8 +15323,8 @@ sub modify_coursedefaults { my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1); if (($changes{'canuse_pdfforms'}) || ($changes{'uploadquota'}) || ($changes{'postsubmit'}) || ($changes{'coursecredits'}) || ($changes{'uselcmath'}) || ($changes{'usejsme'}) || - ($changes{'canclone'}) || ($changes{'mysqltables'})) { - foreach my $item ('canuse_pdfforms','uselcmath','usejsme') { + ($changes{'canclone'}) || ($changes{'mysqltables'}) || ($changes{'texengine'})) { + foreach my $item ('canuse_pdfforms','uselcmath','usejsme','texengine') { if ($changes{$item}) { $domdefaults{$item}=$defaultshash{'coursedefaults'}{$item}; } @@ -13906,6 +15393,11 @@ sub modify_coursedefaults { } else { $resulttext .= '
    • '.&mt('Molecule editor uses JME (Java), if supported by client OS.').'
    • '; } + } elsif ($item eq 'texengine') { + if ($defaultshash{'coursedefaults'}{'texengine'} ne '') { + $resulttext .= '
    • '.&mt('Default method to display mathematics set to: "[_1]"', + $texoptions{$defaultshash{'coursedefaults'}{'texengine'}}).'
    • '; + } } elsif ($item eq 'anonsurvey_threshold') { $resulttext .= '
    • '.&mt('Responder count required for display of anonymous survey submissions set to [_1].',$defaultshash{'coursedefaults'}{'anonsurvey_threshold'}).'
    • '; } elsif ($item eq 'uploadquota') { 500 Internal Server Error

      Internal Server Error

      The server encountered an internal error or misconfiguration and was unable to complete your request.

      Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

      More information about this error may be available in the server error log.