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