--- loncom/interface/lonuserutils.pm	2015/08/09 21:43:18	1.172
+++ loncom/interface/lonuserutils.pm	2016/10/04 21:02:16	1.175
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # Utility functions for managing LON-CAPA user accounts
 #
-# $Id: lonuserutils.pm,v 1.172 2015/08/09 21:43:18 raeburn Exp $
+# $Id: lonuserutils.pm,v 1.175 2016/10/04 21:02:16 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -30,12 +30,29 @@
 
 package Apache::lonuserutils;
 
+=pod
+
+=head1 NAME
+
+Apache::lonuserutils.pm
+
+=head1 SYNOPSIS
+
+    Utilities for management of users and custom roles
+
+    Provides subroutines called by loncreateuser.pm
+
+=head1 OVERVIEW
+
+=cut
+
 use strict;
 use Apache::lonnet;
 use Apache::loncommon();
 use Apache::lonhtmlcommon;
 use Apache::lonlocal;
 use Apache::longroup;
+use HTML::Entities;
 use LONCAPA qw(:DEFAULT :match);
 
 ###############################################################
@@ -1530,10 +1547,10 @@ sub curr_role_permissions {
 # ======================================================= Existing Custom Roles
 
 sub my_custom_roles {
-    my ($crstype) = @_;
+    my ($crstype,$udom,$uname) = @_;
     my %returnhash=();
     my $extra = &Apache::lonnet::freeze_escape({'skipcheck' => 1});
-    my %rolehash=&Apache::lonnet::dump('roles');
+    my %rolehash=&Apache::lonnet::dump('roles',$udom,$uname);
     foreach my $key (keys(%rolehash)) {
         if ($key=~/^rolesdef\_(\w+)$/) {
             if ($crstype eq 'Community') {
@@ -2413,6 +2430,7 @@ sub make_keylist_array {
     $index->{'photo'} = &Apache::loncoursedata::CL_PHOTO();
     $index->{'thumbnail'} = &Apache::loncoursedata::CL_THUMBNAIL();
     $index->{'credits'} = &Apache::loncoursedata::CL_CREDITS();
+    $index->{'instsec'} = &Apache::loncoursedata::CL_INSTSEC();
     $index->{'authorquota'} = &Apache::loncoursedata::CL_AUTHORQUOTA();
     $index->{'authorusage'} = &Apache::loncoursedata::CL_AUTHORUSAGE();
     foreach my $key (keys(%{$index})) {
@@ -3056,13 +3074,14 @@ END
                                 if ($role eq 'st') {
                                     $checkval .= ':'.$in{'type'}.':'.
                                                  $in{'lockedtype'}.':'.
-                                                 $in{'credits'};
+                                                 $in{'credits'}.':'.
+                                                 &escape($in{'instsec'});
                                 }
                              }
                         }
                         if ($showcheckbox) {
                             $r->print('<td><input type="checkbox" name="'.
-                                      'actionlist" value="'.$checkval.'" /></td>');
+                                      'actionlist" value="'.&HTML::Entities::encode($checkval,'&<>"').'" /></td>');
                         } else {
                             $r->print('<td>&nbsp;</td>');
                         }
@@ -3077,7 +3096,7 @@ END
                 if ($item eq 'username') {
                     $r->print('<td>'.&print_username_link($mode,\%in).'</td>');
                 } elsif (($item eq 'start' || $item eq 'end') && ($actionselect)) {
-                    $r->print('<td>'.$in{$item}.'<input type="hidden" name="'.$checkval.'_'.$item.'" value="'.$sdata->[$index{$item}].'" /></td>'."\n");
+                    $r->print('<td>'.$in{$item}.'<input type="hidden" name="'.&HTML::Entities::encode($checkval.'_'.$item.'" value="'.$sdata->[$index{$item}],'&<>"').'" /></td>'."\n");
                 } elsif ($item eq 'status') {
                     my $showitem = $in{$item};
                     if (defined($ltstatus{$in{$item}})) {
@@ -4980,7 +4999,7 @@ sub update_user_list {
     foreach my $item (@changelist) {
         my ($role,$uname,$udom,$cid,$sec,$scope,$result,$type,$locktype,
             @sections,$scopestem,$singlesec,$showsecs,$warn_singlesec,
-            $nothingtodo,$keepnosection,$credits);
+            $nothingtodo,$keepnosection,$credits,$instsec);
         if ($choice eq 'drop') {
             ($uname,$udom,$sec) = split(/:/,$item,-1);
             $role = 'st';
@@ -4993,8 +5012,9 @@ sub update_user_list {
                 $scope = $scopestem.'/'.$sec;
             }
         } elsif ($context eq 'course') {
-            ($uname,$udom,$role,$sec,$type,$locktype,$credits) =
-                split(/\:/,$item);
+            ($uname,$udom,$role,$sec,$type,$locktype,$credits,$instsec) =
+                split(/\:/,$item,8);
+            $instsec = &unescape($instsec);
             $cid = $env{'request.course.id'};
             $scopestem = '/'.$cid;
             $scopestem =~s/\_/\//g;
@@ -5013,8 +5033,9 @@ sub update_user_list {
             } elsif ($setting eq 'author') { 
                 ($uname,$udom,$role,$scope) = split(/\:/,$item);
             } elsif ($setting eq 'course') {
-                ($uname,$udom,$role,$cid,$sec,$type,$locktype,$credits) = 
-                    split(/\:/,$item);
+                ($uname,$udom,$role,$cid,$sec,$type,$locktype,$credits,$instsec) = 
+                    split(/\:/,$item,9);
+                $instsec = &unescape($instsec);
                 $scope = '/'.$cid;
                 $scope =~s/\_/\//g;
                 if ($sec ne '') {
@@ -5036,7 +5057,7 @@ sub update_user_list {
             $end = $now; 
             if ($role eq 'st') {
                 $result = 
-                    &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,$type,$locktype,$cid,'',$context,$credits);
+                    &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,$type,$locktype,$cid,'',$context,$credits,$instsec);
             } else {
                 $result = 
                     &Apache::lonnet::revokerole($udom,$uname,$scope,$role,
@@ -5044,7 +5065,7 @@ sub update_user_list {
             }
         } elsif ($choice eq 'delete') {
             if ($role eq 'st') {
-                &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$now,$start,$type,$locktype,$cid,'',$context,$credits);
+                &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$now,$start,$type,$locktype,$cid,'',$context,$credits,$instsec);
             }
             $result =
                 &Apache::lonnet::assignrole($udom,$uname,$scope,$role,$now,
@@ -5057,7 +5078,7 @@ sub update_user_list {
             }
             if ($choice eq 'reenable') {
                 if ($role eq 'st') {
-                    $result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,$type,$locktype,$cid,'',$context,$credits);
+                    $result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,$type,$locktype,$cid,'',$context,$credits,$instsec);
                 } else {
                     $result = 
                         &Apache::lonnet::assignrole($udom,$uname,$scope,$role,$end,
@@ -5065,14 +5086,14 @@ sub update_user_list {
                 }
             } elsif ($choice eq 'activate') {
                 if ($role eq 'st') {
-                    $result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,$type,$locktype,$cid,'',$context,$credits);
+                    $result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,$type,$locktype,$cid,'',$context,$credits,$instsec);
                 } else {
                     $result = &Apache::lonnet::assignrole($udom,$uname,$scope,$role,$end,
                                             $now,'','',$context);
                 }
             } elsif ($choice eq 'chgdates') {
                 if ($role eq 'st') {
-                    $result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,$type,$locktype,$cid,'',$context,$credits);
+                    $result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$sec,$end,$start,$type,$locktype,$cid,'',$context,$credits,$instsec);
                 } else {
                     $result = &Apache::lonnet::assignrole($udom,$uname,$scope,$role,$end,
                                                 $start,'','',$context);
@@ -5142,7 +5163,7 @@ sub update_user_list {
                     } else {
                         if ($role eq 'st') {
                             $result = 
-                                &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,undef,$end,$start,$type,$locktype,$cid,'',$context,$credits);
+                                &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,undef,$end,$start,$type,$locktype,$cid,'',$context,$credits,$instsec);
                         } else {
                             my $newscope = $scopestem;
                             $result = &Apache::lonnet::assignrole($udom,$uname,$newscope,$role,$end,$start,'','',$context);
@@ -5156,7 +5177,7 @@ sub update_user_list {
                     foreach my $newsec (@newsecs) {
                         if (!grep(/^\Q$newsec\E$/,@retained)) {
                             if ($role eq 'st') {
-                                $result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$newsec,$end,$start,$type,$locktype,$cid,'',$context,$credits);
+                                $result = &Apache::lonnet::modify_student_enrollment($udom,$uname,undef,undef,undef,undef,undef,$newsec,$end,$start,$type,$locktype,$cid,'',$context,$credits,$instsec);
                                 if (@newsecs > 1) {
                                     my $showsingle; 
                                     if ($newsec eq '') {
@@ -5789,7 +5810,7 @@ sub roles_by_context {
     } elsif ($context eq 'author') {
         @allroles = ('ca','aa');
     } elsif ($context eq 'domain') {
-        @allroles = ('li','ad','dg','sc','au','dc');
+        @allroles = ('li','ad','dg','dh','sc','au','dc');
     }
     return @allroles;
 }
@@ -6242,6 +6263,8 @@ sub get_extended_type {
     }
     if ($crstype eq 'Community') {
         $type = 'community';
+    } elsif ($crstype eq 'Placement') {
+        $type = 'placement';
     } elsif ($settings{'internal.coursecode'}) {
         $type = 'official';
     } elsif ($settings{'internal.textbook'}) {
@@ -6300,5 +6323,336 @@ sub selfenrollment_administration {
     return (\@in_course,\@in_domain);
 }
 
+sub custom_role_header {
+    my ($context,$crstype,$templaterolerefs,$prefix) = @_;
+    my %lt = &Apache::lonlocal::texthash(
+                 sele => 'Select a Template',
+    );
+    my ($context_code,$button_code);
+    if ($context eq 'domain') {
+        $context_code = &custom_coursetype_switch($crstype,$prefix);
+    }
+    if (ref($templaterolerefs) eq 'ARRAY') {
+        foreach my $role (@{$templaterolerefs}) {
+            my $display = 'inline';
+            if (($context eq 'domain') && ($role eq 'co')) {
+                $display = 'none';
+            }
+            $button_code .= &make_button_code($role,$crstype,$display,$prefix).' ';
+        }
+    }
+    return <<"END";
+<div class="LC_left_float">
+<fieldset>
+<legend>$lt{'sele'}</legend>
+$button_code
+</fieldset></div>
+$context_code
+<br clear="all" />
+END
+}
+
+sub custom_coursetype_switch {
+    my ($crstype,$prefix) = @_;
+    my ($checkedcourse,$checkedcommunity);
+    if ($crstype eq 'Community') {
+        $checkedcommunity = ' checked="checked"';
+    } else {
+        $checkedcourse = ' checked="checked"';
+    }
+    my %lt = &Apache::lonlocal::texthash(
+        cont => 'Context',
+        cour => 'Course',
+        comm => 'Community',
+    );
+    return <<"END";
+<div class="LC_left_float">
+<fieldset>
+<legend>$lt{'cont'}</legend>
+<label>
+<input type="radio" name="${prefix}_custrolecrstype" value="Course"$checkedcourse onclick="javascript:customSwitchType('$prefix');" />
+$lt{'cour'}
+</label>&nbsp;&nbsp;
+<label>
+<input type="radio" name="${prefix}_custrolecrstype" value="Community"$checkedcommunity onclick="javascript:customSwitchType('$prefix');" />
+$lt{'comm'}
+</label>
+</fieldset>
+</div>
+END
+}
+
+sub custom_role_table {
+    my ($crstype,$full,$levels,$levelscurrent,$prefix) = @_;
+    return unless ((ref($full) eq 'HASH') && (ref($levels) eq 'HASH') &&
+                   (ref($levelscurrent) eq 'HASH'));
+    my %lt=&Apache::lonlocal::texthash (
+                    'prv'  => "Privilege",
+                    'crl'  => "Course Level",
+                    'dml'  => "Domain Level",
+                    'ssl'  => "System Level");
+    my %cr = (
+               course => '_c',
+               domain => '_d',
+               system => '_s',
+             );
+
+    my $output=&Apache::loncommon::start_data_table().
+               &Apache::loncommon::start_data_table_header_row().
+               '<th>'.$lt{'prv'}.'</th><th>'.$lt{'crl'}.'</th><th>'.$lt{'dml'}.
+               '</th><th>'.$lt{'ssl'}.'</th>'.
+               &Apache::loncommon::end_data_table_header_row();
+    foreach my $priv (sort(keys(%{$full}))) {
+        my $privtext = &Apache::lonnet::plaintext($priv,$crstype);
+        $output .= &Apache::loncommon::start_data_table_row().
+                  '<td><span id="'.$prefix.$priv.'">'.$privtext.'</span></td>';
+        foreach my $type ('course','domain','system') {
+            if (($type eq 'system') && ($priv eq 'bre') && ($crstype eq 'Community')) {
+                $output .= '<td>&nbsp;</td>';
+            } else {
+                $output .= '<td>'.
+                  ($levels->{$type}{$priv}?'<input type="checkbox" id="'.$prefix.$priv.$cr{$type}.'"'.
+                  ' name="'.$prefix.$priv.$cr{$type}.'"'.
+                  ($levelscurrent->{$type}{$priv}?' checked="checked"':'').' />':'&nbsp;').
+                  '</td>';
+            }
+        }
+        $output .= &Apache::loncommon::end_data_table_row();
+    }
+    $output .= &Apache::loncommon::end_data_table();
+    return $output;
+}
+
+sub custom_role_privs {
+    my ($privs,$full,$levels,$levelscurrent)= @_;
+    return unless ((ref($privs) eq 'HASH') && (ref($full) eq 'HASH') &&
+                   (ref($levels) eq 'HASH') && (ref($levelscurrent) eq 'HASH'));
+    my %cr = (
+               course => 'cr:c',
+               domain => 'cr:d',
+               system => 'cr:s',
+             );
+    foreach my $type ('course','domain','system') {
+        foreach my $item (split(/\:/,$Apache::lonnet::pr{$cr{$type}})) {
+            my ($priv,$restrict)=split(/\&/,$item);
+            if (!$restrict) { $restrict='F'; }
+            $levels->{$type}->{$priv}=$restrict;
+            if ($privs->{$type}=~/\:$priv/) {
+                $levelscurrent->{$type}->{$priv}=1;
+            }
+            $full->{$priv}=1;
+        }
+    }
+    return;
+}
+
+sub custom_template_roles {
+    my ($context,$crstype) = @_;
+    my @template_roles = ("in","ta","ep");
+    if (($context eq 'domain') || ($context eq 'domprefs')) {
+        push(@template_roles,"ad");
+    }
+    push(@template_roles,"st");
+    if ($context eq 'domain') {
+        unshift(@template_roles,('co','cc'));
+    } else {
+        if ($crstype eq 'Community') {
+            unshift(@template_roles,'co');
+        } else {
+            unshift(@template_roles,'cc');
+        }
+    }
+    return @template_roles;
+}
+
+sub custom_roledefs_js {
+    my ($context,$crstype,$formname,$full,$templaterolesref,$jsback) = @_;
+    my $button_code = "\n";
+    my $head_script = "\n";
+    my (%roletitlestr,$rolenamestr);
+    my %role_titles = (
+                        Course    => [],
+                        Community => [],
+                      );
+    $head_script .= '<script type="text/javascript">'."\n"
+                   .'// <![CDATA['."\n";
+    if (ref($templaterolesref) eq 'ARRAY') {
+        if ($context eq 'domain') {
+            $rolenamestr = join("','",@{$templaterolesref});
+        }
+        foreach my $role (@{$templaterolesref}) {
+            $head_script .= &make_script_template($role,$crstype,$formname);
+            if ($context eq 'domain') {
+                foreach my $type ('Course','Community') {
+                    push(@{$role_titles{$type}},&Apache::lonnet::plaintext($role,$type));
+                }
+            }
+        }
+    }
+    if ($context eq 'domain') {
+        foreach my $type ('Course','Community') {
+            $roletitlestr{$type} = join("','",@{$role_titles{$type}});
+        }
+        my %pt = (
+            Community => {
+                           cst => &mt('Grant/revoke role of Member'),
+                           mdc => &mt('Edit community contents'),
+                           pch => &mt('Post discussion on community resources'),
+                           pfo => &mt('Print for other users and entire community'),
+                         },
+            Course    => {
+                           cst => &mt('Grant/revoke role of Student'),
+                           mdc => &mt('Edit course contents'),
+                           pch => &mt('Post discussion on course resources'),
+                           pfo => &mt('Print for other users and entire course'),
+                         },
+        );
+        $head_script .= <<"ENDJS";
+function customSwitchType(prefix) {
+    var privnames = new Array('cst','mdc','pch','pfo');
+    var privtxtcrs = new Array('$pt{Course}{cst}','$pt{Course}{mdc}','$pt{Course}{pch}','$pt{Course}{pfo}');
+    var privtxtcom = new Array('$pt{Community}{cst}','$pt{Community}{mdc}','$pt{Community}{pch}','$pt{Community}{pfo}');
+    var rolenames = new Array('$rolenamestr');
+    var rolescrs = new Array('$roletitlestr{Course}');
+    var rolescom = new Array('$roletitlestr{Community}');
+    var radio = prefix+'_custrolecrstype';
+    if (document.$formname.elements[radio].length > 1) {
+        for (var i=0; i<document.$formname.elements[radio].length; i++) {
+            if (document.$formname.elements[radio][i].checked) {
+                if ((document.getElementById(prefix+'bre_s')) && (document.getElementById(prefix+'bro_s'))) {
+                    if (document.$formname.elements[radio][i].value == 'Community') {
+                        if (document.getElementById(prefix+'bre_s').checked) {
+                            document.getElementById(prefix+'bro_s').checked = true;
+                            document.getElementById(prefix+'bre_s').checked = false;
+
+                        }
+                        document.getElementById(prefix+'bre_s').style.visibility = 'hidden';
+                    } else {
+                        document.getElementById(prefix+'bre_s').style.visibility = 'visible';
+                        if (document.getElementById(prefix+'bro_s').checked) {
+                            document.getElementById(prefix+'bre_s').checked = true;
+                            document.getElementById(prefix+'bro_s').checked = false;
+                        }
+                    }
+                }
+                for (var j=0; j<privnames.length; j++) {
+                    if (document.getElementById(prefix+privnames[j])) {
+                        if (document.getElementById(prefix+privnames[j])) {
+                            if (document.$formname.elements[radio][i].value == 'Course') {
+                                document.getElementById(prefix+privnames[j]).innerHTML = privtxtcrs[j];
+                            } else {
+                                document.getElementById(prefix+privnames[j]).innerHTML = privtxtcom[j];
+                            }
+                        }
+                    }
+                }
+                for (var j=0; j<rolenames.length; j++) {
+                    if (document.getElementById(prefix+rolenames[j])) {
+                        if (document.getElementById(prefix+rolenames[j])) {
+                            if (document.$formname.elements[radio][i].value == 'Course') {
+                                document.getElementById(prefix+rolenames[j]).value = rolescrs[j];
+                                if (rolenames[j] == 'cc') {
+                                    document.getElementById(prefix+rolenames[j]).style.display = 'inline';
+                                }
+                                if (rolenames[j] == 'co') {
+                                    document.getElementById(prefix+rolenames[j]).style.display = 'none';
+                                }
+                            } else {
+                                document.getElementById(prefix+rolenames[j]).value = rolescom[j];
+                                if (rolenames[j] == 'cc') {
+                                    document.getElementById(prefix+rolenames[j]).style.display = 'none';
+                                }
+                                if (rolenames[j] == 'co') {
+                                    document.getElementById(prefix+rolenames[j]).style.display = 'inline';
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+    return;
+}
+ENDJS
+    }
+    $head_script .= "\n".$jsback."\n"
+                   .'// ]]>'."\n"
+                   .'</script>'."\n";
+    return $head_script;
+}
+
+# --------------------------------------------------------
+sub make_script_template {
+    my ($role,$crstype,$formname) = @_;
+    my $return_script = 'function set_'.$role.'(prefix) {'."\n";
+    my (%full_by_level,%role_priv);
+    foreach my $level ('c','d','s') {
+        foreach my $item (split(/\:/,$Apache::lonnet::pr{'cr:'.$level})) {
+            next if (($level eq 's') && ($crstype eq 'Community') && ($item eq 'bre&S'));
+            my ($priv,$restrict)=split(/\&/,$item);
+            $full_by_level{$level}{$priv}=1;
+        }
+        $role_priv{$level} = {};
+        my @temp = split(/:/,$Apache::lonnet::pr{$role.':'.$level});
+        foreach my $priv (@temp) {
+            my ($priv_item, $dummy) = split(/\&/,$priv);
+            $role_priv{$level}{$priv_item} = 1;
+        }
+    }
+    my %to_check = (
+                      c => ['c','d','s'],
+                      d => ['d','s'],
+                      s => ['s'],
+                   );
+    foreach my $level ('c','d','s') {
+        if (ref($full_by_level{$level}) eq 'HASH') {
+            foreach my $priv (keys(%{$full_by_level{$level}})) {
+                my $value = 'false';
+                if (ref($to_check{$level}) eq 'ARRAY') {
+                    foreach my $lett (@{$to_check{$level}}) {
+                        if (exists($role_priv{$lett}{$priv})) {
+                            $value = 'true';
+                            last;
+                        }
+                    }
+                    $return_script .= "document.$formname.elements[prefix+'".$priv."_".$level."'].checked = $value;\n";
+                }
+            }
+        }
+    }
+    $return_script .= '}'."\n";
+    return ($return_script);
+}
+# ----------------------------------------------------------
+sub make_button_code {
+    my ($role,$crstype,$display,$prefix) = @_;
+    my $label = &Apache::lonnet::plaintext($role,$crstype);
+    my $button_code = '<input type="button" onclick="set_'.$role."('$prefix'".')" '.
+                      'id="'.$prefix.$role.'" value="'.$label.'" '.
+                      'style="display:'.$display.'" />';
+    return ($button_code);
+}
+
+sub custom_role_update {
+    my ($rolename,$prefix) = @_;
+# ------------------------------------------------------- What can be assigned?
+    my %privs = (
+                      c => '',
+                      d => '',
+                      s => '',
+                    );
+    foreach my $level (keys(%privs)) {
+        foreach my $item (split(/\:/,$Apache::lonnet::pr{'cr:'.$level})) {
+            my ($priv,$restrict)=split(/\&/,$item);
+            if (!$restrict) { $restrict=''; }
+            if ($env{'form.'.$prefix.$priv.'_'.$level}) {
+                $privs{$level} .=':'.$item;
+            }
+        }
+    }
+    return %privs;
+}
+
 1;