--- loncom/interface/loncoursegroups.pm 2005/11/15 22:03:05 1.3 +++ loncom/interface/loncoursegroups.pm 2006/07/07 19:50:21 1.44 @@ -1,3 +1,6 @@ +# The LearningOnline Network with CAPA +# +# $Id: loncoursegroups.pm,v 1.44 2006/07/07 19:50:21 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -30,14 +33,18 @@ use Apache::loncommon; use Apache::lonhtmlcommon; use Apache::lonlocal; use Apache::lonnavmaps; +use Apache::longroup; +use Apache::portfolio; use Apache::Constants qw(:common :http); +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; } @@ -51,8 +58,7 @@ sub handler { } &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, - ['action','refpage']); - + ['action','refpage','state','groupname','branch']); my $function = &Apache::loncommon::get_users_function(); my $tabcol = &Apache::loncommon::designparm($function.'.tabbg'); my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; @@ -62,16 +68,20 @@ sub handler { &Apache::lonnet::allowed('vcg',$env{'request.course.id'}); my $manage_permission = &Apache::lonnet::allowed('mdg',$env{'request.course.id'}); - &Apache::lonhtmlcommon::clear_breadcrumbs(); + my $gpterm = &Apache::loncommon::group_term(); + my $ucgpterm = $gpterm; + $ucgpterm =~ s/^(\w)/uc($1)/e; + my $crstype = &Apache::loncommon::course_type(); + my %functions = ( email => 'E-mail', discussion => 'Discussion boards', chat => 'Chat', files => 'File repository', roster => 'Membership roster', - homepage => 'Group home page', + homepage => $ucgpterm.' home page', ); my %idx = (); @@ -79,156 +89,268 @@ sub handler { $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 $rowColor1 = "#dddddd"; + my $rowColor2 = "#eeeeee"; my $action = $env{'form.action'}; + my $state = $env{'form.state'}; + if ((!defined($action)) || ($action eq 'view')) { + if (!defined($state)) { + $state = 'view'; + } + } if ($action eq 'create' || $action eq 'modify' || $action eq 'view') { if ($view_permission || $manage_permission) { - &group_administration($r,$action,$cdom,$cnum,$function,$tabcol, - \%functions,\%idx,$view_permission,$manage_permission); + &group_administration($r,$action,$state,$cdom,$cnum,$function, + $tabcol,\%functions,\%idx,$view_permission, + $manage_permission,$rowColor1,$rowColor2, + $gpterm,$ucgpterm,$crstype); } else { - $r->print('You do not have group administration '. - 'privileges in this course'); + $r->print(&mt('You do not have [_1] administration '. + 'privileges in this [_2]',$gpterm,lc($crstype))); } } else { - &print_main_menu($r,$cdom,$cnum,$function,$tabcol,\%functions,\%idx, - $view_permission,$manage_permission); + &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,$function,$tabcol,$functions,$idx,$view_permission, - $manage_permission) - = @_; - $r->print(&header('Course Groups',&mt('LON-CAPA Course Groups'), - undef,undef,undef,$function)); + my ($r,$cdom,$cnum,$functions,$idx,$view_permission,$manage_permission, + $action,$state,$gpterm,$ucgpterm,$crstype) = @_; + my $pagename = "$crstype $ucgpterm".'s'; + my $jscript = qq| +function changeSort(caller) { + document.$state.sortby.value = caller; + document.$state.submit(); +}\n|; + $r->print(&header($pagename,$jscript,$action,$state)); &Apache::lonhtmlcommon::add_breadcrumb ({href=>"/adm/coursegroups", - text=>"Course Groups",}); - $r->print(&Apache::lonhtmlcommon::breadcrumbs - (undef,'Course Groups')); - &display_groups($r,$cdom,$cnum,$function,$tabcol,$functions,$idx, - $view_permission,$manage_permission); + text=>"$pagename"}); + $r->print(&Apache::lonhtmlcommon::breadcrumbs($pagename)); + &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,$function,$tabcol,$functions,$idx,$view_permission, - $manage_permission) = @_; + my ($r,$cdom,$cnum,$functions,$idx,$view_permission, + $manage_permission,$action,$state,$gpterm,$ucgpterm,$crstype) = @_; my %curr_groups = (); my %grp_info = (); - my $rowColor1 = "#dddddd"; - my $rowColor2 = "#eeeeee"; - - $r->print('

'); + my %actionlinks = ( + modify => ' ' 'Description', + crea => 'Creator', + crtd => 'Created', + last => 'Last Modified', + func => 'Functionality', + quot => 'Quota (Mb)', + memb => 'Members', + file => 'Files', + dibd => 'Discussion Boards', + dius => 'Disk Use (%)', + nogr => 'No '.$gpterm.'s exist.', + crng => 'Create a new '.$gpterm, + alth => 'Although your current role has privileges'. + ' to view any existing '.$gpterm.'s in this'. + lc($crstype).', you do not have privileges'. + 'to create new '.$gpterm.'s.', + ); if ($view_permission) { - if (&Apache::lonnet::get_coursegroups($cdom,$cnum,\%curr_groups) > 0) { - $r->print(&Apache::lonhtmlcommon::start_pick_box()); - if (keys(%curr_groups) > 0) { - $r->print(<<"END"); - - - - - - - - - - - - - - - + if (!defined($action)) { + $action = 'view'; + } + my %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum); + if (%curr_groups) { + if ($manage_permission) { + $r->print('
'.$lt{'crng'}.''); + } + $r->print('

'); + $r->print(&Apache::loncommon::start_data_table(). + &Apache::loncommon::start_data_table_header_row()); + + $r->print(<<"END"); + + + + + + + + + + + END - my %Sortby = (); - foreach my $group (sort(keys(%curr_groups))) { - %{$grp_info{$group}} = - &Apache::loncommon::get_group_settings( + $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($group,\%grp_info); - my $files_result = &group_files($group,\%grp_info); - 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'}}}, + my $members_result = &group_members($cdom,$cnum,$group, + \%grp_info); + my $port_path = '/userfiles/groups/'.$group.'/portfolio'; + my $port_dir = &Apache::loncommon::propath($cdom,$cnum).$port_path; + my $totaldirs = 0; + my $totalfiles = 0; + &group_files($group,$port_dir,\$totalfiles,\$totaldirs); + $grp_info{$group}{'totalfiles'} = $totalfiles; + $grp_info{$group}{'totaldirs'} = $totaldirs; + my $diskuse = &Apache::lonnet::diskusage($cdom,$cnum,$port_dir); + 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 '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); - } + } 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); } - my $rowNum = 0; - my $rowColor; - foreach my $key (sort(keys(%Sortby))) { - foreach my $group (@{$Sortby{$key}}) { - if ($rowNum %2 == 1) { - $rowColor = $rowColor1; - } else { - $rowColor = $rowColor2; - } - my $description = - &Apache::lonnet::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 $boards = $grp_info{$group}{'boards'}; - my $diskuse = $grp_info{$group}{'diskuse'}; - my $functionality; - foreach my $tool (sort keys(%{$functions})) { - if (defined($grp_info{$group}{functions}{$tool})) { - $functionality .= ' '.$tool; - } + } + 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 = 'None available'; + } + if (!$functionality) { + $functionality = &mt('None available'); + } + my $link = $actionlinks{$action}; + if ($action eq 'modify' || $action eq 'delete') { + $link .= $group; + } else { + $link .= $group.'/grppg'; + } + $link .= '">'.$lt{$action}.''; + if ($action eq 'view') { + if (($manage_permission) && + ($env{'form.refpage'} ne 'enrl')) { + $link .= '  '.$actionlinks{'modify'}. + $group.'">'.$lt{'modify'}.''; } - $r->print(''); - $rowNum ++; } + $r->print(&Apache::loncommon::start_data_table_row('LC_data_table_dense'). + ''. + ''. + ''. + ''. + ''. + ''. + ''. + ''. + ''. + ''. + ''. + ''. + &Apache::loncommon::end_data_table_row()); } - $r->print('
ActionGroup NameDescriptionCreator - Created - Last Modified - Functionality - Quota (Mb)MembersFilesDiscussion boardsDisk use
$lt{'act'}$lt{'gname'}$lt{'desc'}$lt{'crea'}$lt{'crtd'}$lt{'last'}$lt{'func'} + $lt{'quot'}$lt{'memb'}$lt{'file'}$lt{'dibd'}$lt{'dius'}
- View Modify'.$group.''.$description.''.$creator.''. &Apache::lonnavmaps::timeToHumanString($creation).''. &Apache::lonnavmaps::timeToHumanString($modified).''.$functionality.''.$quota.''.$totalmembers.''.$totalfiles.''.$boards.''.$diskuse.'
'.$link.''.$group.''.$description.''.$creator.''. &Apache::lonnavmaps::timeToHumanString($creation).''. &Apache::lonnavmaps::timeToHumanString($modified).''.$functionality.''.$quota.''.$totalmembers.''.&mt('Files: ').$totalfiles.'
'.&mt('Folders: ').$totaldirs.'
'.$boards.''.$diskuse.'
'); - $r->print(&Apache::lonhtmlcommon::end_pick_box()); - } + } + $r->print(&Apache::loncommon::end_data_table()); + $r->print(''); + if ($action eq 'view') { + if (!defined($state)) { + $state = 'view'; + } + $r->print(''); + } } else { - $r->print('No groups exist'); + $r->print($lt{'nogr'}); + if ($manage_permission) { + $r->print('

'.$lt{'crng'}.''); + } else { + $r->print('

'.$lt{'alth'}); + + } } } else { - $r->print('You do not have sufficient privileges to allow you to display course groups'); + my @coursegroups = split(/:/,$env{'request.course.groups'}); + if (@coursegroups > 0) { + $r->print('

'); + my %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum); + if (%curr_groups) { + foreach my $group (@coursegroups) { + my %group_info = &Apache::longroup::get_group_settings( + $curr_groups{$group}); + my $description = &unescape( + $group_info{description}); + my ($uname,$udom) = split(/:/,$group_info{creator}); + $r->print(''.$group,'
'.$description.'

