--- loncom/auth/lonroles.pm 2016/08/04 18:53:26 1.269.2.24 +++ loncom/auth/lonroles.pm 2017/03/13 20:43:13 1.269.2.32 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # User Roles Screen # -# $Id: lonroles.pm,v 1.269.2.24 2016/08/04 18:53:26 raeburn Exp $ +# $Id: lonroles.pm,v 1.269.2.32 2017/03/13 20:43:13 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -260,7 +260,9 @@ sub handler { my $envkey; my %dcroles = (); - my $numdc = &check_fordc(\%dcroles,$update,$then); + my %helpdeskroles = (); + my ($numdc,$numhelpdesk,$numadhoc) = + &check_for_adhoc(\%dcroles,\%helpdeskroles,$update,$then); my $loncaparev = $r->dir_config('lonVersion'); # ================================================================== Roles Init @@ -269,8 +271,15 @@ sub handler { my $locknum=&Apache::lonnet::get_locks(); if ($locknum) { return 409; } + my $custom_adhoc; if ($env{'form.newrole'}) { $env{'form.'.$env{'form.newrole'}}=1; +# Check if this is a Domain Helpdesk or Domain Helpdesk Assistant role trying to enter a course + if ($env{'form.newrole'} =~ m{^cr/($match_domain)/\1\-domainconfig/\w+\./\1/$match_courseid$}) { + if ($helpdeskroles{$1}) { + $custom_adhoc = 1; + } + } } if ($env{'request.course.id'}) { # Check if user is CC trying to select a course role @@ -301,75 +310,120 @@ sub handler { "request.role" => 'cm', "request.role.adv" => $env{'user.adv'}, "request.role.domain" => $env{'user.domain'}}); -# Check if user is a DC trying to enter a course or author space and needs privs to be created - if ($numdc > 0) { - foreach my $envkey (keys(%env)) { -# Is this an ad-hoc Coordinator role? - if (my ($ccrole,$domain,$coursenum) = - ($envkey =~ m-^form\.(cc|co)\./($match_domain)/($match_courseid)$-)) { - if ($dcroles{$domain}) { - if (&Apache::lonnet::check_adhoc_privs($domain,$coursenum, - $update,$refresh,$now,$ccrole)) { - &Apache::lonnet::appenv({"environment.internal.$domain.$coursenum.$ccrole.adhoc" => time}); +# Check if Domain Helpdesk role trying to enter a course needs privs to be created + if ($env{'form.newrole'} =~ m{^cr/($match_domain)/\1\-domainconfig/(\w+)\./\1/($match_courseid)(?:/(\w+)|$)}) { + my $cdom = $1; + my $rolename = $2; + my $cnum = $3; + my $sec = $4; + if ($custom_adhoc) { + my ($possroles,$description) = &Apache::lonnet::get_my_adhocroles($cdom.'_'.$cnum,1); + if (ref($possroles) eq 'ARRAY') { + if (grep(/^\Q$rolename\E$/,@{$possroles})) { + if (&Apache::lonnet::check_adhoc_privs($cdom,$cnum,$update,$refresh,$now, + "cr/$cdom/$cdom".'-domainconfig/'.$rolename,undef,$sec)) { + &Apache::lonnet::appenv({"environment.internal.$cdom.$cnum.cr/$cdom/$cdom".'-domainconfig/'."$rolename.adhoc" => time}); } } - last; } -# Is this an ad-hoc CA-role? - if (my ($domain,$user) = - ($envkey =~ m-^form\.ca\./($match_domain)/($match_username)$-)) { - if (($domain eq $env{'user.domain'}) && ($user eq $env{'user.name'})) { - delete($env{$envkey}); - $env{'form.au./'.$domain.'/'} = 1; - my ($server_status,$home) = &check_author_homeserver($user,$domain); - if ($server_status eq 'switchserver') { - my $trolecode = 'au./'.$domain.'/'; - my $switchserver = '/adm/switchserver?otherserver='.$home.'&role='.$trolecode; - $r->internal_redirect($switchserver); - return OK; + } + } elsif (($numdc > 0) || ($numhelpdesk > 0)) { +# Check if user is a DC trying to enter a course or author space and needs privs to be created +# Check if user is a DH or DA trying to enter a course and needs privs to be created + foreach my $envkey (keys(%env)) { + if ($numdc) { +# Is this an ad-hoc Coordinator role? + if (my ($ccrole,$domain,$coursenum) = + ($envkey =~ m-^form\.(cc|co)\./($match_domain)/($match_courseid)$-)) { + if ($dcroles{$domain}) { + if (&Apache::lonnet::check_adhoc_privs($domain,$coursenum, + $update,$refresh,$now,$ccrole)) { + &Apache::lonnet::appenv({"environment.internal.$domain.$coursenum.$ccrole.adhoc" => time}); + } } last; } - if (my ($castart,$caend) = ($env{'user.role.ca./'.$domain.'/'.$user} =~ /^(\d*)\.(\d*)$/)) { - if (((($castart) && ($castart < $now)) || !$castart) && - ((!$caend) || (($caend) && ($caend > $now)))) { +# Is this an ad-hoc CA-role? + if (my ($domain,$user) = + ($envkey =~ m-^form\.ca\./($match_domain)/($match_username)$-)) { + if (($domain eq $env{'user.domain'}) && ($user eq $env{'user.name'})) { + delete($env{$envkey}); + $env{'form.au./'.$domain.'/'} = 1; my ($server_status,$home) = &check_author_homeserver($user,$domain); if ($server_status eq 'switchserver') { - my $trolecode = 'ca./'.$domain.'/'.$user; + my $trolecode = 'au./'.$domain.'/'; my $switchserver = '/adm/switchserver?otherserver='.$home.'&role='.$trolecode; $r->internal_redirect($switchserver); return OK; } last; } - } - # Check if author blocked ca-access - my %blocked=&Apache::lonnet::get('environment',['domcoord.author'],$domain,$user); - if ($blocked{'domcoord.author'} eq 'blocked') { - delete($env{$envkey}); - $env{'user.error.msg'}=':::1:User '.$user.' in domain '.$domain.' blocked domain coordinator access'; + if (my ($castart,$caend) = ($env{'user.role.ca./'.$domain.'/'.$user} =~ /^(\d*)\.(\d*)$/)) { + if (((($castart) && ($castart < $now)) || !$castart) && + ((!$caend) || (($caend) && ($caend > $now)))) { + my ($server_status,$home) = &check_author_homeserver($user,$domain); + if ($server_status eq 'switchserver') { + my $trolecode = 'ca./'.$domain.'/'.$user; + my $switchserver = '/adm/switchserver?otherserver='.$home.'&role='.$trolecode; + $r->internal_redirect($switchserver); + return OK; + } + last; + } + } + # Check if author blocked ca-access + my %blocked=&Apache::lonnet::get('environment',['domcoord.author'],$domain,$user); + if ($blocked{'domcoord.author'} eq 'blocked') { + delete($env{$envkey}); + $env{'user.error.msg'}=':::1:User '.$user.' in domain '.$domain.' blocked domain coordinator access'; + last; + } + if ($dcroles{$domain}) { + my ($server_status,$home) = &check_author_homeserver($user,$domain); + if (($server_status eq 'ok') || ($server_status eq 'switchserver')) { + &Apache::lonnet::check_adhoc_privs($domain,$user,$update, + $refresh,$now,'ca'); + if ($server_status eq 'switchserver') { + my $trolecode = 'ca./'.$domain.'/'.$user; + my $switchserver = '/adm/switchserver?' + .'otherserver='.$home.'&role='.$trolecode; + $r->internal_redirect($switchserver); + return OK; + } + } else { + delete($env{$envkey}); + } + } else { + delete($env{$envkey}); + } last; } - if ($dcroles{$domain}) { - my ($server_status,$home) = &check_author_homeserver($user,$domain); - if (($server_status eq 'ok') || ($server_status eq 'switchserver')) { - &Apache::lonnet::check_adhoc_privs($domain,$user,$update, - $refresh,$now,'ca'); - if ($server_status eq 'switchserver') { - my $trolecode = 'ca./'.$domain.'/'.$user; - my $switchserver = '/adm/switchserver?' - .'otherserver='.$home.'&role='.$trolecode; - $r->internal_redirect($switchserver); - return OK; + } + if ($numhelpdesk) { +# Is this an ad hoc custom role in a course/community? + if (my ($domain,$rolename,$coursenum,$sec) = ($envkey =~ m{^form\.cr/($match_domain)/\1\-domainconfig/(\w+)\./\1/($match_courseid)(?:/(\w+)|$)})) { + if ($helpdeskroles{$domain}) { + my ($possroles,$description) = &Apache::lonnet::get_my_adhocroles($domain.'_'.$coursenum,1); + if (ref($possroles) eq 'ARRAY') { + if (grep(/^\Q$rolename\E$/,@{$possroles})) { + if (&Apache::lonnet::check_adhoc_privs($domain,$coursenum,$update,$refresh,$now, + "cr/$domain/$domain".'-domainconfig/'.$rolename, + undef,$sec)) { + &Apache::lonnet::appenv({"environment.internal.$domain.$coursenum.cr/$domain/$domain". + '-domainconfig/'."$rolename.adhoc" => time}); + } + } else { + delete($env{$envkey}); + } + } else { + delete($env{$envkey}); } } else { delete($env{$envkey}); } - } else { - delete($env{$envkey}); + last; } - last; - } + } } } @@ -508,7 +562,7 @@ ENDENTERKEY $env{'user.name'}, $env{'user.home'}, "Role ".$trolecode); - + &Apache::lonnet::appenv( {'request.role' => $trolecode, 'request.role.domain' => $cdom, @@ -517,6 +571,15 @@ ENDENTERKEY my $tadv=0; if (($cnum) && ($role ne 'ca') && ($role ne 'aa')) { + if ($role =~ m{^\Qcr/$cdom/$cdom\E\-domainconfig/(\w+)$}) { + my $rolename = $1; + my %domdef = &Apache::lonnet::get_domain_defaults($cdom); + if (ref($domdef{'adhocroles'}) eq 'HASH') { + if (ref($domdef{'adhocroles'}{$rolename}) eq 'HASH') { + &Apache::lonnet::appenv({'request.role.desc' => $domdef{'adhocroles'}{$rolename}{'desc'}}); + } + } + } my $msg; my ($furl,$ferr)= &Apache::lonuserstate::readmap($cdom.'/'.$cnum); @@ -710,6 +773,18 @@ ENDENTERKEY $redirect_url); return OK; } + if ($role eq 'dh') { + my $redirect_url = '/adm/menu/'; + &redirect_user($r,&mt('Loading Domain Helpdesk Menu'), + $redirect_url); + return OK; + } + if ($role eq 'da') { + my $redirect_url = '/adm/menu/'; + &redirect_user($r,&mt('Loading Domain Helpdesk Assistant Menu'), + $redirect_url); + return OK; + } if ($role eq 'sc') { my $redirect_url = '/adm/grades?command=scantronupload'; &redirect_user($r,&mt('Loading Data Upload Page'), @@ -746,13 +821,19 @@ ENDENTERKEY my $showcount = &roles_from_env(\%roles_in_env,$update); my $swinfo=&Apache::lonmenu::rawconfig(); - my $start_page=&Apache::loncommon::start_page($pagetitle,undef,{bread_crumbs=>$brcrum}); my %domdefs=&Apache::lonnet::get_domain_defaults($env{'user.domain'}); my $cattype = 'std'; if ($domdefs{'catauth'}) { $cattype = $domdefs{'catauth'}; } my $funcs = &get_roles_functions($showcount,$cattype); + my $crumbsright; + if ($env{'browser.mobile'}) { + $crumbsright = $funcs; + undef($funcs); + } + my $start_page=&Apache::loncommon::start_page($pagetitle,undef,{bread_crumbs=>$brcrum, + bread_crumbs_component=>$crumbsright}); &js_escape(\$standby); my $noscript='
'.&mt('Use of LON-CAPA requires Javascript to be enabled in your web browser.').'
'.&mt('As this is not the case, most functionality in the system will be unavailable.').'

