--- loncom/interface/loncoursegroups.pm 2005/10/27 23:32:27 1.1 +++ loncom/interface/loncoursegroups.pm 2023/07/29 20:33:25 1.132 @@ -1,3 +1,6 @@ +# The LearningOnline Network with CAPA +# +# $Id: loncoursegroups.pm,v 1.132 2023/07/29 20:33:25 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -26,209 +29,4292 @@ package Apache::loncoursegroups; use strict; use Apache::lonnet; -use Apache::loncommon; -use Apache::lonhtmlcommon; +use Apache::loncommon(); +use Apache::lonhtmlcommon(); +use Apache::lonhtmlgateway; use Apache::lonlocal; +use Apache::lonnavmaps(); +use Apache::longroup(); +use Apache::portfolio(); +use Apache::lonuserutils(); use Apache::Constants qw(:common :http); +use HTML::Entities; +use LONCAPA::map(); +use lib '/home/httpd/lib/perl/'; +use LONCAPA; sub handler { my ($r) = @_; + &Apache::loncommon::content_type($r,'text/html'); $r->send_http_header; - + if ($r->header_only) { return OK; } - &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, - ['action','state']); - - $r->print(&header()); - - &Apache::lonhtmlcommon::clear_breadcrumbs(); - &Apache::lonhtmlcommon::add_breadcrumb - ({href=>"/adm/groups", - text=>"Group Management", - faq=>9,bug=>'Instructor Interface',}); # Needs to be in a course if (! ($env{'request.course.fn'})) { # Not in a course $env{'user.error.msg'}= - "/adm/groups:mdg:0:0:Cannot create, modify or delete course groups"; + "/adm/coursegroups:mdg:0:0:Cannot edit or view course/community groups"; return HTTP_NOT_ACCEPTABLE; } + &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, + ['action','refpage','state','groupname','branch']); + my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; + my $view_permission = - &Apache::lonnet::allowed('vcg',$env{'request.course.id'}); + &Apache::lonnet::allowed('vcg',$env{'request.course.id'}.($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:'')); my $manage_permission = - &Apache::lonnet::allowed('mdg',$env{'request.course.id'}); + &Apache::lonnet::allowed('mdg',$env{'request.course.id'}.($env{'request.course.sec'}?'/'.$env{'request.course.sec'}:'')); + &Apache::lonhtmlcommon::clear_breadcrumbs(); - if (! exists($env{'form.action'})) { - $r->print(&Apache::lonhtmlcommon::breadcrumbs - (undef,'Course Group Manager')); - &print_main_menu($r,$manage_permission,$view_permission); - } elsif ($env{'form.action'} eq 'create' && $manage_permission) { + my $gpterm = &Apache::loncommon::group_term(); + my $ucgpterm = $gpterm; + $ucgpterm =~ s/^(\w)/uc($1)/e; + my $crstype = &Apache::loncommon::course_type(); + + my %functions = ( + email => 'Send Messages', + discussion => 'Discussion Boards', + chat => 'Chat Room', + files => 'Group Portfolio', + roster => 'Membership Roster', + homepage => $ucgpterm.' home page', + ); + + my %idx = (); + $idx{id} = &Apache::loncoursedata::CL_ID(); + $idx{fullname} = &Apache::loncoursedata::CL_FULLNAME(); + $idx{udom} = &Apache::loncoursedata::CL_SDOM(); + $idx{uname} = &Apache::loncoursedata::CL_SNAME(); + $idx{section} = &Apache::loncoursedata::CL_SECTION(); + + my $action = $env{'form.action'}; + my $state = $env{'form.state'}; + if ((!defined($action)) || ($action eq 'view') || ($action eq 'modify') || ($action eq 'delete') || ($action eq 'reenable')) { + if (!defined($state)) { + $state = 'view'; + } + } + if ($action eq 'create' || $action eq 'modify' || $action eq 'view' || + $action eq 'delete' || $action eq 'reenable') { + if ($view_permission || $manage_permission) { + if ($state eq 'view') { + &print_main_menu($r,$cdom,$cnum,\%functions,\%idx, + $view_permission,$manage_permission, + $action,$state,$gpterm,$ucgpterm,$crstype); + } else { + &group_administration($r,$action,$state,$cdom,$cnum, + \%functions,\%idx,$view_permission, + $manage_permission,$gpterm,$ucgpterm, + $crstype); + } + } else { + $r->print('<div class="LC_warning">' + .&mt('You do not have '.$gpterm.' administration ' + .'privileges in this '.lc($crstype).'.') + .'</div>'); + } + } else { + &print_main_menu($r,$cdom,$cnum,\%functions,\%idx,$view_permission, + $manage_permission,$action,$state,$gpterm,$ucgpterm, + $crstype); + } + return OK; +} + +sub print_main_menu { + my ($r,$cdom,$cnum,$functions,$idx,$view_permission,$manage_permission, + $action,$state,$gpterm,$ucgpterm,$crstype) = @_; + my $jscript = qq| +function changeSort(caller) { + document.$state.sortby.value = caller; + document.$state.submit(); +} +function openGroupRoster(group,status) { + var url = '/adm/grouproster?'; + url += 'group='+group+'&status='+status+'&ref=popup'; + var title = 'Group_Membership'; + var options = 'scrollbars=1,resizable=1,menubar=0'; + options += ',width=700,height=600'; + rosterbrowser = open(url,title,options,'1'); + rosterbrowser.focus(); +}\n|; + $r->print(&header('My Space',$jscript,$action,$state)); + if ($env{'form.refpage'} eq 'cusr') { &Apache::lonhtmlcommon::add_breadcrumb - ({href=>'/adm/coursegroups?action=create&state=', - text=>"Create Group"}); - $r->print(&Apache::lonhtmlcommon::breadcrumbs - (undef,'Create Group','Course_Create_Group')); - if (! exists($env{'form.state'})) { - &first_creation_form($r); - } elsif ($env{'form.state'} eq 'pick_members') { - &second_creation_form($r); - } elsif ($env{'form.state'} eq 'complete') { - &completed_creation($r); + ({href=>"/adm/createuser", + text=>"User Management"}); + } + &Apache::lonhtmlcommon::add_breadcrumb + ({href=>"/adm/coursegroups", + text=>"Groups"}); + my $helpitem; + if ($manage_permission) { + $helpitem = 'Creating_Groups'; + } + $r->print(&Apache::lonhtmlcommon::breadcrumbs('Groups',$helpitem)); + &display_groups($r,$cdom,$cnum,$functions,$idx,$view_permission, + $manage_permission,$action,$state,$gpterm,$ucgpterm, + $crstype); + $r->print(&footer()); + return; +} + +sub display_groups { + my ($r,$cdom,$cnum,$functions,$idx,$view_permission, + $manage_permission,$action,$state,$gpterm,$ucgpterm,$crstype) = @_; + my %curr_groups = (); + my %grp_info = (); + my %actionlinks = ( + modify => '/adm/coursegroups?action=modify&refpage='. + $env{'form.refpage'}.'&state=pick_task&groupname=', + view => '', + delete => '/adm/coursegroups?action=delete&refpage='. + $env{'form.refpage'}.'&state=verify&groupname=', + reenable => '/adm/coursegroups?action=reenable&refpage='. + $env{'form.refpage'}.'&state=verify&groupname=', + ); + my %lt = &Apache::lonlocal::texthash( + modify => 'Modify', + view => 'View', + delete => 'Delete', + reenable => 'Re-enable', + act => 'Action', + gname => 'Group Name', + desc => 'Group Title', + crea => 'Creator', + crtd => 'Created', + last => 'Last Modified', + func => 'Collaborative Tools', + quot => 'Quota (MB)', + memb => 'Members', + file => 'Files', + dibd => 'Discussion Boards', + dius => 'Disk Use (%)', + nogr => 'No groups exist.', + crng => 'Create a new group', + redg => 'Re-enable a deleted group', + alth => 'Although your current role has privileges'. + ' to view any existing groups in this '. + lc($crstype).', you do not have privileges '. + 'to create new groups.', + ); + if ($view_permission) { + if (!defined($action)) { + $action = 'view'; + } + my ($status,$reenable_link); + if ($action eq 'reenable') { + $status = 'deleted_groups'; + } else { + if ($manage_permission) { + my %deleted_groups = + &Apache::longroup::coursegroups($cdom,$cnum,undef,'deleted_groups'); + if (keys(%deleted_groups) > 0) { + $reenable_link = ' <a href="/adm/coursegroups?action=reenable&refpage='.$env{'form.refpage'}.'">'.$lt{'redg'}.'</a>'; + } + } + } + my %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum,undef, + $status); + + if (%curr_groups) { + my $navmap=Apache::lonnavmaps::navmap->new(); + if (!defined($navmap)) { + $r->print('<div class="LC_error">'. + &mt('An error occurred retrieving information about resources in the course.').'<br />'. + &mt('It is recommended that you [_1]re-initialize the course[_2] and then return to this page.','<a href="/adm/roles?selectrole=1&newrole='.$env{'request.role'}.'&orgurl=%2fadm%2fcoursegroups">','</a>'). + '</div>'); + return; + } + if ($manage_permission) { + if ($action ne 'reenable') { + $r->print('<br /><a href="/adm/coursegroups?action=create&refpage='.$env{'form.refpage'}.'">'.$lt{'crng'}.'</a>'); + } + if ($reenable_link) { + $r->print($reenable_link); + } + } + $r->print('<br /><br />'); + $r->print(&Apache::loncommon::start_data_table(). + &Apache::loncommon::start_data_table_header_row()); + + $r->print(<<"END"); + <th>$lt{'act'}</th> + <th><a href="javascript:changeSort('groupname')">$lt{'gname'}</a></th> + <th><a href="javascript:changeSort('description')">$lt{'desc'}</a></th> + <th><a href="javascript:changeSort('creator')">$lt{'crea'}</a></th> + <th><a href="javascript:changeSort('creation')">$lt{'crtd'}</a></th> + <th><a href="javascript:changeSort('modified')">$lt{'last'}</a></th> + <th>$lt{'func'}</th> + <th><a href="javascript:changeSort('quota')">$lt{'quot'}</a></th> + <th><a href="javascript:changeSort('totalmembers')">$lt{'memb'}</a></th> + <th><a href="javascript:changeSort('totalfiles')">$lt{'file'}</a></th> + <th><a href="javascript:changeSort('boards')">$lt{'dibd'}</a></th> + <th><a href="javascript:changeSort('diskuse')">$lt{'dius'}</a></th> +END + $r->print(&Apache::loncommon::end_data_table_header_row()); + my %Sortby = (); + foreach my $group (sort(keys(%curr_groups))) { + %{$grp_info{$group}} = + &Apache::longroup::get_group_settings( + $curr_groups{$group}); + my $members_result = &group_members($cdom,$cnum,$group, + \%grp_info); + my $port_path = '/userfiles/groups/'.$group.'/portfolio'; + my $totaldirs = 0; + my $totalfiles = 0; + &group_files($group,$port_path,\$totalfiles,\$totaldirs); + $grp_info{$group}{'totalfiles'} = $totalfiles; + $grp_info{$group}{'totaldirs'} = $totaldirs; + my $getpropath = 1; + my $diskuse = &Apache::lonnet::diskusage($cdom,$cnum,$port_path, $getpropath); + if ($grp_info{$group}{'quota'} > 0) { + my $pct_use = 0.1 * $diskuse/$grp_info{$group}{'quota'}; + $grp_info{$group}{'diskuse'} = sprintf("%.0f",$pct_use); + } else { + $grp_info{$group}{'diskuse'} = 'N/A'; + } + my ($groupboards,$boardshash)=&Apache::longroup::get_group_bbinfo( + $cdom,$cnum,$group); + $grp_info{$group}{'boards'} = scalar(@{$groupboards}); + if ($env{'form.sortby'} eq 'groupname') { + push(@{$Sortby{$group}},$group); + } elsif ($env{'form.sortby'} eq 'description') { + push(@{$Sortby{$grp_info{$group}{'description'}}},$group); + } elsif ($env{'form.sortby'} eq 'creator') { + push(@{$Sortby{$grp_info{$group}{'creator'}}},$group); + } elsif ($env{'form.sortby'} eq 'creation') { + push(@{$Sortby{$grp_info{$group}{'creation'}}},$group); + } elsif ($env{'form.sortby'} eq 'modified') { + push(@{$Sortby{$grp_info{$group}{'modified'}}},$group); + } elsif ($env{'form.sortby'} eq 'quota') { + push(@{$Sortby{$grp_info{$group}{'quota'}}},$group); + } elsif ($env{'form.sortby'} eq 'totalmembers') { + push(@{$Sortby{$grp_info{$group}{'totalmembers'}}}, + $group); + } elsif ($env{'form.sortby'} eq 'totalfiles') { + push(@{$Sortby{$grp_info{$group}{'totalfiles'}}},$group); + } elsif ($env{'form.sortby'} eq 'boards') { + push(@{$Sortby{$grp_info{$group}{'boards'}}},$group); + } elsif ($env{'form.sortby'} eq 'diskuse') { + push(@{$Sortby{$grp_info{$group}{'diskuse'}}},$group); + } else { + push(@{$Sortby{$group}},$group); + } + } + foreach my $key (sort(keys(%Sortby))) { + foreach my $group (@{$Sortby{$key}}) { + my $description = + &unescape($grp_info{$group}{'description'}); + my $creator = $grp_info{$group}{'creator'}; + my $creation = $grp_info{$group}{'creation'}; + my $modified = $grp_info{$group}{'modified'}; + my $quota = $grp_info{$group}{'quota'}; + my $totalmembers = $grp_info{$group}{'totalmembers'}; + my $totalfiles = $grp_info{$group}{'totalfiles'}; + my $totaldirs = $grp_info{$group}{'totaldirs'}; + my $boards = $grp_info{$group}{'boards'}; + my $diskuse = $grp_info{$group}{'diskuse'}; + my $functionality; + foreach my $tool (sort(keys(%{$functions}))) { + if ($grp_info{$group}{functions}{$tool} eq 'on') { + $functionality .= ' '.$tool; + } + } + if (!$functionality) { + $functionality = &mt('None available'); + } + my $link; + if ($action eq 'modify' || $action eq 'delete' || + $action eq 'reenable') { + $link = '<a href="'.&HTML::Entities::encode($actionlinks{$action}.$group,'<>&"'). + '">'.$lt{$action}.'</a>'; + } else { + $link = + &Apache::longroup::get_group_link($cdom,$cnum,$group,$navmap, + $view_permission); + if ($link) { + $link = '<a href="'.$link; + $link .= (($link=~/\?/)?'&':'?').'ref=grouplist'; + if (exists($env{'form.refpage'})) { + $link .= '&refpage='.$env{'form.refpage'}; + } + $link .= '">'.$lt{$action}.'</a>'; + } + } + if ($action eq 'view') { + if ($manage_permission) { + $link .= ' <a href="'. + &HTML::Entities::encode($actionlinks{'modify'}.$group,'<>&"'). + '">'.$lt{'modify'}.'</a> <a href="'. + &HTML::Entities::encode($actionlinks{'delete'}.$group,'<>&"'). + '">'.$lt{'delete'}.'</a>'; + } + } + $r->print(&Apache::loncommon::start_data_table_row('LC_data_table_dense'). + '<td>'.$link.'</td>'. + '<td>'.$group.'</td>'. + '<td>'.$description.'</td>'. + '<td>'.$creator.'</td>'. + '<td>'. &Apache::lonnavmaps::timeToHumanString($creation).'</td>'. + '<td>'. &Apache::lonnavmaps::timeToHumanString($modified).'</td>'. + '<td>'.$functionality.'</td>'. + '<td align="right">'.$quota.'</td>'. + '<td align="right">'.$totalmembers.'</td>'. + '<td align="right">'. + '<span class="LC_nobreak">'.&mt('Files: [_1]',$totalfiles).'</span><br />'. + '<span class="LC_nobreak">'.&mt('Folders: [_1]',$totaldirs).'</span>'. + '</td>'. + '<td align="right">'.$boards.'</td>'. + '<td align="right">'.$diskuse.'</td>'. + &Apache::loncommon::end_data_table_row()); + } + } + $r->print(&Apache::loncommon::end_data_table()); + $r->print('<input type="hidden" name="refpage" '. + 'value="'.$env{'form.refpage'}.'" />'); + if ($action eq 'view') { + if (!defined($state)) { + $state = 'view'; + } + $r->print('<input type="hidden" name="state" value="'. + $state.'" />'); + } } else { - &first_creation_form($r); + $r->print($lt{'nogr'}); + if ($manage_permission) { + $r->print('<br /><br /><a href="/adm/coursegroups?action=create&refpage='.$env{'form.refpage'}.'">'.$lt{'crng'}.'</a>'); + if ($action ne 'reenable') { + if ($reenable_link) { + $r->print($reenable_link); + } + } + } else { + $r->print('<br /><br />'.$lt{'alth'}); + } + } + } else { + my @coursegroups = split(/:/,$env{'request.course.groups'}); + if (@coursegroups > 0) { + my $numlinks = 0; + $r->print('<br /><br />'); + my %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum); + if (%curr_groups) { + my $navmap=Apache::lonnavmaps::navmap->new(); + if (!defined($navmap)) { + $r->print('<div class="LC_error">'. + &mt('An error occurred retrieving information about resources in the course.').'<br />'. + &mt('It is recommended that you [_1]re-initialize the course[_2] and then return to this page.','<a href="/adm/roles?selectrole=1&newrole='.$env{'request.role'}.'&orgurl=%2fadm%2fcoursegroups">','</a>'). + '</div>'); + return; + } + foreach my $group (@coursegroups) { + my %group_info = &Apache::longroup::get_group_settings( + $curr_groups{$group}); + my $description = &unescape( + $group_info{description}); + my ($link,$hidden) = + &Apache::longroup::get_group_link($cdom,$cnum,$group,$navmap, + $view_permission); + if ($link) { + $link .= '&ref=grouplist'; + $r->print('<span style="font-size: larger"><a href="'.$link.'">'. + $description.'</a></span><br /><br />'); + $numlinks ++; + } elsif ($hidden) { + my $numtools = 0; + my $refarg = '&ref=grouplist'; + my $output = + &Apache::longroup::display_group_links($r,$env{'form.grade_target'},$group,'view', + $refarg,\$numtools,$hidden,%group_info); + if ($numtools) { + $r->print('<span style="font-size: larger">'.$description.'</a></span>'. + '<br />'.$output.'<br /><br />'); + $numlinks ++; + } + } + } + } + if (!$numlinks) { + $r->print( + '<p class="LC_info">' + .&mt('You do not currently have access to any '.$gpterm.'s' + .' in this '.lc($crstype).'.') + .'</p>' + ); + } + } else { + $r->print( + '<p class="LC_info">' + .&mt('You are not currently a member of any active '.$gpterm.'s' + .' in this '.lc($crstype).'.') + .'</p>' + ); + } + } + return; +} + +sub group_administration { + my ($r,$action,$state,$cdom,$cnum,$functions,$idx,$view_permission, + $manage_permission,$gpterm,$ucgpterm,$crstype) = @_; + my %sectioncount = (); + my @tools = (); + my @types = (); + my @roles = (); + my @sections = (); + my %users = (); + my %userdata = (); + my @members = (); + my %usertools = (); + my %stored = (); + my %memchg; + my @member_changes = ('deletion','expire','activate','reenable', + 'changefunc','changepriv'); + my ($groupname,$description,$startdate,$enddate,$granularity,$specificity, + $quota,$validate_script); + + if (defined($env{'form.groupname'})) { + $groupname = $env{'form.groupname'}; + } + + if (($action eq 'create') && ($state eq '')) { + $state = 'pick_name'; + } + if (($action eq 'create') || + (($action eq 'modify') && ($state eq 'chgresult'))) { + ($startdate,$enddate) = &get_dates_from_form(); + if (defined($env{'form.description'})) { + $description = $env{'form.description'}; + } + if (defined($env{'form.tool'})) { + @tools=&Apache::loncommon::get_env_multiple('form.tool'); + } + if (defined($env{'form.granularity'})) { + $granularity=$env{'form.granularity'}; + } + if (defined($env{'form.specificity'})) { + $specificity=$env{'form.specificity'}; + } + if (defined($env{'form.quota'})) { + $quota=$env{'form.quota'}; + } + } + if (($action eq 'create') || (($action eq 'modify') + && (($state eq 'pick_privs') || ($state eq 'addresult')))) { + if (defined($env{'form.member'})) { + @members = &Apache::loncommon::get_env_multiple('form.member'); + foreach my $user (@members) { + %{$usertools{$user}} = (); + } + } + } + + if ($action eq 'modify') { + if ($state eq '') { + if (defined($env{'form.groupname'})) { + $state = 'pick_task'; + } + } else { + %stored = &retrieve_settings($cdom,$cnum,$groupname,$action); + if (ref($stored{'types'}) eq 'ARRAY') { + @types = @{$stored{'types'}}; + } + if (ref($stored{'roles'}) eq 'ARRAY') { + @roles = @{$stored{'roles'}}; + } + if (ref($stored{'sectionpick'}) eq 'ARRAY') { + @sections = @{$stored{'sectionpick'}}; + } + unless ($state eq 'chgresult') { + if (ref($stored{'tool'}) eq 'ARRAY') { + @tools = @{$stored{'tool'}}; + } + $startdate = $stored{'startdate'}; + $enddate = $stored{'enddate'}; + $description = $stored{'description'}; + $granularity = $stored{'granularity'}; + $specificity = $stored{'specificity'}; + $quota = $stored{'quota'}; + } + } + } + + my $toolprivs = &Apache::longroup::get_tool_privs($gpterm); + + my $fixedprivs = &Apache::longroup::get_fixed_privs(); + + my %elements = + ( + create => { + pick_name => { + startdate_month => 'selectbox', + startdate_hour => 'selectbox', + enddate_month => 'selectbox', + enddate_hour => 'selectbox', + startdate_day => 'text', + startdate_year => 'text', + startdate_minute => 'text', + startdate_second => 'text', + enddate_day => 'text', + enddate_year => 'text', + enddate_minute => 'text', + enddate_second => 'text', + groupname => 'text', + description => 'text', + quota => 'text', + tool => 'checkbox', + granularity => 'radio', + no_end_date => 'checkbox', + }, + pick_members => { + member => 'checkbox', + defpriv => 'checkbox', + }, + }, + ); + + $elements{'modify'} = { + change_settings => { + %{$elements{'create'}{'pick_name'}}, + specificity => 'radio', + defpriv => 'checkbox', + autorole => 'checkbox', + autoadd => 'radio', + autodrop => 'radio', + }, + add_members => { + types => 'selectbox', + roles => 'selectbox', + }, + }; + + if (ref($stored{'autorole'}) eq 'ARRAY') { + foreach my $role (@{$stored{'autorole'}}) { + unless (($role eq 'cc') || ($role eq 'co')) { + $elements{'modify'}{'change_settings'}{'sec_'.$role} = + 'selectbox'; + } + } + } + + if (($action eq 'create') && ($state eq 'pick_name')) { + $elements{'create'}{'pick_name'}{'types'} = 'selectbox'; + $elements{'create'}{'pick_name'}{'roles'} = 'selectbox'; + } + if ((($action eq 'create') && + (($state eq 'pick_name') || ($state eq 'pick_privs'))) || + (($action eq 'modify') && (($state eq 'change_settings') || + ($state eq 'add_members')))) { + %sectioncount = &Apache::loncommon::get_sections($cdom,$cnum); + $elements{'create'}{'pick_name'}{'sectionpick'} = 'selectbox'; + $elements{'modify'}{'change_mapping'}{'sectionpick'} = 'selectbox'; + $elements{'modify'}{'add_members'}{'sectionpick'} = 'selectbox'; + } + + if (($action eq 'create') || + ($action eq 'modify' && $state eq 'pick_members')) { + if (defined($env{'form.types'})) { + @types=&Apache::loncommon::get_env_multiple('form.types'); + } + if (defined($env{'form.roles'})) { + @roles=&Apache::loncommon::get_env_multiple('form.roles'); + } + if (defined($env{'form.sectionpick'})) { + @sections=&Apache::loncommon::get_env_multiple('form.sectionpick'); + } + } + + if (($state eq 'pick_members') || ($state eq 'pick_privs') || ($state eq 'change_privs')) { + &build_members_list($cdom,$cnum,\@types,\@roles,\@sections,\%users, + \%userdata); + } + if ($state eq 'pick_members') { + if ((keys(%users) > 0) && (@tools > 0)) { + if ($granularity eq 'Yes') { + $elements{$action}{'pick_members'}{'togglefunc'} = 'checkbox'; + } + foreach my $tool (@tools) { + if ($granularity eq 'Yes') { + $elements{$action}{'pick_members'}{'user_'.$tool} = 'checkbox'; + } + } + $elements{$action}{'pick_members'}{'specificity'} = 'radio'; + } + } + if ($state eq 'change_members') { + my %membership = &Apache::lonnet::get_group_membership($cdom,$cnum, + $groupname); + my $now = time; + my $num_expire = 0; + my $num_activate = 0; + my $num_reenable = 0; + my $num_deletion = 0; + my $numusers = 0; + foreach my $key (sort(keys(%membership))) { + if ($key =~ /^\Q$groupname\E:([^:]+:[^:]+)$/) { + my $user = $1; + my($end,$start,@userprivs) = split(/:/,$membership{$key}); + unless ($start == -1) { + $numusers ++; + $num_deletion ++; + if (($end > 0) && ($end < $now)) { + $num_reenable ++; + next; + } elsif (($start > $now)) { + $num_activate ++; + next; + } else { + $num_expire ++; + next; + } + next; + } + if ($num_reenable && $num_activate && $num_expire) { + last; + } + } + } + if ($num_deletion) { + $elements{$action}{'change_members'}{'deletion'} = 'checkbox'; + } + if ($num_expire) { + $elements{$action}{'change_members'}{'expire'} = 'checkbox'; + } + if ($num_activate) { + $elements{$action}{'change_members'}{'activate'} = 'checkbox'; + } + if ($num_reenable) { + $elements{$action}{'change_members'}{'reenable'} = 'checkbox'; + } + if ($numusers) { + if ($granularity eq 'Yes') { + $elements{$action}{'change_members'}{'togglefunc'} = 'checkbox'; + } + foreach my $tool (@tools) { + if ($granularity eq 'Yes') { + $elements{$action}{'change_members'}{'user_'.$tool} = 'checkbox'; + } + } + if ($specificity eq 'Yes') { + $elements{$action}{'change_members'}{'changepriv'} = 'checkbox'; + } + } + } + + if (($state eq 'pick_privs') || ($state eq 'change_privs') || + (($specificity eq 'No') && + ($state eq 'memresult' || $state eq 'result' || $state eq 'addresult'))) { + foreach my $tool (@tools) { + my @values = &Apache::loncommon::get_env_multiple('form.user_'.$tool); + foreach my $user (@values) { + if ($state eq 'pick_privs' || $state eq 'result' + || $state eq 'addresult') { + if (!grep(/^\Q$user\E$/,@members)) { + next; + } + } + unless(exists($usertools{$user}{$tool})) { + $usertools{$user}{$tool} = 1; + } + } + } + } + + if (($action eq 'modify') && (($state eq 'change_privs') || ($state eq 'memresult'))) { + foreach my $chg (@member_changes) { + if (defined($env{'form.'.$chg})) { + @{$memchg{$chg}} = &Apache::loncommon::get_env_multiple('form.'.$chg); + } + } + + if ($state eq 'change_privs') { + my %membership = &Apache::lonnet::get_group_membership($cdom,$cnum, + $groupname); + my $now = time; + foreach my $key (sort(keys(%membership))) { + if ($key =~ /^\Q$groupname\E:([^:]+:[^:]+)$/) { + my $user = $1; + my $changefunc = 0; + my ($end,$start,@userprivs) = split(/:/,$membership{$key}); + unless ($start == -1) { + if (($end > 0) && ($end < $now)) { + unless (grep/^$user$/,$memchg{'reenable'}) { + next; + } + } + my @currtools = (); + if (@userprivs > 0) { + foreach my $tool (sort(keys(%{$fixedprivs}))) { + foreach my $priv (keys(%{$$fixedprivs{$tool}})) { + if (grep/^$priv$/,@userprivs) { + push(@currtools,$tool); + last; + } + } + } + } + foreach my $tool (@currtools) { + if (keys(%{$usertools{$user}}) > 0) { + if (!$usertools{$user}{$tool}) { + push(@{$memchg{'changefunc'}},$user); + $changefunc = 1; + last; + } + } else { + push(@{$memchg{'changefunc'}},$user); + $changefunc = 1; + } + } + if ($changefunc) { + next; + } + if (keys(%{$usertools{$user}}) > 0) { + foreach my $tool (keys(%{$usertools{$user}})) { + if (!grep/^$tool$/,@currtools) { + push(@{$memchg{'changefunc'}},$user); + $changefunc = 1; + last; + } + } + } + } + } + } + &check_changes(\@member_changes,\%memchg); + my %temptools; + foreach my $change (@member_changes) { + if (($change eq 'deletion') || ($change eq 'expire')) { + next; + } + foreach my $user (@{$memchg{$change}}) { + unless (exists($usertools{$user})) { + %{$usertools{$user}} = (); + } + %{$temptools{$user}} = %{$usertools{$user}}; + } + } + %usertools = %temptools; + } elsif ($state eq 'memresult') { + foreach my $change (@member_changes) { + if ($change eq 'expire' || $change eq 'deletion') { + next; + } + if (ref($memchg{$change}) eq 'ARRAY') { + my @users = @{$memchg{$change}}; + foreach my $user (@users) { + unless (exists($usertools{$user})) { + %{$usertools{$user}} = (); + } + } + } + } + } + } + + if ((($state eq 'pick_privs') || ($state eq 'change_privs')) + && ($specificity eq 'Yes')) { + foreach my $user (sort(keys(%usertools))) { + foreach my $tool (keys(%{$usertools{$user}})) { + foreach my $priv (keys(%{$$toolprivs{$tool}})) { + unless (exists($$fixedprivs{$tool}{$priv})) { + $elements{$action}{$state}{'userpriv_'.$priv} = 'checkbox'; + } + } + } + } + } + + if (($action eq 'create' && $state eq 'pick_name') || + ($action eq 'modify' && $state eq 'change_settings')) { + my ($crsquota,$freespace,$maxposs) = &get_quota_constraints($action,\%stored); + my $space_trim = '/^\s*|\s*\$/g,""'; + my $float_check = '/^([0-9]*\.?[0-9]*)$/'; + $validate_script = ' + var newquota = new String(document.'.$state.'.quota.value); + newquota.replace('.$space_trim.'); + if (newquota == "" ) { + document.'.$state.'.quota.value = 0; + newquota = "0"; + } + var maxposs = '.sprintf("%.2f",$maxposs).'; + if (newquota > maxposs) { + alert("The group portfolio quota you entered for this group ("+newquota+" MB) exceeds the maximum possible ("+maxposs+" MB). Please enter a smaller number."); + return; + } + var re_quota = '.$float_check.'; + var check_quota = newquota.match(re_quota); + if (check_quota == null) { + alert("The quota you entered contains invalid characters, the quota should only include numbers, with or without a decimal point."); + return; + } + if (newquota == 0) { + var warn_zero = 0; + for (var i=0; i<document.'.$state.'.tool.length; i++) { + if (document.'.$state.'.tool[i].value == "files") { + if (document.'.$state.'.tool[i].checked) { + warn_zero = 1; + } + } + } + if (warn_zero == 1) { + alert("You have indicated that the group portfolio should be enabled, but you have set the repository quota to 0 MB.\nThis will prevent any upload of files.\nPlease set a value or disable the repository feature."); + return; + } + } +'; + } + my $jscript = &Apache::loncommon::check_uncheck_jscript(); + $jscript .= qq| +function nextPage(formname,nextstate) { + formname.state.value= nextstate; + $validate_script + formname.submit(); +} +function backPage(formname,prevstate) { + formname.state.value = prevstate; + formname.submit(); +} +function changeSort(caller) { + document.$state.state.value = '$state'; + document.$state.sortby.value = caller; + document.$state.submit(); +} + +|; + $jscript .= &Apache::lonhtmlcommon::set_form_elements( + \%{$elements{$action}{$state}},\%stored); + my $page = 0; + my %states = (); + my %branchstates = (); + @{$states{'create'}} = ('pick_name','pick_members','pick_privs','result'); + @{$states{'modify'}} = ('pick_task'); + @{$states{'delete'}} = ('verify','result'); + @{$states{'reenable'}} = ('verify','result'); + @{$branchstates{'noprivs'}} = ('result'); + @{$branchstates{'settings'}} = ('change_settings','chgresult'); + @{$branchstates{'members'}} = ('change_members','change_privs','memresult'); + @{$branchstates{'adds'}} = ('add_members','pick_members','pick_privs', + 'addresult'); + + if (defined($env{'form.branch'})) { + push (@{$states{$action}},@{$branchstates{$env{'form.branch'}}}); + } + + if (($action eq 'create') || ($action eq 'modify') || ($action eq 'delete') || ($action eq 'reenable')) { + my $done = 0; + my $i=0; + while ($i<@{$states{$action}} && !$done) { + if ($states{$action}[$i] eq $state) { + $page = $i; + $done = 1; + } + $i++; + } + } + + my $loaditems = &onload_action($action,$state); + $r->print(&header("Groups Manager", + $jscript,$action,$state,$page,$loaditems)); + + if ($env{'form.refpage'} eq 'cusr') { + &Apache::lonhtmlcommon::add_breadcrumb + ({href=>"/adm/createuser", + text=>"User Management", + faq=>9,bug=>'Instructor Interface',}); + if ($action eq 'modify' || $action eq 'delete') { + &Apache::lonhtmlcommon::add_breadcrumb + ({href=>"/adm/coursegroups?refpage=cusr&action=$action", + text=>"Groups", + faq=>9,bug=>'Instructor Interface',}); + } + } else { + &Apache::lonhtmlcommon::add_breadcrumb + ({href=>"/adm/coursegroups", + text=>"Groups", + faq=>9,bug=>'Instructor Interface',}); + if ($env{'form.refpage'} eq 'grouplist') { + &Apache::lonhtmlcommon::add_breadcrumb + ({href=>"/adm/$cdom/$cnum/$env{'form.groupname'}/smppg?ref=grouplist", + text=>&mt('Group').": $description", + no_mt=>1}); + } + } + + my %trail = (); + %{$trail{'create'}} = &Apache::lonlocal::texthash ( + pick_name => $ucgpterm.' Settings', + pick_members => 'Select Members', + pick_privs => 'Choose Privileges', + result => 'Creation Complete', + ); + %{$trail{'modify'}} = &Apache::lonlocal::texthash( + pick_task => 'Choose Task', + change_settings => "$ucgpterm Settings", + change_members => 'Modify/Delete Members', + change_privs => 'Change Privileges', + change_mapping => 'Membership Mapping', + add_members => 'Add Members', + pick_members => 'Select Members', + pick_privs => 'Choose Privileges', + chgresult => 'Setting Changes Complete', + memresult => 'Modifications Complete', + addresult => 'Additions Complete', + ); + %{$trail{'delete'}} = &Apache::lonlocal::texthash( + verify => 'Verify deletion', + result => 'Deletion Complete' + ); + %{$trail{'reenable'}} = &Apache::lonlocal::texthash( + verify => 'Verify Re-enable', + result => 'Re-enabled' + ); + my %navbuttons = &Apache::lonlocal::texthash( + gtns => 'Next',#'Go to next step', + gtps => 'Back',#'Go to previous step', + crgr => 'Create '.$gpterm, + mose => 'Save',#'Modify settings', + gtpp => 'Back',#'Go to previous page', + adme => 'Add members', + ); + if ((($action eq 'create') || ($action eq 'modify') || ($action eq 'delete') || ($action eq 'reenable')) && + ($manage_permission)) { + for (my $i=0; $i<@{$states{$action}}; $i++) { + if ($state eq $states{$action}[$i]) { + &Apache::lonhtmlcommon::add_breadcrumb( + {text=>"$trail{$action}{$state}"}); + $r->print(&Apache::lonhtmlcommon::breadcrumbs + ("Groups Manager","Creating_Groups")); + &display_control($r,$cdom,$cnum,$action,$state,$page, + \%sectioncount,$groupname,$description,$functions, + \@tools,$toolprivs,$fixedprivs,$startdate,$enddate, + \%users,\%userdata,$idx,\%memchg,\%usertools, + $view_permission,$manage_permission, + \%stored,$granularity,$quota,$specificity,\@types,\@roles, + \@sections,\%states,\%navbuttons,$gpterm,$ucgpterm, + $crstype); + last; + } else { + if (($action eq 'create') || ($action eq 'modify')) { + if (($state eq 'result') && ($i > 0)) { + &Apache::lonhtmlcommon::add_breadcrumb( + {href=>"javascript:backPage(document.$state,'$states{$action}[0]')", + text=>"$trail{$action}{$states{$action}[$i]}"}); + } else { + &Apache::lonhtmlcommon::add_breadcrumb( + {href=>"javascript:backPage(document.$state,'$states{$action}[$i]')", + text=>"$trail{$action}{$states{$action}[$i]}"}); + } + } + } } + } elsif (($action eq 'view') && ($view_permission)) { + &Apache::lonhtmlcommon::add_breadcrumb( + {text=>"View $gpterm".'s'}); + $r->print(&Apache::lonhtmlcommon::breadcrumbs + ('Groups Manager')); + &display_groups($r,$cdom,$cnum,$functions,$idx,$view_permission, + $manage_permission,$action,$state,$gpterm,$ucgpterm, + $crstype); } $r->print(&footer()); - return OK; + return; } -sub header { - my $html=&Apache::lonxml::xmlbegin(); - my $bodytag=&Apache::loncommon::bodytag('Course Groups Manager'); - my $title = &mt('LON-CAPA Groups Manager'); - return(<<ENDHEAD); -$html -<head> -<title>$title</title> -</head> -$bodytag -<form method="post" - action="/adm/coursegroup" name="form"> -ENDHEAD +sub retrieve_settings { + my ($cdom,$cnum,$groupname,$action) = @_; + my %curr_groups; + my $namespace; + if ($action eq 'reenable') { + $namespace = 'deleted_groups'; + } else { + $namespace = 'coursegroups'; + } + %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum,$groupname, + $namespace); + + return if (!%curr_groups); + + my %groupinfo = + &Apache::longroup::get_group_settings($curr_groups{$groupname}); + + my %stored; + + $stored{'description'} = + &unescape($groupinfo{'description'}); + $stored{'startdate'} = $groupinfo{'startdate'}; + $stored{'enddate'} = $groupinfo{'enddate'}; + if ($stored{'enddate'} == 0) { + $stored{'no_end_date'} = 1; + } + $stored{'granularity'} = $groupinfo{'granularity'}; + $stored{'specificity'} = $groupinfo{'specificity'}; + $stored{'creation'} = $groupinfo{'creation'}; + $stored{'creator'} = $groupinfo{'creator'}; + $stored{'quota'} = $groupinfo{'quota'}; + + foreach my $tool (sort(keys(%{$groupinfo{'functions'}}))) { + if ($groupinfo{functions}{$tool} eq 'on') { + push(@{$stored{tool}},$tool); + } + } + foreach my $role (@{$groupinfo{'roles'}}) { + push(@{$stored{roles}},$role); + } + foreach my $type (@{$groupinfo{'types'}}) { + push(@{$stored{types}},$type); + } + foreach my $section (@{$groupinfo{'sectionpick'}}) { + push(@{$stored{sectionpick}},$section); + } + foreach my $defpriv (@{$groupinfo{'defpriv'}}) { + push(@{$stored{defpriv}},$defpriv); + } + $stored{'autoadd'} = $groupinfo{'autoadd'}; + $stored{'autodrop'} = $groupinfo{'autodrop'}; + if (exists($groupinfo{'autosec'})) { + foreach my $role (sort(keys(%{$groupinfo{'autosec'}}))) { + if (ref($groupinfo{'autosec'}{$role}) eq 'ARRAY') { + foreach my $section (@{$groupinfo{'autosec'}{$role}}) { + + push (@{$stored{'sec_'.$role}},$section); + } + if (@{$groupinfo{'autosec'}{$role}} > 0) { + push(@{$stored{'autorole'}},$role); + } + } + } + } + return %stored; } -sub print_main_menu { - my ($r,$manage_permission,$view_permission)=@_; - my ($cdom,$cnum) = split/_/,$env{'request.course.id'}; - my @menu = - ( - { text => 'Create a new group', - help => 'Course_Create_Group', - action => 'create', - permission => $manage_permission, - }, - { text => 'Modify an existing group', - help => 'Course_Modify_Group', - action => 'modify', - permission => $manage_permission, - }, - { text => 'Delete an existing group', - help => 'Course_Delete_Group', - action => 'delete', - permission => $manage_permission, - }, - { text => 'Enter an existing group', - help => 'Course_Display_Group', - action => 'display', - permission => $view_permission, - }, - ); - my $menu_html = ''; - foreach my $menu_item (@menu) { - next if (! $menu_item->{'permission'}); - $menu_html.='<p>'; - $menu_html.='<font size="+1">'; - if (exists($menu_item->{'url'})) { - $menu_html.=qq{<a href="$menu_item->{'url'}">}; - } else { - $menu_html.= - qq{<a href="/adm/coursegroups?action=$menu_item->{'action'}">}; - } - $menu_html.= &mt($menu_item->{'text'}).'</a></font>'; - if (exists($menu_item->{'help'})) { - $menu_html.= - &Apache::loncommon::help_open_topic($menu_item->{'help'}); +sub display_control { + my ($r,$cdom,$cnum,$action,$state,$page,$sectioncount,$groupname, + $description,$functions,$tools,$toolprivs,$fixedprivs,$startdate, + $enddate,$users,$userdata,$idx,$memchg,$usertools, + $view_permission,$manage_permission,$stored,$granularity,$quota, + $specificity,$types,$roles,$sections,$states,$navbuttons, + $gpterm,$ucgpterm,$crstype) = @_; + if ($action eq 'create') { + if ($state eq 'pick_name') { + &general_settings_form($r,$cdom,$cnum,$action,$state,$page, + $functions,$tools,$toolprivs,$fixedprivs, + $sectioncount,$stored,$states,$navbuttons, + $gpterm,$ucgpterm,$crstype); + } elsif ($state eq 'pick_members') { + &choose_members_form($r,$cdom,$cnum,$action,$state,$page, + $groupname,$description,$granularity,$quota, + $startdate,$enddate,$tools,$fixedprivs, + $toolprivs,$functions,$users,$userdata,$idx, + $stored,$states,$navbuttons,$gpterm,$ucgpterm, + $crstype); + } elsif ($state eq 'pick_privs') { + &choose_privs_form($r,$cdom,$cnum,$action,$state,$page, + $startdate,$enddate,$tools,$functions, + $toolprivs,$fixedprivs,$userdata,$usertools, + $idx,$states,$stored,$sectioncount,$navbuttons, + $gpterm,$ucgpterm,$crstype); + } elsif ($state eq 'result') { + &process_request($r,$cdom,$cnum,$action,$state,$page, + $groupname,$description,$specificity,$userdata, + $startdate,$enddate,$tools,$functions, + $toolprivs,$usertools,$idx,$types,$roles, + $sections,$states,$navbuttons,$memchg, + $sectioncount,$stored,$gpterm,$ucgpterm,$crstype); + } + } elsif ($action eq 'modify') { + my $groupname = $env{'form.groupname'}; + if ($state eq 'pick_task') { + &modify_menu($r,$groupname,$page,$gpterm); + } elsif ($state eq 'change_settings') { + &general_settings_form($r,$cdom,$cnum,$action,$state,$page, + $functions,$tools,$toolprivs,$fixedprivs, + $sectioncount,$stored,$states,$navbuttons, + $gpterm,$ucgpterm,$crstype); + } elsif ($state eq 'change_members') { + &change_members_form($r,$cdom,$cnum,$action,$state,$page, + $groupname,$description,$startdate,$enddate, + $tools,$fixedprivs,$functions,$users, + $userdata,$granularity,$quota,$specificity, + $idx,$states,$navbuttons,$gpterm,$ucgpterm); + } elsif ($state eq 'add_members') { + &add_members_form($r,$cdom,$cnum,$action,$state,$page,$startdate, + $enddate,$groupname,$description,$granularity, + $quota,$sectioncount,$tools,$functions,$stored, + $states,$navbuttons,$gpterm,$ucgpterm,$crstype); + } elsif ($state eq 'pick_members') { + &choose_members_form($r,$cdom,$cnum,$action,$state,$page, + $groupname,$description,$granularity,$quota, + $startdate,$enddate,$tools,$fixedprivs, + $toolprivs,$functions,$users,$userdata,$idx, + $stored,$states,$navbuttons,$gpterm,$ucgpterm, + $crstype); + } elsif ($state eq 'pick_privs') { + &choose_privs_form($r,$cdom,$cnum,$action,$state,$page, + $startdate,$enddate,$tools,$functions, + $toolprivs,$fixedprivs,$userdata,$usertools, + $idx,$states,$stored,$sectioncount,$navbuttons, + $gpterm,$ucgpterm,$crstype); + } elsif ($state eq 'change_privs') { + &change_privs_form($r,$cdom,$cnum,$action,$state,$page, + $startdate,$enddate,$tools,$functions, + $toolprivs,$fixedprivs,$userdata,$usertools, + $memchg,$idx,$states,$stored,$sectioncount, + $navbuttons,$gpterm,$ucgpterm); + } elsif ($state eq 'chgresult' || $state eq 'memresult' || + $state eq 'addresult') { + &process_request($r,$cdom,$cnum,$action,$state,$page, + $groupname,$description,$specificity,$userdata, + $startdate,$enddate,$tools,$functions, + $toolprivs,$usertools,$idx,$types,$roles, + $sections,$states,$navbuttons,$memchg, + $sectioncount,$stored,$gpterm,$ucgpterm,$crstype); } - $menu_html.='</p>'.$/; + } elsif ($action eq 'delete') { + my %stored = &retrieve_settings($cdom,$cnum,$groupname,$action); + if ($state eq 'verify') { + &verify_delete($r,$groupname,$state,$action,$page,$states, + \%stored,$crstype); + } elsif ($state eq 'result') { + &delete_group($r,$cdom,$cnum,$groupname,$crstype); + } + } elsif ($action eq 'reenable') { + my %stored = &retrieve_settings($cdom,$cnum,$groupname,$action); + if ($state eq 'verify') { + &verify_reenable($r,$groupname,$state,$action,$page,$states, + \%stored,$crstype); + } elsif ($state eq 'result') { + &reenable_group($r,$cdom,$cnum,$groupname,$crstype); + } + } +} + +sub verify_delete { + my ($r,$groupname,$formname,$action,$page,$states,$stored,$crstype) = @_; + $r->print(&Apache::lonhtmlcommon::echo_form_input([])); + $r->print(&mt('You have requested deletion of the group [_1].' + ,'<i>'.$stored->{'description'}.'</i>'). + '<br /><br />'.&mt('When a group is deleted the following occurs:').'<ul>'. + '<li>'.&mt('All group membership is terminated.').'</li>'. + '<li>'.&mt('The group ceases to be available either for viewing or for modification of group settings and membership.').'</li>'); + if ($crstype eq 'Community') { + $r->print( '<li>'.&mt("The group folder is removed from the folder containing it - normally this is the 'Community Groups' folder which contains folders for all groups in the community.").'</li>'. + '</ul>'.&mt("Although a deleted group is no longer accessible, the group name used for the group will be reserved, and will not be available for assignment to a new group in the same community in the future.")); + } else { + $r->print( '<li>'.&mt("The group folder is removed from the folder containing it - normally this is the 'Course Groups' folder which contains folders for all groups in the course.").'</li>'. + '</ul>'.&mt("Although a deleted group is no longer accessible, the group name used for the group will be reserved, and will not be available for assignment to a new group in the same course in the future.")); } - $r->print($menu_html); + my $prevtext = &mt('Go back'); + my $nexttext = &mt('Delete group'); + my $prev; + if ($env{'form.refpage'} eq 'cusr') { + $prev = 'view'; + } + &display_navbuttons($r,$formname,$prev,$prevtext, + $$states{$action}[$page+1],$nexttext); return; } +sub delete_group { + my ($r,$cdom,$cnum,$groupname,$crstype) = @_; + my %membership = &Apache::lonnet::get_group_membership($cdom,$cnum, + $groupname); + my $now = time; + my $num_users = 0; + my $num_fail = 0; + my $num_ok = 0; + my @deleted; + my @undeleted; + my %usersettings; + my $context = 'deletegroup'; + foreach my $key (sort(keys(%membership))) { + if ($key =~ /^\Q$groupname\E:([^:]+:[^:]+)$/) { + my $user = $1; + my($end,$start,$userprivs) = split(/:/,$membership{$key},3); + if ($start != -1) { + $num_users ++; + $usersettings{$groupname.':'.$user} = $now.':-1:'.$userprivs; + if (&Apache::lonnet::modify_group_roles($cdom,$cnum, + $groupname,$user, + $now,'-1',$userprivs, + '',$context) + eq 'ok') { + $num_ok ++; + push(@deleted,$user); + } else { + push(@undeleted,$user); + $num_fail ++; + } + } + } + } + if ($num_ok > 0) { + my $roster_result = + &Apache::lonnet::modify_coursegroup_membership($cdom,$cnum, + \%usersettings); + } + if ($num_fail > 0) { + $r->print('<div class="LC_error">' + .&mt('Group deletion failed because deletion of [_1] out of [_2] members failed.' + ,$num_fail,$num_users) + .'</div>'); + } else { + my ($result,$message) = + &Apache::lonnet::toggle_coursegroup_status($cdom,$cnum, + $groupname,'delete'); + if ($result eq 'ok') { + my $outcome = &modify_folders($cdom,$cnum,$groupname,$crstype); + if ($outcome eq '') { + my $message = &Apache::lonhtmlcommon::confirm_success(&mt('Group successfully deleted.')); + $message = &Apache::loncommon::confirmwrapper($message); + $r->print($message); + } else { + $r->print('<div class="LC_error">'); + if ($crstype eq 'Community') { + $r->print(&mt("Although the group was deleted, an error occurred when removing the group's folder from the 'Community Groups' folder: [_1]",$outcome)); + } else { + $r->print(&mt("Although the group was deleted, an error occurred when removing the group's folder from the 'Course Groups' folder: [_1]",$outcome)); + } + $r->print('</div>'); + } + } else { + my $msg = &Apache::lonhtmlcommon::confirm_success(&mt('Group deletion failed.'),1); + $msg = &Apache::loncommon::confirmwrapper($msg); + $r->print($msg); + } + } + return; +} + +sub reenable_folder { + my ($cdom,$cnum,$groupname,$description,$crstype) = @_; + my $outcome; + my $crspath = '/uploaded/'.$cdom.'/'.$cnum.'/'; + my $allgrpsmap = $crspath.'group_allfolders.sequence'; + my $foldertitle; + if ($crstype eq 'Community') { + $foldertitle = &mt("Community Folder -[_1]",$description); + } else { + $foldertitle = &mt("Course Folder -[_1]",$description); + } + my $mapurl = $crspath.'group_folder_'. + $groupname.'.sequence'; + my ($errtext,$fatal)=&LONCAPA::map::mapread($allgrpsmap); + if ($fatal) { + $outcome='<div class="LC_error">' + .&mt('An error occurred when reading contents of parent folder to group:') + ."<br />($allgrpsmap): $errtext" + .'</div>'; + } else { + my $idx=&LONCAPA::map::getresidx($mapurl); + $LONCAPA::map::resources[$idx] = $foldertitle.':'.$mapurl. + ':false:normal:res'; + $LONCAPA::map::order[1+$#LONCAPA::map::order]=$idx; + my ($outtext,$errtext) = &LONCAPA::map::storemap($allgrpsmap,1,1); + if ($errtext) { + $outcome='<div class="LC_error">' + .&mt('An error occurred when saving updated parent folder to group:' + ,"<br />$allgrpsmap - $errtext") + .'</div>'; + } else { + my ($furl,$ferr) = + &Apache::lonuserstate::readmap($cdom.'/'.$cnum); + } + } + return $outcome; +} + +sub modify_folders { + my ($cdom,$cnum,$groupname,$crstype) = @_; + my ($outcome,$groupmap,$groupmapres,$map,$id,$src); + my $navmap = Apache::lonnavmaps::navmap->new(); + if (!defined($navmap)) { + $outcome = '<div class="LC_error">'; + if ($crstype eq 'Community') { + $outcome .= &mt("Error reading community contents.").' '. + &mt("You need to re-initialize the community."); + } else { + $outcome .= &mt("Error reading course contents.").' '. + &mt("You need to re-initialize the course."); + } + $outcome .= '</div>'; + return $outcome; + } + $groupmap = '/uploaded/'.$cdom.'/'.$cnum.'/'.'group_folder_'. + $groupname.'.sequence'; + $groupmapres = $navmap->getResourceByUrl($groupmap); + if ($groupmapres) { + ($map,$id,$src)=&Apache::lonnet::decode_symb($groupmapres->symb()); + } + undef($navmap); + if ($map) { + $map = '/'.$map; + my ($errtext,$fatal) = &LONCAPA::map::mapread($map); + if ($fatal) { + $outcome='<div class="LC_error">' + .&mt('An error occurred when reading contents of parent folder to group:') + ."<br />($map): $errtext" + .'</div>'; + } else { + my $idx = 0; + my $grpidx; + foreach my $item (@LONCAPA::map::order) { + my ($name,$url)=split(/\:/,$LONCAPA::map::resources[$item]); + $url=&LONCAPA::map::qtescape($url); + if ($url eq $groupmap) { + $grpidx = $idx; + last; + } else { + $idx++; + } + } + + if ($grpidx ne '') { + &LONCAPA::map::makezombie($LONCAPA::map::order[$grpidx]); + for (my $i=$grpidx;$i<$#LONCAPA::map::order;$i++) { + $LONCAPA::map::order[$i] = $LONCAPA::map::order[$i+1]; + } + $#LONCAPA::map::order--; + my ($outtext,$errtext) = &LONCAPA::map::storemap($map,1,1); + if ($errtext) { + $outcome='<div class="LC_error">' + .&mt('An error occurred when saving updated parent folder to group:') + ."<br />$map - $errtext" + .'</div>'; + } else { + my ($furl,$ferr) = + &Apache::lonuserstate::readmap($cdom.'/'.$cnum); + } + } + } + } + return $outcome; +} + +sub verify_reenable { + my ($r,$groupname,$formname,$action,$page,$states,$stored,$crstype) = @_; + $r->print(&Apache::lonhtmlcommon::echo_form_input([])); + $r->print(&mt('You have requested enabling the previously deleted group [_1].' + ,'<i>'.$stored->{'description'}.'</i>'). + '<br /><br />'.&mt('When a deleted group is re-enabled the following occurs:').'<ul>'. + '<li>'.&mt('Group settings and membership at the time the group was deleted are reinstated.').'</li><li>'); + if ($crstype eq 'Community') { + $r->print(&mt("A group folder is added to the 'Community Groups' folder which contains folders for all groups in the community.")); + } else { + $r->print(&mt("A group folder is added to the 'Course Groups' folder which contains folders for all groups in the course.")); + } + $r->print('</li></ul>'); + my $prevtext = &mt('Go back'); + my $nexttext = &mt('Reenable group'); + my $prev; + if ($env{'form.refpage'} eq 'cusr') { + $prev = 'view'; + } + &display_navbuttons($r,$formname,$prev,$prevtext, + $$states{$action}[$page+1],$nexttext); + return; +} + +sub reenable_group { + my ($r,$cdom,$cnum,$groupname,$crstype) = @_; + my %groups = + &Apache::longroup::coursegroups($cdom,$cnum,$groupname, + 'deleted_groups'); + if (keys(%groups) == 0) { + $r->print(&mt('The group [_1] was not re-enabled, because it is not a deleted group.[_2]Perhaps it has already been re-enabled?','<i>'.$groupname.'</i>'),'<br />'); + return; + } + my %groupinfo = + &Apache::longroup::get_group_settings($groups{$groupname}); + my $defstart = $groupinfo{'startdate'}; + my $defend = $groupinfo{'enddate'}; + my $showstart = &Apache::lonlocal::locallocaltime($defstart); + my $showend; + if ($defend == 0) { + $showend = &mt('No end date set'); + } else { + $showend = &Apache::lonlocal::locallocaltime($defend); + } + my $description = &unescape($groupinfo{'description'}); + my $num_users = 0; + my $num_ok = 0; + my $num_fail = 0; + my $context = 'reenablegroup'; + my (%usersettings,@enabled,@unenabled); + my ($result,$message) = + &Apache::lonnet::toggle_coursegroup_status($cdom,$cnum,$groupname, + 'reenable'); + if ($result eq 'ok') { + my %membership = &Apache::lonnet::get_group_membership($cdom,$cnum, + $groupname); + foreach my $key (sort(keys(%membership))) { + if ($key =~ /^\Q$groupname\E:([^:]+:[^:]+)$/) { + my $user = $1; + my($end,$start,$userprivs) = split(/:/,$membership{$key},3); + if (($start == -1) && ($end == $groupinfo{'modified'})) { + $num_users ++; + $usersettings{$groupname.':'.$user} = $defend.':'. + $defstart.':'. + $userprivs; + if (&Apache::lonnet::modify_group_roles($cdom,$cnum, + $groupname,$user, + $defend,$defstart, + $userprivs,'', +$context) eq 'ok') { + $num_ok ++; + push(@enabled,$user); + } else { + push(@unenabled,$user); + $num_fail ++; + } + } + } + } + if ($num_users > 0) { + if ($num_ok > 0) { + my $roster_result = + &Apache::lonnet::modify_coursegroup_membership($cdom,$cnum, + \%usersettings); + if ($roster_result eq 'ok') { + $r->print('<div class="LC_success">' + .&mt('Membership reinstated for [quant,_1,user], each with start and end dates for group access set to defaults: [_2] and [_3]',$num_ok,$showstart,$showend) + .'</div>'); + } + } else { + $r->print('<div class="LC_error">' + .&mt('A problem occurred when trying to reinstate [_1] of the [_2] members of the pre-existing group.',$num_fail,$num_users) + .'</div>'); + } + } else { + $r->print('<div class="LC_info">' + .&mt('There were no group members to reinstate, as none were removed when the group was deleted.') + .'</div>'); + } + my $outcome = &reenable_folder($cdom,$cnum,$groupname,$description,$crstype); + if ($outcome eq '') { + my $message = &Apache::lonhtmlcommon::confirm_success(&mt('Group successfully re-enabled.')); + $message = &Apache::loncommon::confirmwrapper($message); + $r->print($message); + } else { + $r->print('<div class="LC_error">'); + if ($crstype eq 'Community') { + $r->print(&mt("Although the group was re-enabled, an error occurred when adding the group's folder to the 'Community Groups' folder: [_1]",$outcome)); + } else { + $r->print(&mt("Although the group was re-enabled, an error occurred when adding the group's folder to the 'Course Groups' folder: [_1]",$outcome)); + } + $r->print('</div>'); + } + } else { + my $message = &Apache::lonhtmlcommon::confirm_success(&mt('Re-enabling group failed.'),1); + $message = &Apache::loncommon::confirmwrapper($message); + $r->print($message); + } + return; +} + +sub header { + my ($bodytitle,$jscript,$action,$state,$page,$loaditems) = @_; + my $start_page= + &Apache::loncommon::start_page($bodytitle, + '<script type="text/javascript">'. + $jscript.'</script>', + {'add_entries' => $loaditems,}); + my $output = <<"END"; +$start_page +<form method="post" name="$state" action=""> + +END + if ($action eq 'create' || $action eq 'modify') { + $output .= <<"END"; + <input type="hidden" name="action" value="$action" /> + <input type="hidden" name="state" value="" /> + <input type="hidden" name="origin" value="$state" /> + <input type="hidden" name="page" value="$page" /> +END + } + return $output; +} + +sub onload_action { + my ($action,$state) = @_; + my %loaditems; + if ((defined($env{'form.origin'})) && ($action eq 'create') && + ($state eq 'pick_name' || $state eq 'pick_members' || + $state eq 'pick_privs')) { + unless ($env{'form.origin'} eq '') { + $loaditems{'onload'} = + 'javascript:setFormElements(document.'.$state.')'; + } + } + if (($action eq 'modify') && + ($state eq 'change_settings' || $state eq 'change_members' || + $state eq 'change_privs' || $state eq 'add_members')) { + $loaditems{'onload'} = + 'javascript:setFormElements(document.'.$state.')'; + } + return \%loaditems; +} + sub footer { + my $end_page = &Apache::loncommon::end_page(); return(<<ENDFOOT); + <input type="hidden" name="sortby" value="$env{'form.sortby'}" /> </form> - </body> -</html> +$end_page ENDFOOT - } -sub first_creation_form { - my ($r) = @_; +sub build_members_list { + my ($cdom,$cnum,$types,$roles,$sections,$users,$userdata) = @_; + my %access = (); + foreach my $role (@{$roles}) { + %{$$users{$role}} = (); + } + foreach my $type (@{$types}) { + $access{$type} = $type; + } + &Apache::loncommon::get_course_users($cdom,$cnum,\%access,$roles, + $sections,$users,$userdata); + return; +} + +sub group_files { + my ($group,$portpath,$numfiles,$numdirs) = @_; + my $dirptr=16384; + my ($dirlistref,$listerror) = + &Apache::portfolio::get_dir_list($portpath,undef,$group); + if (ref($dirlistref) eq 'ARRAY') { + foreach my $line (@{$dirlistref}) { + my ($filename,$dom,undef,$testdir,undef,undef,undef,undef,$size,undef,$mtime,undef,undef,undef,$obs,undef)=split(/\&/,$line,16); + if (($filename !~ /^\.\.?$/) && ($filename !~ /\.meta$/ ) && ($filename !~ /(.*)\.(\d+)\.([^\.]*)$/) && ($filename ne 'no_such_dir')) { + if ($dirptr&$testdir) { + $portpath .= '/'.$filename; + $$numdirs ++; + &group_files($group,$portpath,$numfiles,$numdirs) + } else { + $$numfiles ++; + } + } + } + } + return; +} + +sub group_members { + my ($cdom,$cnum,$group,$group_info) = @_; + my %memberhash = &Apache::lonnet::get_group_membership($cdom,$cnum,$group); + my $now = time; + my %lt = &Apache::lonlocal::texthash ( + active => 'active', + previous => 'previous', + future => 'future', + ); + my %membercounts = ( + active => 0, + previous => 0, + future => 0, + ); + my $totalmembers = 0; + foreach my $member (keys(%memberhash)) { + $totalmembers ++; + my ($end,$start) = split(/:/,$memberhash{$member}); + unless ($start == -1) { + if (($end!=0) && ($end<$now)) { + $membercounts{previous} ++; + } elsif (($start!=0) && ($start>$now)) { + $membercounts{future} ++; + } else { + $membercounts{active} ++; + } + } + } + if ($totalmembers == 0) { + $$group_info{$group}{'totalmembers'} = 'None'; + } else { + foreach my $type ('active','previous','future') { + $$group_info{$group}{'totalmembers'} .= + &open_list_window($group,$type,$membercounts{$type},$lt{$type}); + } + } + return 'ok'; +} + +sub open_list_window { + my ($group,$status,$count,$text) = @_; + my $entry; + if ($count > 0) { + $entry = '<span class="LC_nobreak"><a href="javascript:openGroupRoster('. + "'$group','$status'".')">'.$text.'</a> - '.$count. + '</span><br />'; + } else { + $entry = '<span class="LC_nobreak">'.$text.' - '.$count.'</span><br />'; + } + return $entry; +} + + +sub general_settings_form { + my ($r,$cdom,$cnum,$action,$formname,$page,$functions,$tools, + $toolprivs,$fixedprivs,$sectioncount,$stored,$states,$navbuttons, + $gpterm,$ucgpterm,$crstype) = @_; + my ($nexttext,$prevtext); + &groupsettings_options($r,$functions,$action,$formname,$stored,1, + $gpterm,$ucgpterm,$crstype); + &access_date_settings($r,$action,$formname,$stored,2,$gpterm,$ucgpterm); + if ($action eq 'create') { + &membership_options($r,$cdom,$cnum,$action,$formname,$sectioncount,3, + $gpterm,$ucgpterm,$crstype); + $nexttext = $$navbuttons{'gtns'}; + } else { + my @available = (); + my @unavailable = (); + &check_tools($functions,$tools,\@available,\@unavailable); + @{$tools} = sort(keys(%{$functions})); + &privilege_specificity($r,$action,3,$tools,$stored,$toolprivs, + $fixedprivs,\@available,$formname, + $gpterm,$ucgpterm,$functions,$crstype); + &mapping_options($r,$action,$formname,$page,$sectioncount, + $states,$stored,$navbuttons,4,5, + $gpterm,$ucgpterm,$crstype,$cdom,$cnum); + $nexttext = $$navbuttons{'mose'}; + } + $prevtext = $$navbuttons{'gtpp'}; + &display_navbuttons($r,$formname,$$states{$action}[$page-1],$prevtext, + $$states{$action}[$page+1],$nexttext); + return; +} + +sub groupsettings_options { + my ($r,$functions,$action,$formname,$stored,$image,$gpterm, + $ucgpterm,$crstype) = @_; my %lt = &Apache::lonlocal::texthash( - 'gmem' => 'Group membership options', - 'picr' => 'Pick the criteria to use to build a list of course users from which you will select members of the new group', - 'gdat' => 'Group open and close dates', - 'sten' => 'Set a start date/time and end date/time for the group', - 'acst' => 'Active/Inactive status', - 'coro' => 'Course roles', - 'cose' => 'Course sections', - 'gfun' => 'Group functionality', + 'gdat' => "Group access start and end dates", + 'gnde' => "Group name, title and available collaborative tools", + 'desc' => 'Group Title', + 'func' => 'Collaborative Tools', + 'gnam' => 'Group Name', + 'lett' => 'Letters, numbers and underscore only', + 'doyo' => 'Different subsets of the chosen collaborative tools '. + 'for different group members?', + 'gran' => 'Granularity', + 'dquo' => 'Disk quota', ); + my ($crsquota,$freespace,$maxposs) = &get_quota_constraints($action,$stored); + $r->print(&Apache::lonhtmlcommon::topic_bar($image,$lt{'gnde'})); + # Group Name + $r->print(&Apache::lonhtmlcommon::start_pick_box() + .&Apache::lonhtmlcommon::row_title($lt{'gnam'}) + ); + if ($action eq 'create') { + $r->print('<input type="text" name="groupname" size="25" />' + .' <span class="LC_nobreak">(' + .$lt{'lett'}.')</span>' + ); + } else { + $r->print('<input type="hidden" name="groupname" value="'. + $env{'form.groupname'}.'" />'.$env{'form.groupname'}); + } + $r->print(&Apache::lonhtmlcommon::row_closure()); + + # Group Title + $r->print(&Apache::lonhtmlcommon::row_title($lt{'desc'}) + .'<input type="text" name="description" size="40" value="" />' + .&Apache::lonhtmlcommon::row_closure() + ); + + # Collaborative Tools + my $numitems = keys(%{$functions}); + my $halfnum = int($numitems/2); + my $remnum = $numitems%2; + if ($remnum) { + $halfnum ++; + } + my @allfunctions = sort(keys(%{$functions})); + + $r->print(&Apache::lonhtmlcommon::row_title($lt{'func'}) + .'<div>' + .'<input type="button" value="'.&mt('check all').'"' + .' onclick="javascript:checkAll(document.'.$formname.'.tool)" />' + .' <input type="button" value="'.&mt('uncheck all').'"' + .' onclick="javascript:uncheckAll(document.'.$formname.'.tool)" />' + .'</div>' + .'<table cellpadding="5px"><tr>' # FIXME Get rid of inflexible table (-> float) + ); + for (my $i=0; $i<@allfunctions; $i++) { + $r->print('<td><label><span class="LC_nobreak">' + .'<input type="checkbox" name="tool" value="' + .$allfunctions[$i].'" /> ' + .&mt($$functions{$allfunctions[$i]}) + .'</span></label></td>' + ); + if ($i == $halfnum - 1) { + $r->print('</tr><tr>'); + } + } + $r->print('</tr></table>' + .&Apache::lonhtmlcommon::row_closure() + ); + + # Granularity + $r->print(&Apache::lonhtmlcommon::row_title($lt{'gran'}) + .$lt{'doyo'}.'<br />' + .'<label>' + .'<input type="radio" name="granularity" value="Yes" />'.&mt('Yes') + .'</label> <label>' + .'<input type="radio" name="granularity" value="No" checked="checked" />'.&mt('No') + .'</label>' + ); + if ($action eq 'modify') { + $r->print(' <span class="LC_nobreak">(' + .&mt('Currently set to [_1].' + ,'"'.&mt($$stored{'granularity'}).'"') + .')</span>' + ); + } + $r->print(&Apache::lonhtmlcommon::row_closure()); + + # Disk Quota + $r->print(&Apache::lonhtmlcommon::row_title($lt{'dquo'})); + if ($action eq 'create') { + $r->print('<span class="LC_info">' + .&mt('If you enable the group portfolio for the '.$gpterm + .', allocate a disk quota.') + .'</span>' + ); + } else { + $r->print(&mt('Quota allocated to group portfolio:')); + } + $r->print(' '.&mt('[_1] MB','<input type="text" name="quota" size="4" />')); + if ($action eq 'create') { + $r->print('<br />' + .&mt('A total of [_1] MB can be divided amongst all '.$gpterm.'s in the ' + .lc($crstype).', and [_2] MB are currently unallocated.' + ,$crsquota,sprintf("%.2f",$freespace)) + ); + } else { + $r->print(' ('.&mt('The quota is currently [_1] MB', + $$stored{'quota'}).').'); + + $r->print('<br />' + .&mt('The quota can be increased to [_1] MB, ' + .'by adding all unallocated space for '.$gpterm.'s in the '.lc($crstype).'.' + ,sprintf("%.2f",$maxposs))); + } + $r->print(&Apache::lonhtmlcommon::row_closure(1)); + + $r->print(&Apache::lonhtmlcommon::end_pick_box()); + + return; +} + +sub get_quota_constraints { + my ($action,$stored) = @_; + my ($crsquota,$freespace,$maxposs); + $crsquota = $env{'course.'.$env{'request.course.id'}.'.internal.coursequota'}; + if ($crsquota eq '') { + my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; + my $crstype = &Apache::loncommon::course_type(); + my %domdefs = &Apache::lonnet::get_domain_defaults($cdom); + my %coursehash = ( + 'internal.coursecode' => $env{'course.'.$env{'request.course.id'}.'.internal.coursecode'}, + 'internal.textbook' => $env{'course.'.$env{'request.course.id'}.'.internal.textbook'}, + ); + my %staticdefaults = ( + coursequota => 20, + ); + my $quotatype = &Apache::lonuserutils::get_extended_type($cdom,$cnum,$crstype,\%coursehash); + if ($crsquota eq '') { + $crsquota = $domdefs{$quotatype.'coursequota'}; + if ($crsquota eq '') { + $crsquota = $staticdefaults{'coursequota'}; + } + } + } + $freespace = $crsquota - &Apache::longroup::sum_quotas(); + if ($action eq 'create') { + $maxposs = $freespace; + } else { + $maxposs = $$stored{'quota'} + $freespace; + } + return ($crsquota,$freespace,$maxposs); +} + +sub membership_options { + my ($r,$cdom,$cnum,$action,$state,$sectioncount,$image,$gpterm,$ucgpterm,$crstype)=@_; + my %lt = &Apache::lonlocal::texthash( + 'pipa' => 'Build a list of users for selection of group members', + 'gmem' => 'Group membership selection list criteria:', + 'picr' => 'Pick the criteria to use to build a list of course users from which you will select members of the new group.', + 'pica' => 'Pick the criteria to use to build a list of course users from which you will select additional members of the group.', + 'ifno' => 'If you do not wish to add members when you first create the group, there is no need to pick any criteria.', + 'acty' => 'Access types', + 'coro' => 'Course roles', + 'cose' => 'Course sections', + ); + if ($crstype eq 'Community') { + $lt{'picr'} = &mt('Pick the criteria to use to build a list of community participants from which you will select '); + $lt{'asub'} = &mt('A subsequent step will also allow you to specify automatic adding/dropping of group members triggered by specified user role and section [_1]changes[_2] in the course.','<i>','</i>'); + $lt{'coro'} = &mt('Community roles'); + $lt{'cose'} = &mt('Community sections'); + } else { + $lt{'asub'} = &mt('A subsequent step will also allow you to specify automatic adding/dropping of group members triggered by specified user role and section [_1]changes[_2] in the course.','<i>','</i>'); + } my %status_types = ( active => &mt('Currently has access'), previous => &mt('Previously had access'), future => &mt('Will have future access'), ); - my @roles = ('st','cc','in','ta','ep','cr'); - - my %sectioncount = (); - my @sections = (); - my $section_sel = ''; - my $numvisible; - my $numsections = &Apache::loncommon::get_sections($env{'course.'.$env{'request.course.id'}.'.domain'},$env{'course.'.$env{'request.course.id'}.'.num'},\%sectioncount); - - @sections = sort {$a cmp $b} keys(%sectioncount); - unshift(@sections,'all'); # Put 'all' at the front of the list - if ($numsections < 4) { - $numvisible = $numsections + 1; + my @roles = ('st'); + if ($crstype eq 'Community') { + push(@roles,'co'); + } else { + push(@roles,'cc'); } + push (@roles,('in','ta','ep','ad','cr')); - $r->print(<<"END"); -<b>$lt{'gmem'}</b><br/> $lt{'picr'} -<br /><br /> -<table border="0"> - <tr> - <td><b>$lt{'acst'}</b></td> - <td> </td> - <td><b>$lt{'coro'}</b></td> - <td> </td> - <td><b>$lt{'cose'}</b></td> - <td> </td> - </tr> - <tr> - <tr> -END + my @sections = keys(%{$sectioncount}); + + $r->print(&Apache::lonhtmlcommon::topic_bar($image,$lt{'pipa'}).' + <b>'.$lt{'gmem'}.'</b><br />'); + if ($action eq 'create') { + $r->print($lt{'picr'}.'<br />'.$lt{'ifno'}.'<br />'.$lt{'asub'}); + } else { + $r->print($lt{'pica'}); + } + $r->print(' + <br /> + <br /> + <table class="LC_status_selector"> + <tr> + <th>'.$lt{'acty'}.'</th> + <th>'.$lt{'coro'}.'</th> + <th>'.$lt{'cose'}.'</th> + </tr><tr><td>'); $r->print(&Apache::lonhtmlcommon::status_select_row(\%status_types)); - $r->print('<td> </td>'); - $r->print(&Apache::lonhtmlcommon::role_select_row(\@roles)); + $r->print('</td><td>'); + $r->print(&Apache::lonhtmlcommon::role_select_row(\@roles,undef,undef,1,$cdom,$cnum)); + if (@sections > 0) { + @sections = sort {$a cmp $b} @sections; + unshift(@sections,'none'); # Put 'no sections' next + unshift(@sections,'all'); # Put 'all' at the front of the list + } else { + @sections = ('all','none'); + } + $r->print('</td><td>'. + §ions_selection(\@sections,'sectionpick').'</td> + </tr> + </table>'); + return; +} + +sub sections_selection { + my ($sections,$elementname) = @_; + my $section_sel; + my $numvisible = 4; + if (@{$sections} < 4) { + $numvisible = @{$sections}; + } + foreach my $sec (@{$sections}) { + if ($sec eq 'all') { + $section_sel .= ' <option value="'.$sec.'">'.&mt('all sections').'</option>'."\n"; + } elsif ($sec eq 'none') { + $section_sel .= ' <option value="'.$sec.'">'.&mt('no section').'</option>'."\n"; + } else { + $section_sel .= ' <option value="'.$sec.'">'.$sec."</option>\n"; + } + } + my $output = ' + <select name="'.$elementname.'" multiple="multiple" size="'.$numvisible.'"> + '.$section_sel.' + </select>'; + return $output; +} + +sub access_date_settings { + my ($r,$action,$formname,$stored,$image,$gpterm,$ucgpterm) = @_; + my $sten = &mt("Default start and end dates for $gpterm access"); + my $starttime = time; + my $endtime = time+(6*30*24*60*60); # 6 months from now, approx + if ($action eq 'modify') { + $starttime = $$stored{'startdate'}; + unless ($$stored{'enddate'} == 0) { + $endtime = $$stored{'enddate'}; + } + } + my ($table) = &date_setting_table($starttime,$endtime,$formname); + $r->print(&Apache::lonhtmlcommon::topic_bar($image,$sten).' + '.$table.' + '); + return; +} + +sub choose_members_form { + my ($r,$cdom,$cnum,$action,$formname,$page,$groupname,$description, + $granularity,$quota,$startdate,$enddate,$tools,$fixedprivs,$toolprivs, + $functions,$users,$userdata,$idx,$stored,$states,$navbuttons, + $gpterm,$ucgpterm,$crstype) = @_; + my @regexps = ('user_','userpriv_','sec_'); + my %origmembers; + $r->print(&Apache::lonhtmlcommon::echo_form_input( + ['origin','action','state','page','member','specificity','branch', + 'defpriv','autorole','autoadd','autodrop','sortby','togglefunc'], + \@regexps)); + my $earlyout = &validate_groupname($groupname,$action,$cdom,$cnum,$gpterm, + $ucgpterm,$crstype); + if ($earlyout) { + $r->print($earlyout); + &display_navbuttons($r,$formname,$$states{$action}[$page-1], + $$navbuttons{'gtps'}); + return; + } + my ($specimg,$memimg); + my @available = (); + my @unavailable = (); + &check_tools($functions,$tools,\@available,\@unavailable); + if ($action eq 'create') { + &print_current_settings($r,$action,$functions,$startdate,$enddate, + $groupname,$description,$granularity,$quota, + \@available,\@unavailable,$gpterm,$ucgpterm); + $specimg = 4; + $memimg = 5; + } else { + $specimg = 2; + $memimg = 3; + my %membership = &Apache::lonnet::get_group_membership($cdom,$cnum, + $groupname); + foreach my $key (sort(keys(%membership))) { + if ($key =~ /^\Q$groupname\E:([^:]+):([^:]+)$/) { + my ($end,$start,@userprivs) = split(/:/,$membership{$key}); + unless ($start == -1) { + my $uname = $1; + my $udom = $2; + my $user = $uname.':'.$udom; + $origmembers{$user} = 1; + } + } + } + } + &privilege_specificity($r,$action,$specimg,$tools,$stored,$toolprivs, + $fixedprivs,\@available,$formname,$gpterm,$ucgpterm, + $functions,$crstype); + my $newusers = &pick_new_members($r,$action,$formname,\@available,$idx, + $stored,$memimg,$users,$userdata, + $granularity,\%origmembers,$gpterm, + $ucgpterm); + if ($newusers || $action eq 'create') { + &display_navbuttons($r,$formname,$$states{$action}[$page-1], + $$navbuttons{'gtps'},$$states{$action}[$page+1], + $$navbuttons{'gtns'}); + } else { + &display_navbuttons($r,$formname,$$states{$action}[$page-1], + $$navbuttons{'gtps'}); + } + return; +} + +sub display_navbuttons { + my ($r,$formname,$prev,$prevtext,$next,$nexttext) = @_; + $r->print('<div class="LC_navbuttons">'); + if ($prev) { + $r->print(' + <input type="button" name="previous" value = "'.$prevtext.'" + onclick="javascript:backPage(document.'.$formname.','."'".$prev."'".')"/> + '); + } elsif ($prevtext) { + $r->print(' + <input type="button" name="previous" value = "'.$prevtext.'" + onclick="javascript:history.back()"/> + '); + } + if ($next) { + $r->print(' + <input type="button" name="next" value="'.$nexttext.'" + onclick="javascript:nextPage(document.'.$formname.','."'".$next."'".')" />'); + } + $r->print('</div>'); +} + +sub check_tools { + my ($functions,$tools,$available,$unavailable) = @_; + foreach my $item (sort(keys(%{$functions}))) { + if (grep/^$item$/,@{$tools}) { + push(@{$available},$item); + } else { + push(@{$unavailable},$item); + } + } + return; +} + +sub print_current_settings { + my ($r,$action,$functions,$startdate,$enddate,$groupname,$description, + $granularity,$quota,$available,$unavailable,$gpterm,$ucgpterm) = @_; + + my %lt = &Apache::lonlocal::texthash( + grna => 'Group Name', + desc => 'Group Title', + grfn => "Collaborative Tools", + gran => 'Granularity', + quot => 'File quota', + dfac => 'Default access dates', + ygrs => "Your group selections - ", + tfwa => "The following settings will apply to the group:", + stda => 'Start date:', + enda => 'End date:', + ); + $lt{'difn'} = &mt('Different collaborative tools[_1]for different members:','<br />'); + my $showstart = &Apache::lonlocal::locallocaltime($startdate); + my $showend; + if ($enddate == 0) { + $showend = &mt('No end date set'); + } else { + $showend = &Apache::lonlocal::locallocaltime($enddate); + } + if ($action eq 'create') { + $r->print(' +<div><span style="font-size: larger">'.$lt{'ygrs'}.'</span> +<br />'.$lt{'tfwa'}.' +</div>'); + } + $r->print(&Apache::loncommon::start_data_table('LC_course_group_status'). + &Apache::loncommon::start_data_table_header_row()); + $r->print(' + <th>'.$lt{'grna'}.'</th> + <th>'.$lt{'desc'}.'</th> + <th>'.$lt{'grfn'}.'</th> + <th>'.$lt{'gran'}.'</th> + <th>'.$lt{'quot'}.'</th> + <th>'.$lt{'dfac'}.'</th> +'); + $r->print(&Apache::loncommon::end_data_table_header_row(). + &Apache::loncommon::start_data_table_row('LC_data_table_dense')); + $r->print(' + <td valign="top">'.$groupname.'</td> + <td valign="top">'.$description.'</td> + <td> +'); + + if (@{$available} > 0) { + $r->print('<b>'.&mt('Available for assignment to members:').'</b>'); + $r->print('<ul>'); + for (my $i=0; $i<@{$available}; $i++) { + $r->print('<li>'.&mt($$functions{$$available[$i]}).'</li>'); + } + $r->print('</ul>'); + } + + if (@{$unavailable} > 0) { + $r->print('<b>'.&mt('Unavailable for assignment:').'</b>'); + $r->print('<ul>'); + for (my $i=0; $i<@{$unavailable}; $i++) { + $r->print('<li>'.&mt($$functions{$$unavailable[$i]}).'</li>'); + } + $r->print('</ul>'); + } + + my $quota_text=&mt('[_1] MB',$quota); + my $granu_text=&mt($granularity); $r->print(<<"END"); - <td> </td> - <td align="center"> - <select name="sectionpick" multiple="true" size="$numvisible"> - $section_sel - </select> </td> - </tr> -</table> + <td valign="top"><b>$lt{'difn'}</b> $granu_text</td> + <td valign="top">$quota_text</td> + <td valign="top"><b>$lt{'stda'}</b> $showstart<br /> + <b>$lt{'enda'}</b> $showend + </td> END + $r->print(&Apache::loncommon::end_data_table_row(). + &Apache::loncommon::end_data_table()); return; } -sub second_creation_form { - my ($r) = @_; +sub pick_new_members { + my ($r,$action,$formname,$available,$idx,$stored,$img,$users,$userdata, + $granularity,$origmembers,$gpterm,$ucgpterm) = @_; + my %lt = &Apache::lonlocal::texthash( + 'gpme' => "Group membership", + 'addm' => 'Add members', + 'setf' => 'Assign collaborative tools', + 'func' => 'Tools', + 'nome' => 'No members to add at this time, as there are no users '. + 'matching the specified type(s), role(s) and section(s).', + 'nnew' => "There are no users to add as new members, as all users". + " matching the specified type(s), role(s), and ". + "section(s) are already affiliated with this group.", + 'yoma' => "You may need to use the 'modify existing, past or ". + "future members' page if you need to re-enable ". + "or activate access for previous or future members.", + ); + my %members; + my $totalusers = 0; + my $newusers = 0; + foreach my $role (keys(%{$users})) { + foreach my $user (keys(%{$$users{$role}})) { + $totalusers ++; + if (ref($origmembers) eq 'HASH') { + if (exists($$origmembers{$user})) { + next; + } + } + unless (defined($members{$user})) { + @{$members{$user}} = @{$$userdata{$user}}; + $newusers ++; + } + } + } + if (keys(%members) > 0) { + if (@{$available} > 0 && $granularity eq 'Yes') { + $r->print(&check_uncheck_tools($r,$available)); + } + } + $r->print(&Apache::lonhtmlcommon::topic_bar($img,$lt{'gpme'})); + if (keys(%members) > 0) { + $r->print(' + <table> + <tr>'); + &check_uncheck_buttons($r,$formname,'member',$lt{'addm'}); + if (@{$available} > 0 && $granularity eq 'Yes') { + $r->print('<td> + <fieldset><legend>'.$lt{'setf'}.'</legend> + <span class="LC_nobreak"> + <input type="button" value="'.&mt('check all').'" + onclick="javascript:checkAllTools(document.'.$formname.')" /> + + <input type="button" value="'.&mt('uncheck all').'" + onclick="javascript:uncheckAllTools(document.'.$formname.')" /> + </span> + </fieldset></td>'); + } + $r->print('</tr></table> + '); + $r->print(&Apache::loncommon::start_data_table(). + &Apache::loncommon::start_data_table_header_row()); + $r->print(' + <th>'.&mt('Add?').'</b></td> + <th><a href="javascript:changeSort('."'fullname'".')">'.&mt('Name').'</a></td> + <th><a href="javascript:changeSort('."'username'".')">'.&mt('Username').'</a></td> + <th><a href="javascript:changeSort('."'domain'".')">'.&mt('Domain').'</a></td> + <th><a href="javascript:changeSort('."'id'".')">'.&mt('ID').'</a></td> + <th><a href="javascript:changeSort('."'section'".')">'.&mt('Section').'</a></td> +'); + if (@{$available} > 0) { + $r->print('<th>'.$lt{'func'}.'</th>'); + } + $r->print(&Apache::loncommon::end_data_table_header_row()); + if (@{$available} > 0) { + if ($granularity eq 'Yes') { + $r->print(&Apache::loncommon::start_data_table_row('LC_data_table_dense LC_data_table_highlight').' + <td colspan="6"> </td> + <td align="center"><span class="LC_nobreak"><b>'.&mt('All:').'</b> '); + foreach my $tool (@{$available}) { + $r->print('<label><input type="checkbox" name="togglefunc" '. + 'onclick="javascript:toggleTools(document.'.$formname.'.user_'.$tool.',this);"'. + ' value="'.$tool.'">'.'<b>'.$tool.'</b></label> '); + } + $r->print('</span></td></tr>'); + } + } + my %Sortby = (); + foreach my $user (sort(keys(%members))) { + if ($env{'form.sortby'} eq 'fullname') { + push(@{$Sortby{$members{$user}[$$idx{fullname}]}},$user); + } elsif ($env{'form.sortby'} eq 'username') { + push(@{$Sortby{$members{$user}[$$idx{uname}]}},$user); + } elsif ($env{'form.sortby'} eq 'domain') { + push(@{$Sortby{$members{$user}[$$idx{udom}]}},$user); + } elsif ($env{'form.sortby'} eq 'id') { + push(@{$Sortby{$members{$user}[$$idx{id}]}},$user); + } elsif ($env{'form.sortby'} eq 'section') { + push(@{$Sortby{$members{$user}[$$idx{section}]}},$user); + } else { + push(@{$Sortby{$members{$user}[$$idx{fullname}]}},$user); + } + } + foreach my $key (sort(keys(%Sortby))) { + foreach my $user (@{$Sortby{$key}}) { + my $id = $members{$user}[$$idx{id}]; + my $fullname = $members{$user}[$$idx{fullname}]; + my $udom = $members{$user}[$$idx{udom}]; + my $uname = $members{$user}[$$idx{uname}]; + my $section = $members{$user}[$$idx{section}]; + $r->print(&Apache::loncommon::start_data_table_row('LC_data_table_dense'). + '<td align="right"><input type="checkbox" name="member" value="'.$user.'" /></td>'. + '<td>'.$fullname.'</td>'. + '<td>'.$uname.'</td>'. + '<td>'.$udom.'</td>'. + '<td>'.$id.'</td>'. + '<td>'.$section.'</td>'); + if (@{$available} > 0) { + $r->print('<td align="center"><span class="LC_nobreak">'. + ' '); + foreach my $tool (@{$available}) { + if ($granularity eq 'Yes') { + $r->print('<input type="checkbox" name="user_'. + $tool.'" value="'.$user.'" />'.$tool.' '); + } else { + $r->print('<input type="hidden" name="user_'. + $tool.'" value="'.$user.'" />'.$tool.' '); + } + } + $r->print('</span></td>'); + } + $r->print(&Apache::loncommon::end_data_table_row()."\n"); + } + } + $r->print(&Apache::loncommon::end_data_table()); + } else { + if ($totalusers > 0) { + $r->print($lt{'nnew'}.'<br /><br />'.$lt{'yoma'}); + } else { + $r->print($lt{'nome'}); + } + } + return $newusers; } -sub completed_creation { - my ($r) = @_; +sub privilege_specificity { + my ($r,$action,$img,$tools,$stored,$toolprivs,$fixedprivs,$available, + $formname,$gpterm,$ucgpterm,$functions,$crstype) = @_; + my %lt = &Apache::lonlocal::texthash ( + 'uprv' => 'User privileges for collaborative tools', + 'frty' => 'For each collaborative tool you have chosen to include, '. + 'there is a set of core privileges which all group members '. + 'assigned use of the tool will receive.', + 'thar' => 'For some tools there are also additional optional '. + 'privileges which can be set.', + 'plch' => 'Choose one of the following:', + 'fort' => 'For the collaborative tools you have chosen to include '. + 'only core privileges are available, '. + 'so there are no optional privileges to assign.', + 'eaty' => 'Each collaborative tool includes core '. + 'privileges assigned to all members with access to the '. + 'tool. Some tools may also feature additional privileges '. + 'which can be set for specific members.', + 'cutg' => 'Currently the group is configured ', + 'sdif' => 'so different members can receive different optional privileges for a particular tool.', + 'sall' => 'so all members will receive the same optional privileges for a particular tool.', + 'algm' => 'All group members will receive the same privileges for any tool assigned to them, including the default set of optional privileges.', + 'smgp' => 'Different group members may receive different privileges from '. + 'others for the tools they have been assigned.', + 'thwi' => 'These will be the privileges all group members receive for a particular assigned tool, '. + 'if you selected the first option above.', + 'thes' => "These will be the privileges given to members assigned ". + "in the future via automatic group assignment ". + "for users who receive specific sections/roles in the course ", + 'asyo' => "As you have chosen not to include any collaborative tools ". + "in the group, no default optional privileges need to be set.", + 'will receive by default for a specific tool.', + 'oppr' => 'Optional privileges', + 'defp' => 'The default privileges new members will receive are:', + ); + $lt{'plin'} = &mt('Indicate which [_1]optional[_2] privileges members '. + 'will receive by default for a specific tool.','<b>','</b>'); + if ($crstype eq 'Community') { + $lt{'thes'} = &mt('These will be the privileges given to members assigned in the future via automatic group assignment for users who receive specific sections/roles in the community '); + } + my $totaloptionalprivs = 0; + foreach my $tool (@{$tools}) { + foreach my $priv (sort(keys(%{$$toolprivs{$tool}}))) { + if (!exists($$fixedprivs{$tool}{$priv})) { + $totaloptionalprivs ++; + } + } + } + $r->print(&Apache::lonhtmlcommon::topic_bar($img,$lt{'uprv'})); + if ((($action eq 'create') && (@{$available} > 0)) || + (($action eq 'modify') && ($formname eq 'change_settings'))) { + my %specific = ( + 'No' => 'checked="checked"', + 'Yes' => '', + ); + if ($action eq 'create') { + $r->print($lt{'frty'}.'<br />'); + if ($totaloptionalprivs) { + $r->print($lt{'thar'}.'<br /><br />'.$lt{'plch'}); + } else { + $r->print($lt{'fort'}); + } + } else { + $r->print($lt{'eaty'}.' '.$lt{cutg}); + if ($$stored{'specificity'} eq 'Yes') { + $r->print($lt{'sdif'}); + $specific{'Yes'} = $specific{'No'}; + $specific{'No'} = ''; + } else { + $r->print($lt{'sall'}); + } + } + if ($totaloptionalprivs) { + $r->print(' +<br /> +<label><span class="LC_nobreak"><input type="radio" name="specificity" value="No" '.$specific{'No'}.' /> '.$lt{'algm'}.'</span></label><br /> +<label><span class="LC_nobreak"><input type="radio" name="specificity" value="Yes" '.$specific{'Yes'}.' /> '.$lt{'smgp'}.'</span></label><br /><br />'); + } else { + $r->print('<input type="hidden" name="specificity" value="No" />'); + } + if ($totaloptionalprivs) { + $r->print($lt{'plin'}); + if ($action eq 'create') { + $r->print('<br />'.$lt{'thwi'}); + } + $r->print('<br />'.$lt{'thes'}); + if ($action eq 'create') { + $r->print('('.&mt('if enabled on the next page').').'); + } else { + $r->print('('.&mt('if enabled below').').'); + } + $r->print('<br /><br /> + <table><tr>'); + &check_uncheck_buttons($r,$formname,'defpriv',$lt{'oppr'}); + $r->print(' + </tr> + </table> + <br /> +'); + } else { + $r->print($lt{'algm'}.'<br /><br />'); + } + &default_privileges($r,$action,$tools,$toolprivs,$fixedprivs, + $available,$functions); + } else { + if ($action eq 'create') { + $r->print($lt{'asyo'}); + $r->print('<input type="hidden" name="specificity" value="No" />'); + } elsif ($action eq 'modify' && $formname eq 'pick_members') { + my @defprivs; + if (ref($$stored{'defpriv'}) eq 'ARRAY') { + @defprivs = @{$$stored{'defpriv'}}; + } + $r->print($lt{'eaty'}.' '.$lt{cutg}); + if ($$stored{'specificity'} eq 'Yes') { + $r->print($lt{'sdif'}); + } else { + $r->print($lt{'sall'}); + } + $r->print(' '.$lt{'defp'}.'<br /><br />'); + &display_defprivs($r,$tools,$toolprivs,\@defprivs); + } + } + return; +} + +sub default_privileges { + my ($r,$action,$tools,$toolprivs,$fixedprivs,$available,$functions) = @_; + my %lt = &Apache::lonlocal::texthash( + 'addp' => 'Additional privileges', + 'fixp' => 'Fixed privileges', + 'oppr' => 'Optional privileges', + 'func' => 'Collaborative Tool', + ); + $r->print(&Apache::lonhtmlcommon::start_pick_box('LC_group_priv_box'). + &Apache::lonhtmlcommon::row_title($lt{'func'},undef, + 'LC_groups_functionality')); + my @tableHeader; + if ((ref($functions) eq 'HASH') && (ref($tools) eq 'ARRAY')) { + @tableHeader = map { $functions->{$_}; } @{$tools}; + } + $r->print(join('</td><td class="LC_groups_functionality">', @tableHeader)); + $r->print(&Apache::lonhtmlcommon::row_closure(1)); + my $fixed = ''; + my $dynamic = ''; + foreach my $tool (@{$tools}) { + my $privcount = 0; + if ($dynamic ne '') { + $dynamic .= '</td><td class="LC_groups_optional">'; + } + $dynamic .= '<table class="LC_group_priv"><tr>'; + foreach my $priv (sort(keys(%{$$toolprivs{$tool}}))) { + if (exists($$fixedprivs{$tool}{$priv})) { + if ($fixed ne '') { + $fixed .= '</td><td class="LC_groups_fixed">'; + } + $fixed .= '<input type="hidden" name="defpriv" value="'.$priv.'" />' + .'<span class="LC_nobreak">'.&mt($$toolprivs{$tool}{$priv}).' '; + if ($action eq 'modify') { + if (grep(/^$tool$/,@{$available})) { + $fixed .= '<small>'.&mt('(on)').'<small> '; + } else { + $fixed .= '<small>'.&mt('(off)').'<small> '; + } + } + $fixed .= '</span>'; + } else { + $privcount++; + if ($privcount == 3) { + $dynamic .= '</tr> + <tr>'."\n"; + } + $dynamic .= '<td><span class="LC_nobreak"><label><input type="checkbox" name="defpriv" value="'.$priv.'" />'.&mt($$toolprivs{$tool}{$priv}).'</label></span></td>'."\n"; + } + } + if ($privcount == 0) { + $dynamic .= '<td>'.&mt('None').'</td>'."\n"; + } + if ($privcount < 3) { + $dynamic .= '<td> </td>'."\n"; + } elsif ($privcount%2) { + $dynamic = '<td> </td>'."\n"; + } + $dynamic .= '</tr></table>'; + } + $r->print(&Apache::lonhtmlcommon::row_title($lt{'fixp'},undef, + 'LC_groups_fixed'). + $fixed. + &Apache::lonhtmlcommon::row_closure(1)); + $r->print(&Apache::lonhtmlcommon::row_title($lt{'oppr'},undef, + 'LC_groups_optional'). + $dynamic. + &Apache::lonhtmlcommon::end_pick_box()); + $r->print('<br />'); + return; + +} + +sub display_defprivs { + my ($r,$tools,$toolprivs,$defprivs) = @_; + my $function = &Apache::loncommon::get_users_function(); + my $tabcol = &Apache::loncommon::designparm($function.'.tabbg'); + my $rowColor1 = "#dddddd"; + my $rowColor2 = "#eeeeee"; + my %lt = &Apache::lonlocal::texthash( + 'priv' => 'Privileges', + 'func' => 'Collaborative Tool', + ); + $r->print(&Apache::lonhtmlcommon::start_pick_box()); + $r->print('<tr>'); + my $numrows = 0; + my %currprivs; + foreach my $tool (@{$tools}) { + @{$currprivs{$tool}} = (); + foreach my $priv (sort(keys(%{$$toolprivs{$tool}}))) { + if (ref($defprivs) eq 'ARRAY') { + if (grep(/^\Q$priv\E$/,@{$defprivs})) { + push(@{$currprivs{$tool}},$priv); + } + } + } + my $rowcount = int(@{$currprivs{$tool}}/3); + if (@{$currprivs{$tool}}%3 > 0) { + $rowcount ++; + } + if ($rowcount > $numrows) { + $numrows = $rowcount; + } + } + my @rowCols = ($rowColor1,$rowColor2); + foreach my $tool (@{$tools}) { + $r->print('<td align="center" valign="top"> + <table cellspacing="0" cellpadding="5"> + <tr bgcolor="#cccccc"> + <td colspan="3" align="center"><b>'.$tool.'</b></td> + </tr> + '); + my $rownum = 1; + my $privcount = 0; + $r->print('<tr bgcolor="'.$rowColor1.'">'); + foreach my $priv (@{$currprivs{$tool}}) { + $privcount ++; + if ($privcount%4 == 0) { + $rownum ++; + my $bgcol = $rownum%2; + $r->print('</tr> + <tr bgcolor="'.$rowCols[$bgcol].'">'."\n"); + } + $r->print('<td>'.$$toolprivs{$tool}{$priv}.'</td>'."\n"); + } + if ($privcount%3 > 0) { + my $emptycells = 3-($privcount%3); + while($emptycells > 0) { + $r->print('<td> </td>'."\n"); + $emptycells --; + } + } + while ($rownum < $numrows) { + $rownum ++; + my $bgcol = $rownum%2; + $r->print('<tr bgcolor="'.$rowCols[$bgcol].'"><td colspan="3"> </td></tr>'); + } + $r->print('</table>'."\n".'</td>'); + } + $r->print('</tr>'."\n"); + $r->print(&Apache::lonhtmlcommon::end_pick_box()); + $r->print('<br />'); + return; +} + + +sub change_members_form { + my ($r,$cdom,$cnum,$action,$formname,$page,$groupname,$description, + $startdate,$enddate,$tools,$fixedprivs,$functions,$users,$userdata, + $granularity,$quota,$specificity,$idx,$states,$navbuttons,$gpterm, + $ucgpterm) = @_; + my %lt = &Apache::lonlocal::texthash( + grse => "$ucgpterm settings", + mogm => "Modify $gpterm membership", + ); + my @regexps = ('user_','userpriv_'); + $r->print(&Apache::lonhtmlcommon::echo_form_input( + ['origin','action','state','page','expire','deletion', + 'reenable','activate','changepriv','sortby', + 'togglefunc'],\@regexps)); + my $rowimg = 1; + my @available = (); + my @unavailable = (); + &check_tools($functions,$tools,\@available,\@unavailable); + my $nexttext = $$navbuttons{'gtns'}; + my $prevtext = $$navbuttons{'gtpp'}; + $r->print(' +<br /> +'); + $r->print(&Apache::lonhtmlcommon::topic_bar(1,$lt{'grse'})); + &print_current_settings($r,$action,$functions,$startdate,$enddate, + $groupname,$description,$granularity,$quota, + \@available,\@unavailable,$gpterm,$ucgpterm); + $r->print(&Apache::lonhtmlcommon::topic_bar(2,$lt{'mogm'})); + my $numcurrent = ¤t_membership($r,$cdom,$cnum,$formname,$groupname, + \@available,\@unavailable,$fixedprivs, + $granularity,$specificity); + if ($numcurrent > 0) { + &display_navbuttons($r,$formname,$$states{$action}[$page-1],$prevtext, + $$states{$action}[$page+1],$nexttext); + } else { + &display_navbuttons($r,$formname,$$states{$action}[$page-1],$prevtext); + } + return; +} + +sub current_membership { + my ($r,$cdom,$cnum,$formname,$groupname,$available,$unavailable, + $fixedprivs,$granularity,$specificity) = @_; + my %membership = &Apache::lonnet::get_group_membership($cdom,$cnum, + $groupname); + my %lt = &Apache::lonlocal::texthash( + 'actn' => 'Action?', + 'name' => 'Name', + 'usnm' => 'Username', + 'stid' => 'ID', + 'doma' => 'Domain', + 'stda' => 'Start Date', + 'enda' => 'End Date', + 'expi' => 'Expire', + 'reen' => 'Re-enable', + 'acti' => 'Activate', + 'dele' => 'Delete', + 'curf' => 'Current Tool Set', + 'chpr' => 'Change Privileges' + ); + my ($current,$num_items,$hastools,$addtools) = + &Apache::longroup::group_memberlist($cdom,$cnum,$groupname,$fixedprivs, + $available); + my $numcurrent = scalar(keys(%{$current})); + if ($numcurrent > 0) { + $r->print(' + <table> + <tr>'); + if ($num_items->{'active'}) { + &check_uncheck_buttons($r,$formname,'expire',$lt{'expi'}); + } + if ($num_items->{'previous'}) { + &check_uncheck_buttons($r,$formname,'reenable',$lt{'reen'}); + } + if ($num_items->{'future'}) { + &check_uncheck_buttons($r,$formname,'activate',$lt{'acti'}); + } + &check_uncheck_buttons($r,$formname,'deletion',$lt{'dele'}); + if (@{$available} > 0) { + if ($specificity eq 'Yes') { + &check_uncheck_buttons($r,$formname,'changepriv',$lt{'chpr'}); + } + if ($granularity eq 'Yes') { + $r->print(&check_uncheck_tools($r,$available)); + $r->print(' + <td> + <span class="LC_nobreak"> + <fieldset><legend>'.$lt{'curf'}.'</legend> + <input type="button" value="'.&mt('check all').'" + onclick="javascript:checkAllTools(document.'.$formname.')" /> + + <input type="button" value="'.&mt('uncheck all').'" + onclick="javascript:uncheckAllTools(document.'.$formname.')" /> + </fieldset> + </span> + </td> +'); + } + } + $r->print(<<"END"); + </tr> + </table> + <br /> +END + $r->print(&Apache::loncommon::start_data_table(). + &Apache::loncommon::start_data_table_header_row()); + $r->print(<<"END"); + <th>$lt{'actn'}</th> + <th><a href="javascript:changeSort('fullname')">$lt{'name'}</a></th> + <th><a href="javascript:changeSort('username')">$lt{'usnm'}</a></th> + <th><a href="javascript:changeSort('domain')">$lt{'doma'}</a></th> + <th><a href="javascript:changeSort('id')">$lt{'ID'}</a></th> + <th><a href="javascript:changeSort('start')">$lt{'stda'}</a></th> + <th><a href="javascript:changeSort('end')">$lt{'enda'}</a></th> +END + my $colspan = 0; + if ($hastools) { + $r->print('<th>'.$lt{'curf'}.'</th>'); + $colspan++; + } + if ($addtools) { + $r->print('<th>'.&mt('Additional Tools').'</th>'); + $colspan++; + } + $r->print(&Apache::loncommon::end_data_table_header_row()); + if ($colspan) { + if ($granularity eq 'Yes') { + $r->print(&Apache::loncommon::start_data_table_row('LC_data_table_dense LC_data_table_highlight').' + <td colspan="7"> </td> + <td colspan="'.$colspan.'" align="center"><span class="LC_nobreak"><b>'.&mt('All:'). + '</b> '); + foreach my $tool (@{$available}) { + $r->print('<label><input type="checkbox" name="togglefunc"'. + ' onclick="javascript:toggleTools(document.'.$formname.'.user_'.$tool.',this);"'. + ' value="'.$tool.'" />'.'<b>'.$tool.'</b></label> '); + } + $r->print('</span></td></tr>'); + } + } + my %Sortby = (); + foreach my $user (sort(keys(%{$current}))) { + if ($env{'form.sortby'} eq 'fullname') { + push(@{$Sortby{$$current{$user}{fullname}}},$user); + } elsif ($env{'form.sortby'} eq 'username') { + push(@{$Sortby{$$current{$user}{uname}}},$user); + } elsif ($env{'form.sortby'} eq 'domain') { + push(@{$Sortby{$$current{$user}{udom}}},$user); + } elsif ($env{'form.sortby'} eq 'id') { + push(@{$Sortby{$$current{$user}{id}}},$user); + } else { + push(@{$Sortby{$$current{$user}{fullname}}},$user); + } + } + foreach my $key (sort(keys(%Sortby))) { + foreach my $user (@{$Sortby{$key}}) { + my $id = $$current{$user}{id}; + my $fullname = $$current{$user}{fullname}; + my $udom = $$current{$user}{udom}; + my $uname = $$current{$user}{uname}; + my $start = $$current{$user}{start}; + my $end = $$current{$user}{end}; + $r->print(&Apache::loncommon::start_data_table_row('LC_data_table_dense').' + <td>'); + if ($$current{$user}{changestate} eq 'reenable') { + $r->print('<span class="LC_nobreak"><label>'. + '<input type="checkbox" name="reenable" value="'.$user.'" />'. + $lt{'reen'}.'</label></span><br />'); + } elsif ($$current{$user}{changestate} eq 'expire') { + $r->print('<span class="LC_nobreak"><label>'. + '<input type="checkbox" name="expire" value="'.$user.'" />'. + $lt{'expi'}.'</label></span><br />'); + } elsif ($$current{$user}{changestate} eq 'activate') { + $r->print('<span class="LC_nobreak"><label>'. + '<input type="checkbox" name="activate" value="'.$user.'" />'. + $lt{'acti'}.'</label></span><br />'); + } + $r->print('<span class="LC_nobreak"><label>'. + '<input type="checkbox" name="deletion" value="'.$user.'" />'. + $lt{'dele'}.'</label></span>'); + if ($specificity eq 'Yes') { + $r->print('<br /><span class="LC_nobreak"><label>'. + '<input type="checkbox" name="changepriv" value="'.$user.'" />'.$lt{'chpr'}. + '</label></span>'); + } + $r->print(' + </td>'. + '<td>'.$fullname.'</td>'. + '<td>'.$uname.'</td>'. + '<td>'. $udom.'</td>'. + '<td>'.$id.'</td>'. + '<td>'.$start.'</td>'. + '<td>'.$end.'</td>'); + if ($hastools) { + $r->print('<td align="left"><span class="LC_nobreak">'. + ' '); + foreach my $tool (@{$$current{$user}{currtools}}) { + if ($granularity eq 'Yes') { + $r->print('<label><input type="checkbox" '. + 'checked="checked" '. + 'name="user_'.$tool.'" value="'. + $user.'" />'.$tool.'</label>'); + } else { + $r->print('<input type="hidden" '. + 'checked="checked" '. + 'name="user_'.$tool.'" value="'. + $user.'" />'.$tool); + } + $r->print(' '); + } + $r->print('</span></td>'); + } + if ($addtools) { + $r->print('<td align="left">'); + if ($granularity eq 'Yes') { + foreach my $tool (@{$$current{$user}{newtools}}) { + $r->print('<span class="LC_nobreak"><label><input type="checkbox" + name="user_'.$tool.'" value="'. + $user.'" />'.$tool. + '</label></span> '); + } + } else { + foreach my $tool (@{$$current{$user}{newtools}}) { + $r->print('<span class="LC_nobreak"><input type="hidden" + name="user_'. $tool.'" value="'. + $user.'" />'.$tool. + '</span> '); + } + } + $r->print('</td>'); + } + $r->print(&Apache::loncommon::end_data_table_row()."\n"); + } + } + $r->print(&Apache::loncommon::end_data_table()); + } else { + $r->print( + '<p class="LC_info">' + .&mt('There are no active, future or previous group members to modify.') + .'</p>'); + } + return $numcurrent; +} + +sub check_uncheck_buttons { + my ($r,$formname,$field,$title,$colspan) = @_; + $r->print(' + <td '.$colspan.'> + <fieldset> + <legend>'.$title.'</legend> + <span class="LC_nobreak"> + <input type="button" value="'.&mt('check all').'" + onclick="javascript:checkAll(document.'.$formname.'.'.$field.')" /> + + <input type="button" value="'.&mt('uncheck all').'" + onclick="javascript:uncheckAll(document.'.$formname.'.'.$field.')" /> + </span> + </fieldset> + </td> +'); +} + + +sub change_privs_form { + my ($r,$cdom,$cnum,$action,$formname,$page,$startdate,$enddate, + $tools,$functions,$toolprivs,$fixedprivs,$userdata,$usertools, + $memchg,$idx,$states,$stored,$sectioncount,$navbuttons,$gpterm, + $ucgpterm) = @_; + my @regexps = ('userpriv_'); + my $nexttext; + my %lt = &Apache::lonlocal::texthash( + 'tode' => 'To be deleted', + 'toex' => 'To be expired', + 'nome' => "No members to be deleted or expired from the $gpterm.", + ); + $r->print(&Apache::lonhtmlcommon::echo_form_input( + ['origin','action','state','page','sortby'],\@regexps)); + if ($env{'form.branch'} eq 'adds') { + $nexttext = $$navbuttons{'adme'}; + } else { + $nexttext = $$navbuttons{'mose'}; + } + $r->print(&Apache::lonhtmlcommon::topic_bar(3,&mt('Members to delete or expire'))); + my $exp_or_del = 0; + if (ref($$memchg{'deletion'}) eq 'ARRAY') { + if (@{$$memchg{'deletion'}} > 0) { + $r->print('<b>'.$lt{'tode'}.':</b><br /><ul>'); + foreach my $user (@{$$memchg{'deletion'}}) { + $r->print('<li>'.$$userdata{$user}[$$idx{fullname}]. + ' ('.$user.')</li>'); + } + $r->print('</ul>'); + $exp_or_del += @{$$memchg{'deletion'}}; + } + } + if (ref($$memchg{'expire'}) eq 'ARRAY') { + if (@{$$memchg{'expire'}} > 0) { + $r->print('<b>'.$lt{'toex'}.':</b><br /><ul>'); + foreach my $user (@{$$memchg{'expire'}}) { + $r->print('<li>'.$$userdata{$user}[$$idx{fullname}]. + ' ('.$user.')</li>'); + } + $r->print('</ul>'); + $exp_or_del += @{$$memchg{'expire'}}; + } + } + if (!$exp_or_del) { + $r->print('<p class="LC_info">'.$lt{'nome'}.'</p>'); + } + + $r->print(&Apache::lonhtmlcommon::topic_bar(4,&mt('Setting optional privileges for specific group members'))); + + my $numchgs = &member_privileges_form($r,$action,$formname,$tools, + $toolprivs,$fixedprivs,$userdata, + $usertools,$idx,$memchg,$states, + $stored,$gpterm); + my $prevtext = $$navbuttons{'gtps'}; + if ($numchgs || $exp_or_del) { + &display_navbuttons($r,$formname,$$states{$action}[$page-1],$prevtext, + $$states{$action}[$page+1],$nexttext); + } else { + &display_navbuttons($r,$formname,$$states{$action}[$page-1],$prevtext); + } + return; +} + +sub add_members_form { + my ($r,$cdom,$cnum,$action,$formname,$page,$startdate,$enddate,$groupname, + $description,$granularity,$quota,$sectioncount,$tools,$functions, + $stored,$states,$navbuttons,$gpterm,$ucgpterm,$crstype)=@_; + $r->print(' <br />'); + my @available = (); + my @unavailable = (); + &check_tools($functions,$tools,\@available,\@unavailable); + &print_current_settings($r,$action,$functions,$startdate,$enddate, + $groupname,$description,$granularity,$quota, + \@available,\@unavailable,$gpterm,$ucgpterm); + &membership_options($r,$cdom,$cnum,$action,$formname,$sectioncount,1,$gpterm, + $ucgpterm,$crstype); + my $nexttext = $$navbuttons{'gtns'}; + my $prevtext = $$navbuttons{'gtpp'}; + &display_navbuttons($r,$formname,$$states{$action}[$page-1],$prevtext, + $$states{$action}[$page+1],$nexttext); + return; +} + +sub choose_privs_form { + my ($r,$cdom,$cnum,$action,$formname,$page,$startdate,$enddate, + $tools,$functions,$toolprivs,$fixedprivs,$userdata,$usertools,$idx, + $states,$stored,$sectioncount,$navbuttons,$gpterm,$ucgpterm, + $crstype) = @_; + + my @regexps = ('userpriv_'); + my $nexttext; + + if ($action eq 'create') { + push(@regexps,'sec_'); + $r->print(&Apache::lonhtmlcommon::echo_form_input( + ['origin','action','state','page','sortby','autoadd','autodrop'], + \@regexps)); + $nexttext = $$navbuttons{'crgr'}; + } else { + $r->print(&Apache::lonhtmlcommon::echo_form_input( + ['origin','action','state','page','sortby'],\@regexps)); + $nexttext = $$navbuttons{'adme'}; + } + + $r->print(&Apache::lonhtmlcommon::topic_bar(6,&mt('Setting optional privileges for specific group members'))); + + &member_privileges_form($r,$action,$formname,$tools,$toolprivs, + $fixedprivs,$userdata,$usertools,$idx,undef, + $states,$stored,$gpterm); + + if ($action eq 'create') { + my $img1 = 7; + my $img2 = 8; + &mapping_options($r,$action,$formname,$page,$sectioncount, + $states,$stored,$navbuttons,$img1,$img2, + $gpterm,$ucgpterm,$crstype,$cdom,$cnum); + } + my $prevtext = $$navbuttons{'gtps'}; + &display_navbuttons($r,$formname,$$states{$action}[$page-1],$prevtext, + $$states{$action}[$page+1],$nexttext); + return; +} + +sub build_boxes { + my ($r,$tools,$usertools,$fixedprivs,$toolprivs,$showtools, + $showboxes,$prefix,$specificity,$excluded) = @_; + my $totalboxes = 0; + if (@{$tools} > 0) { + if ($specificity eq 'Yes') { + foreach my $tool (@{$tools}) { + @{$$showboxes{$tool}} = (); + foreach my $user (sort(keys(%{$usertools}))) { + if (ref($excluded) eq 'ARRAY') { + if (grep/^$user$/,@{$excluded}) { + next; + } + } + if ($$usertools{$user}{$tool}) { + unless (grep/^$tool$/,@{$showtools}) { + push(@{$showtools},$tool); + } + foreach my $priv (sort(keys(%{$$toolprivs{$tool}}))) { + unless (exists($$fixedprivs{$tool}{$priv})) { + unless(grep(/^$priv$/,@{$$showboxes{$tool}})) { + push(@{$$showboxes{$tool}},$priv); + $totalboxes ++; + } + } + } + } + } + } + if ($totalboxes > 0) { + $r->print(' +<script type="text/javascript"> +function checkAllTools(formname) { +'); + foreach my $tool (sort(keys(%{$showboxes}))) { + foreach my $priv (@{$$showboxes{$tool}}) { + $r->print(' checkAll(formname.'.$prefix.$priv.');'."\n"); + } + } + $r->print(' +} +function uncheckAllTools(formname) { +'); + foreach my $tool (sort(keys(%{$showboxes}))) { + foreach my $priv (@{$$showboxes{$tool}}) { + $r->print(' uncheckAll(formname'.$prefix.$priv.');'."\n"); + } + } + $r->print(' +} +</script> + '); + } + } + } + return $totalboxes; +} + +sub member_privileges_form { + my ($r,$action,$formname,$tools,$toolprivs,$fixedprivs,$userdata, + $usertools,$idx,$memchg,$states,$stored,$gpterm) = @_; + my %lt = &Apache::lonlocal::texthash( + 'addp' => 'Additional privileges', + 'fixp' => 'Core privileges', + 'oppr' => 'Optional privileges', + 'func' => 'Tool', + 'forf' => 'For the collaborative tools included for group '. + 'members being added or modified, '. + 'there are no optional privileges to set '. + 'for specific members.', + 'algr' => 'All new group members will receive the same privileges.', + 'ifex' => 'If previously expired members are being re-enabled, or '. + 'if access for future members is being activated now, '. + 'previously set privileges will be preserved.', + 'asno' => 'As no group members are being added, '. + 'there are no specific user privileges to set.', + 'asng' => 'As no group tools will be made available to users, '. + 'there are no specific user privileges to set.', + 'nogm' => 'No group member privileges to display or set, '. + 'as you have not indicated that you will be activating,'. + ' re-enabling, changing privileges, or adding/removing '. + 'tools for any current members.', + 'full' => 'Fullname', + 'user' => 'Username', + 'doma' => 'Domain', + ); + my @defprivs; + my $specificity; + if ($action eq 'create') { + if (defined($env{'form.defpriv'})) { + @defprivs = &Apache::loncommon::get_env_multiple('form.defpriv'); + } + $specificity = $env{'form.specificity'}; + } else { + if (defined($$stored{'defpriv'})) { + @defprivs = @{$$stored{'defpriv'}}; + } + $specificity = $$stored{'specificity'}; + } + my @showtools; + my %showboxes = (); + my $numtools = 1 + @{$tools}; + + my @excluded = (); + my $numchgs = 0; + if ($formname eq 'change_privs') { + my @currmembers = (); + if (ref($$memchg{'deletion'}) eq 'ARRAY') { + push(@excluded,@{$$memchg{'deletion'}}); + } + if (ref($$memchg{'expire'}) eq 'ARRAY') { + push(@excluded,@{$$memchg{'expire'}}); + } + if (@excluded > 0) { + foreach my $user (sort(keys(%{$usertools}))) { + if (grep/^$user$/,@excluded) { + next; + } + push(@currmembers,$user); + } + } else { + @currmembers = sort(keys(%{$usertools})); + } + $numchgs = @currmembers; + if (!$numchgs) { + $r->print($lt{'nogm'}); + return $numchgs; + } + } + + my $totalboxes = &build_boxes($r,$tools,$usertools,$fixedprivs, + $toolprivs,\@showtools,\%showboxes, + 'userpriv_',$specificity,\@excluded); + if (@{$tools} > 0) { + if ($specificity eq 'Yes') { + if ($totalboxes > 0) { + my $numcells = 2; + my $colspan = $numcells + 1; + my %total; + if (keys(%{$usertools}) > 1) { + $r->print(' + <table border="0" cellspacing="2" cellpadding="2"> + <tr> +'); + foreach my $tool (@{$tools}) { + if (@{$showboxes{$tool}} > 0) { + $r->print('<td valign="top">'); + $r->print('<fieldset><legend>'.&mt($tool).'</legend>'); + $r->print('<table><tr>'); + my $privcount = 0; + foreach my $priv (@{$showboxes{$tool}}) { + $privcount ++; + if (($privcount == @{$showboxes{$tool}}) && + ($privcount > 1)) { + if ($privcount%$numcells) { + $r->print('<td colspan="'.$colspan.'">'); + } else { + $r->print('<td>'); + } + } else { + $r->print('<td>'); + } + $r->print( + '<fieldset><legend>'.&mt($$toolprivs{$tool}{$priv}).'</legend>' +.'<span class="LC_nobreak">' +.' <input type="button" value="'.&mt('check all').'"' +.' onclick="javascript:checkAll(document.'.$formname.'.userpriv_'.$priv.')" />' +.' ' +.'<input type="button" value="'.&mt('uncheck all').'"' +.' onclick="javascript:uncheckAll(document.'.$formname.'.userpriv_'.$priv.')" />' +.'</span></fieldset><br />' + ); + $r->print('</td>'); + if ($privcount < @{$showboxes{$tool}}) { + if (@{$showboxes{$tool}} > 2) { + if ($privcount%$numcells == 0) { + $r->print('</tr><tr>'); + } + } else { + $r->print('<tr></tr>'); + } + } + } + $r->print('</tr></table></fieldset></td><td> </td>'); + } + } + $r->print('</tr></table>'); + } + $r->print(&Apache::loncommon::start_data_table(). + &Apache::loncommon::start_data_table_header_row()); + $r->print(<<"END"); + <th>$lt{'full'}</th> + <th>$lt{'user'}</th> + <th>$lt{'doma'}</th> + <th colspan="$numtools">$lt{'addp'}</th> +END + $r->print(&Apache::loncommon::end_data_table_header_row()); + &member_privs_entries($r,$usertools,$toolprivs,$fixedprivs, + $userdata,$idx,\@showtools,\@defprivs, + \@excluded); + $r->print(&Apache::loncommon::end_data_table()); + } else { + $r->print($lt{'forf'}.'<br />'); + &display_defprivs($r,$tools,$toolprivs,\@defprivs); + } + } else { + if (keys(%{$usertools}) > 0) { + $r->print($lt{'algr'}.'<br />'.$lt{'ifex'}.'<br /><br />'); + &display_defprivs($r,$tools,$toolprivs,\@defprivs); + } else { + $r->print($lt{'asno'}.'<br />'); + } + } + } else { + $r->print($lt{'asng'}); + } + return $numchgs; +} + +sub process_request { + my ($r,$cdom,$cnum,$action,$state,$page,$groupname,$description, + $specificity,$userdata,$startdate,$enddate,$tools,$functions,$toolprivs, + $usertools,$idx,$types,$roles,$sections,$states,$navbuttons,$memchg, + $sectioncount,$stored,$gpterm,$ucgpterm,$crstype) = @_; + + $r->print(&Apache::lonhtmlcommon::echo_form_input( + ['origin','action','state','page','sortby'])); + + my $earlyout = &validate_groupname($groupname,$action,$cdom,$cnum,$gpterm, + $ucgpterm,$crstype); + if ($earlyout) { + $r->print(' +<table width="100%" cellpadding="0" cellspacing="0" border="0"> + <tr> + <td> </td> + <td colspan="3"> +'.$earlyout.'</td></tr>'); + &display_navbuttons($r,$state,$$states{$action}[$page-1], + $$navbuttons{'gtps'}); + $r->print('</table>'); + return; + } + + my @defprivs = (); + if ($action eq 'create' || $state eq 'chgresult') { + if (defined($env{'form.defpriv'})) { + @defprivs = &Apache::loncommon::get_env_multiple('form.defpriv'); + } + if ($state eq 'chgresult') { + my @okprivs = (); + foreach my $tool (@{$tools}) { + foreach my $priv (sort(keys(%{$$toolprivs{$tool}}))) { + push(@okprivs,$priv); + } + } + my @temp = (); + foreach my $defpriv (@defprivs) { + if (grep/^$defpriv$/,@okprivs) { + push(@temp,$defpriv); + } + } + @defprivs = @temp; + } + } else { + if (defined($$stored{'defpriv'})) { + @defprivs = @{$$stored{'defpriv'}}; + } + } + + my $outcome; + if ($action eq 'create' || $state eq 'chgresult') { + $outcome = &write_group_data($r,$cdom,$cnum,$action,$state,$groupname, + $description,$startdate,$enddate, + $specificity,$functions,$tools, + $sectioncount,$roles,$types,$sections, + \@defprivs,$stored,$gpterm,$ucgpterm, + $crstype); + } + if (($action eq 'create' && $outcome eq 'ok') || (($action eq 'modify') && + (($state eq 'memresult') || ($state eq 'addresult')))) { + &process_membership($r,$cdom,$cnum,$action,$state,$groupname,$tools, + $enddate,$startdate,$userdata,$idx,$toolprivs, + $usertools,$specificity,\@defprivs,$memchg,$gpterm, + $ucgpterm,$crstype); + } + return; +} + +sub write_group_data { + my ($r,$cdom,$cnum,$action,$state,$groupname,$description,$startdate, + $enddate,$specificity,$functions,$tools,$sectioncount,$roles,$types, + $sections,$defprivs,$stored,$gpterm,$ucgpterm,$crstype) = @_; + my $now = time; + my $creation = $now; + my $creator = $env{'user.name'}.':'.$env{'user.domain'}; + if ($state eq 'chgresult') { + $creation = $$stored{'creation'}; + $creator = $$stored{'creator'}; + } + my $esc_description = &escape($description); + my @single_attributes = ('description','functions','startdate','enddate', + 'creation','modified','creator','granularity', + 'specificity','autoadd','autodrop','quota'); + my @mult_attributes = ('roles','types','sectionpick','defpriv'); + + my ($crsquota,$freespace,$maxposs) = &get_quota_constraints($action, + $stored); + my $quota = $env{'form.quota'}; + + $quota =~ s/^\s*([^\s]*)\s*$/$1/; + if ($quota eq '') { + $quota = 0; + } + if ($quota !~ /^\d*\.?\d*$/) { + $quota = 0; + $r->print('<div class="LC_warning">' + .&mt('The value you entered for the quota for the group portfolio in this '.$gpterm + .' contained invalid characters, so it has been set to 0 MB. You can change this by' + .' modifying the '.$gpterm.' settings.') + .'</div>'); + } + if ($quota > $maxposs) { + $quota = $maxposs; + $r->print('<div class="LC_warning">' + .&mt('The value you entered for the quota for the group portfolio in this '.$gpterm + .' exceeded the maximum possible value, so it has been set to [_1] MB ' + .'(the maximum possible value).',sprintf("%.2f",$maxposs)) + .'</div>'); + } + my %groupinfo = ( + description => $esc_description, + startdate => $startdate, + enddate => $enddate, + creation => $creation, + modified => $now, + creator => $creator, + granularity => $env{'form.granularity'}, + specificity => $specificity, + autoadd => $env{'form.autoadd'}, + autodrop => $env{'form.autodrop'}, + quota => $quota, + ); + + foreach my $func (keys(%{$functions})) { + my $status; + if (grep(/^$func$/,@{$tools})) { + $status = 'on'; + } else { + $status = 'off'; + } + $groupinfo{'functions'} .= qq|<name id="$func">$status</name>|; + } + + $groupinfo{'roles'} = $roles; + $groupinfo{'types'} = $types; + $groupinfo{'sectionpick'} = $sections; + $groupinfo{'defpriv'} = $defprivs; + + my %groupsettings = (); + foreach my $item (@single_attributes) { + $groupsettings{$groupname} .= qq|<$item>$groupinfo{$item}</$item>|; + } + foreach my $item (@mult_attributes) { + foreach my $entry (@{$groupinfo{$item}}) { + $groupsettings{$groupname} .= qq|<$item>$entry</$item>|; + } + } + my $autosec; + my @autorole = &Apache::loncommon::get_env_multiple('form.autorole'); + + foreach my $role (@autorole) { + if (defined($env{'form.sec_'.$role})) { + my @autosections=&Apache::loncommon::get_env_multiple('form.sec_'. + $role); + $autosec .= '<role id="'.$role.'">'; + foreach my $sec (@autosections) { + $autosec .= '<section>'.$sec.'</section>'; + } + $autosec .= '</role>'; + } + } + if ($autosec) { + $groupsettings{$groupname} .= qq|<autosec>$autosec</autosec>|; + } + my $result = &Apache::lonnet::modify_coursegroup($cdom,$cnum, + \%groupsettings); + + if ($result eq 'ok') { + if ($action eq 'create') { + my $result = &add_group_folder($cdom,$cnum,$now,$groupname,$action, + $description,$tools,\%groupinfo, + $gpterm,$ucgpterm,$crstype); + if ($result eq 'ok') { + my $msg = &Apache::lonhtmlcommon::confirm_success(&mt($ucgpterm.' [_1] was created.','<i>'.$groupname.'</i>')); + $msg = &Apache::loncommon::confirmwrapper($msg); + $r->print($msg); + } else { + my $msg = &Apache::lonhtmlcommon::confirm_success(&mt('A problem occurred when creating folders for the new '.$gpterm.' [_1]:' + ,'<i>'.$groupname.'</i>') + .'<br />'.$result,1); + $msg = &Apache::loncommon::confirmwrapper($msg); + $r->print($msg); + } + } elsif ($action eq 'modify') { + my (@oldtools,@newtools); + if (ref($$stored{'tool'}) eq 'ARRAY') { + @oldtools = @{$$stored{'tool'}}; + } + if (ref($tools) eq 'ARRAY') { + @newtools = @{$tools}; + } + if (!grep(/^discussion$/,@oldtools) && + grep(/^discussion$/,@newtools)) { + my $crspath = '/uploaded/'.$cdom.'/'.$cnum.'/'; + my $boardsmap = $crspath.'group_boards_'.$groupname.'.sequence'; + my $navmap = Apache::lonnavmaps::navmap->new(); + my ($bbmapres,$error); + if (defined($navmap)) { + $bbmapres = $navmap->getResourceByUrl($boardsmap); + undef($navmap); + if (!$bbmapres) { + my $grpmap = $crspath.'group_folder_'.$groupname.'.sequence'; + my $disctitle = &mt('Discussion Boards'); + my $outcome = &map_updater($cdom,$cnum,'group_boards_'. + $groupname.'.sequence','bbseq', + $disctitle,$grpmap); + my ($furl,$ferr) = + &Apache::lonuserstate::readmap($cdom.'/'.$cnum); + # modify parameter + if ($outcome eq 'ok') { + $navmap = Apache::lonnavmaps::navmap->new(); + if (defined($navmap)) { + my $parm_result = &parm_setter($navmap,$cdom,$boardsmap, + $groupname); + if ($parm_result) { + $error = &mt('An error occurred while setting parameters ' + .'for Discussion Boards folder: ' + .'[_1]',$parm_result); + } else { + $r->print('<div class="LC_success">'. + &mt('Discussion Boards Folder created.') + .'</div>'); + } + undef($navmap); + } else { + if ($crstype eq 'Community') { + $error = &mt("An error occurred while setting parameters '. + 'for Discussion Boards folder: '. + 'Could not retrieve community information"); + } else { + $error = &mt("An error occurred while setting parameters '. + 'for Discussion Boards folder: '. + 'Could not retrieve course information"); + } + } + } else { + $r->print($outcome); + } + } + } else { + $error = &mt("An error occurred while retrieving the contents of the group's folder.").'<br />'; + if ($crstype eq 'Community') { + $error .= &mt("You need to re-initialize the community."); + + } else { + $error .= &mt("You need to re-initialize the course."); + } + } + if ($error ne '') { + $r->print('<div class="LC_error">'.$error.'</div>'); + } + } + my $message = &Apache::lonhtmlcommon::confirm_success(&mt($ucgpterm.' [_1] was updated.','<i>'.$groupname.'</i>')); + $message = &Apache::loncommon::confirmwrapper($message); + $r->print($message); + } + } else { + my %actiontype = ( + 'create' => 'creating', + 'modify' => 'modifying', + ); + &Apache::lonnet::logthis("Failed to store $gpterm $groupname ". + 'in '.lc($crstype).': '.$cnum. + ' in domain: '.$cdom); + $r->print('<p class="LC_error">' + .&mt('An error occurred when '.$actiontype{$action}.' the '.$gpterm.'. ' + .'Please try again.') + .'</p'); + } + return $result; +} + +sub process_membership { + my ($r,$cdom,$cnum,$action,$state,$groupname,$tools,$enddate,$startdate, + $userdata,$idx,$toolprivs,$usertools,$specificity,$defprivs,$memchg, + $gpterm,$ucgpterm,$crstype)=@_; + my %usersettings = (); + my %added= (); + my %failed = (); + my $num_ok = 0; + my $num_fail = 0; + my %group_privs = (); + my %curr_privs = (); + my %curr_start = (); + my %curr_end = (); + my %tooltype = (); + my $context = 'processgroupmembership'; + + foreach my $tool (@{$tools}) { + foreach my $priv (sort(keys(%{$$toolprivs{$tool}}))) { + $tooltype{$priv} = $tool; + if ($specificity eq 'Yes') { + my @users = + &Apache::loncommon::get_env_multiple('form.userpriv_'.$priv); + foreach my $user (@users) { + $group_privs{$user} .= $priv.':'; + if ($state eq 'memresult') { + unless (exists($$usertools{$user}{$tool})) { + $$usertools{$user}{$tool} = 1; + } + } + } + } else { + if (@{$defprivs} > 0) { + if (grep/^\Q$priv\E$/,@{$defprivs}) { + foreach my $user (sort(keys(%{$usertools}))) { + if ($$usertools{$user}{$tool}) { + $group_privs{$user} .= $priv.':'; + } + } + } + } + } + } + } + foreach my $user (keys(%group_privs)) { + $group_privs{$user} =~ s/:$//; + } + + my $now = time; + my @activate = (); + my @expire = (); + my @deletion = (); + my @reenable = (); + my @unchanged = (); + if ($state eq 'memresult') { + if (ref($$memchg{'activate'}) eq 'ARRAY') { + @activate = @{$$memchg{'activate'}}; + } + if (ref($$memchg{'expire'}) eq 'ARRAY') { + @expire = @{$$memchg{'expire'}}; + } + if (ref($$memchg{'deletion'}) eq 'ARRAY') { + @deletion = @{$$memchg{'deletion'}}; + } + if (ref($$memchg{'reenable'}) eq 'ARRAY') { + @reenable = @{$$memchg{'reenable'}}; + } + my %membership = &Apache::lonnet::get_group_membership($cdom,$cnum, + $groupname); + foreach my $key (sort(keys(%membership))) { + if ($key =~ /^\Q$groupname\E:([^:]+:[^:]+)$/) { + ($curr_end{$1},$curr_start{$1},$curr_privs{$1}) = + split(/:/,$membership{$key},3); + } + } + if (@expire + @deletion > 0) { + foreach my $user (@expire) { + my $savestart = $curr_start{$user}; + if ($savestart > $now) { + $savestart = $now; + } + $usersettings{$groupname.':'.$user} = $now.':'.$savestart.':'. + $curr_privs{$user}; + if (&Apache::lonnet::modify_group_roles($cdom,$cnum,$groupname, + $user,$now,$savestart, + $curr_privs{$user},'',$context) eq 'ok') { + push(@{$added{'expired'}},$user); + $num_ok ++; + } else { + push(@{$failed{'expired'}},$user); + $num_fail ++; + } + } + foreach my $user (@deletion) { + $usersettings{$groupname.':'.$user} = $now.':-1:'; + if (&Apache::lonnet::modify_group_roles($cdom,$cnum,$groupname, + $user,$now,'-1','','',$context) + eq 'ok') { + push(@{$added{'deleted'}},$user); + $num_ok ++; + } else { + push(@{$failed{'deleted'}},$user); + $num_fail ++; + } + } + } + } + + foreach my $user (sort(keys(%{$usertools}))) { + if ((grep(/^$user$/,@expire)) || (grep(/^$user$/,@deletion))) { + next; + } + my $type; + my $start = $startdate; + my $end = $enddate; + if ($state eq 'memresult') { + if (@activate > 0) { + if (grep/^$user$/,@activate) { + $start = $now; + $end = $enddate; + $type = 'activated'; + } + } + if (@reenable > 0) { + if (grep/^$user$/,@reenable) { + $start = $startdate; + $end = $enddate; + $type = 're-enabled'; + } + } + if ($type eq '') { + if ($curr_privs{$user} eq $group_privs{$user}) { + push(@unchanged,$user); + next; + } + if (exists($curr_start{$user})) { + $start = $curr_start{$user}; + } + if (exists($curr_end{$user})) { + $end = $curr_end{$user}; + } + $type = 'modified'; + } + } else { + $type = 'added'; + } + $usersettings{$groupname.':'.$user} = $end.':'.$start.':'. + $group_privs{$user}; + if (&Apache::lonnet::modify_group_roles($cdom,$cnum,$groupname, + $user,$end,$start, + $group_privs{$user},'',$context) eq 'ok') { + push(@{$added{$type}},$user); + $num_ok ++; + } else { + push(@{$failed{$type}},$user); + $num_fail ++; + } + } + my $roster_result = &Apache::lonnet::modify_coursegroup_membership($cdom, + $cnum,\%usersettings); + if ($num_ok) { + my $msgall =''; + foreach my $type (sort(keys(%added))) { + my $message; + my $tmsg = "The following users were successfully $type"; + if (!($type eq 'deleted' || $type eq 'expired')) { + $tmsg .= ' with the following privileges'; + } + $message .= &mt($tmsg.':').'<br/>'; + foreach my $user (@{$added{$type}}) { + my $privlist = ''; + if (!($type eq 'deleted' || $type eq 'expired')) { + $privlist = ': '; + my @privs = split(/:/,$group_privs{$user}); + my $curr_tool = ''; + foreach my $priv (@privs) { + unless ($curr_tool eq $tooltype{$priv}) { + $curr_tool = $tooltype{$priv}; + $privlist .= '<b>'.$curr_tool.'</b>: '; + } + $privlist .= $$toolprivs{$curr_tool}{$priv}.', '; + } + $privlist =~ s/, $//; + } + $message .= $$userdata{$user}[$$idx{fullname}].' - '.$user.$privlist.'<br />'; + } + $message .= '<br/>'; + $message = &Apache::lonhtmlcommon::confirm_success($message); + $msgall .= $message; + } + $msgall = &Apache::loncommon::confirmwrapper($msgall); + $r->print($msgall); + } + if ($num_fail) { + foreach my $type (sort(keys(%failed))) { + $r->print('<div class="LC_error">' + .&mt("The following users could not be $type, because an error occurred:") + .'</div>'); + foreach my $user (@{$failed{$type}}) { + $r->print($$userdata{$user}[$$idx{fullname}].' - '.$user.'<br />'); + } + } + $r->print('<br />'); + } +# Is that really needed? +# +# if (@unchanged > 0) { +# $r->print(&mt('No change occurred for the following users:').'<br />'); +# foreach my $user (sort(@unchanged)) { +# $r->print($$userdata{$user}[$$idx{fullname}].' - '.$user.'<br />'); +# } +# $r->print('<br />'); +# } + if ($roster_result eq 'ok') { + $r->print('<div class="LC_success">' + .&mt($ucgpterm.' membership list updated.') + .'</div>'); + $r->print('<p class="LC_info">'); + if ($crstype eq 'Community') { + $r->print(&mt("Any currently logged in community users affected by the changes you made" + .' to group membership or privileges for the [_1] group will need to log out' + .' and log back in for their LON-CAPA sessions to reflect these changes.' + ,'<i>'.$groupname.'</i>')); + + } else { + $r->print(&mt("Any currently logged in course users affected by the changes you made" + .' to group membership or privileges for the [_1] group will need to log out' + .' and log back in for their LON-CAPA sessions to reflect these changes.' + ,'<i>'.$groupname.'</i>')); + } + $r->print('</p>'); + } else { + $r->print('<div class="LC_error">' + .&mt("An error occurred while updating the $gpterm membership list:") + .'<br />'.$roster_result + .'</div>'); + } + return; +} + +sub mapping_options { + my ($r,$action,$formname,$page,$sectioncount,$states,$stored, + $navbuttons,$img1,$img2,$gpterm,$ucgpterm,$crstype,$cdom,$cnum) = @_; + my %lt = &Apache::lonlocal::texthash( + 'auto' => "Settings for automatic $gpterm enrollment", + 'gmma' => "$ucgpterm membership mapping to specific sections/roles", + 'endi' => "Enable/disable automatic $gpterm enrollment for ". + "users in specified roles and sections", + 'adds' => "If automatic $gpterm enrollment is enabled, when a user is newly assigned a ".lc($crstype)."-wide or section-specific role, he/she will automatically be added as a member of the $gpterm, with start and end access dates defined by the default dates set for the $gpterm, unless he/she is already a $gpterm member, with access dates that permit either current or future $gpterm access.", + 'drops' => "If automatic $gpterm disenrollment is enabled, when a user's role is expired, access to the $gpterm will be terminated unless the user continues to have other ".lc($crstype)."-wide or section-specific active or future roles which receive automatic membership in the $gpterm.", + 'pirs' => "Pick roles and sections for automatic $gpterm enrollment", + 'on' => 'on', + 'off' => 'off', + 'auad' => "Automatically enable $gpterm membership when roles are added?", + 'auex' => "Automatically expire $gpterm membership when roles are removed?", + 'mapr' => "Mapping of roles and sections affected by automatic $gpterm enrollment/disenrollment follows scheme chosen below.", + ); + &automapping($r,$action,$stored,\%lt,$img1); + &mapping_settings($r,$sectioncount,\%lt,$stored,$img2,$crstype,$cdom,$cnum, + $action); + return; +} + +sub automapping { + my ($r,$action,$stored,$lt,$image) = @_; + my $add = 'off'; + my $drop = 'off'; + if (exists($$stored{'autoadd'})) { + $add = $$stored{'autoadd'}; + } + if (exists($$stored{'autodrop'})) { + $drop = $$stored{'autodrop'}; + } + $r->print(&Apache::lonhtmlcommon::topic_bar($image,$$lt{'endi'}).' + <b>'.$$lt{'gmma'}.':</b><br />'.$$lt{'adds'}.'<br />'.$$lt{'drops'}.'<br /><br /> + <span class="LC_nobreak">'.$$lt{'auad'}.': + <label><input type="radio" name="autoadd" value="on" />'.&mt('on').' </label><label><input type="radio" name="autoadd" value="off" checked="checked" />'.&mt('off').'</label>'); + if ($action eq 'modify') { + $r->print(' ('.&mt('Currently set to [_1].','<b>'.$$lt{$add}.'</b>').')'); + } + $r->print(' + </span><br /> + <span class="LC_nobreak">'.$$lt{'auex'}.': + <label><input type="radio" name="autodrop" value="on" />'.&mt('on').' </label><label><input type="radio" name="autodrop" value="off" checked="checked" />'.&mt('off').'</label>'); + if ($action eq 'modify') { + $r->print(' ('.&mt('Currently set to [_1].','<b>'.$$lt{$drop}.'</b>').')'); + } + $r->print('</span><br /><br />'.$$lt{'mapr'}); +} + +sub mapping_settings { + my ($r,$sectioncount,$lt,$stored,$image,$crstype,$cdom,$cnum,$action) = @_; + my @sections = keys(%{$sectioncount}); + if (@sections > 0) { + @sections = sort {$a cmp $b} @sections; + unshift(@sections,'none'); # Put 'no sections' next + unshift(@sections,'all'); # Put 'all' at the front of the list + } else { + @sections = ('all','none'); + } + $r->print(&Apache::lonhtmlcommon::topic_bar($image,$$lt{'pirs'})); + my @roles = &standard_roles($crstype); + my %customroles = &Apache::lonhtmlcommon::course_custom_roles($cdom,$cnum); + $r->print(&Apache::loncommon::start_data_table(). + &Apache::loncommon::start_data_table_header_row()); + $r->print(' + <th>'.&mt('Active?').'</th> + <th>'.&mt('Role').'</th>'); + if (@sections > 0) { + $r->print('<th>'.&mt('Sections').'</th>'); + } + $r->print(&Apache::loncommon::end_data_table_header_row()."\n"); + foreach my $role (@roles) { + my $roletitle=&Apache::lonnet::plaintext($role,$crstype); + $r->print(&print_autorole_item($role,$roletitle,\@sections)); + } + my @customs; + foreach my $role (sort(keys(%customroles))) { + my ($roletitle) = ($role =~ m|^cr/[^/]+/[^/]+/(.+)$|); + push (@customs,$role); + $r->print(&print_autorole_item($role,$roletitle,\@sections)); + } + if ($action eq 'modify') { + foreach my $role (@{$$stored{'autorole'}}) { + if ((!grep(/^\Q$role\E$/,@customs)) && + (!grep(/^\Q$role\E$/,@roles))) { + my $roletitle; + if ($role =~ /^cr/) { + ($roletitle) = ($role =~ m|_([^_]+)$|); + } else { + $roletitle = &Apache::lonnet::plaintext($role,$crstype); + } + $r->print(&print_autorole_item($role,$roletitle,\@sections)); + } + } + } + $r->print(&Apache::loncommon::end_data_table()); + return; +} + +sub print_autorole_item { + my ($role,$roletitle,$sections) = @_; + my $sections_sel; + if (@{$sections} > 0) { + if (($role eq 'cc') || ($role eq 'co')) { + $sections_sel = '<td align="right">'. + &mt('all sections').'<input type="hidden" '. + 'name="sec_'.$role.'" value="all" /></td>'; + } else { + $sections_sel='<td align="right">'. + §ions_selection($sections,'sec_'.$role). + '</td>'; + } + } + my $output = &Apache::loncommon::start_data_table_row(). + '<td><input type="checkbox" '. + 'name="autorole" value="'.$role.'" />'. + '</td><td>'.$roletitle.'</td>'.$sections_sel. + &Apache::loncommon::end_data_table_row(); + return $output; +} + +sub standard_roles { + my ($crstype) = @_; + my @roles = qw(in ta ep ad st); + if ($crstype eq 'Community') { + unshift(@roles,'co'); + } else { + unshift(@roles,'cc'); + } + return @roles; +} + +sub modify_menu { + my ($r,$groupname,$page,$gpterm) = @_; + my @menu = + ( { categorytitle =>'Group Actions', + items => [ + + { linktext => "Modify default $gpterm settings", + url => '/adm/coursegroups?action=modify&refpage='.$env{'form.refpage'}.'&groupname='.$groupname.'&state=change_settings&branch=settings', + icon => 'grp_settings.png', + alttext => "Modify default $gpterm settings", + permission => '1', + help => 'Course_Modify_Group', + }, + { linktext => 'Modify access, tools and privileges for members', + url => '/adm/coursegroups?action=modify&refpage='.$env{'form.refpage'}.'&groupname='.$groupname.'&state=change_members&branch=members', + icon => 'grp_tools.png', + alttext => 'Modify access, tools and privileges for members', + permission => '1', + help => 'Course_Modify_Group_Membership', + }, + { linktext => "Add member(s) to the $gpterm", + url => '/adm/coursegroups?action=modify&refpage='.$env{'form.refpage'}.'&groupname='.$groupname.'&state=add_members&branch=adds', + icon => 'grp_add.png', + alttext => "Add member(s) to the $gpterm", + permission => '1', + help => 'Course_Group_Add_Members', + }]} + ); + $r->print(&Apache::lonhtmlcommon::generate_menu(@menu)); + return; +} + +sub member_privs_entries { + my ($r,$usertools,$toolprivs,$fixedprivs,$userdata,$idx,$showtools, + $defprivs,$excluded) = @_; + foreach my $user (sort(keys(%{$usertools}))) { + if (defined($excluded)) { + if (ref($excluded) eq 'ARRAY') { + if (grep/^$user$/,@{$excluded}) { + next; + } + } + } + my ($uname,$udom) = split(/:/,$user); + $r->print(&Apache::loncommon::start_data_table_row().' + <td>'.$$userdata{$user}[$$idx{fullname}].'</td> + <td>'.$uname.'</td> + <td>'.$udom.'</td> + <td valign="top"> + <table> + <tr> + <td><b>'. + &mt('Collaborative Tool').'</b></td> + </tr> + <tr> + <td><b>'.&mt('Fixed').'</b></td> + </tr> + <tr> + <td><b>'.&mt('Optional').'</b></td> + </tr> + </table> + </td>'); + foreach my $tool (@{$showtools}) { + if (exists($$usertools{$user}{$tool})) { + $r->print('<td valign="top"><table><tr><th colspan="2">'.$tool.'</th></tr>'); + my $privcount = 0; + my $fixed = ''; + my $dynamic = ''; + foreach my $priv (sort(keys(%{$$toolprivs{$tool}}))) { + if (exists($$fixedprivs{$tool}{$priv})) { + $fixed .= '<input type="hidden" name="userpriv_'.$priv.'" value="'.$user.'" />'.$$toolprivs{$tool}{$priv}.' '; + } else { + $privcount ++; + if ($privcount == 3) { + $dynamic .= '</tr><tr>'; + } + $dynamic .='<td><span class="LC_nobreak"><label><input type="checkbox" '. + 'name="userpriv_'.$priv.'" value="'.$user.'"'; + if (grep/^\Q$priv\E$/,@{$defprivs}) { + $dynamic .= ' checked="checked" '; + } + $dynamic .= ' />'.$$toolprivs{$tool}{$priv}. + '</label></span></td>'; + } + } + $r->print('<tr><td colspan="2"><span class="LC_nobreak">'.$fixed.'</span></td></tr><tr>'.$dynamic.'</tr></table></td>'); + } else { + $r->print('<td valign="top"><table width="100%"><tr><th colspan="2">'.$tool.'</th></tr><tr><td> </td></tr><tr><td> </td></tr></table></td>'); + } + } + $r->print(&Apache::loncommon::end_data_table_row()); + } +} + +sub get_dates_from_form { + my $startdate; + my $enddate; + $startdate = &Apache::lonhtmlcommon::get_date_from_form('startdate'); + $enddate = &Apache::lonhtmlcommon::get_date_from_form('enddate'); + if ( exists ($env{'form.no_end_date'}) ) { + $enddate = 0; + } + return ($startdate,$enddate); +} + +sub date_setting_table { + my ($starttime,$endtime,$formname) = @_; + my $startform = &Apache::lonhtmlcommon::date_setter($formname, + 'startdate',$starttime); + my $endform = &Apache::lonhtmlcommon::date_setter($formname, + 'enddate',$endtime); + my $perpetual = ' <span class="LC_nobreak"><label>' + .'<input type="checkbox" name="no_end_date" />' + .&mt('No end date') + .'</label></span>'; + my $table = &Apache::lonhtmlcommon::start_pick_box() + .&Apache::lonhtmlcommon::row_title(&mt('Start Date')) + .$startform + .&Apache::lonhtmlcommon::row_closure() + .&Apache::lonhtmlcommon::row_title(&mt('End Date')) + .$endform + .$perpetual + .&Apache::lonhtmlcommon::row_closure(1) + .&Apache::lonhtmlcommon::end_pick_box(); + return $table; +} + +sub add_group_folder { + my ($cdom,$cnum,$now,$groupname,$action,$description,$tools,$groupinfo, + $gpterm,$ucgpterm,$crstype) = @_; + if ($cdom eq '' || $cnum eq '') { + my $error = '<span class="LC_error">'; + if ($crstype eq 'Community') { + $error .= &mt("Error: invalid community domain or number - group folder creation failed."); + } else { + $error .= &mt("Error: invalid course domain or number - group folder creation failed."); + } + $error .= '</span>'; + return $error; + } + my ($outcome,$allgrpsmap,$grpmap,$boardsmap,$grppage,$warning); + my $crspath = '/uploaded/'.$cdom.'/'.$cnum.'/'; + $allgrpsmap = $crspath.'group_allfolders.sequence'; + if ($action eq 'create') { + if (&get_folder_lock($cdom,$cnum,'group_allfolders',$now) eq 'ok') { + # check if group_allfolders.sequence exists. + my $mapcontents = &Apache::lonnet::getfile($allgrpsmap); + if ($mapcontents eq '-1') { #file does not exist; + my $grpstitle = &mt("$crstype $ucgpterm".'s'); + my $topmap_url = '/'.$env{'course.'.$env{'request.course.id'}.'.url'}; + $topmap_url =~ s|/+|/|g; + if ($topmap_url =~ m|^/uploaded|) { + $outcome = &map_updater($cdom,$cnum,'group_allfolders.sequence', + 'toplevelgroup',$grpstitle,$topmap_url); + } else { + $outcome = '<span class="LC_warning">' + .&mt('Non-standard course - folder for all groups not added.') + .'</span>'; + } + if ($outcome ne 'ok') { + my $delresult = &release_folder_lock($cdom,$cnum,'group_allfolders'); + if ($delresult ne 'ok') { + $warning = $delresult; + } + return $outcome.$warning; + } + } + my $delresult = &release_folder_lock($cdom,$cnum,'group_allfolders'); + if ($delresult ne 'ok') { + $warning = $delresult ; + } + } else { + $outcome = '<span class="LC_error">' + .&mt('Could not obtain exclusive lock to check status of the folder for all groups. No group folder added.') + .'</span>'; + return $outcome; + } + my $grpfolder = &mt($ucgpterm.' Folder - [_1]',$description); + $grppage='/adm/'.$cdom.'/'.$cnum.'/'.$groupname.'/smppg'; + my $grptitle = &mt('Group homepage - [_1]',$description); + my ($discussions,$disctitle); + my $outcome = &map_updater($cdom,$cnum,'group_folder_'.$groupname.'.sequence', + 'grpseq',$grpfolder,$allgrpsmap,$grppage, + $grptitle); + if ($outcome ne 'ok') { + return $outcome.$warning; + } + my $pageout = &create_homepage($cdom,$cnum,$groupname,$groupinfo, + $tools,$gpterm,$ucgpterm,$now); + # Link to folder for bulletin boards + $grpmap = $crspath.'group_folder_'.$groupname.'.sequence'; + if (grep/^discussion$/,@{$tools}) { + $disctitle = &mt('Discussion Boards'); + my $outcome = &map_updater($cdom,$cnum,'group_boards_'.$groupname. + '.sequence','bbseq',$disctitle,$grpmap); + if ($outcome ne 'ok') { + return $outcome.$warning; + } + $boardsmap = $crspath.'group_boards_'.$groupname.'.sequence'; + } + } else { + #modify group folder if status of discussions tools is changed + } + my ($furl,$ferr)= &Apache::lonuserstate::readmap($cdom.'/'.$cnum); + my $navmap = Apache::lonnavmaps::navmap->new(); + if (!defined($navmap)) { + $warning .= '<span class="LC_error">'; + if ($crstype eq 'Community') { + $warning .= &mt("Error retrieving community contents"). + ' '.&mt("You need to re-initialize the community."); + } else { + $warning .= &mt("Error retrieving course contents"). + ' '.&mt("You need to re-initialize the course."); + } + $warning .= '</span>'; + return $warning; + } + # modify parameters + my $parm_result; + if ($action eq 'create') { + if ($grpmap) { + $parm_result .= &parm_setter($navmap,$cdom,$grpmap,$groupname); + } + if ($grppage) { + $parm_result .= &parm_setter($navmap,$cdom,$grppage,$groupname); + } + if ($boardsmap) { + $parm_result .= &parm_setter($navmap,$cdom,$boardsmap,$groupname); + } + } + undef($navmap); + if ($parm_result) { + return $warning.$parm_result; + } else { + return 'ok'; + } +} + +sub get_folder_lock { + my ($cdom,$cnum,$folder_name,$now) = @_; + # get lock for folder being edited. + my $lockhash = { + $folder_name."\0".'locked_folder' => $now.':'.$env{'user.name'}. + ':'.$env{'user.domain'}, + }; + my $tries = 0; + my $gotlock = &Apache::lonnet::newput('coursegroups',$lockhash,$cdom,$cnum); + + while (($gotlock ne 'ok') && $tries <3) { + $tries ++; + sleep(1); + $gotlock = &Apache::lonnet::newput('coursegroups',$lockhash,$cdom,$cnum); + } + return $gotlock; +} + +sub release_folder_lock { + my ($cdom,$cnum,$folder_name) = @_; + # remove lock + my @del_lock = ($folder_name."\0".'locked_folder'); + my $dellockoutcome=&Apache::lonnet::del('coursegroups',\@del_lock,$cdom,$cnum); + if ($dellockoutcome ne 'ok') { + return ('<div class="LC_error">' + .&mt('Warning: failed to release lock for folder: [_1].','<tt>'.$folder_name.'</tt>') + .'</div>' + ); + } else { + return 'ok'; + } +} + +sub map_updater { + my ($cdom,$cnum,$newfile,$itemname,$itemtitle,$parentmap,$startsrc, + $starttitle,$endsrc,$endtitle) = @_; + my $outcome; + $env{'form.'.$itemname} = &new_map($startsrc,$starttitle,$endsrc, + $endtitle); + my $newmapurl=&Apache::lonnet::finishuserfileupload($cnum,$cdom,$itemname, + $newfile); + if ($newmapurl !~ m|^/uploaded|) { + $outcome = '<div class="LC_error">' + .&mt('Error uploading new folder.')." ($newfile): $newmapurl" + .'</div>'; + return $outcome; + } + my ($errtext,$fatal)=&LONCAPA::map::mapread($parentmap); + if ($fatal) { + $outcome = '<div class="LC_error">' + .&mt('Error reading contents of parent folder.')." ($parentmap): $errtext" + .'</div>'; + return $outcome; + } else { + my $newidx=&LONCAPA::map::getresidx($newmapurl); + $LONCAPA::map::resources[$newidx] = $itemtitle.':'.$newmapurl. + ':false:normal:res'; + $LONCAPA::map::order[1+$#LONCAPA::map::order]=$newidx; + my ($outtext,$errtext) = &LONCAPA::map::storemap($parentmap,1,1); + if ($errtext) { + $outcome = '<div class="LC_error">' + .&mt('Error saving updated parent folder.')." ($parentmap): $errtext" + .'</div>'; + return $outcome; + } + } + return 'ok'; +} + +sub new_map { + my ($startsrc,$starttitle,$endsrc,$endtitle) = @_; + my $newmapstr = ' +<map> + <resource id="1" src="'.$startsrc.'" type="start" title="'.$starttitle.'"></resource> + <link from="1" to="2" index="1"></link> + <resource id="2" src="'.$endsrc.'" type="finish" title="'.$endtitle.'"></resource> +</map> +'; + return $newmapstr; +} + +sub parm_setter { + my ($navmap,$cdom,$url,$groupname,$crstype) = @_; + if (!defined($navmap)) { + my $allresults; + if ($crstype eq 'Community') { + $allresults = &mt("Parameters not set for [_1] because the contents of the community could not be retrieved.",$url).' '. + &mt("You need to reinitialize the community."); + } else { + $allresults = &mt("Parameters not set for [_1] because the contents of the course could not be retrieved.",$url).' '. + &mt("You need to reinitialize the course."); + + } + return '<div class="LC_warning">'.$allresults.'</div>'; + } + my %hide_settings = ( + 'course' => { + 'num' => 13, + 'set' => 'yes', + }, + 'group' => { + 'num' => 5, + 'set' => 'no', + 'extra' => $groupname, + }, + ); + my $res = $navmap->getResourceByUrl($url); + my $allresults; + if ($res) { + my $symb = $res->symb(); + foreach my $level (keys(%hide_settings)) { + my $parmresult = + &Apache::lonparmset::storeparm_by_symb($symb, + '0_hiddenresource', + $hide_settings{$level}{'num'}, + $hide_settings{$level}{'set'}, + 'string_yesno',undef,$cdom, + undef,undef, + $hide_settings{$level}{'extra'}); + if ($parmresult) { + $allresults .= $level.': '.$parmresult; + } + } + } else { + $allresults = '<div class="LC_warning">'; + if ($crstype eq 'Community') { + $allresults .= &mt("Parameters not set for [_1] because the resource was not recognized as part of the community.",'<tt>'.$url.'</tt>'); + } else { + $allresults .= &mt('Parameters not set for [_1] because the resource was not recognized as part of the course.','<tt>'.$url.'</tt>'); + } + $allresults .= '</div>'; + } + return $allresults; +} + +sub create_homepage { + my ($cdom,$cnum,$name,$groupinfo,$tools,$gpterm,$ucgpterm,$now) = @_; + my $functionality = join(',',@{$tools}); + my $content = &unescape($$groupinfo{description}); + chomp($content); + my $gateway = Apache::lonhtmlgateway->new(); + $content = $gateway->process_incoming_html($content,1); + + my %pageinfo = ( + 'aaa_title' => "$ucgpterm: $name", + 'abb_links' => $functionality, + 'bbb_content' => $content, + 'ccc_webreferences' => '', + 'uploaded.lastmodified' => $now, + ); + my $putresult = &Apache::lonnet::put('grppage_'.$name,\%pageinfo,$cdom,$cnum); + return $putresult; +} + +sub check_uncheck_tools { + my ($r,$available) = @_; + if (ref($available) eq 'ARRAY') { + $r->print(' +<script type="text/javascript"> +function checkAllTools(formname) { +'); + foreach my $tool (@{$available}) { + $r->print(' checkAll(formname.user_'.$tool.');'."\n"); + } + $r->print(' checkAll(formname.togglefunc);'."\n"); + $r->print(' +} +function uncheckAllTools(formname) { +'); + foreach my $tool (@{$available}) { + $r->print(' uncheckAll(formname.user_'.$tool.');'."\n"); + } + $r->print(' uncheckAll(formname.togglefunc);'."\n"); + $r->print(' +} +function toggleTools(field,caller) { + if (caller.checked) { + checkAll(field); + } else { + uncheckAll(field); + } + return; +} +</script> +'); + } + return; +} + +sub validate_groupname { + my ($groupname,$action,$cdom,$cnum,$gpterm,$ucgpterm,$crstype) = @_; + my %sectioncount = &Apache::loncommon::get_sections($cdom,$cnum); + my %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum); + my %deleted_groups = &Apache::longroup::coursegroups($cdom,$cnum,undef, + 'deleted_groups'); + if (my $tmp = &Apache::lonnet::error(%deleted_groups)) { + undef(%deleted_groups); + &Apache::lonnet::logthis('Error retrieving groups: '.$tmp.' in '.$cnum.':'.$cdom); + } + my %lt = &Apache::lonlocal::texthash ( + igna => "Invalid $gpterm name", + grna => "$ucgpterm names and section names used in a ". + "$crstype must be unique.", + gnmo => "$ucgpterm names may only contain letters, ". + "numbers or underscores.", + ); + + my $exitmsg = '<span class="LC_error">'.$lt{'igna'}.'</span><br /><br />'; + my $nameshown = &Apache::loncommon::cleanup_html($groupname); + if (($groupname eq '') || ($groupname =~ /\W/)) { + return $exitmsg. + &mt("The $gpterm name entered '[_1]' is not a valid name.",$nameshown). + '<br />'.$lt{'gnmo'}; + } elsif ($groupname eq 'syllabus') { + return $exitmsg. + &mt("The $gpterm name entered '[_1]' is reserved for use by LON-CAPA.",$nameshown); + } + if (exists($sectioncount{$groupname})) { + return $exitmsg. + &mt("The $gpterm name entered '[_1]' can not be used as it is the name of a section in this $crstype.",$nameshown). + '<br />'.$lt{'grna'}; + } + if ($action eq 'create') { + if (exists($curr_groups{$groupname})) { + return $exitmsg. + &mt("The $gpterm name entered '[_1]' can not be used as it is the name of an existing $gpterm in this $crstype.",$nameshown). + '<br />'.$lt{'grna'}; + } elsif (exists($deleted_groups{$groupname})) { + return $exitmsg. + &mt("The $gpterm name entered '[_1]' can not be used as it is the name of a $gpterm which previously existed in this $crstype.",$nameshown). + '<br />'.$lt{'grna'}; + } + } elsif ($action eq 'modify') { + unless(exists($curr_groups{$groupname})) { + return &mt("$ucgpterm name: [_1] does not correspond to the name of an existing $gpterm in this $crstype.",$nameshown); + } + } + return; +} + +sub check_changes { + my ($member_changes,$memchg) = @_; + my %exclusions; + @{$exclusions{'changefunc'}} = ('expire'); + @{$exclusions{'changepriv'}} = ('expire','changefunc'); + + foreach my $change (@{$member_changes}) { + if ($change eq 'deletion') { + next; + } + my @checks = ('deletion'); + if (exists($exclusions{$change})) { + push(@checks,@{$exclusions{$change}}); + } + my @temp = (); + foreach my $item (@{$$memchg{$change}}) { + my $match = 0; + foreach my $check (@checks) { + if (defined($$memchg{$check})) { + if (ref(@{$$memchg{$check}}) eq 'ARRAY') { + if (@{$$memchg{$check}} > 0) { + if (grep/^$item$/,@{$$memchg{$check}}) { + $match = 1; + last; + } + } + } + } + } + if ($match) { + next; + } + push(@temp,$item); + } + @{$$memchg{$change}} = @temp; + } } 1; +