'); + } + } + } else { + $r->print(&mt('You are not currently a member of any '. + 'active [_1]s in this [_2]',$gpterm, + lc($crstype))); + } } return; } sub group_administration { - my ($r,$action,$cdom,$cnum,$function,$tabcol,$functions,$idx, - $view_permission,$manage_permission) = @_; + my ($r,$action,$state,$cdom,$cnum,$function,$tabcol,$functions,$idx, + $view_permission,$manage_permission,$rowColor1,$rowColor2,$gpterm, + $ucgpterm,$crstype) = @_; my %sectioncount = (); my @tools = (); my @types = (); @@ -238,105 +360,157 @@ sub group_administration { 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); - my $state = $env{'form.state'}; - my ($groupname,$description,$startdate,$enddate); + if (defined($env{'form.groupname'})) { + $groupname = $env{'form.groupname'}; + } - if ($action eq 'create') { + 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 '') { - $state = 'pick_name'; - } else { - ($startdate,$enddate) = &get_dates_from_form(); if (defined($env{'form.groupname'})) { - $groupname = $env{'form.groupname'}; - } - if (defined($env{'form.description'})) { - $description = $env{'form.description'}; - } - if (defined($env{'form.tool'})) { - @tools=&Apache::loncommon::get_env_multiple('form.tool'); + $state = 'pick_task'; + } else { + $state = 'pick_group'; } - if (defined($env{'form.member'})) { - @members = &Apache::loncommon::get_env_multiple('form.member'); - foreach my $user (@members) { - %{$usertools{$user}} = (); + } else { + %stored = &retrieve_settings($cdom,$cnum,$groupname); + 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') { + $elements{'modify'}{'change_settings'}{'sec_'.$role} = + 'selectbox'; } } } - my %toolprivs = (); - %{$toolprivs{'email'}} = ( - sgm => 'Send group mail', - sgb => 'Broadcast mail', - ); - %{$toolprivs{'discussion'}} = ( - cgb => 'Create boards', - pgd => 'Post', - pag => 'Anon. posts', - rgi => 'Get identities', - vgb => 'View boards', - ); - %{$toolprivs{'chat'}} = ( - pgc => 'Chat', - ); - %{$toolprivs{'files'}} = ( - rgf => 'Retrieve', - ugf => 'Upload', - dgf => 'Delete', - ); - %{$toolprivs{'roster'}} = ( - vgm => 'View', - ); - %{$toolprivs{'homepage'}} = ( - vgh => 'View page', - mgh => 'Modify page', - ); - my %fixedprivs = (); - %{$fixedprivs{'email'}} = ('sgm' => 1); - %{$fixedprivs{'discussion'}} = ('vgb' => 1); - %{$fixedprivs{'chat'}} = ('pgc' => 1); - %{$fixedprivs{'files'}} = ('rgf' => 1); - %{$fixedprivs{'roster'}} = ('vgm' => 1); - %{$fixedprivs{'homepage'}} = ('vgh' => 1); - - my %elements = (); - %{$elements{'create'}} = (); - %{$elements{'modify'}} = (); - %{$elements{'create'}{'pick_name'}} = ( - startdate_month => 'selectbox', - startdate_hour => 'selectbox', - enddate_month => 'selectbox', - enddate_hour => 'selectbox', - types => 'selectbox', - roles => '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', - tool => 'checkbox', - granularity => 'radio', - no_end_date => 'checkbox', - ); - %{$elements{'create'}{'pick_members'}} = ( - member => 'checkbox', - ); if (($action eq 'create') && ($state eq 'pick_name')) { - my $numsections = &Apache::loncommon::get_sections($cdom,$cnum, - \%sectioncount); - if ($numsections > 0) { + $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); + if (%sectioncount) { $elements{'create'}{'pick_name'}{'sectionpick'} = 'selectbox'; + $elements{'modify'}{'change_mapping'}{'sectionpick'} = 'selectbox'; + $elements{'modify'}{'add_members'}{'sectionpick'} = 'selectbox'; } } - - if (($action eq 'create') && (($state eq 'pick_members') || - ($state eq 'pick_privs'))) { + + if (($action eq 'create') || + ($action eq 'modify' && $state eq 'pick_members')) { if (defined($env{'form.types'})) { @types=&Apache::loncommon::get_env_multiple('form.types'); } @@ -345,69 +519,302 @@ sub group_administration { } if (defined($env{'form.sectionpick'})) { @sections=&Apache::loncommon::get_env_multiple('form.sectionpick'); - if (grep/^_all$/,@sections) { + if (grep/^all$/,@sections) { @sections = sort {$a cmp $b} keys(%sectioncount); } } - &build_members_list($cdom,$cnum,\@types,\@roles, - \@sections,\%users,\%userdata); + } + + 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 = 1; + 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 ($env{'form.granularity'} eq 'Yes') { - $elements{'create'}{'pick_members'}{'user_'.$tool} = 'checkbox'; + if ($granularity eq 'Yes') { + $elements{$action}{'change_members'}{'user_'.$tool} = 'checkbox'; } } - $elements{'create'}{'pick_members'}{'specificity'} = 'radio'; + if ($specificity eq 'Yes') { + $elements{$action}{'change_members'}{'changepriv'} = 'checkbox'; + } } } - if (($action eq 'create') && (($state eq 'pick_privs') || (($state eq 'result') && - ($env{'form.specificity'} eq 'No')))) { + 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 (($state eq 'pick_privs') && ($env{'form.specificity'} eq 'Yes')) { - foreach my $member (@members) { - foreach my $tool (keys(%{$usertools{$member}})) { - foreach my $priv (keys(%{$toolprivs{$tool}})) { - unless (exists($fixedprivs{$tool}{$priv})) { - $elements{'create'}{'pick_privs'}{'userpriv_'.$priv} = - 'checkbox'; + } + + 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}} = (); } } } } } } - - my $jscript; - if ($env{'form.action'} eq 'create') { - $jscript = &Apache::loncommon::check_uncheck_jscript(); + + 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 = document.'.$state.'.quota.value; + newquota.replace('.$space_trim.'); + if (newquota == "" ) { + document.'.$state.'.quota.value = 0; + newquota = 0; + } + var maxposs = '.$maxposs.'; + if (newquota > maxposs) { + alert("The file repository 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; iprint(&header('Course Groups Manager',&mt('LON-CAPA Groups Manager'), - $jscript,$action,$state,$function,$loaditems)); + my $crumbtitle = "$crstype $ucgpterm".'s'; + $r->print(&header("$crumbtitle Manager", + $jscript,$action,$state,$page,$loaditems)); if ($env{'form.refpage'} eq 'enrl') { &Apache::lonhtmlcommon::add_breadcrumb @@ -417,23 +824,39 @@ function backPage(formname,prevstate) { } else { &Apache::lonhtmlcommon::add_breadcrumb ({href=>"/adm/coursegroups", - text=>"Course Groups", + text=>"$crumbtitle", faq=>9,bug=>'Instructor Interface',}); } - my %states = (); - @{$states{'create'}} = ('pick_name','pick_members','pick_privs','result'); - @{$states{'modify'}} = (); - my %trail = (); - %{$trail{'create'}} = ( - pick_name => 'Group Settings', + %{$trail{'create'}} = &Apache::lonlocal::texthash ( + pick_name => $ucgpterm.' Settings', pick_members => 'Select Members', pick_privs => 'Choose Privileges', result => 'Creation Complete', ); - %{$trail{'modify'}} = (); - + %{$trail{'modify'}} = &Apache::lonlocal::texthash( + pick_group => $ucgpterm.'s', + 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', + ); + my %navbuttons = &Apache::lonlocal::texthash( + gtns => 'Go to next step', + gtps => 'Go to previous step', + crgr => 'Create '.$gpterm, + mose => 'Modify settings', + gtpp => 'Go to previous page', + adme => 'Add members', + ); if ((($action eq 'create') || ($action eq 'modify')) && ($manage_permission)) { for (my $i=0; $i<@{$states{$action}}; $i++) { @@ -441,14 +864,18 @@ function backPage(formname,prevstate) { &Apache::lonhtmlcommon::add_breadcrumb( {text=>"$trail{$action}{$state}"}); $r->print(&Apache::lonhtmlcommon::breadcrumbs - (undef,'Course Groups Manager')); - &display_control($r,$cdom,$cnum,$tabcol,$action,$state, - \%sectioncount,$groupname,$description,$functions, - \@tools,\%toolprivs,\%fixedprivs,$startdate,$enddate, - \%users,\%userdata,$idx,\@members,\%usertools); + ("$crumbtitle Manager")); + &display_control($r,$cdom,$cnum,$tabcol,$action,$state,$page, + \%sectioncount,$groupname,$description,$functions, + \@tools,$toolprivs,$fixedprivs,$startdate,$enddate, + \%users,\%userdata,$idx,\%memchg,\%usertools, + $function,$view_permission,$manage_permission, + \%stored,$granularity,$quota,$specificity,\@types,\@roles, + \@sections,\%states,\%navbuttons,$rowColor1,$rowColor2, + $gpterm,$ucgpterm,$crstype); last; } else { - if ($state eq 'result' && $i > 0) { + if (($state eq 'result') && ($i > 0)) { &Apache::lonhtmlcommon::add_breadcrumb( {href=>"javascript:backPage(document.$state,'$states{$action}[0]')", text=>"$trail{$action}{$states{$action}[$i]}"}); @@ -461,55 +888,184 @@ function backPage(formname,prevstate) { } } elsif (($action eq 'view') && ($view_permission)) { &Apache::lonhtmlcommon::add_breadcrumb( - {text=>"View groups"}); + {text=>"View $gpterm".'s'}); + my $crumbtitle = "$crstype $ucgpterm".'s Manager'; $r->print(&Apache::lonhtmlcommon::breadcrumbs - (undef,'Course Groups Manager')); - &display_groups($r,$cdom,$cnum,$function,$tabcol,$functions,$idx, - $view_permission,$manage_permission); + (&mt($crumbtitle))); + &display_groups($r,$cdom,$cnum,$functions,$idx,$view_permission, + $manage_permission,$action,$state,$gpterm,$ucgpterm, + $crstype); } $r->print(&footer()); return; } +sub retrieve_settings { + my ($cdom,$cnum,$groupname) = @_; + my %curr_groups = &Apache::longroup::coursegroups($cdom,$cnum,$groupname); + + 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 display_control { - my ($r,$cdom,$cnum,$tabcol,$action,$state,$sectioncount,$groupname, + my ($r,$cdom,$cnum,$tabcol,$action,$state,$page,$sectioncount,$groupname, $description,$functions,$tools,$toolprivs,$fixedprivs,$startdate, - $enddate,$users,$userdata,$idx,$members,$usertools) = @_; + $enddate,$users,$userdata,$idx,$memchg,$usertools,$function, + $view_permission,$manage_permission,$stored,$granularity,$quota, + $specificity,$types,$roles,$sections,$states,$navbuttons,$rowColor1, + $rowColor2,$gpterm,$ucgpterm,$crstype) = @_; if ($action eq 'create') { if ($state eq 'pick_name') { - &first_creation_form($r,$cdom,$cnum,$tabcol,$state,$functions, - $sectioncount); + &general_settings_form($r,$cdom,$cnum,$action,$tabcol,$state,$page, + $functions,$tools,$toolprivs,$fixedprivs, + $sectioncount,$stored,$states,$navbuttons, + $rowColor1,$rowColor2,$gpterm,$ucgpterm, + $crstype); } elsif ($state eq 'pick_members') { - &second_creation_form($r,$cdom,$cnum,$tabcol,$state,$groupname, - $description,$startdate,$enddate,$tools, - $functions,$users,$userdata,$idx); + &choose_members_form($r,$cdom,$cnum,$tabcol,$action,$state,$page, + $groupname,$description,$granularity,$quota, + $startdate,$enddate,$tools,$fixedprivs, + $toolprivs,$functions,$users,$userdata,$idx, + $stored,$states,$navbuttons,$rowColor1, + $rowColor2,$gpterm,$ucgpterm,$crstype); } elsif ($state eq 'pick_privs') { - &third_creation_form($r,$cdom,$cnum,$tabcol,$state,$startdate, - $enddate,$tools,$functions,$toolprivs,$fixedprivs, - $userdata,$members,$usertools,$idx); + &choose_privs_form($r,$cdom,$cnum,$tabcol,$action,$state,$page, + $startdate,$enddate,$tools,$functions, + $toolprivs,$fixedprivs,$userdata,$usertools, + $idx,$states,$stored,$sectioncount,$navbuttons, + $rowColor1,$rowColor2,$gpterm,$ucgpterm, + $crstype); } elsif ($state eq 'result') { - &completed_creation($r,$cdom,$cnum,$tabcol,$state,$groupname, - $description,$userdata,$startdate,$enddate,$tools, - $functions,$toolprivs,$members,$usertools,$idx); + &process_request($r,$cdom,$cnum,$tabcol,$action,$state,$page, + $groupname,$description,$specificity,$userdata, + $startdate,$enddate,$tools,$functions, + $toolprivs,$usertools,$idx,$types,$roles, + $sections,$states,$navbuttons,$memchg, + $sectioncount,$stored,$rowColor1,$rowColor2, + $gpterm,$ucgpterm,$crstype); + } + } elsif ($action eq 'modify') { + my $groupname = $env{'form.groupname'}; + if ($state eq 'pick_group') { + &display_groups($r,$cdom,$cnum,$functions,$idx,$view_permission, + $manage_permission,$action,$state,$gpterm, + $ucgpterm,$crstype); + } elsif ($state eq 'pick_task') { + &modify_menu($r,$groupname,$page,$gpterm); + } elsif ($state eq 'change_settings') { + &general_settings_form($r,$cdom,$cnum,$action,$tabcol,$state,$page, + $functions,$tools,$toolprivs,$fixedprivs, + $sectioncount,$stored,$states,$navbuttons, + $rowColor1,$rowColor2,$gpterm,$ucgpterm, + $crstype); + } elsif ($state eq 'change_members') { + &change_members_form($r,$cdom,$cnum,$tabcol,$action,$state,$page, + $groupname,$description,$startdate,$enddate, + $tools,$fixedprivs,$functions,$users, + $userdata,$granularity,$quota,$specificity, + $idx,$states,$navbuttons,$rowColor1,$rowColor2, + $gpterm,$ucgpterm); + } elsif ($state eq 'add_members') { + &add_members_form($r,$tabcol,$action,$state,$page,$startdate, + $enddate,$groupname,$description,$granularity, + $quota,$sectioncount,$tools,$functions,$stored, + $states,$navbuttons,$rowColor1,$rowColor2,$gpterm, + $ucgpterm); + } elsif ($state eq 'pick_members') { + &choose_members_form($r,$cdom,$cnum,$tabcol,$action,$state,$page, + $groupname,$description,$granularity,$quota, + $startdate,$enddate,$tools,$fixedprivs, + $toolprivs,$functions,$users,$userdata,$idx, + $stored,$states,$navbuttons,$rowColor1, + $rowColor2,$gpterm,$ucgpterm,$crstype); + } elsif ($state eq 'pick_privs') { + &choose_privs_form($r,$cdom,$cnum,$tabcol,$action,$state,$page, + $startdate,$enddate,$tools,$functions, + $toolprivs,$fixedprivs,$userdata,$usertools, + $idx,$states,$stored,$sectioncount,$navbuttons, + $rowColor1,$rowColor2,$gpterm,$ucgpterm,$crstype); + } elsif ($state eq 'change_privs') { + &change_privs_form($r,$cdom,$cnum,$tabcol,$action,$state,$page, + $startdate,$enddate,$tools,$functions, + $toolprivs,$fixedprivs,$userdata,$usertools, + $memchg,$idx,$states,$stored,$sectioncount, + $navbuttons,$rowColor1,$rowColor2,$gpterm, + $ucgpterm); + } elsif ($state eq 'chgresult' || $state eq 'memresult' || + $state eq 'addresult') { + &process_request($r,$cdom,$cnum,$tabcol,$action,$state,$page, + $groupname,$description,$specificity,$userdata, + $startdate,$enddate,$tools,$functions, + $toolprivs,$usertools,$idx,$types,$roles, + $sections,$states,$navbuttons,$memchg, + $sectioncount,$stored,$rowColor1,$rowColor2, + $gpterm,$ucgpterm,$crstype); } } } sub header { - my ($bodytitle,$title,$jscript,$action,$state,$function,$loaditems) = @_; - my $html=&Apache::lonxml::xmlbegin(); - my $bodytag=&Apache::loncommon::bodytag($bodytitle,$function,$loaditems); + my ($bodytitle,$jscript,$action,$state,$page,$loaditems) = @_; + my $start_page= + &Apache::loncommon::start_page($bodytitle, + '', + {'add_entries' => $loaditems,}); my $output = <<"END"; -$html - -$title - - -$bodytag -
+$start_page + END if ($action eq 'create' || $action eq 'modify') { @@ -517,6 +1073,7 @@ END + END } return $output; @@ -524,23 +1081,31 @@ END sub onload_action { my ($action,$state) = @_; - my $loaditems; + 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.')"'; + $loaditems{'onload'} = + 'javascript:setFormElements(document.'.$state.')'; } } - return $loaditems; + if (($action eq 'modify') && + ($state eq 'change_settings' || $state eq 'change_members' || + $state eq 'change_privs' || $state eq 'add_members' || + $state eq 'pick_members')) { + $loaditems{'onload'} = + 'javascript:setFormElements(document.'.$state.')'; + } + return \%loaditems; } sub footer { + my $end_page = &Apache::loncommon::end_page(); return(< - - +$end_page ENDFOOT } @@ -554,98 +1119,134 @@ sub build_members_list { $access{$type} = $type; } &Apache::loncommon::get_course_users($cdom,$cnum,\%access,$roles, - $sections,$users,$userdata); + $sections,$users,$userdata); return; } sub group_files { + my ($group,$currdir,$numfiles,$numdirs) = @_; + my $dirptr=16384; + my @dir_list=&Apache::portfolio::get_dir_list($currdir,$group); + foreach my $line (@dir_list) { + 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) { + $currdir .= '/'.$filename; + $$numdirs ++; + &group_files($numfiles,$numdirs) + } else { + $$numfiles ++; + } + } + } return; } sub group_members { - return; + my ($cdom,$cnum,$group,$group_info) = @_; + my %memberhash = &Apache::lonnet::get_group_membership($cdom,$cnum,$group); + my $now = time; + my ($tmp)=keys(%memberhash); + if ($tmp=~/^error:/) { + $$group_info{'totalmembers'} = 'Unknown - an error occurred'; + return $tmp; + } + my $totalmembers = 0; + my $active = 0; + my $previous = 0; + my $future = 0; + foreach my $member (keys %memberhash) { + $totalmembers ++; + my ($end,$start) = split(/:/,$memberhash{$member}); + unless ($start == -1) { + if (($end!=0) && ($end<$now)) { + $previous ++; + } elsif (($start!=0) && ($start>$now)) { + $future ++; + } else { + $active ++; + } + } + } + if ($totalmembers == 0) { + $$group_info{$group}{'totalmembers'} = 'None'; + } else { + $$group_info{$group}{'totalmembers'} = $active.' - active
'.$previous.' -previous
'.$future.' -future'; + } + return 'ok'; } -sub first_creation_form { - my ($r,$cdom,$cnum,$tabcol,$formname,$functions,$sectioncount) = @_; +sub general_settings_form { + my ($r,$cdom,$cnum,$action,$tabcol,$formname,$page,$functions,$tools, + $toolprivs,$fixedprivs,$sectioncount,$stored,$states,$navbuttons, + $rowColor1,$rowColor2,$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,$action,$formname,$sectioncount,3,$gpterm, + $ucgpterm); + $nexttext = $$navbuttons{'gtns'}; + } else { + my @available = (); + my @unavailable = (); + &check_tools($functions,$tools,\@available,\@unavailable); + @{$tools} = sort(keys(%{$functions})); + &privilege_specificity($r,$tabcol,$rowColor1,$rowColor2,$action, + 3,$tools,$stored,$toolprivs,$fixedprivs, + \@available,$formname,$gpterm,$ucgpterm); + &mapping_options($r,$action,$formname,$page,$sectioncount, + $states,$stored,$navbuttons,4,5, + $gpterm,$ucgpterm,$crstype); + $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', - 'acty' => 'Access types', - 'coro' => 'Course roles', - 'cose' => 'Course sections', - 'gfun' => 'Group functionality', + 'gdat' => "$ucgpterm open and close dates", + 'sten' => "Set a start date/time and end date/time for the $gpterm", + 'gfun' => "$ucgpterm functionality", + 'gnde' => "$ucgpterm name, description and available functionality", + 'desc' => 'Description', + 'func' => 'Functionality', + 'gnam' => "$ucgpterm Name", + 'doyo' => "Do you want to assign different functionality ". + "to different $gpterm members?", ); - - 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 $starttime = time; - my $endtime = time+(6*30*24*60*60); # 6 months from now, approx - my ($start_table,$end_table) = &date_setting_table - ($starttime,$endtime,$formname); - - my @sections = (); - my $section_sel = ''; - my $numvisible = 4; - - @sections = sort {$a cmp $b} keys(%{$sectioncount}); - if (@sections > 0) { - unshift(@sections,'_all'); # Put 'all' at the front of the list - if (@sections < 4) { - $numvisible = @sections; - } - foreach (@sections) { - if ($_ eq '_all') { - $section_sel .= ' '."\n"; + } elsif ($sec eq 'none') { + $section_sel .= ' '."\n"; + } else { + $section_sel .= ' \n"; } } - if ($earlyout) { - $r->print(' - - - - - - - - - - - -
 '.$earlyout.'
 
  - -
- '); - return; + my $output = ' + '; + return $output; +} + +sub access_date_settings { + my ($r,$action,$formname,$stored,$image,$gpterm,$ucgpterm) = @_; + my %lt = &Apache::lonlocal::texthash( + 'sten' => "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 $rowColor1 = "#dddddd"; - my $rowColor2 = "#eeeeee"; - my $showstart = &Apache::lonlocal::locallocaltime($startdate); - my $showend = &Apache::lonlocal::locallocaltime($enddate); - $r->print(' - - - - -
New group selections -
When you create the new group, the following settings will apply: -
'); - $r->print(&Apache::lonhtmlcommon::start_pick_box()); + my ($table) = &date_setting_table($starttime,$endtime,$formname); + &topic_bar($r,$image,$lt{'sten'}); $r->print(' - - - - - - - - - - - + + + + + +'); + $r->print(&Apache::loncommon::end_data_table_header_row(). + &Apache::loncommon::start_data_table_row('LC_data_table_dense')); + $r->print(' + + + - + + - -
Group NameDescriptionGroup FunctionalityDefault access dates
'.$groupname.''.$description.' -'); + '.$table.' + '); + return; +} + +sub choose_members_form { + my ($r,$cdom,$cnum,$tabcol,$action,$formname,$page,$groupname,$description, + $granularity,$quota,$startdate,$enddate,$tools,$fixedprivs,$toolprivs, + $functions,$users,$userdata,$idx,$stored,$states,$navbuttons, + $rowColor1,$rowColor2,$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,$tabcol,$rowColor1,$rowColor2,$action, + $specimg,$tools,$stored,$toolprivs, + $fixedprivs,\@available,$formname,$gpterm,$ucgpterm); + 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('
'); + if ($prev) { + $r->print(' + +    '); + } + if ($next) { + $r->print(' + '); + } + $r->print('
'); +} + +sub check_tools { + my ($functions,$tools,$available,$unavailable) = @_; foreach my $item (sort(keys(%{$functions}))) { if (grep/^$item$/,@{$tools}) { - push(@available,$item); + push(@{$available},$item); } else { - push(@unavailable,$item); + push(@{$unavailable},$item); } } - if (@available > 0) { - $r->print('Available: + 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 => "$ucgpterm Name", + desc => 'Description', + grfn => "$ucgpterm Functions", + gran => 'Granularity', + quot => 'File quota', + dfac => 'Default access dates', + ygrs => "Your $gpterm selections", + tfwa => "The following settings will apply to the $gpterm:", + difn => 'Different functionality
for different members:', + stda => 'Start date', + enda => 'End date:', + ); + 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(' +
'.$lt{'ygrs'}.' +
'.$lt{'tfwa'}.' +
'); + } + $r->print(&Apache::loncommon::start_data_table('LC_course_group_status'). + &Apache::loncommon::start_data_table_header_row()); + $r->print(' +
'.$lt{'grna'}.''.$lt{'desc'}.''.$lt{'grfn'}.''.$lt{'gran'}.''.$lt{'quot'}.''.$lt{'dfac'}.''.$groupname.''.$description.' +'); + if (@{$available} > 0) { + $r->print('Available: '); - my $rowcell = int(@available/2) + @available%2; - for (my $i=0; $i<@available; $i++) { - if (@available > 3) { + my $rowcell = int(@{$available}/2) + @{$available}%2; + for (my $i=0; $i<@{$available}; $i++) { + if (@{$available} > 3) { if ($i==$rowcell) { $r->print(''); } } - $r->print(''); + $r->print(''); } - if ((@available > 3) && (@available%2)) { + if ((@{$available} > 3) && (@{$available}%2)) { $r->print(''); } $r->print('
'.$$functions{$available[$i]}. - ' '.$$functions{$$available[$i]}. + '   

'); } - if (@unavailable > 0) { - $r->print('Unavailable: + if (@{$unavailable} > 0) { + $r->print('Unavailable: '); - my $rowcell = int(@unavailable/2) + @unavailable%2; - for (my $j=0; $j<@unavailable; $j++) { - if (@unavailable > 3) { + my $rowcell = int(@{$unavailable}/2) + @{$unavailable}%2; + for (my $j=0; $j<@{$unavailable}; $j++) { + if (@{$unavailable} > 3) { if ($j==$rowcell) { $r->print(''); } } - $r->print(''); + $r->print(''); } - if ((@unavailable > 3) && (@unavailable%2)) { + if ((@{$unavailable} > 3) && (@{$unavailable}%2)) { $r->print(''); } $r->print('
'.$$functions{$unavailable[$j]}. - ' '.$$functions{$$unavailable[$j]}. + '   
'); } $r->print(<<"END");
Start date: $showstart
- End date: $showend
+
$lt{'difn'} $granularity$quota Mb$lt{'stda'} $showstart
+ $lt{'enda'} $showend
END - $r->print(&Apache::lonhtmlcommon::end_pick_box()); - my %members = (); + $r->print(&Apache::loncommon::end_data_table_row(). + &Apache::loncommon::end_data_table()); + return; +} + +sub pick_new_members { + my ($r,$action,$formname,$available,$idx,$stored,$img,$users,$userdata, + $granularity,$origmembers,$gpterm,$ucgpterm) = @_; + my %lt = &Apache::lonlocal::texthash( + 'gpme' => "$ucgpterm membership", + 'addm' => 'Add members', + 'setf' => 'Set functionality', + 'func' => 'Functionality', + 'nome' => 'No members to add at this time.', + 'nnew' => "There are no users to add as new members, as all users". + " matching the specified type(s), role(s), and/or ". + "section(s) are already affiliated with this $gpterm.", + '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 ++; } } } - $r->print('

'); if (keys(%members) > 0) { - if ($env{'form.granularity'} eq 'Yes') { - $r->print(' - - '); - } - $r->print(<<"END"); + +sub default_privileges { + my ($r,$action,$tools,$toolprivs,$fixedprivs,$available) = @_; + my %lt = &Apache::lonlocal::texthash( + 'addp' => 'Additional privileges', + 'fixp' => 'Fixed privileges', + 'oppr' => 'Optional privileges', + 'func' => 'Function', + ); + $r->print(&Apache::lonhtmlcommon::start_pick_box('LC_group_priv_box'). + &Apache::lonhtmlcommon::row_title($lt{'func'},undef, + 'LC_groups_functionality')); + $r->print(join('',@{$tools})); + $r->print(&Apache::lonhtmlcommon::row_closure(1)); + my $fixed = ''; + my $dynamic = ''; + foreach my $tool (@{$tools}) { + my $privcount = 0; + if ($dynamic ne '') { + $dynamic .= ''; + } + $dynamic .= ''; + foreach my $priv (sort(keys(%{$$toolprivs{$tool}}))) { + if (exists($$fixedprivs{$tool}{$priv})) { + if ($fixed ne '') { + $fixed .= ' + '."\n"; + } + $dynamic .= ''."\n"; + } + } + if ($privcount == 0) { + $dynamic .= ''."\n"; + } + if ($privcount < 3) { + $dynamic .= ' + '."\n"; + } elsif ($privcount%2) { + $dynamic = ''."\n"; + } + $dynamic .= '
'; + } + $fixed .= ''.$$toolprivs{$tool}{$priv}.' '; + if ($action eq 'modify') { + if (grep(/^$tool$/,@{$available})) { + $fixed .= ''.&mt('(on)').' '; + } else { + $fixed .= ''.&mt('(off)').' '; + } + } + } else { + $privcount++; + if ($privcount == 3) { + $dynamic .= '
None  
'; + } + $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('
'); + return; + +} + +sub display_defprivs { + my ($r,$tabcol,$rowColor1,$rowColor2,$tools,$toolprivs,$defprivs) = @_; + my %lt = &Apache::lonlocal::texthash( + 'priv' => 'Privileges', + 'func' => 'Function', + ); + $r->print(&Apache::lonhtmlcommon::start_pick_box()); + $r->print(''); + 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(' + + + + + '); + my $rownum = 1; + my $privcount = 0; + $r->print(''); + foreach my $priv (@{$currprivs{$tool}}) { + $privcount ++; + if ($privcount%4 == 0) { + $rownum ++; + my $bgcol = $rownum%2; + $r->print(' + '."\n"); + } + $r->print(''."\n"); + } + if ($privcount%3 > 0) { + my $emptycells = 3-($privcount%3); + while($emptycells > 0) { + $r->print(''."\n"); + $emptycells --; + } + } + while ($rownum < $numrows) { + $rownum ++; + my $bgcol = $rownum%2; + $r->print(''); + } + $r->print('
'.$tool.'
'.$$toolprivs{$tool}{$priv}.' 
 
'."\n".''); + } + $r->print(''."\n"); + $r->print(&Apache::lonhtmlcommon::end_pick_box()); + $r->print('
'); + return; +} + + +sub change_members_form { + my ($r,$cdom,$cnum,$tabcol,$action,$formname,$page,$groupname,$description, + $startdate,$enddate,$tools,$fixedprivs,$functions,$users,$userdata, + $granularity,$quota,$specificity,$idx,$states,$navbuttons,$rowColor1, + $rowColor2,$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(' +
- +'); + &topic_bar($r,1,$lt{'grse'}); + $r->print(' + - - - - + '); + &topic_bar($r,2,$lt{'mogm'}); + $r->print(' - - + + '); + &display_navbuttons($r,$formname,$$states{$action}[$page-1],$prevtext, + $$states{$action}[$page+1],$nexttext); + $r->print('
  -   - - - - Select group members - -  
+'); + &print_current_settings($r,$action,$functions,$startdate,$enddate, + $groupname,$description,$granularity,$quota, + \@available,\@unavailable,$gpterm,$ucgpterm); +$r->print(' +
 
 
  +'); + ¤t_membership($r,$cdom,$cnum,$formname,$tabcol,$rowColor1, + $rowColor2,$groupname,\@available,\@unavailable, + $fixedprivs,$granularity,$specificity); + $r->print('
'); + return; +} + +sub current_membership { + my ($r,$cdom,$cnum,$formname,$tabcol,$rowColor1,$rowColor2,$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', + 'doma' => 'Domain', + 'stda' => 'Start Date', + 'enda' => 'End Date', + 'expi' => 'Expire', + 'reen' => 'Re-enable', + 'acti' => 'Activate', + 'dele' => 'Delete', + 'curf' => 'Current Functionality', + 'chpr' => 'Change Privileges' + ); + my ($current,$hastools,$addtools,$num_reenable,$num_activate,$num_expire) = + &Apache::longroup::group_memberlist($cdom,$cnum,$groupname,$fixedprivs, + $available); + if (keys(%{$current}) > 0) { + $r->print('   - - - -END - if (@available > 0 && $env{'form.granularity'} eq 'Yes') { - $r->print(''); + if ($num_expire) { + &check_uncheck_buttons($r,$formname,'expire',$lt{'expi'}); + } + if ($num_reenable) { + &check_uncheck_buttons($r,$formname,'reenable',$lt{'reen'}); + } + if ($num_activate) { + &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(' + '); + + + +'); + } } - $r->print('
- -
Add members - -    - -
- Set functionality - +
+ +
'.$lt{'curf'}.' +    - -
+ $r->print(<<"END"); + +   @@ -993,34 +2152,56 @@ END   - '); +END $r->print(&Apache::lonhtmlcommon::start_pick_box()); $r->print(<<"END"); - - - + + - + + + END - if (@available > 0) { - $r->print(''); + my $colspan = 0; + if ($hastools) { + $r->print(''); + $colspan ++; + } + if ($addtools) { + $r->print(''); + $colspan ++; } $r->print(''); + if ($colspan) { + if ($granularity eq 'Yes') { + $r->print(' + + '); + } + } my %Sortby = (); - foreach my $user (sort(keys(%members))) { + foreach my $user (sort(keys(%{$current}))) { if ($env{'form.sortby'} eq 'fullname') { - push(@{$Sortby{$members{$user}[$$idx{fullname}]}},$user); + push(@{$Sortby{$$current{$user}{fullname}}},$user); } elsif ($env{'form.sortby'} eq 'username') { - push(@{$Sortby{$members{$user}[$$idx{uname}]}},$user); + push(@{$Sortby{$$current{$user}{uname}}},$user); } elsif ($env{'form.sortby'} eq 'domain') { - push(@{$Sortby{$members{$user}[$$idx{udom}]}},$user); + push(@{$Sortby{$$current{$user}{udom}}},$user); } elsif ($env{'form.sortby'} eq 'id') { - push(@{$Sortby{$members{$user}[$$idx{id}]}},$user); + push(@{$Sortby{$$current{$user}{id}}},$user); } else { - push(@{$Sortby{$members{$user}[$$idx{fullname}]}},$user); + push(@{$Sortby{$$current{$user}{fullname}}},$user); } } my $rowNum = 0; @@ -1032,23 +2213,75 @@ END } else { $rowColor = $rowColor2; } - my $id = $members{$user}[$$idx{id}]; - my $fullname = $members{$user}[$$idx{fullname}]; - my $udom = $members{$user}[$$idx{udom}]; - my $uname = $members{$user}[$$idx{uname}]; - $r->print(' + + '); - if (@available > 0) { - $r->print(''); + if ($hastools) { + $r->print(''); + } + if ($addtools) { + $r->print(''); @@ -1060,259 +2293,607 @@ END $r->print(&Apache::lonhtmlcommon::end_pick_box()); $r->print(' - - - '); - if (@available > 0) { - $r->print(' - - - - - - - - - - - - - + } + return; +} + +sub check_uncheck_buttons { + my ($r,$formname,$field,$title,$colspan) = @_; + $r->print(' + '); - } - $r->print(' - - - - - '); +} + + +sub change_privs_form { + my ($r,$cdom,$cnum,$tabcol,$action,$formname,$page,$startdate,$enddate, + $tools,$functions,$toolprivs,$fixedprivs,$userdata,$usertools, + $memchg,$idx,$states,$stored,$sectioncount,$navbuttons,$rowColor1, + $rowColor2,$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 { - $r->print('No members to add'); + $nexttext = $$navbuttons{'mose'}; } - $r->print(' -
Add?NameUsername + $lt{'actn'}$lt{'name'}$lt{'usnm'} Domain$lt{'doma'} ID$lt{'stda'}$lt{'enda'}Functionality'.$lt{'curf'}.'Additional Functionality
 '.&mt('All:'). + ' '); + foreach my $tool (@{$available}) { + $r->print('   '); + } + $r->print('
- '. + 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('
'); + if ($$current{$user}{changestate} eq 'reenable') { + $r->print('
'); + } elsif ($$current{$user}{changestate} eq 'expire') { + $r->print('
'); + } elsif ($$current{$user}{changestate} eq 'activate') { + $r->print('
'); + } + $r->print(''); + if ($specificity eq 'Yes') { + $r->print('
'); + } + $r->print(' +
'. $fullname.''.$uname.''. - $udom.''.$id.''); - foreach my $tool (@available) { - if ($env{'form.granularity'} eq 'Yes') { - $r->print(''.$tool.'   '); - } else { - $r->print(''.$tool.'   '); + $udom.''.$id.''.$start. + ''.$end.''. + '      '); + foreach my $tool (@{$$current{$user}{currtools}}) { + if ($granularity eq 'Yes') { + $r->print(''); + } else { + $r->print(''.$tool); + } + $r->print('   '); + } + $r->print(''); + if ($granularity eq 'Yes') { + foreach my $tool (@{$$current{$user}{newtools}}) { + $r->print('   '); + } + } else { + foreach my $tool (@{$$current{$user}{newtools}}) { + $r->print(''.$tool. + '   '); } } $r->print('
 
  -   - - - User privileges - -  
  -
- For each type of functionality you have chosen to include, there is a -set of standard privileges which apply to all of those for whom the functionality is enabled.
There are also additional privileges which can be set for some, or all, members. Please choose one of the following:
-
 All group members will receive the same privileges.
 Some group members will receive different privileges from others. -
 
+ +
+ '.$title.' + +    + +
+
+
  - -     - -
- - -'); + $r->print('
'); + &topic_bar($r,3,&mt('Members to delete or expire')); + my $exp_or_del = 0; + if (ref($$memchg{'deletion'}) eq 'ARRAY') { + if (@{$$memchg{'deletion'}} > 0) { + $r->print(''); + $exp_or_del += @{$$memchg{'deletion'}}; + } + } + if (ref($$memchg{'expire'}) eq 'ARRAY') { + if (@{$$memchg{'expire'}} > 0) { + $r->print(''); + $exp_or_del += @{$$memchg{'expire'}}; + } + } + if (!$exp_or_del) { + $r->print(''); + } + + &topic_bar($r,4,&mt('[_1] member privileges',$ucgpterm)); + + my $numchgs = &member_privileges_form($r,$tabcol,$action,$formname,$tools, + $toolprivs,$fixedprivs,$userdata, + $usertools,$idx,$memchg,$states, + $stored,$rowColor1,$rowColor2, + $gpterm); + $r->print(''); + 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); + } + $r->print('
 '.$lt{'tode'}.':
    '); + foreach my $user (@{$$memchg{'deletion'}}) { + $r->print('
  • '.$$userdata{$user}[$$idx{fullname}]. + ' ('.$user.')
  • '); + } + $r->print('
 
 '.$lt{'toex'}.':
    '); + foreach my $user (@{$$memchg{'expire'}}) { + $r->print('
  • '.$$userdata{$user}[$$idx{fullname}]. + ' ('.$user.')
  • '); + } + $r->print('
 
 '.$lt{'nome'}. + '
 
 
'); return; } -sub third_creation_form { - my ($r,$cdom,$cnum,$tabcol,$formname,$startdate,$enddate,$tools,$functions, - $toolprivs,$fixedprivs,$userdata,$members,$usertools,$idx) = @_; - my @regexps = ('userpriv_','allpriv_'); - $r->print(&Apache::lonhtmlcommon::echo_form_input( - ['origin','action','state'],\@regexps)); - my %possibles = (); - my %showboxes = (); +sub add_members_form { + my ($r,$tabcol,$action,$formname,$page,$startdate,$enddate,$groupname, + $description,$granularity,$quota,$sectioncount,$tools,$functions, + $stored,$states,$navbuttons,$rowColor1,$rowColor2,$gpterm,$ucgpterm)=@_; + $r->print('
'); + 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,$action,$formname,$sectioncount,1,$gpterm,$ucgpterm); + 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,$tabcol,$action,$formname,$page,$startdate,$enddate, + $tools,$functions,$toolprivs,$fixedprivs,$userdata,$usertools,$idx, + $states,$stored,$sectioncount,$navbuttons,$rowColor1,$rowColor2, + $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('
'); + &topic_bar($r,6,&mt('[_1] member privileges',$ucgpterm)); + + &member_privileges_form($r,$tabcol,$action,$formname,$tools,$toolprivs, + $fixedprivs,$userdata,$usertools,$idx,undef, + $states,$stored,$rowColor1,$rowColor2,$gpterm); + + $r->print(''); + if ($action eq 'create') { + if (keys(%{$sectioncount}) > 0) { + my $img1 = 7; + my $img2 = 8; + &mapping_options($r,$action,$formname,$page,$sectioncount, + $states,$stored,$navbuttons,$img1,$img2, + $gpterm,$ucgpterm,$crstype); + } + } + my $prevtext = $$navbuttons{'gtps'}; + &display_navbuttons($r,$formname,$$states{$action}[$page-1],$prevtext, + $$states{$action}[$page+1],$nexttext); + $r->print('
 
'); + return; +} + +sub build_boxes { + my ($r,$tools,$usertools,$fixedprivs,$toolprivs,$showtools, + $showboxes,$prefix,$specificity,$excluded) = @_; my $totalboxes = 0; - my $rowColor1 = "#dddddd"; - my $rowColor2 = "#eeeeee"; - my $numtools = 1 + @{$tools}; - foreach my $tool (@{$tools}) { - @{$showboxes{$tool}} = (); - foreach my $user (@{$members}) { - if (exists($$usertools{$user}{$tool})) { - foreach my $priv (sort(keys(%{$$toolprivs{$tool}}))) { - unless (exists($$fixedprivs{$tool}{$priv})) { - push(@{$possibles{$user}},$priv); - unless(grep(/^$priv$/,@{$showboxes{$tool}})) { - push(@{$showboxes{$tool}},$priv); - $totalboxes ++; + 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(' + if ($totalboxes > 0) { + $r->print(' + '); } } - $r->print(' + } + return $totalboxes; } - - '); - } - $r->print(<<"END"); -
- - - - - - - - - - - - - '); + $r->print(''); } + $r->print(' + + + + + +END + &member_privs_entries($r,$tabcol,$rowColor1,$rowColor2, + $usertools,$toolprivs,$fixedprivs, + $userdata,$idx,\@showtools,\@defprivs, + \@excluded); + $r->print(''); + $r->print(&Apache::lonhtmlcommon::end_pick_box()); + } else { + $r->print($lt{'forf'}.'
'); + &display_defprivs($r,$tabcol,$rowColor1,$rowColor2,$tools, + $toolprivs,\@defprivs); } - if ($dynamic eq '') { - $dynamic = ''; - } - if ($privcount < 3) { - $dynamic .= ''; - } elsif ($privcount%2) { - $dynamic = ''; + } else { + if (keys(%{$usertools}) > 0) { + $r->print($lt{'algr'}.'

'); + &display_defprivs($r,$tabcol,$rowColor1,$rowColor2,$tools, + $toolprivs,\@defprivs); + } else { + $r->print($lt{'asno'}.'
'); } - $r->print(''.$dynamic.'
  -   - - Group member privileges - -  
 
  -END - if ($env{'form.specificity'} eq 'Yes') { - $r->print(' + +sub member_privileges_form { + my ($r,$tabcol,$action,$formname,$tools,$toolprivs,$fixedprivs,$userdata, + $usertools,$idx,$memchg,$states,$stored,$rowColor1,$rowColor2, + $gpterm) = @_; + my %lt = &Apache::lonlocal::texthash( + 'addp' => 'Additional privileges', + 'fixp' => 'Fixed privileges', + 'oppr' => 'Optional privileges', + 'func' => 'Function', + 'forf' => 'For the functionality you have chosen to include '. + 'there are no optional privileges to set besides '. + 'the standard privileges.', + 'algr' => "All $gpterm members will receive the same privileges.", + 'asno' => "As no $gpterm members are being added, ". + "there are no specific user privileges to set.", + 'asng' => "As no $gpterm tools will be made available to users, ". + "there are no specific user privileges to set.", + 'nogm' => "No $gpterm member privileges to display or set, ". + "as you have not indicated that you will be activating,". + " re-enabling, changing privileges, or adding/removing ". + "functionality 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(' - - - - - - -END - } - if ($env{'form.specificity'} eq 'Yes') { - &member_privs_entries($r,$tabcol,$rowColor1,$rowColor2,$members,$tools, - $usertools,$toolprivs,$fixedprivs,$userdata,$idx); - } - if ($env{'form.specificity'} eq 'Yes') { - $r->print(''); - $r->print(&Apache::lonhtmlcommon::end_pick_box()); - $r->print(' - '); - foreach my $tool (@{$tools}) { - $r->print(''); + } } - $dynamic .= ''; + $r->print('
'); - $r->print(&Apache::lonhtmlcommon::start_pick_box()); - $r->print(<<"END"); -
FullnameUsernameDomainAdditional Privileges
  '); - my @toolboxes = sort(keys(%showboxes)); - foreach my $tool (@{$tools}) { - if (@{$showboxes{$tool}} > 0) { - $r->print(''); - foreach my $priv (@{$showboxes{$tool}}) { - $r->print(qq| -
'.$tool.'
+ foreach my $tool (@{$tools}) { + if (@{$showboxes{$tool}} > 0) { + $r->print(''); + $r->print(''); + my $privcount = 0; + foreach my $priv (@{$showboxes{$tool}}) { + $privcount ++; + if (($privcount == @{$showboxes{$tool}}) && + ($privcount > 1)) { + if ($privcount%$numcells) { + $r->print('|); - } - $r->print('
'. + $tool.'
'); + } else { + $r->print(''); + } + } else { + $r->print(''); + } + $r->print(qq|
$$toolprivs{$tool}{$priv} + -   +   -


'); - } - } - $r->print('
'); - } else { - $r->print(&Apache::lonhtmlcommon::start_pick_box()); - $r->print('
Function
Fixed privileges'. - '
Optional privileges
'. - ''); - my $privcount = 0; - my $fixed = ''; - my $dynamic = ''; - foreach my $priv (sort(keys(%{$$toolprivs{$tool}}))) { - if (exists($$fixedprivs{$tool}{$priv})) { - $fixed .= ''.$$toolprivs{$tool}{$priv}.' '; - } else { - $privcount ++; - if ($privcount == 3) { - $dynamic .= ''; +
|); + $r->print(''); + if ($privcount < @{$showboxes{$tool}}) { + if (@{$showboxes{$tool}} > 2) { + if ($privcount%$numcells == 0) { + $r->print(''); + } + } else { + $r->print(''); + } + } + } + $r->print('
'.$tool.'
 '.$$toolprivs{$tool}{$priv}.'
 
 '); + $r->print(&Apache::lonhtmlcommon::start_pick_box()); + $r->print(<<"END"); +
$lt{'full'}$lt{'user'}$lt{'doma'}$lt{'addp'}
None
  
'.$fixed.'
'); } - $r->print(''); - $r->print(&Apache::lonhtmlcommon::end_pick_box()); + } else { + $r->print($lt{'asng'}); } - $r->print(' - -   - + return $numchgs; +} + +sub process_request { + my ($r,$cdom,$cnum,$tabcol,$action,$state,$page,$groupname,$description, + $specificity,$userdata,$startdate,$enddate,$tools,$functions,$toolprivs, + $usertools,$idx,$types,$roles,$sections,$states,$navbuttons,$memchg, + $sectioncount,$stored,$rowColor1,$rowColor2,$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(' + - - '); - $r->print(' -
  - -    - -
'); +'.$earlyout.''); + &display_navbuttons($r,$state,$$states{$action}[$page-1], + $$navbuttons{'gtps'}); + $r->print(''); + 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); + } return; } -sub completed_creation { - my ($r,$cdom,$cnum,$tabcol,$formname,$groupname,$description,$userdata, -$startdate,$enddate,$tools,$functions,$toolprivs,$members,$usertools,$idx) = @_; - - $r->print(&Apache::lonhtmlcommon::echo_form_input( - ['origin','action','state'])); - my @added= (); - my @failed = (); +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(&mt('The value you entered for the quota for the file repository in this [_1] contained invalid characters, so it has been set to 0 Mb. You can change this by modifying the [_1] settings.
',$gpterm)); + } + if ($quota > $maxposs) { + $quota = $maxposs; + $r->print(&mt('The value you entered for the quota for the file repository in this [_1] exceeded the maximum possible value, so it has been set to [_2] Mb (the maximum possible value).
',$gpterm,$maxposs)); + } + 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|$status|; + } + + $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}|; + } + foreach my $item (@mult_attributes) { + foreach my $entry (@{$groupinfo{$item}}) { + $groupsettings{$groupname} .= qq|<$item>$entry|; + } + } + 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 .= ''; + foreach my $sec (@autosections) { + $autosec .= '
'.$sec.'
'; + } + $autosec .= '
'; + } + } + if ($autosec) { + $groupsettings{$groupname} .= qq|$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 ne 'ok') { + $r->print(&mt('A problem occurred when creating folders for the new [_1]. [_2].
',$gpterm,$result)); + } + $r->print(&mt('[_1] [_2] was created.
',$ucgpterm,$groupname)); + } else { + $r->print(&mt('[_1] [_2] was updated.
',$ucgpterm,$groupname)); + } + } else { + my %actiontype = ( + 'create' => 'creating', + 'modify' => 'modifying', + ); + &Apache::lonnet::logthis("Failed to store $gpterm $groupname ". + 'in '.lc($crstype).': '.$cnum. + ' in domain: '.$cdom); + $r->print(&mt('An error occurred when [_1] the [_2]. '. + 'Please try again.',$actiontype{$action},$gpterm)); + } + return $result; +} + +sub process_membership { + my ($r,$cdom,$cnum,$action,$state,$groupname,$tools,$enddate,$startdate, + $userdata,$idx,$toolprivs,$usertools,$specificity,$defprivs,$memchg, + $gpterm,$ucgpterm)=@_; + 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 = (); + foreach my $tool (@{$tools}) { foreach my $priv (sort(keys(%{$$toolprivs{$tool}}))) { $tooltype{$priv} = $tool; - if ($env{'form.specificity'} eq 'Yes') { + 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 (defined($env{'form.allpriv_'.$priv})) { - foreach my $user (@{$members}) { - if ($$usertools{$user}{$tool}) { - $group_privs{$user} .= $priv.':'; + if (@{$defprivs} > 0) { + if (grep/^\Q$priv\E$/,@{$defprivs}) { + foreach my $user (sort(keys(%{$usertools}))) { + if ($$usertools{$user}{$tool}) { + $group_privs{$user} .= $priv.':'; + } } } } @@ -1322,139 +2903,390 @@ $startdate,$enddate,$tools,$functions,$t foreach my $user (keys(%group_privs)) { $group_privs{$user} =~ s/:$//; } - my $esc_description = &Apache::lonnet::escape($description); - my @attributes = ('description','functions','startdate','enddate','creation','modified','creator'); - - my %groupinfo = ( - description => $esc_description, - startdate => $startdate, - enddate => $enddate, - creation => $now, - modified => $now, - creator => $env{'user.name'}.':'.$env{'user.domain'}, - ); - foreach my $func (keys(%{$functions})) { - my $status; - if (grep(/^$func$/,@{$tools})) { - $status = 'on'; - } else { - $status = 'off'; - } - $groupinfo{'functions'} .= qq|$status|; - } - my %curr_groups = (); - my %groupsettings = (); - my %usersettings = (); - if (&Apache::lonnet::get_coursegroups($cdom,$cnum,\%curr_groups) > 0) { - if (exists($curr_groups{$groupname})) { - $r->print('Non-unique name -please choose another'); - return; + 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}) 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','') + eq 'ok') { + push(@{$added{'deleted'}},$user); + $num_ok ++; + } else { + push(@{$failed{'deleted'}},$user); + $num_fail ++; + } + } } } - my $groupentry; - foreach my $item (@attributes) { - $groupsettings{$groupname} .= qq|<$item>$groupinfo{$item}|; + + 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 = 'reenabled'; + } + } + 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}) eq 'ok') { + push(@{$added{$type}},$user); + $num_ok ++; + } else { + push(@{$failed{$type}},$user); + $num_fail ++; + } } - my $result = &Apache::lonnet::modify_coursegroup($cdom,$cnum, - \%groupsettings); - if ($result eq 'ok') { - my $put_result = &create_homepage($cdom,$cnum,$groupname,\%groupinfo,$tools); - foreach my $user (sort(@{$members})) { - $usersettings{$groupname.':'.$user} = $enddate.':'.$startdate.':'.$group_privs{$user}; - if (&Apache::lonnet::modify_group_roles($cdom,$cnum,$groupname, - $user,$enddate,$startdate,$group_privs{$user}) eq 'ok') { - push(@added,$user); - } else { - push(@failed,$user); + my $roster_result = &Apache::lonnet::modify_coursegroup_membership($cdom, + $cnum,\%usersettings); + if ($num_ok) { + foreach my $type (sort(keys(%added))) { + $r->print(&mt('The following users were successfully [_1]',$type)); + if (!($type eq 'deleted' || $type eq 'expired')) { + $r->print(&mt(' with the following privileges')); + } + $r->print(':
'); + 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 .= ''.$curr_tool.': '; + } + $privlist .= $$toolprivs{$curr_tool}{$priv}.', '; + } + $privlist =~ s/, $//; + } + $r->print($$userdata{$user}[$$idx{fullname}].' - '.$user.$privlist.'
'); } + $r->print('
'); } - my $roster_result = &Apache::lonnet::modify_coursegroup_membership( - $cdom,$cnum,\%usersettings); - $r->print(' -Group '.$groupname.' was created.
'); - if (@added > 0) { - $r->print('Users were added with following privileges:
'); - foreach my $user (@added) { - my @privs = split(/:/,$group_privs{$user}); - my $privlist= ''; - my $curr_tool = ''; - foreach my $priv (@privs) { - unless ($curr_tool eq $tooltype{$priv}) { - $curr_tool = $tooltype{$priv}; - $privlist .= ''.$curr_tool.': '; - } - $privlist .= $$toolprivs{$curr_tool}{$priv}.', '; - } - $privlist =~ s/, $//; - $r->print($$userdata{$user}[$$idx{fullname}].' - '.$user.': '.$privlist.'
'); - } - } - if (@failed > 0) { - $r->print('Addition of the following users was unsuccessful:
'); - foreach my $user (@failed) { + } + if ($num_fail) { + foreach my $type (sort(keys(%failed))) { + $r->print(&mt('The following users could not be [_1], because an error occurred:
',$type)); + foreach my $user (@{$failed{$type}}) { $r->print($$userdata{$user}[$$idx{fullname}].' - '.$user.'
'); } } - if ($roster_result eq 'ok') { - $r->print('
Group membership list updated.'); - } else { - $r->print('
An error occurred while updating the group membership list -'.$roster_result.'
'); - } + $r->print('
'); + } + if (@unchanged > 0) { + $r->print(&mt('No change occurred for the following users:
')); + foreach my $user (sort(@unchanged)) { + $r->print($$userdata{$user}[$$idx{fullname}].' - '.$user.'
'); + } + $r->print('
'); + } + if ($roster_result eq 'ok') { + $r->print('
'.&mt('[_1] membership list updated.',$ucgpterm)); + $r->print('

'.&mt("For full access to all of [_1]'s privileges, users will need to log out and log back in.",$groupname).'

'); } else { - &Apache::lonnet::logthis('Failed to create group '.$groupname. - 'in course: '.$cnum.' in domain: '.$cdom); + $r->print('
'.&mt('An error occurred while updating the [_1] membership list -',$gpterm).$roster_result.'
'); + } + return; +} + +sub mapping_options { + my ($r,$action,$formname,$page,$sectioncount,$states,$stored, + $navbuttons,$img1,$img2,$gpterm,$ucgpterm,$crstype) = @_; + 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 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", + 'curr' => 'Currently set to', + '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); + $r->print(' + +   + '); + &mapping_settings($r,$sectioncount,\%lt,$stored,$img2,$crstype); + 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'}; + } + &topic_bar($r,$image,$$lt{'endi'}); + $r->print(' + '.$$lt{'gmma'}.':
'.$$lt{'adds'}.'
'.$$lt{'drops'}.'

+ '.$$lt{'auad'}.':  + '); + if ($action eq 'modify') { + $r->print('    ('.$$lt{'curr'}.' '.$$lt{$add}.')'); + } + $r->print(' +
+ '.$$lt{'auex'}.':  + '); + if ($action eq 'modify') { + $r->print('    ('.$$lt{'curr'}.' '.$$lt{$drop}.')'); + } + $r->print('

'.$$lt{'mapr'}); +} + +sub mapping_settings { + my ($r,$sectioncount,$lt,$stored,$image,$crstype) = @_; + 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 + } + &topic_bar($r,$image,$$lt{'pirs'}); + my @roles = &standard_roles(); + my %customroles = &my_custom_roles(); + $r->print(&Apache::loncommon::start_data_table(). + &Apache::loncommon::start_data_table_header_row()); + $r->print(' + '.&mt('Active?').' + '.&mt('Role').''); + if (@sections > 0) { + $r->print(''.&mt('Sections').''); + } + $r->print(&Apache::loncommon::end_data_table_header_row()."\n"); + foreach my $role (@roles) { + my $plrole=&Apache::lonnet::plaintext($role,$crstype); + my $sections_sel; + if (@sections > 0) { + if ($role eq 'cc') { + $sections_sel = ''. + &mt('all sections').''; + } else { + $sections_sel=''. + §ions_selection(\@sections,'sec_'.$role). + ''; + } + } + $r->print(&Apache::loncommon::start_data_table_row(). + ''.$plrole. + ''.$sections_sel. + &Apache::loncommon::end_data_table_row()); + } + foreach my $role (sort(keys(%customroles))) { + my $sections_sel; + if (@sections > 0) { + $sections_sel = + ''.§ions_selection(\@sections,'sec_'.$role).''; + } + $r->print(&Apache::loncommon::start_data_table_row(). + ''.$role.''. + $sections_sel. + &Apache::loncommon::end_data_table_row()); + } + $r->print(&Apache::loncommon::end_data_table()); + return; +} - $r->print('An error occurred when creating the new group. '. - 'Please try again.'); +sub standard_roles { + my @roles = ('cc','in','ta','ep','st'); + return @roles; +} + +sub my_custom_roles { + my %returnhash=(); + my %rolehash=&Apache::lonnet::dump('roles'); + foreach (keys %rolehash) { + if ($_=~/^rolesdef\_(\w+)$/) { + $returnhash{$1}=$1; + } + } + return %returnhash; +} + +sub modify_menu { + my ($r,$groupname,$page,$gpterm) = @_; + my @menu = + ( + { text => "Modify default $gpterm settings", + help => 'Course_Modify_Group', + state => 'change_settings', + branch => 'settings', + }, + { text => 'Modify access, tools and/or privileges for previous, '. + 'future, or current members', + help => 'Course_Modify_Group_Membership', + state => 'change_members', + branch => 'members', + }, + { text => "Add member(s) to the $gpterm", + help => 'Course_Group_Add_Members', + state => 'add_members', + branch => 'adds', + }, + ); + my $menu_html = ''; + foreach my $menu_item (@menu) { + $menu_html .= + '

'; + $menu_html.= &mt($menu_item->{'text'}).''; + if (exists($menu_item->{'help'})) { + $menu_html.= + &Apache::loncommon::help_open_topic($menu_item->{'help'}); + } + $menu_html.='

'.$/; } + $r->print($menu_html); return; } sub member_privs_entries { - my ($r,$tabcol,$rowColor1,$rowColor2,$members,$tools,$usertools,$toolprivs, - $fixedprivs,$userdata,$idx) = @_; + my ($r,$tabcol,$rowColor1,$rowColor2,$usertools,$toolprivs, + $fixedprivs,$userdata,$idx,$showtools,$defprivs,$excluded) = @_; my $rowColor; my $rowNum = 0; - foreach my $member (@{$members}) { - my ($uname,$udom) = split(/:/,$member); + foreach my $user (sort(keys(%{$usertools}))) { + if (defined($excluded)) { + if (ref($excluded) eq 'ARRAY') { + if (grep/^$user$/,@{$excluded}) { + next; + } + } + } + my ($uname,$udom) = split(/:/,$user); if ($rowNum %2 == 1) { $rowColor = $rowColor1; } else { $rowColor = $rowColor2; } $r->print(' - '.$$userdata{$member}[$$idx{fullname}].' + '.$$userdata{$user}[$$idx{fullname}].' '.$uname.' '.$udom.'
Function
Fixed
Optional
'); - foreach my $tool (@{$tools}) { - if (exists($$usertools{$member}{$tool})) { + foreach my $tool (@{$showtools}) { + if (exists($$usertools{$user}{$tool})) { $r->print(''); my $privcount = 0; my $fixed = ''; my $dynamic = ''; foreach my $priv (sort(keys(%{$$toolprivs{$tool}}))) { if (exists($$fixedprivs{$tool}{$priv})) { - $fixed .= ''.$$toolprivs{$tool}{$priv}.' '; + $fixed .= ''.$$toolprivs{$tool}{$priv}.' '; } else { $privcount ++; if ($privcount == 3) { $dynamic .= ''; } - $dynamic .= ''; + $dynamic .=''; } } $r->print(''.$dynamic.'
'.$tool.'
'.$$toolprivs{$tool}{$priv}.'
'.$fixed.'
'); } else { - $r->print(' '); + $r->print('
'.$tool.'
 
 
'); } } $rowNum ++; } } - sub get_dates_from_form { my $startdate; my $enddate; @@ -1464,51 +3296,331 @@ sub get_dates_from_form { $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 = ' - no ending date'; - my $start_table = ''; - $start_table .= "\n"; - $start_table .= ''. + my $perpetual = + ''; + my $table = "
Default starting date for - member access
\n". + ''. ''. - ''."\n"; - $start_table .= "
'.&mt('Start:').''.$startform.' 
"; - my $end_table = ''; - $end_table .= "\n"; - $end_table .= ''. + ''."\n". + ''. ''. - ''."\n"; - $end_table .= "
Default ending date for - member access 
'.&mt('End:').''.$endform.''.$perpetual.'
\n"; - return ($start_table, $end_table); + ''.$perpetual.''."\n". + "\n"; + return $table; +} + +sub add_group_folder { + my ($cdom,$cnum,$now,$groupname,$action,$description,$tools,$groupinfo, + $gpterm,$ucgpterm,$crstype) = @_; + if ($cdom eq '' || $cnum eq '') { + return &mt('Error: invalid course domain or number - group folder creation failed'); + } + my ($outcome,$allgrpsmap,$grpmap,$boardsmap,$grppage); + my $navmap = Apache::lonnavmaps::navmap->new(); + my $crspath = '/uploaded/'.$cdom.'/'.$cnum.'/'; + $allgrpsmap = $crspath.'default_0.sequence'; + my $topmap = $navmap->getResourceByUrl($allgrpsmap); + undef($navmap); + if ($action eq 'create') { + # check if default_0.sequence exists. + if (!$topmap) { + my $grpstitle = &mt('[_1] [_2]',$crstype,$ucgpterm); + my $topmap_url = '/'.$env{'course.'.$env{'request.course.id'}.'.url'}; + $topmap_url =~ s|/+|/|g; + if ($topmap_url =~ m|^/uploaded|) { + $outcome = &map_updater($cdom,$cnum,'default_0.sequence', + 'toplevelgroup',$grpstitle,$topmap_url); + if ($outcome ne 'ok') { + return $outcome; + } + } else { + $outcome = &mt('Non-standard course - group folder not added.'); + return $outcome; + } + } + my $grpfolder = &mt('[_1] Folder -',$ucgpterm,).$description; + $grppage='/adm/'.$cdom.'/'.$cnum.'/'.$groupname.'/grppg'; + my $grptitle = &mt('Group homepage').' - '.$description; + my ($seqid,$discussions,$disctitle); + my $outcome = &map_updater($cdom,$cnum,'default_'.$now.'.sequence', + 'grpseq',$grpfolder,$allgrpsmap,$grppage, + $grptitle); + if ($outcome ne 'ok') { + return $outcome; + } + my $pageout = &create_homepage($cdom,$cnum,$groupname,$groupinfo, + $tools,$gpterm,$ucgpterm,$now); + # Link to folder for bulletin boards + $grpmap = $crspath.'default_'.$now.'.sequence'; + if (grep/^discussion$/,@{$tools}) { + $seqid = $now + 1; + $disctitle = &mt('Discussion Boards'); + my $outcome = &map_updater($cdom,$cnum,'default_'.$seqid. + '.sequence','bbseq',$disctitle,$grpmap); + if ($outcome ne 'ok') { + return $outcome; + } + $boardsmap = $crspath.'default_'.$seqid.'.sequence'; + } + } else { + #modify group folder if status of discussions tools is changed + } + my ($furl,$ferr)= &Apache::lonuserstate::readmap($cdom.'/'.$cnum); + $navmap = Apache::lonnavmaps::navmap->new(); + # modify parameters + my $parm_result; + if ($action eq 'create') { + if ($allgrpsmap) { + $parm_result .= &parm_setter($navmap,$cdom,$allgrpsmap,$groupname); + } + 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); + } + } + if ($parm_result) { + return $parm_result; + } 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 = &mt('Error uploading new folder.')." ($newfile): $newmapurl".'
'; + return $outcome; + } + my ($errtext,$fatal)=&Apache::lonratedt::mapread($parentmap); + if ($fatal) { + $outcome = &mt('Error reading contents of parent folder')." ($parentmap): $errtext".'
'; + return $outcome; + } else { + my $newidx=&Apache::lonratedt::getresidx($newmapurl); + $Apache::lonratedt::resources[$newidx] = $itemtitle.':'.$newmapurl. + ':false:normal:res'; + $Apache::lonratedt::order[1+$#Apache::lonratedt::order]=$newidx; + my ($outtext,$errtext) = &Apache::lonratedt::storemap($parentmap,1); + if ($errtext) { + $outcome = &mt('Error storing updated parent folder')." ($parentmap): $errtext".'
'; + return $outcome; + } + } + return 'ok'; +} + +sub new_map { + my ($startsrc,$starttitle,$endsrc,$endtitle) = @_; + my $newmapstr = ' + + + + + +'; + return $newmapstr; +} + +sub parm_setter { + my ($navmap,$cdom,$url,$groupname) = @_; + my $allresults; + my %hide_settings = ( + 'course' => { + 'num' => 13, + 'set' => 'yes', + }, + 'group' => { + 'num' => 5, + 'set' => 'no', + 'extra' => $groupname, + }, + ); + my $res = $navmap->getResourceByUrl($url); + 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; + } + } + return $allresults; } sub create_homepage { - my ($cdom,$cnum,$name,$groupinfo,$tools) = @_; + my ($cdom,$cnum,$name,$groupinfo,$tools,$gpterm,$ucgpterm,$now) = @_; my $functionality = join(',',@{$tools}); - my $content = &Apache::lonnet::unescape($$groupinfo{description}); + my $content = &unescape($$groupinfo{description}); $content=~s/\s+$//s; $content=~s/^\s+//s; $content=~s/\$//s; $content=&Apache::lonfeedback::clear_out_html($content,1); my %pageinfo = ( - 'aaa_title' => 'Group: '.$name, + 'aaa_title' => "$ucgpterm: $name", 'abb_links' => $functionality, 'bbb_content' => $content, 'ccc_webreferences' => '', - 'uploaded.lastmodified' => time, + '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(' + +'); + } + 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 %lt = &Apache::lonlocal::texthash ( + igna => "Invalid $gpterm name", + tgne => "The $gpterm name entered ", + grna => "$ucgpterm names and section names used in a ". + "$crstype must be unique.", + isno => "is not a valid name.", + gnmo => "$ucgpterm names may only contain letters, ". + "numbers or underscores.", + cnnb => "can not be used as it is the name of ", + inth => " in this $crstype", + thgr => "- does not correspond to the name of an ". + "existing $gpterm", + ); + + my $exitmsg = ''.$lt{'igna'}.'

'. + $lt{'tgne'}.' "'.$groupname.'" '; + my $dupmsg = $lt{'grna'}; + my $earlyout; + if (($groupname eq '') || ($groupname =~ /\W/)) { + $earlyout = $exitmsg.$lt{'isno'}.'
'.$lt{'gnmo'}; + return $earlyout; + } + if (exists($sectioncount{$groupname})) { + return $exitmsg.$lt{'cnnb'}.&mt('a section').$lt{'inth'}. + '
'.$lt{'grna'}; + } + if ($action eq 'create' + && exists($curr_groups{$groupname})) { + + return $exitmsg.$lt{'cnnb'}.&mt('an existing [_1]',$gpterm). + $lt{'inth'}.'
'.$lt{'grna'}; + + } elsif ($action eq 'modify') { + unless(exists($curr_groups{$groupname})) { + $earlyout = &mt('[_1] name:',$ucgpterm).' '.$groupname.$lt{'thgr'}. + $lt{'inth'}; + return $earlyout; + } + } + return; +} + +sub topic_bar { + my ($r,$imgnum,$title) = @_; + $r->print(' +
+   + '.$title.' +
+'); + 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;