'; @@ -926,7 +1007,7 @@ ENDHEADER my $doheaders = &roletable_headers($r,\%roleclass,\%sortrole, $nochoose); &print_rolerows($r,$doheaders,\%roleclass,\%sortrole,\%dcroles, - \%roletext); + \%roletext,$update,$then); my $tremark=''; my $tbg; if ($env{'request.role'} eq 'cm') { @@ -951,7 +1032,7 @@ ENDHEADER } # ----------------------------------------------------------------------- Table - if ($numdc > 0) { + if (($numdc > 0) || (($numhelpdesk > 0) && ($numadhoc > 0))) { $r->print(&coursepick_jscript()); $r->print(&Apache::loncommon::coursebrowser_javascript(). &Apache::loncommon::authorbrowser_javascript()); @@ -984,9 +1065,11 @@ ENDHEADER $roletext{'user.role.'.$role}->[1]. &Apache::loncommon::end_data_table_row(); } - if ($role =~ m{dc\./($match_domain)/} + if ($role =~ m{^dc\./($match_domain)/$} && $dcroles{$1}) { $output .= &adhoc_roles_row($1,'recent'); + } elsif ($role =~ m{^(dh|da)\./($match_domain)/$}) { + $output .= &adhoc_customroles_row($1,$2,'recent',$update,$then); } } elsif ($numdc > 0) { unless ($role =~/^error\:/) { @@ -1015,7 +1098,7 @@ ENDHEADER $doheaders ++; } } - &print_rolerows($r,$doheaders,\%roleclass,\%sortrole,\%dcroles,\%roletext); + &print_rolerows($r,$doheaders,\%roleclass,\%sortrole,\%dcroles,\%roletext,$update,$then); if ($countactive > 1) { my $tremark=''; my $tbg; @@ -1092,6 +1175,7 @@ sub gather_roles { my $tryagain = $env{'form.tryagain'}; my @ids = &Apache::lonnet::current_machine_ids(); if (ref($roles_in_env) eq 'HASH') { + my %adhocdesc; foreach my $envkey (sort(keys(%{$roles_in_env}))) { my $button = 1; my $switchserver=''; @@ -1143,8 +1227,10 @@ sub gather_roles { my $trole; if ($role =~ /^cr\//) { my ($rdummy,$rdomain,$rauthor,$rrole)=split(/\//,$role); - if ($tremark) { $tremark.='
'; } - $tremark.=&mt('Custom role defined by [_1].',$rauthor.':'.$rdomain); + unless ($rauthor eq $rdomain.'-domainconfig') { + if ($tremark) { $tremark.='
'; } + $tremark.=&mt('Custom role defined by [_1].',$rauthor.':'.$rdomain); + } } $trole=Apache::lonnet::plaintext($role); my $ttype; @@ -1190,7 +1276,32 @@ sub gather_roles { } elsif ($trest) { my $tcourseid=$tdom.'_'.$trest; $ttype = &Apache::loncommon::course_type($tcourseid); - $trole = &Apache::lonnet::plaintext($role,$ttype,$tcourseid); + if ($role !~ /^cr/) { + $trole = &Apache::lonnet::plaintext($role,$ttype,$tcourseid); + } elsif ($role =~ m{^\Qcr/$tdom/$tdom\E\-domainconfig/(\w+)$}) { + my $rolename = $1; + my $desc; + if (ref($adhocdesc{$tdom}) eq 'HASH') { + $desc = $adhocdesc{$tdom}{$rolename}; + } else { + my %domdef = &Apache::lonnet::get_domain_defaults($tdom); + if (ref($domdef{'adhocroles'}) eq 'HASH') { + foreach my $rolename (sort(keys(%{$domdef{'adhocroles'}}))) { + if (ref($domdef{'adhocroles'}{$rolename}) eq 'HASH') { + $adhocdesc{$tdom}{$rolename} = $domdef{'adhocroles'}{$rolename}{'desc'}; + $desc = $adhocdesc{$tdom}{$rolename}; + } + } + } + } + if ($desc ne '') { + $trole = $desc; + } else { + $trole = &mt('Helpdesk[_1]',' '.$rolename); + } + } else { + $trole = (split(/\//,$role,4))[-1]; + } if ($env{'course.'.$tcourseid.'.description'}) { my $home=$env{'course.'.$tcourseid.'.home'}; $twhere=$env{'course.'.$tcourseid.'.description'}; @@ -1328,7 +1439,7 @@ sub roletable_headers { my $doheaders; if ((ref($sortrole) eq 'HASH') && (ref($roleclass) eq 'HASH')) { $r->print('
' - .&Apache::loncommon::start_data_table() + .&Apache::loncommon::start_data_table('LC_textsize_mobile') .&Apache::loncommon::start_data_table_header_row() ); if (!$nochoose) { $r->print(' '); } @@ -1359,7 +1470,7 @@ sub roletypes { } sub print_rolerows { - my ($r,$doheaders,$roleclass,$sortrole,$dcroles,$roletext) = @_; + my ($r,$doheaders,$roleclass,$sortrole,$dcroles,$roletext,$update,$then) = @_; if ((ref($roleclass) eq 'HASH') && (ref($sortrole) eq 'HASH')) { my @types = &roletypes(); foreach my $type (@types) { @@ -1377,12 +1488,14 @@ sub print_rolerows { &Apache::loncommon::end_data_table_row(); } } - if ($sortrole->{$which} =~ m-dc\./($match_domain)/-) { + if ($sortrole->{$which} =~ m{^user\.role\.dc\./($match_domain)/}) { if (ref($dcroles) eq 'HASH') { if ($dcroles->{$1}) { $output .= &adhoc_roles_row($1,''); } } + } elsif ($sortrole->{$which} =~ m{^user\.role\.(dh|da)\./($match_domain)/}) { + $output .= &adhoc_customroles_row($1,$2,'',$update,$then); } } } @@ -1550,10 +1663,13 @@ sub privileges_info { } sub build_roletext { - my ($trolecode,$tdom,$trest,$tstatus,$tryagain,$advanced,$tremark,$tbg,$trole,$twhere,$tpstart,$tpend,$nochoose,$button,$switchserver,$reinit,$switchwarning) = @_; - my ($roletext,$roletext_end); - my $is_dc=($trolecode =~ m/^dc\./); - my $rowspan=($is_dc) ? '' + my ($trolecode,$tdom,$trest,$tstatus,$tryagain,$advanced,$tremark,$tbg,$trole,$twhere, + $tpstart,$tpend,$nochoose,$button,$switchserver,$reinit,$switchwarning) = @_; + my ($roletext,$roletext_end,$poss_adhoc); + if ($trolecode =~ m/^d(c|h|a)\./) { + $poss_adhoc = 1; + } + my $rowspan=($poss_adhoc) ? '' : ' rowspan="2" '; unless ($nochoose) { @@ -1614,7 +1730,7 @@ sub build_roletext { .''.$twhere.'' .''.$tpstart.'' .''.$tpend.''; - if (!$is_dc) { + unless ($poss_adhoc) { $roletext_end = ''. $tremark.' '. ''; @@ -1639,29 +1755,44 @@ sub check_author_homeserver { } } -sub check_fordc { - my ($dcroles,$update,$then) = @_; +sub check_for_adhoc { + my ($dcroles,$helpdeskroles,$update,$then) = @_; my $numdc = 0; - if ($env{'user.adv'}) { + my $numhelpdesk = 0; + my $numadhoc = 0; + my $num_custom_adhoc = 0; + if (($env{'user.adv'}) || ($env{'user.rar'})) { foreach my $envkey (sort(keys(%env))) { - if ($envkey=~/^user\.role\.dc\.\/($match_domain)\/$/) { - my $dcdom = $1; - my $livedc = 1; + if ($envkey=~/^user\.role\.(dc|dh|da)\.\/($match_domain)\/$/) { + my $role = $1; + my $roledom = $2; + my $liverole = 1; my ($tstart,$tend)=split(/\./,$env{$envkey}); my $limit = $update; - if ($env{'request.role'} eq 'dc./'.$dcdom.'/') { + if ($env{'request.role'} eq "$role./$roledom/") { $limit = $then; } - if ($tstart && $tstart>$limit) { $livedc = 0; } - if ($tend && $tend <$limit) { $livedc = 0; } - if ($livedc) { - $$dcroles{$dcdom} = $envkey; - $numdc++; + if ($tstart && $tstart>$limit) { $liverole = 0; } + if ($tend && $tend <$limit) { $liverole = 0; } + if ($liverole) { + if ($role eq 'dc') { + $dcroles->{$roledom} = $envkey; + $numdc++; + } else { + $helpdeskroles->{$roledom} = $envkey; + my %domdefaults = &Apache::lonnet::get_domain_defaults($roledom); + if (ref($domdefaults{'adhocroles'}) eq 'HASH') { + if (keys(%{$domdefaults{'adhocroles'}})) { + $numadhoc ++; + } + } + $numhelpdesk++; + } } } } } - return $numdc; + return ($numdc,$numhelpdesk,$numadhoc); } sub adhoc_course_role { @@ -1702,9 +1833,15 @@ sub adhoc_course_role { my %cgroups = &Apache::lonnet::get_active_groups($env{'user.domain'}, $env{'user.name'},$cdom,$cnum); + my $ccrole; + if ($crstype eq 'Community') { + $ccrole = 'co'; + } else { + $ccrole = 'cc'; + } foreach my $group (keys(%cgroups)) { $group_privs{$group} = - $env{'user.priv.cc./'.$cdom.'/'.$cnum.'./'.$cdom.'/'.$cnum.'/'.$group}; + $env{'user.priv.'.$ccrole.'./'.$cdom.'/'.$cnum.'./'.$cdom.'/'.$cnum.'/'.$group}; } $newgroups{'/'.$cdom.'/'.$cnum} = \%group_privs; my $area = '/'.$cdom.'/'.$cnum; @@ -1753,20 +1890,21 @@ sub check_forcc { } sub courselink { - my ($dcdom,$rowtype) = @_; + my ($roledom,$rowtype,$role) = @_; my $courseform=&Apache::loncommon::selectcourse_link - ('rolechoice','dccourse'.$rowtype.'_'.$dcdom, - 'dcdomain'.$rowtype.'_'.$dcdom,'coursedesc'.$rowtype.'_'. - $dcdom,$dcdom,undef,'Course/Community'); - my $hiddenitems = ''. - ''. - ''. - ''; + ('rolechoice','course'.$rowtype.'_'.$roledom.'_'.$role, + 'domain'.$rowtype.'_'.$roledom.'_'.$role, + 'coursedesc'.$rowtype.'_'.$roledom.'_'.$role, + $roledom.':'.$role,undef,'Course/Community'); + my $hiddenitems = ''. + ''. + ''. + ''; return $courseform.$hiddenitems; } sub coursepick_jscript { - my $js_lt = &Apache::lonlocal::texthash( + my %js_lt = &Apache::lonlocal::texthash( plsu => "Please use the 'Select Course/Community' link to open a separate pick course window where you may select the course or community you wish to enter.", youc => 'You can only use this screen to select courses and communities in the current domain.', ); @@ -1852,11 +1990,12 @@ sub display_cc_role { sub adhoc_roles_row { my ($dcdom,$rowtype) = @_; my $output = &Apache::loncommon::continue_data_table_row() - .' ' + .' ' .&mt('[_1]Ad hoc[_2] roles in domain [_3] --' ,'','',$dcdom) .' '; - my $selectcclink = &courselink($dcdom,$rowtype); + my $role = 'cc'; + my $selectcclink = &courselink($dcdom,$rowtype,$role); my $ccrole = &Apache::lonnet::plaintext('co',undef,undef,1); my $carole = &Apache::lonnet::plaintext('ca'); my $selectcalink = &coauthorlink($dcdom,$rowtype); @@ -1866,6 +2005,30 @@ sub adhoc_roles_row { return $output; } +sub adhoc_customroles_row { + my ($role,$dhdom,$rowtype,$update,$then) = @_; + my $liverole = 1; + my ($tstart,$tend)=split(/\./,$env{"user.role.$role./$dhdom/"}); + my $limit = $update; + if (($role eq 'dh') && ($env{'request.role'} eq 'dh./'.$dhdom.'/')) { + $limit = $then; + } + if ($tstart && $tstart>$limit) { $liverole = 0; } + if ($tend && $tend <$limit) { $liverole = 0; } + return unless ($liverole); + my %domdefaults = &Apache::lonnet::get_domain_defaults($dhdom); + if (ref($domdefaults{'adhocroles'}) eq 'HASH') { + if (scalar(keys(%{$domdefaults{'adhocroles'}})) > 0) { + return &Apache::loncommon::continue_data_table_row() + .' ' + .&mt('[_1]Ad hoc[_2] course/community roles in domain [_3] --', + '','',$dhdom) + .' '.&courselink($dhdom,$rowtype,$role); + } + } + return; +} + sub recent_filename { my $area=shift; return 'nohist_recent_'.&escape($area); @@ -2694,15 +2857,30 @@ sub get_roles_functions { unless ($cattype eq 'none') { push(@links,['/adm/coursecatalog','ccat-22x22',&mt('Course catalog')]); } - my $funcs = &Apache::lonhtmlcommon::start_funclist(); - foreach my $link (@links) { - $funcs .= &Apache::lonhtmlcommon::add_item_funclist( - ''. - ''.$link->[2].''. - $link->[2].''); + my $funcs; + if ($env{'browser.mobile'}) { + my @functions; + foreach my $link (@links) { + push(@functions,[$link->[0],$link->[2]]); + } + my $title = 'Display options'; + if ($env{'user.adv'}) { + $title = 'Roles options'; + } + $funcs = &Apache::lonmenu::create_submenu('','',$title,\@functions,1,'LC_breadcrumbs_hoverable'); + $funcs = '
    '.$funcs.'
'; + } else { + $funcs = &Apache::lonhtmlcommon::start_funclist(); + foreach my $link (@links) { + $funcs .= &Apache::lonhtmlcommon::add_item_funclist( + ''. + ''.$link->[2].''. + $link->[2].''); + } + $funcs .= &Apache::lonhtmlcommon::end_funclist(); + $funcs = &Apache::loncommon::head_subbox($funcs); } - $funcs .= &Apache::lonhtmlcommon::end_funclist(); - return &Apache::loncommon::head_subbox($funcs); + return $funcs; } sub get_queued {