[LON-CAPA-cvs] cvs: loncom /interface loncommon.pm loncoursegroups.pm /lonnet/perl lonnet.pm

raeburn lon-capa-cvs@mail.lon-capa.org
Tue, 15 Nov 2005 22:03:06 -0000


This is a MIME encoded message

--raeburn1132092186
Content-Type: text/plain

raeburn		Tue Nov 15 17:03:06 2005 EDT

  Modified files:              
    /loncom/lonnet/perl	lonnet.pm 
    /loncom/interface	loncoursegroups.pm loncommon.pm 
  Log:
  Course groups. Work still in progress. Creation of group, addition of members, assignment of member privileges etc.  
  
  
--raeburn1132092186
Content-Type: text/plain
Content-Disposition: attachment; filename="raeburn-20051115170306.txt"

Index: loncom/lonnet/perl/lonnet.pm
diff -u loncom/lonnet/perl/lonnet.pm:1.678 loncom/lonnet/perl/lonnet.pm:1.679
--- loncom/lonnet/perl/lonnet.pm:1.678	Tue Nov 15 16:35:02 2005
+++ loncom/lonnet/perl/lonnet.pm	Tue Nov 15 17:02:58 2005
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # TCP networking package
 #
-# $Id: lonnet.pm,v 1.678 2005/11/15 21:35:02 raeburn Exp $
+# $Id: lonnet.pm,v 1.679 2005/11/15 22:02:58 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -3702,6 +3702,49 @@
     return $response;
 }
 
+# ------------------------------------------------------- Course Group routines
+
+sub get_coursegroups {
+    my ($cdom,$cnum,$curr_groups,$group) = @_;
+    my $numgroups = 0;
+    %{$curr_groups} = &dump('coursegroups',$cdom,$cnum,$group);
+    my ($tmp)=keys(%{$curr_groups});
+    if ($tmp eq 'error: 2 tie(GDBM) Failed while attempting dump') {
+        my %emptyhash = ();
+        if (&put('coursegroups',\%emptyhash,$cdom,$cnum) eq 'ok') {
+            %{$curr_groups} = &dump('coursegroups',$cdom,$cnum,$group);
+            $tmp=keys(%{$curr_groups});
+        }
+    }
+    if ($tmp=~/^error:/) {
+        &logthis('Error retrieving groups: '.$tmp.' in '.$cnum.':'.$cdom);
+    } else {
+        my @groups = keys(%{$curr_groups});
+        $numgroups = @groups;
+    }
+    return $numgroups;
+}
+
+sub modify_coursegroup {
+    my ($cdom,$cnum,$groupsettings) = @_;
+    return(&put('coursegroups',$groupsettings,$cdom,$cnum));
+}
+
+sub modify_group_roles {
+    my ($cdom,$cnum,$group_id,$user,$end,$start,$userprivs) = @_;
+    my $url = '/'.$cdom.'/'.$cnum.'/'.$group_id;
+    my $role = 'gr/'.&escape($userprivs);
+    my ($uname,$udom) = split(/:/,$user);
+    my $result = &assignrole($udom,$uname,$url,$role,$end,$start);
+    return $result;
+}
+
+sub modify_coursegroup_membership {
+    my ($cdom,$cnum,$membership) = @_;
+    my $result = &put('groupmembership',$membership,$cdom,$cnum);
+    return $result;
+}
+
 # ------------------------------------------------------------------ Plain Text
 
 sub plaintext {
Index: loncom/interface/loncoursegroups.pm
diff -u loncom/interface/loncoursegroups.pm:1.2 loncom/interface/loncoursegroups.pm:1.3
--- loncom/interface/loncoursegroups.pm:1.2	Thu Oct 27 19:42:17 2005
+++ loncom/interface/loncoursegroups.pm	Tue Nov 15 17:03:05 2005
@@ -29,10 +29,12 @@
 use Apache::loncommon;
 use Apache::lonhtmlcommon;
 use Apache::lonlocal;
+use Apache::lonnavmaps;
 use Apache::Constants qw(:common :http);
 
 sub handler {
     my ($r) = @_;
+
     &Apache::loncommon::content_type($r,'text/html');
     $r->send_http_header;
                                                                                 
@@ -40,113 +42,498 @@
         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 groups";
         return HTTP_NOT_ACCEPTABLE;
     }
 
+    &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
+                                            ['action','refpage']);
+                                                                                      
+    my $function = &Apache::loncommon::get_users_function();
+    my $tabcol = &Apache::loncommon::designparm($function.'.tabbg');
+    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'});
     my $manage_permission =
-        &Apache::lonnet::allowed('mdg',$env{'request.course.id'});
+          &Apache::lonnet::allowed('mdg',$env{'request.course.id'});
 
-    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) {
-        &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);
+    &Apache::lonhtmlcommon::clear_breadcrumbs();
+
+    my %functions = (
+                      email => 'E-mail',
+                      discussion => 'Discussion boards',
+                      chat => 'Chat',
+                      files => 'File repository',
+                      roster => 'Membership roster',
+                      homepage => 'Group 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();
+
+    my $action = $env{'form.action'};
+    if ($action eq 'create' || $action eq 'modify' || $action eq 'view') { 
+        if ($view_permission || $manage_permission) {
+            &group_administration($r,$action,$cdom,$cnum,$function,$tabcol,
+                        \%functions,\%idx,$view_permission,$manage_permission);
         } else {
-            &first_creation_form($r);
+            $r->print('You do not have group administration '.
+                      'privileges in this course');
         }
+    } else {
+        &print_main_menu($r,$cdom,$cnum,$function,$tabcol,\%functions,\%idx,
+                                          $view_permission,$manage_permission);
     }
-    $r->print(&footer());
     return OK;
 }
 
+sub print_main_menu {
+    my ($r,$cdom,$cnum,$function,$tabcol,$functions,$idx,$view_permission,
+                                                          $manage_permission)
+                                                                          = @_;
+    $r->print(&header('Course Groups',&mt('LON-CAPA Course Groups'),
+                                                 undef,undef,undef,$function));
+    &Apache::lonhtmlcommon::add_breadcrumb
+        ({href=>"/adm/coursegroups",
+          text=>"Course Groups",});
+    $r->print(&Apache::lonhtmlcommon::breadcrumbs
+                (undef,'Course Groups'));
+    &display_groups($r,$cdom,$cnum,$function,$tabcol,$functions,$idx,
+                                          $view_permission,$manage_permission);
+    $r->print(&footer());
+    return;
+}
+
+sub display_groups {
+    my ($r,$cdom,$cnum,$function,$tabcol,$functions,$idx,$view_permission,
+                                                          $manage_permission) = @_;
+    my %curr_groups = ();
+    my %grp_info = ();
+    my $rowColor1 = "#dddddd";
+    my $rowColor2 = "#eeeeee";
+
+    $r->print('<br /><br />');
+    if ($view_permission) {
+        if (&Apache::lonnet::get_coursegroups($cdom,$cnum,\%curr_groups) > 0) {
+            $r->print(&Apache::lonhtmlcommon::start_pick_box());
+            if (keys(%curr_groups) > 0) {
+                $r->print(<<"END");
+      <table border="0" cellpadding="4" cellspacing="1">
+       <tr bgcolor="$tabcol" align="center">
+        <td><b>Action</b></td>
+        <td><b><a href="javascript:changeSort('groupname')">Group Name</a></b></td>
+        <td><b><a href="javascript:changeSort('description')">Description</a></b></td>
+        <td><b><a href="javascript:changeSort('creator')">Creator</a></b>
+        </td>
+        <td><b><a href="javascript:changeSort('creation')">Created</a></b>
+        </td>
+        <td><b><a href="javascript:changeSort('modified')">Last Modified</a></b>
+        </td>
+        <td><b>Functionality</b>
+        </td>
+        <td><b><a href="javascript:changeSort('quota')">Quota (Mb)</a></b></td>
+        <td><b><a href="javascript:changeSort('totalmembers)">Members</a></b></td>
+        <td><b><a href="javascript:changeSort('totalfiles')">Files</a></b></td>
+        <td><b><a href="javascript:changeSort('boards')">Discussion boards</a></b></td>
+        <td><b><a href="javascript:changeSort('diskuse')">Disk use</a></b></td>
+       </tr>
+END
+                my %Sortby = ();
+                foreach my $group (sort(keys(%curr_groups))) {
+                    %{$grp_info{$group}} = 
+                                      &Apache::loncommon::get_group_settings(
+                                                         $curr_groups{$group});
+                    my $members_result = &group_members($group,\%grp_info);
+                    my $files_result = &group_files($group,\%grp_info); 
+                    if ($env{'form.sortby'} eq 'groupname') {
+                        push(@{$Sortby{$group}},$group);
+                    } elsif ($env{'form.sortby'} eq 'description') {
+                        push(@{$Sortby{$grp_info{$group}{'description'}}},
+                                                                     $group);
+                    } elsif ($env{'form.sortby'} eq 'creator') {
+                        push(@{$Sortby{$grp_info{$group}{'creator'}}},$group);
+                    } elsif ($env{'form.sortby'} eq 'creation') {
+                        push(@{$Sortby{$grp_info{$group}{'creation'}}},$group);
+                    } elsif ($env{'form.sortby'} eq 'modified') {
+                        push(@{$Sortby{$grp_info{$group}{'modified'}}},$group);
+                    } elsif ($env{'form.sortby'} eq 'quota') {
+                        push(@{$Sortby{$grp_info{$group}{'quota'}}},$group);
+                    } elsif ($env{'form.sortby'} eq 'totalmembers') {
+                        push(@{$Sortby{$grp_info{$group}{'totalmembers'}}},
+                                                                       $group);
+                    } elsif ($env{'form.sortby'} eq 'totalfiles') {
+                        push(@{$Sortby{$grp_info{$group}{'totalfiles'}}},
+                                                                       $group);
+                    } elsif ($env{'form.sortby'} eq 'boards') {
+                        push(@{$Sortby{$grp_info{$group}{'boards'}}},$group);
+                    } elsif ($env{'form.sortby'} eq 'diskuse') {
+                        push(@{$Sortby{$grp_info{$group}{'diskuse'}}},$group);
+                    } else {
+                        push(@{$Sortby{$group}},$group);
+                    }
+                }
+                my $rowNum = 0;
+                my $rowColor;
+                foreach my $key (sort(keys(%Sortby))) {
+                    foreach my $group (@{$Sortby{$key}}) {
+                        if ($rowNum %2 == 1) {
+                            $rowColor = $rowColor1;
+                        } else {
+                            $rowColor = $rowColor2;
+                        }
+                        my $description = 
+                   &Apache::lonnet::unescape($grp_info{$group}{'description'});
+                        my $creator = $grp_info{$group}{'creator'};
+                        my $creation = $grp_info{$group}{'creation'};
+                        my $modified = $grp_info{$group}{'modified'}; 
+                        my $quota = $grp_info{$group}{'quota'};
+                        my $totalmembers = $grp_info{$group}{'totalmembers'};
+                        my $totalfiles = $grp_info{$group}{'totalfiles'};
+                        my $boards = $grp_info{$group}{'boards'};
+                        my $diskuse = $grp_info{$group}{'diskuse'};
+                        my $functionality;
+                        foreach my $tool (sort keys(%{$functions})) {
+                            if (defined($grp_info{$group}{functions}{$tool})) {
+                                $functionality .= ' '.$tool;
+                            }
+                        }
+                        if (!$functionality) {
+                            $functionality = 'None available';
+                        }
+                        $r->print('<tr bgcolor="'.$rowColor.'"><td align="right">
+   <a href="/adm/entergroup?group='.$group.'"/>View</a>&nbsp;<a href="/adm/coursegroups?action=modify&group='.$group.'">Modify</a></td><td><small>'.$group.'</small></td><td><small>'.$description.'</small></td><td><small>'.$creator.'</small></td><td><small>'. &Apache::lonnavmaps::timeToHumanString($creation).'</small></td><td><small>'. &Apache::lonnavmaps::timeToHumanString($modified).'</small></td><td><small>'.$functionality.'</small></td><td><small>'.$quota.'</small></td><td><small>'.$totalmembers.'</small></td><td><small>'.$totalfiles.'</small></td><td><small>'.$boards.'</small></td><td><small>'.$diskuse.'</small></td></tr>');
+                        $rowNum ++;
+                    }
+                }
+                $r->print('</table>');
+                $r->print(&Apache::lonhtmlcommon::end_pick_box());
+            }               
+        } else {
+            $r->print('No groups exist');
+        }
+    } else {
+        $r->print('You do not have sufficient privileges to allow you to display course groups');
+    }
+    return;
+}
+
+sub group_administration {
+    my ($r,$action,$cdom,$cnum,$function,$tabcol,$functions,$idx,
+                                     $view_permission,$manage_permission) = @_;
+    my %sectioncount = ();
+    my @tools = ();
+    my @types = ();
+    my @roles = ();
+    my @sections = ();
+    my %users = ();
+    my %userdata = ();
+    my @members = ();
+    my %usertools = ();
+
+    my $state = $env{'form.state'};
+    my ($groupname,$description,$startdate,$enddate);
+
+    if ($action eq 'create') {
+        if ($state eq '') {
+            $state = 'pick_name';
+        } else {
+            ($startdate,$enddate) = &get_dates_from_form();
+            if (defined($env{'form.groupname'})) {
+                $groupname = $env{'form.groupname'};
+            }
+            if (defined($env{'form.description'})) {
+                $description = $env{'form.description'};
+            }
+            if (defined($env{'form.tool'})) {
+                @tools=&Apache::loncommon::get_env_multiple('form.tool');
+            }
+            if (defined($env{'form.member'})) {
+                @members = &Apache::loncommon::get_env_multiple('form.member');
+                foreach my $user (@members) {
+                    %{$usertools{$user}} = ();
+                }
+            }
+        }
+    }
+
+    my %toolprivs = ();
+    %{$toolprivs{'email'}} = (
+                                 sgm => 'Send group mail',
+                                 sgb => 'Broadcast mail',
+                             );
+    %{$toolprivs{'discussion'}} =  (
+                                     cgb => 'Create boards',
+                                     pgd => 'Post',
+                                     pag => 'Anon. posts',
+                                     rgi => 'Get identities', 
+                                     vgb => 'View boards',
+                                   );
+    %{$toolprivs{'chat'}} =  (
+                                pgc => 'Chat',
+                             );
+    %{$toolprivs{'files'}} =  (
+                                 rgf => 'Retrieve',
+                                 ugf => 'Upload',
+                                 dgf => 'Delete',
+                              );
+    %{$toolprivs{'roster'}} = (
+                                 vgm => 'View',
+                              );
+    %{$toolprivs{'homepage'}} = (
+                                vgh => 'View page',
+                                mgh => 'Modify page',
+                              );
+    my %fixedprivs = ();
+    %{$fixedprivs{'email'}} = ('sgm' => 1);
+    %{$fixedprivs{'discussion'}} = ('vgb' => 1);
+    %{$fixedprivs{'chat'}} = ('pgc' => 1);
+    %{$fixedprivs{'files'}} = ('rgf' => 1);
+    %{$fixedprivs{'roster'}} = ('vgm' => 1);
+    %{$fixedprivs{'homepage'}} = ('vgh' => 1);
+
+    my %elements = ();
+    %{$elements{'create'}} = ();
+    %{$elements{'modify'}} = ();
+    %{$elements{'create'}{'pick_name'}} = (
+        startdate_month => 'selectbox',
+        startdate_hour => 'selectbox',
+        enddate_month => 'selectbox',
+        enddate_hour => 'selectbox',
+        types => 'selectbox',
+        roles => 'selectbox',
+        startdate_day => 'text',
+        startdate_year => 'text',
+        startdate_minute => 'text',
+        startdate_second => 'text',
+        enddate_day => 'text',
+        enddate_year => 'text',
+        enddate_minute => 'text',
+        enddate_second => 'text',
+        groupname => 'text',
+        description => 'text',
+        tool => 'checkbox',
+        granularity => 'radio',
+        no_end_date => 'checkbox',
+    );
+    %{$elements{'create'}{'pick_members'}} = (
+        member => 'checkbox',
+    );
+    if (($action eq 'create') && ($state eq 'pick_name')) {
+        my $numsections = &Apache::loncommon::get_sections($cdom,$cnum,
+                           \%sectioncount);
+        if ($numsections > 0) {
+            $elements{'create'}{'pick_name'}{'sectionpick'} = 'selectbox';
+        }
+    }
+                                                                                      
+    if (($action eq 'create') && (($state eq 'pick_members') ||
+                                  ($state eq 'pick_privs'))) {
+        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 (grep/^_all$/,@sections) {
+                @sections = sort {$a cmp $b} keys(%sectioncount);
+            }
+        }
+        &build_members_list($cdom,$cnum,\@types,\@roles,
+                        \@sections,\%users,\%userdata);
+        if ((keys(%users) > 0) && (@tools > 0)) {
+            foreach my $tool (@tools) {
+                if ($env{'form.granularity'} eq 'Yes') {
+                    $elements{'create'}{'pick_members'}{'user_'.$tool} = 'checkbox';
+                }
+            }
+            $elements{'create'}{'pick_members'}{'specificity'} = 'radio';
+        }
+    }
+
+    if (($action eq 'create') && (($state eq 'pick_privs') || (($state eq 'result') &&
+         ($env{'form.specificity'} eq 'No')))) {
+        foreach my $tool (@tools) {
+            my @values = &Apache::loncommon::get_env_multiple('form.user_'.$tool);
+            foreach my $user (@values) {
+                unless(exists($usertools{$user}{$tool})) {
+                    $usertools{$user}{$tool} = 1;
+                }
+            }
+        }
+        if (($state eq 'pick_privs') && ($env{'form.specificity'} eq 'Yes')) {
+            foreach my $member (@members) {
+                foreach my $tool (keys(%{$usertools{$member}})) {
+                    foreach my $priv (keys(%{$toolprivs{$tool}})) {
+                        unless (exists($fixedprivs{$tool}{$priv})) {
+                            $elements{'create'}{'pick_privs'}{'userpriv_'.$priv} =
+                                                                   'checkbox';
+                        }
+                    }
+                }
+            }
+        }
+    }
+ 
+    my $jscript;
+    if ($env{'form.action'} eq 'create') {
+        $jscript = &Apache::loncommon::check_uncheck_jscript();
+    }
+    $jscript .= qq|
+function nextPage(formname,nextstate) {
+    formname.state.value= nextstate;
+    formname.submit();
+}
+function backPage(formname,prevstate) {
+    formname.state.value = prevstate;
+    formname.submit();
+}
+                                                                                      
+|;
+                                                                                      
+    $jscript .= &Apache::lonhtmlcommon::set_form_elements(
+                           \%{$elements{$action}{$state}});
+
+
+    my $loaditems =  &onload_action($action,$state);
+    $r->print(&header('Course Groups Manager',&mt('LON-CAPA Groups Manager'),
+                               $jscript,$action,$state,$function,$loaditems));
+
+    if ($env{'form.refpage'} eq 'enrl') {
+        &Apache::lonhtmlcommon::add_breadcrumb
+        ({href=>"/adm/dropadd",
+          text=>"Enrollment Manager",
+          faq=>9,bug=>'Instructor Interface',});
+    } else {
+        &Apache::lonhtmlcommon::add_breadcrumb
+       ({href=>"/adm/coursegroups",
+          text=>"Course Groups",
+          faq=>9,bug=>'Instructor Interface',});
+    }
+
+    my %states = ();
+    @{$states{'create'}} = ('pick_name','pick_members','pick_privs','result');
+    @{$states{'modify'}} = ();
+                                                                                      
+    my %trail = ();
+    %{$trail{'create'}} = (
+                            pick_name => 'Group Settings',
+                            pick_members => 'Select Members',
+                            pick_privs => 'Choose Privileges',
+                            result => 'Creation Complete',
+                          );
+    %{$trail{'modify'}} = ();
+                                                                                      
+    if ((($action eq 'create') || ($action eq 'modify')) &&
+              ($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
+                     (undef,'Course Groups Manager'));
+                &display_control($r,$cdom,$cnum,$tabcol,$action,$state,
+                         \%sectioncount,$groupname,$description,$functions,
+                         \@tools,\%toolprivs,\%fixedprivs,$startdate,$enddate,
+                         \%users,\%userdata,$idx,\@members,\%usertools);
+                last;
+            } else {
+                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 groups"});
+        $r->print(&Apache::lonhtmlcommon::breadcrumbs
+                     (undef,'Course Groups Manager'));
+        &display_groups($r,$cdom,$cnum,$function,$tabcol,$functions,$idx,
+                                        $view_permission,$manage_permission);
+
+    }
+    $r->print(&footer());
+    return;
+}
+
+sub display_control {
+    my ($r,$cdom,$cnum,$tabcol,$action,$state,$sectioncount,$groupname,
+        $description,$functions,$tools,$toolprivs,$fixedprivs,$startdate,
+                      $enddate,$users,$userdata,$idx,$members,$usertools) = @_;
+    if ($action eq 'create') {
+        if ($state eq 'pick_name') {
+            &first_creation_form($r,$cdom,$cnum,$tabcol,$state,$functions,
+                                                                $sectioncount);
+        } elsif ($state eq 'pick_members') {
+            &second_creation_form($r,$cdom,$cnum,$tabcol,$state,$groupname,
+                                  $description,$startdate,$enddate,$tools,
+                                             $functions,$users,$userdata,$idx);
+        } elsif ($state eq 'pick_privs') {
+            &third_creation_form($r,$cdom,$cnum,$tabcol,$state,$startdate,
+                           $enddate,$tools,$functions,$toolprivs,$fixedprivs,
+                                           $userdata,$members,$usertools,$idx);
+        } elsif ($state eq 'result') {
+            &completed_creation($r,$cdom,$cnum,$tabcol,$state,$groupname,
+                            $description,$userdata,$startdate,$enddate,$tools,
+                               $functions,$toolprivs,$members,$usertools,$idx);
+        }
+    }
+}
+
 sub header {
+    my ($bodytitle,$title,$jscript,$action,$state,$function,$loaditems) = @_;
     my $html=&Apache::lonxml::xmlbegin();
-    my $bodytag=&Apache::loncommon::bodytag('Course Groups Manager');
-    my $title = &mt('LON-CAPA Groups Manager');
-    return(<<ENDHEAD);
+    my $bodytag=&Apache::loncommon::bodytag($bodytitle,$function,$loaditems);
+    my $output = <<"END";
 $html
 <head>
 <title>$title</title>
+<script type="text/javascript">
+$jscript
+</script>
 </head>
 $bodytag
-<form method="post"
-      action="/adm/coursegroup" name="form">
-ENDHEAD
+<form method="post" name="$state">
+
+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" />
+END
+    }
+    return $output;
 }
 
-sub print_main_menu {
-    my ($r,$manage_permission,$view_permission)=@_;
-    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 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.')"';
         }
-        $menu_html.='</p>'.$/;
     }
-    $r->print($menu_html);
-    return;
+    return $loaditems;
 }
 
 sub footer {
@@ -155,19 +542,40 @@
  </body>
 </html>
 ENDFOOT
- 
 }
 
+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 {
+    return;
+}
+
+sub group_members {
+    return;
+}
+
+
 sub first_creation_form {
-    my ($r) = @_;
-    my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
-    my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+    my ($r,$cdom,$cnum,$tabcol,$formname,$functions,$sectioncount) = @_;
     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',   
+        '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',
+        'acty' => 'Access types',
         'coro' => 'Course roles',
         'cose' => 'Course sections',
         'gfun' => 'Group functionality',
@@ -181,56 +589,926 @@
 
     my @roles = ('st','cc','in','ta','ep','cr');
 
-    my %sectioncount = ();
+    my $starttime = time;
+    my $endtime = time+(6*30*24*60*60); # 6 months from now, approx
+    my ($start_table,$end_table) = &date_setting_table
+                                    ($starttime,$endtime,$formname);
+
     my @sections = ();
     my $section_sel = '';
-    my $numvisible;
-    my $numsections = &Apache::loncommon::get_sections($cdom,$cnum,
-                           \%sectioncount);
+    my $numvisible = 4;
 
-    @sections = sort {$a cmp $b} keys(%sectioncount);
-    unshift(@sections,'all'); # Put 'all' at the front of the list
-    if ($numsections < 4) {
-        $numvisible = $numsections + 1;
+    @sections = sort {$a cmp $b} keys(%{$sectioncount});
+    if (@sections > 0) {
+        unshift(@sections,'_all'); # Put 'all' at the front of the list
+        if (@sections < 4) {
+            $numvisible = @sections;
+        }
+        foreach (@sections) {
+            if ($_ eq '_all') {
+                $section_sel .= '  <option value="'.$_.'" />all sections'."\n";
+            } else {
+                $section_sel .= '  <option value="'.$_.'" />'.$_."\n";
+            }
+        }
     }
 
     $r->print(<<"END");
-<b>$lt{'gmem'}</b><br/> $lt{'picr'}
-<br /><br />
-<table border="0">
+ <br />
+ <table width="100%" cellpadding="0" cellspacing="0" border="0">
+   <tr bgcolor="$tabcol">
+     <td>&nbsp;</td>
+     <td valign="bottom"><nobr><img src="/res/adm/pages/bl_step1.gif" 
+         valign="bottom">&nbsp;&nbsp;</nobr>
+     </td>
+     <td align="left"><nobr>
+       <font face="arial,helvetica,sans-serif"><b>Group name, description
+         and available functionality</b></font></nobr> 
+     </td>
+     <td width="100%">&nbsp;</td>
+   </tr>
+   <tr>
+    <td colspan="4">&nbsp;</td>
+   </tr>
+   <tr>
+    <td>&nbsp;</td>
+    <td colspan="3">
+     <table border="0" cellpadding="2" cellspacing="2">
+      <tr>
+       <td><b>Group Name:</b></td>
+       <td colspan="5"><input type="text" name="groupname" size="25" />
+       </td>
+      <tr>
+      <tr>
+       <td><b>Description:</b></td>
+       <td colspan="5"><input type="text" name="description" size="40" />
+       </td>
+      <tr>
+      <tr>
+       <td><b>Functionality:</b></td>
+END
+    my $numitems = keys(%{$functions});
+    my $halfnum = int($numitems/2);
+    my $remnum = $numitems%2;
+    if ($remnum) {
+        $halfnum ++;
+    }
+    my @allfunctions = sort (keys (%{$functions}));
+    for (my $i=0; $i<$halfnum; $i++) {
+        $r->print('<td><input type="checkbox" name="tool" value="'.
+                  $allfunctions[$i].'" />&nbsp;'.
+                   $$functions{$allfunctions[$i]}.'</td>
+                   <td>&nbsp;</td><td>&nbsp;</td>');
+    }
+    $r->print('<td><input type="button" value="check all" '.
+              'onclick="javascript:checkAll(document.'.$formname.'.tool)" />'.
+              '</td></tr><tr><td>&nbsp;</td>');
+    for (my $j=$halfnum; $j<@allfunctions; $j++) {
+        $r->print('<td><input type="checkbox" name="tool" value="'.
+                  $allfunctions[$j].'" />&nbsp;'.
+                  $$functions{$allfunctions[$j]}.'</td>
+                  <td>&nbsp;</td><td>&nbsp;</td>');
+    }
+    if ($remnum) {
+        $r->print('<td>&nbsp;</td>');
+    }
+    $r->print(<<"END"); 
+       <td>
+        <input type="button" value="uncheck all" 
+          onclick="javascript:uncheckAll(document.$formname.tool)" />
+       </td>
+      </tr>
+      <tr>
+       <td><b>Granularity:</b></td>
+       <td colspan="9">Do you want to assign different functionality to different group members?&nbsp;<input type="radio" name="granularity" value="Yes" />Yes&nbsp;<input type="radio" name="granularity" value="No" checked="checked" />No</td>
+      </tr> 
+     </table>
+    </td>
+   </tr>
+   <tr>
+    <td colspan="4">&nbsp;</td>
+   </tr>
+   <tr bgcolor="$tabcol">
+    <td>&nbsp;</td>
+    <td valign="bottom"><nobr><img src="/res/adm/pages/bl_step2.gif" 
+        valign="bottom">&nbsp;&nbsp;</nobr>
+    </td>
+    <td align="left"><nobr>
+      <font face="arial,helvetica,sans-serif"><b>Start and end dates for group
+                                                  access</b></font></nobr>
+    </td>
+    <td width="100%">&nbsp;</td>
+   </tr>
+   <tr>
+    <td colspan="4">&nbsp;</td>
+   </tr>
+   <tr>
+    <td>&nbsp;</td>
+    <td colspan="3">$start_table</td>
+   <tr>
+   <tr>
+    <td colspan="4">&nbsp;</td>
+   </tr>
+   <tr>
+    <td>&nbsp;</td>
+    <td colspan="3">$end_table</td>
+   <tr>
+   <tr>
+    <td colspan="4">&nbsp;</td>
+   </tr>
+   <tr bgcolor="$tabcol">
+    <td>&nbsp;</td>
+    <td valign="bottom"><nobr><img src="/res/adm/pages/bl_step3.gif" 
+        valign="bottom">&nbsp;&nbsp;</nobr>
+    </td>
+    <td align="left"><nobr>
+     <font face="arial,helvetica,sans-serif"><b>Pick parameters to generate 
+         membership list</b></nobr>
+     </font>
+    </td>
+    <td width="100%">&nbsp;</td>
+   </tr>
+   <tr>
+    <td colspan="4">&nbsp;</td>
+   </tr>
+   <tr>
+    <td>&nbsp;</td>
+    <td colspan="3">
+     <b>$lt{'gmem'}</b><br/> $lt{'picr'}
+     <br /><br />
+     <table border="0">
+      <tr>
+       <td><b>$lt{'acty'}</b></td>
+       <td>&nbsp;</td>
+       <td><b>$lt{'coro'}</b></td>
+END
+    if (@sections >0) {
+         $r->print('
+       <td>&nbsp;</td>
+       <td><b>'.$lt{'cose'}.'</b></td>
+       <td>&nbsp;</td>');
+    }
+    $r->print('</tr><tr>');
+    $r->print(&Apache::lonhtmlcommon::status_select_row(\%status_types));
+    $r->print('<td>&nbsp;</td>');
+    $r->print(&Apache::lonhtmlcommon::role_select_row(\@roles));
+    if (@sections > 0) {
+        $r->print('
+       <td>&nbsp;</td>
+       <td colspan="3" align="center" valign="top">
+        <select name="sectionpick" multiple="true" size="'.$numvisible.'">
+          '.$section_sel.'
+        </select>
+       </td>');
+    }
+    $r->print('
+      </tr>
+     </table>
+    </td>
+   </tr>
+   <tr>
+    <td colspan="4">&nbsp;</td>
+   </tr>
+   <tr>
+    <td>&nbsp;</td>
+    <td colspan="3" align="left">
+     <input type="button" value="Go to next step"  
+     onclick="javascript:nextPage(document.'.$formname.','."'pick_members'".')>
+    </td>
+   </tr>
+</table>
+');
+    return;
+}
+
+sub second_creation_form {
+    my ($r,$cdom,$cnum,$tabcol,$formname,$groupname,$description,$startdate,
+                        $enddate,$tools,$functions,$users,$userdata,$idx) = @_;
+    my @regexps = ('user_','userpriv_');
+    $r->print(&Apache::lonhtmlcommon::echo_form_input(
+                ['origin','action','state','member','specificity'],\@regexps));
+    my %sectioncount = ();
+    my $numsec = &Apache::loncommon::get_sections($cdom,$cnum,\%sectioncount);
+    my %curr_groups = ();
+    my $numgroups = &Apache::lonnet::get_coursegroups($cdom,$cnum,\%curr_groups);
+    my $earlyout = '';
+    my $exitmsg = '<b>Invalid group name</b><br /><br />The group name entered "'.
+                  $groupname.'" ';
+    my $dupmsg = 'Group names and section names used in a course must be unique.'; 
+    if ($groupname =~ /\W/) {
+        $earlyout = $exitmsg.'is not a valid name.<br />Group names may only contain letters, numbers or underscores';
+    }
+    if ($numsec) {
+        if (exists($sectioncount{$groupname})) {
+            $earlyout = $exitmsg.'can not be used as it is the name of a section 
+                                                in this course.<br />'.$dupmsg;
+        }
+    }
+    if ($numgroups) {
+        if (exists($curr_groups{$groupname})) {
+            $earlyout = $exitmsg.'can not be used as it is the name of an 
+                                 existing group in this course.<br />'.$dupmsg;
+        }
+    }
+    if ($earlyout) {
+        $r->print('<table border="0" cellpadding="2" cellspacing="2">
  <tr>
- <td><b>$lt{'acst'}</b></td>
   <td>&nbsp;</td>
-  <td><b>$lt{'coro'}</b></td>
+  <td>'.$earlyout.'</td>
+ </tr>
+ <tr>
+  <td colspan="2">&nbsp;</td>
+ </tr>
+ <tr>
   <td>&nbsp;</td>
-  <td><b>$lt{'cose'}</b></td>
+  <td align="left">
+   <input type="button" name="previous" value = "Go to previous page"
+    onclick="javascript:backPage(document.'.$formname.','."'pick_name'".')"/>
+  </td>
+ </tr>
+</table>
+        ');
+        return;
+    }
+    my $rowColor1 = "#dddddd";
+    my $rowColor2 = "#eeeeee";
+    my $showstart = &Apache::lonlocal::locallocaltime($startdate);
+    my $showend = &Apache::lonlocal::locallocaltime($enddate);
+    $r->print('<table border="0" cellpadding="0" cellspacing="20">
+<tr>
+ <td><font face="arial,helvetica,sans-serif"><b>New group selections</b></font>
+<br />When you create the new group, the following settings will apply:
+ </td>
+</tr>
+<tr>
+ <td>');
+    $r->print(&Apache::lonhtmlcommon::start_pick_box());
+    $r->print('
+<table cellspacing="1" cellpadding="4">
+ <tr bgcolor="'.$tabcol.'" align="center">
+  <td><b>Group Name</b></td>
+  <td><b>Description</b></td>
+  <td><b>Group Functionality</b></td>
+  <td><b>Default access dates</b></td>
+ </tr>
+ <tr bgcolor="'.$rowColor2.'">
+  <td valign="top"><small>'.$groupname.'</small></td>
+  <td valign="top"><small>'.$description.'</small></td>
+  <td>
+');
+    my @available = ();
+    my @unavailable = ();
+    foreach my $item (sort(keys(%{$functions}))) {
+        if (grep/^$item$/,@{$tools}) {
+            push(@available,$item);
+        } else {
+            push(@unavailable,$item);
+        }
+    }
+    if (@available > 0) {
+        $r->print('<small><b>Available:</b></small>
+                    <table cellpadding="" cellspacing="1"><tr>');
+        my $rowcell = int(@available/2) + @available%2;
+        for (my $i=0; $i<@available; $i++) {
+            if (@available > 3) {
+                if ($i==$rowcell) {
+                    $r->print('</tr><tr>');
+                }
+            }
+            $r->print('<td><small>'.$$functions{$available[$i]}.
+                                          '</small></td><td>&nbsp;</td>');
+        }
+        if ((@available > 3) && (@available%2)) {
+            $r->print('<td>&nbsp;</td><td>&nbsp;</td>');
+        }
+        $r->print('</tr></table><br />');
+    }
+    if (@unavailable > 0) {
+        $r->print('<small><b>Unavailable:</b></small>
+                    <table cellpadding="0" cellspacing="1"  border="0"><tr>');
+        my $rowcell = int(@unavailable/2) + @unavailable%2;
+        for (my $j=0; $j<@unavailable; $j++) {
+            if (@unavailable > 3) {
+                if ($j==$rowcell) {
+                    $r->print('</tr><tr>');
+                }
+            }
+            $r->print('<td><small>'.$$functions{$unavailable[$j]}.
+                                              '</small></td><td>&nbsp;</td>');
+        }
+        if ((@unavailable > 3) && (@unavailable%2)) {
+            $r->print('<td>&nbsp;</td><td>&nbsp;</td>');
+        }
+        $r->print('</tr></table>');
+    }
+    $r->print(<<"END");
+  </td>
+  <td valign="top"><small><b>Start date:</b> $showstart<br />
+      <b>End date:</b> $showend</small>
+  </td>
+ </tr>
+</table>
+END
+    $r->print(&Apache::lonhtmlcommon::end_pick_box());
+    my %members = ();
+    foreach my $role (keys(%{$users})) {
+        foreach my $user (keys(%{$$users{$role}})) {
+            unless (defined($members{$user})) {
+                @{$members{$user}} = @{$$userdata{$user}};
+            }
+        }
+    }
+    $r->print('</td></tr></table><br />');
+    if (keys(%members) > 0) {
+        if ($env{'form.granularity'} eq 'Yes') {
+            $r->print('
+<script type="text/javascript">
+function checkAllTools(formname) {
+');
+            foreach my $tool (@available) {
+                $r->print('  checkAll(formname.user_'.$tool.');'."\n");
+            }
+            $r->print('
+}
+function uncheckAllTools(formname) {
+');
+            foreach my $tool (@available) {
+                $r->print('  uncheckAll(formname.user_'.$tool.');'."\n");
+            }
+            $r->print('
+}
+</script>
+            ');
+       }
+       $r->print(<<"END");
+<table width="100%" cellpadding="0" cellspacing="0" border="0">
+ <tr bgcolor="$tabcol">
   <td>&nbsp;</td>
+  <td valign="top" align="left">
+   <nobr><img src="/res/adm/pages/bl_step4.gif" valign="middle">&nbsp;</nobr>
+  </td>
+  <td alin="left">
+   <nobr>
+   <font face="arial,helvetica,sans-serif">
+    <b>Select group members</b>
+   </font></nobr>
+  </td>
+  <td width="100%">&nbsp;</td>
  </tr>
  <tr>
+  <td colspan="4">&nbsp;</td>
+ </tr>
  <tr>
-END
-    $r->print(&Apache::lonhtmlcommon::status_select_row(\%status_types));
-    $r->print('<td>&nbsp;</td>');
-    $r->print(&Apache::lonhtmlcommon::role_select_row(\@roles));
-    $r->print(<<"END");
   <td>&nbsp;</td>
-  <td align="center">
-   <select name="sectionpick" multiple="true" size="$numvisible">
-    $section_sel
-   </select>
+  <td colspan="2">
+    <table>
+     <tr>
+      <td>
+   <nobr>
+    <fieldset><legend><b>Add members</b></legend>
+     <input type="button" value="check all" 
+       onclick="javascript:checkAll(document.$formname.member)" />
+      &nbsp;&nbsp;
+     <input type="button" value="uncheck all" 
+      onclick="javascript:uncheckAll(document.$formname.member)" />
+    </fieldset></nobr></td>
+END
+        if (@available > 0 && $env{'form.granularity'} eq 'Yes') {
+            $r->print('<td><nobr><fieldset><legend><b> 
+                                      Set functionality</b></legend>
+     <input type="button" value="check all" 
+       onclick="javascript:checkAllTools(document.'.$formname.')" />
+       &nbsp;&nbsp;
+     <input type="button" value="uncheck all" 
+        onclick="javascript:uncheckAllTools(document.'.$formname.')" />
+    </fieldset></nobr></td>');
+        }
+        $r->print('</tr></table>
   </td>
+  <td width="100%">&nbsp;</td>
  </tr>
-</table>
+ <tr>
+  <td colspan="4">&nbsp;</td>
+ </tr>
+ <tr>
+  <td>&nbsp;</td>
+  <td colspan="3">
+        ');
+        $r->print(&Apache::lonhtmlcommon::start_pick_box());
+        $r->print(<<"END");
+   <table border="0" cellpadding="4" cellspacing="1">
+    <tr bgcolor="$tabcol" align="center">
+     <td><b>Add?</b></td>
+     <td><b><a href="javascript:changeSort('fullname')">Name</a></b></td>
+     <td><b><a href="javascript:changeSort('username')">Username</a></b>
+     </td>
+     <td><b><a href="javascript:changeSort('domain')">Domain</a></b></td>
+     <td><b><a href="javascript:changeSort('id')">ID</a></b></td>
 END
+        if (@available > 0) {
+            $r->print('<td><b>Functionality</b></td>');
+        }
+        $r->print('</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);
+            } else {
+                push(@{$Sortby{$members{$user}[$$idx{fullname}]}},$user);
+            }
+        }
+        my $rowNum = 0;
+        my $rowColor;
+        foreach my $key (sort(keys(%Sortby))) {
+            foreach my $user (@{$Sortby{$key}}) {
+                if ($rowNum %2 == 1) {
+                    $rowColor = $rowColor1;
+                } else {
+                    $rowColor = $rowColor2;
+                }
+                my $id = $members{$user}[$$idx{id}];
+                my $fullname = $members{$user}[$$idx{fullname}];
+                my $udom = $members{$user}[$$idx{udom}];
+                my $uname = $members{$user}[$$idx{uname}];
+                $r->print('<tr bgcolor="'.$rowColor.'"><td align="right">
+   <input type="checkbox" name="member" value="'.$user.'" /></td><td><small>'.
+    $fullname.'</small></td><td><small>'.$uname.'</small></td><td><small>'.
+    $udom.'</small></td><td><small>'.$id.'</small></td>');
+                if (@available > 0) {
+                    $r->print('<td align="center"><small>'); 
+                    foreach my $tool (@available) {
+                        if ($env{'form.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('</small></td>');
+                }
+                $r->print('</tr>'."\n");
+                $rowNum ++;
+            }
+        }
+        $r->print(&Apache::lonhtmlcommon::end_pick_box());
+        $r->print('
+  </td>
+ </tr>
+ <tr>
+  <td colspan="4">&nbsp;</td>
+ </tr>');
+        if (@available > 0) {
+            $r->print('
+ <tr bgcolor="'.$tabcol.'">
+  <td>&nbsp;</td>
+  <td valign="middle" align="left">
+   <nobr><img src="/res/adm/pages/bl_step5.gif" valign="middle">&nbsp;</nobr>
+  </td>
+  <td align="left"><nobr>
+   <font face="arial,helvetica,sans-serif">
+    <b>User privileges</b>
+   </font></nobr>
+  </td>
+  <td width="100%">&nbsp;</td>
+ </tr>
+ <tr>
+  <td>&nbsp;</td>
+  <td colspan="3">
+   <br />
+   For each type of functionality you have chosen to include, there is a 
+set of standard privileges which apply to all of those for whom the functionality is enabled.<br />There are also additional privileges which can be set for some, or all, members. Please choose one of the following:<br />
+<br /><input type="radio" name="specificity" value="No" checked="checked" />&nbsp;All group members will receive the same privileges.<br/><input type="radio" name="specificity" value="Yes" />&nbsp;Some group members will receive different privileges from others.
+  </td>
+ </tr>
+ <tr>
+  <td colspan="4">&nbsp;</td>
+ </tr>
+');
+        }
+        $r->print('    
+ <tr>
+  <td>&nbsp;</td>
+  <td colspan="3">
+   <input type="button" name="previous" value = "Go to previous page" 
+    onclick="javascript:backPage(document.'.$formname.','."'pick_name'".')"/>
+   &nbsp;&nbsp;&nbsp;
+   <input type="button" name="next" value="Go to next page" 
+ onclick="javascript:nextPage(document.'.$formname.','."'pick_privs'".')" />
+  </td>
+ </tr>
+        ');
+    } else {
+        $r->print('No members to add');
+    }
+    $r->print('
+   </table>
+  </td>
+ </tr>
+</table>');
     return;
 }
 
-sub second_creation_form {
-    my ($r) = @_;
+sub third_creation_form {
+    my ($r,$cdom,$cnum,$tabcol,$formname,$startdate,$enddate,$tools,$functions,
+               $toolprivs,$fixedprivs,$userdata,$members,$usertools,$idx) = @_;
+    my @regexps = ('userpriv_','allpriv_');
+    $r->print(&Apache::lonhtmlcommon::echo_form_input(
+                             ['origin','action','state'],\@regexps));
+    my %possibles = ();
+    my %showboxes = ();
+    my $totalboxes = 0;
+    my $rowColor1 = "#dddddd";
+    my $rowColor2 = "#eeeeee";
+    my $numtools = 1 + @{$tools};
+    foreach my $tool (@{$tools}) {
+        @{$showboxes{$tool}} = ();
+        foreach my $user (@{$members}) {
+            if (exists($$usertools{$user}{$tool})) {
+                foreach my $priv (sort(keys(%{$$toolprivs{$tool}}))) {
+                    unless (exists($$fixedprivs{$tool}{$priv})) {
+                        push(@{$possibles{$user}},$priv);
+                        unless(grep(/^$priv$/,@{$showboxes{$tool}})) {
+                            push(@{$showboxes{$tool}},$priv);
+                            $totalboxes ++;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    if ($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.userpriv_'.$priv.');'."\n");
+            }
+        }
+        $r->print('
+}
+function uncheckAllTools(formname) {
+');
+        foreach my $tool (sort(keys(%showboxes))) {
+            foreach my $priv (@{$showboxes{$tool}}) {
+                $r->print('  uncheckAll(formname.userpriv_'.$priv.');'."\n");
+            }
+        }
+        $r->print('
+}
+</script>
+       ');
+   }
+   $r->print(<<"END");
+<br />
+<table width="100%" cellpadding="0" cellspacing="0" border="0">
+ <tr bgcolor="$tabcol">
+  <td>&nbsp;</td>
+  <td valign="middle" align="left">
+   <nobr><img src="/res/adm/pages/bl_step6.gif" valign="middle">&nbsp;</nobr>
+  </td>
+  <th align="left"><nobr>
+    Group member privileges
+   </nobr>
+  </th>
+  <td width="100%">&nbsp;</td>
+ </tr>
+ <tr>
+  <td colspan="4">&nbsp;</td>
+ </tr>
+ <tr>
+  <td>&nbsp;</td>
+  <td colspan="3">
+END
+    if ($env{'form.specificity'} eq 'Yes') {
+        $r->print('
+   <table border="0" cellspacing="2" cellpadding="2" border="0">
+    <tr>
+     <td valign="top">
+');
+        $r->print(&Apache::lonhtmlcommon::start_pick_box());
+        $r->print(<<"END");
+   <tr bgcolor="$tabcol">
+    <th><b>Fullname</th>
+    <th><b>Username</th>
+    <th>Domain</th>
+    <th colspan="$numtools">Additional Privileges</th>
+  </tr>
+END
+    }
+    if ($env{'form.specificity'} eq 'Yes') {
+        &member_privs_entries($r,$tabcol,$rowColor1,$rowColor2,$members,$tools,
+                             $usertools,$toolprivs,$fixedprivs,$userdata,$idx);
+    }
+    if ($env{'form.specificity'} eq 'Yes') {
+        $r->print('</td>');
+        $r->print(&Apache::lonhtmlcommon::end_pick_box());
+        $r->print('<td>&nbsp;</td>
+               <td>&nbsp;</td><td valign="top">');
+        my @toolboxes = sort(keys(%showboxes));
+        foreach my $tool (@{$tools}) {
+            if (@{$showboxes{$tool}} > 0) { 
+                $r->print('<table class="thinborder"><tr bgcolor="'.$tabcol.
+                      '"><th>'.$tool.'</th></tr>');
+                foreach my $priv (@{$showboxes{$tool}}) {
+                    $r->print(qq|
+       <tr><td>
+       <fieldset><legend><b>$$toolprivs{$tool}{$priv}</b></legend>
+       <input type="button" value="check all"
+         onclick="javascript:checkAll(document.$formname.userpriv_$priv)" />
+       &nbsp; 
+       <input type="button" value="uncheck all"
+        onclick="javascript:uncheckAll(document.$formname.userpriv_$priv)" />
+      </nobr></fieldset></td></tr>|);
+                }
+                $r->print('</table><br /><br />');
+            }
+        }
+        $r->print('</td></tr></table>');
+    } else {
+        $r->print(&Apache::lonhtmlcommon::start_pick_box());
+        $r->print('<tr><td bgcolor="'.$tabcol.'" valign="top"><table cellspacing="0" cellpadding="1"><tr><td valign="top"><b>Function</b></td></tr><tr><td valign="top"><b>Fixed privileges</b>'.
+                  '</td></tr><tr><td valign="top"><b>Optional privileges</b></td></tr></table></td>');
+        foreach my $tool (@{$tools}) {
+            $r->print('<td align="center" valign="top"><table cellspacing="0" cellpadding="1"><tr bgcolor="#cccccc">'.
+                       '<td colspan="2" align="center"><b>'.$tool.'</b></td></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="allpriv_'.$priv.'" value="all" />'.$$toolprivs{$tool}{$priv}.'&nbsp;';
+                } else {
+                    $privcount ++;
+                    if ($privcount == 3) {
+                        $dynamic .= '</tr><tr bgcolor="'.$rowColor1.'">';
+                    }
+                    $dynamic .= '<td><input type="checkbox" name="allpriv_'.$priv.'" value="all" />'.$$toolprivs{$tool}{$priv}.'</td>';
+                }
+            }
+            if ($dynamic eq '') {
+                $dynamic = '<td>None</td>'; 
+            }
+            if ($privcount < 3) {
+                $dynamic .= '</tr><tr bgcolor="'.$rowColor1.'"><td colspan="2">&nbsp;</td>';
+            } elsif ($privcount%2) {
+                $dynamic = '<td>&nbsp;</td>';
+            }
+            $r->print('<tr bgcolor="'.$rowColor2.'"><td colspan="2" align="center"><nobr>'.$fixed.'</nobr></td></tr><tr bgcolor="'.$rowColor1.'">'.$dynamic.'</tr></table></td>');
+        }
+        $r->print('</tr>');
+        $r->print(&Apache::lonhtmlcommon::end_pick_box());
+    }
+    $r->print('</td></tr>
+ <tr>
+  <td colspan="4">&nbsp;</td>
+ </tr>
+ <tr>
+  <td>&nbsp;</td>
+  <td colspan="3">
+   <input type="button" name="previous" value = "Go to previous page"
+    onclick="javascript:backPage(document.'.$formname.','."'pick_members'".')"/>
+   &nbsp;&nbsp;
+   <input type="button" name="next" value="Create group"
+ onclick="javascript:nextPage(document.'.$formname.','."'result'".')" />
+  </td>
+ </tr>
+    ');
+    $r->print('
+</table>');
+
+    return;
 }
 
 sub completed_creation {
-    my ($r) = @_;
+    my ($r,$cdom,$cnum,$tabcol,$formname,$groupname,$description,$userdata,
+$startdate,$enddate,$tools,$functions,$toolprivs,$members,$usertools,$idx) = @_;
+
+    $r->print(&Apache::lonhtmlcommon::echo_form_input(
+                                        ['origin','action','state']));
+    my @added= ();
+    my @failed = ();
+    my $now = time;
+    my %group_privs = ();
+    my %tooltype = ();
+    foreach my $tool (@{$tools}) {
+        foreach my $priv (sort(keys(%{$$toolprivs{$tool}}))) {
+            $tooltype{$priv} = $tool;
+            if ($env{'form.specificity'} eq 'Yes') {
+                my @users =
+                  &Apache::loncommon::get_env_multiple('form.userpriv_'.$priv);
+                foreach my $user (@users) {
+                    $group_privs{$user} .= $priv.':';
+                }
+            } else {
+                if (defined($env{'form.allpriv_'.$priv})) {
+                    foreach my $user (@{$members}) {
+                        if ($$usertools{$user}{$tool}) {
+                            $group_privs{$user} .= $priv.':';
+                        }
+                    }
+                }
+            }
+        }
+    }
+    foreach my $user (keys(%group_privs)) {
+        $group_privs{$user} =~ s/:$//;
+    }
+    my $esc_description = &Apache::lonnet::escape($description);
+    my @attributes = ('description','functions','startdate','enddate','creation','modified','creator');
+        
+    my %groupinfo = (
+                      description => $esc_description,
+                      startdate => $startdate,
+                      enddate => $enddate,
+                      creation => $now,
+                      modified => $now,
+                      creator => $env{'user.name'}.':'.$env{'user.domain'}, 
+                    );
+    foreach my $func (keys(%{$functions})) {
+        my $status;
+        if (grep(/^$func$/,@{$tools})) {
+            $status = 'on';
+        } else {
+            $status = 'off';
+        } 
+        $groupinfo{'functions'} .=  qq|<name id="$func">$status</name>|;
+    }
+
+    my %curr_groups = ();
+    my %groupsettings = ();
+    my %usersettings = ();
+    if (&Apache::lonnet::get_coursegroups($cdom,$cnum,\%curr_groups) > 0) {
+        if (exists($curr_groups{$groupname})) {
+            $r->print('Non-unique name -please choose another');
+            return; 
+        }
+    }
+    my $groupentry;
+    foreach my $item (@attributes) {
+        $groupsettings{$groupname} .= qq|<$item>$groupinfo{$item}</$item>|;
+    }
+    my $result = &Apache::lonnet::modify_coursegroup($cdom,$cnum,
+                                                          \%groupsettings);
+    if ($result eq 'ok') {
+        my $put_result = &create_homepage($cdom,$cnum,$groupname,\%groupinfo,$tools);
+        foreach my $user (sort(@{$members})) {
+            $usersettings{$groupname.':'.$user} = $enddate.':'.$startdate.':'.$group_privs{$user};
+            if (&Apache::lonnet::modify_group_roles($cdom,$cnum,$groupname,
+                  $user,$enddate,$startdate,$group_privs{$user}) eq 'ok') {
+                push(@added,$user);
+            } else {
+                push(@failed,$user);
+            }
+        }
+        my $roster_result = &Apache::lonnet::modify_coursegroup_membership(
+             $cdom,$cnum,\%usersettings);
+        $r->print('
+Group '.$groupname.' was created.<br />');
+        if (@added > 0) {
+            $r->print('Users were added with following privileges:<br />');
+            foreach my $user (@added) {
+                my @privs = split(/:/,$group_privs{$user});
+                my $privlist= '';
+                my $curr_tool = '';
+                foreach my $priv (@privs) {
+                    unless ($curr_tool eq $tooltype{$priv}) {
+                        $curr_tool = $tooltype{$priv};
+                        $privlist .= '<b>'.$curr_tool.'</b>: ';
+                    }
+                    $privlist .= $$toolprivs{$curr_tool}{$priv}.', ';
+                }
+                $privlist =~ s/, $//;
+                $r->print($$userdata{$user}[$$idx{fullname}].'&nbsp;-&nbsp;'.$user.': '.$privlist.'<br />');
+            }
+        }
+        if (@failed > 0) {
+            $r->print('Addition of the following users was unsuccessful:<br />');
+            foreach my $user (@failed) {
+                $r->print($$userdata{$user}[$$idx{fullname}].' - '.$user.'<br />');
+            }
+        }
+        if ($roster_result eq 'ok') {
+            $r->print('<br />Group membership list updated.');
+        } else {
+            $r->print('<br />An error occurred while updating the group membership list -'.$roster_result.'<br />');
+        } 
+    } else {
+        &Apache::lonnet::logthis('Failed to create group '.$groupname. 
+                                 'in course: '.$cnum.' in domain: '.$cdom);
+
+        $r->print('An error occurred when creating the new group. '.
+                                                      'Please try again.');
+    }
+    return;
+}
+
+sub member_privs_entries {
+    my ($r,$tabcol,$rowColor1,$rowColor2,$members,$tools,$usertools,$toolprivs,
+                                              $fixedprivs,$userdata,$idx) = @_;
+    my $rowColor;
+    my $rowNum = 0;
+    foreach my $member (@{$members}) {
+        my ($uname,$udom) = split(/:/,$member);
+        if ($rowNum %2 == 1) {
+            $rowColor = $rowColor1;
+        } else {
+            $rowColor = $rowColor2;
+        }
+        $r->print('<tr bgcolor="'.$rowColor.'">
+                <td>'.$$userdata{$member}[$$idx{fullname}].'</td>
+                <td>'.$uname.'</td>
+                <td>'.$udom.'</td>
+                <td valign="top"><table><tr><td><b>Function</b></td></tr><tr><td><b>Fixed</b></td></tr><tr><td><b>Optional</b></td></tr></table></td>');
+        foreach my $tool (@{$tools}) {
+            if (exists($$usertools{$member}{$tool})) {
+                $r->print('<td valign="top"><table><tr bgcolor="'.$tabcol.'"><td colspan="2" align="center"><b>'.$tool.'</b></td></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="'.$member.'" />'.$$toolprivs{$tool}{$priv}.'&nbsp;';
+                    } else {
+                        $privcount ++;
+                        if ($privcount == 3) {
+                            $dynamic .= '</tr><tr>';
+                        }
+                        $dynamic .= '<td><input type="checkbox" name="userpriv_'.$priv.'" value="'.$member.'" />'.$$toolprivs{$tool}{$priv}.'</td>';
+                    }
+                }
+                $r->print('<tr><td colspan="2"><nobr>'.$fixed.'</nobr></td></tr><tr>'.$dynamic.'</tr></table></td>');
+            } else {
+                $r->print('<td>&nbsp;</td>');
+            }
+        }
+        $rowNum ++;
+    }
+}
+
+
+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 = '<nobr><input type="checkbox" name="no_end_date" />
+                                                  no ending date</nobr>';
+    my $start_table = '';
+    $start_table .= "<table>\n";
+    $start_table .= '<tr><td align="right">Default starting date for 
+                                           member access</td>'.
+        '<td>'.$startform.'</td>'.
+        '<td>&nbsp;</td>'."</tr>\n";
+    $start_table .= "</table>";
+    my $end_table = '';
+    $end_table .= "<table>\n";
+    $end_table .= '<tr><td align="right">Default ending date for 
+                                         member access</td>'.
+        '<td>'.$endform.'</td>'.
+        '<td>'.$perpetual.'</td>'."</tr>\n";
+    $end_table .= "</table>\n";
+    return ($start_table, $end_table);
+}
+
+sub create_homepage {
+    my ($cdom,$cnum,$name,$groupinfo,$tools) = @_;
+    my $functionality = join(',',@{$tools});
+    my $content = &Apache::lonnet::unescape($$groupinfo{description});
+    $content=~s/\s+$//s;
+    $content=~s/^\s+//s;
+    $content=~s/\<br\s*\/*\>$//s;
+    $content=&Apache::lonfeedback::clear_out_html($content,1);
+
+    my %pageinfo = (
+                     'aaa_title' => 'Group: '.$name,
+                     'abb_links' => $functionality,
+                     'bbb_content' => $content,
+                     'ccc_webreferences' => '',
+                     'uploaded.lastmodified' => time,
+                   );
+   my $putresult = &Apache::lonnet::put('grppage_'.$name,\%pageinfo,$cdom,$cnum);
+   return $putresult;
 }
 
+
 1;
Index: loncom/interface/loncommon.pm
diff -u loncom/interface/loncommon.pm:1.293 loncom/interface/loncommon.pm:1.294
--- loncom/interface/loncommon.pm:1.293	Tue Nov 15 15:46:40 2005
+++ loncom/interface/loncommon.pm	Tue Nov 15 17:03:05 2005
@@ -1,7 +1,7 @@
 # The LearningOnline Network with CAPA
 # a pile of common routines
 #
-# $Id: loncommon.pm,v 1.293 2005/11/15 20:46:40 raeburn Exp $
+# $Id: loncommon.pm,v 1.294 2005/11/15 22:03:05 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -3085,6 +3085,83 @@
 ###############################################
 
 =pod
+
+=item get_group_settings
+
+Uses TokeParser to extract group information from the
+XML used to describe course groups.
+
+Input:
+Scalar containing XML (as retrieved from &lonnet::get_coursegroups).
+
+Output:
+Hash containing group information as key=values for (a), and
+hash of hashes for (b)
+
+Keys (in two categories):
+(a) groupname, creator, creation, modified, startdate,enddate.
+Corresponding values are name of the group, creator of the group
+(username:domain), UNIX time for date group was created, and
+settings were last modified, and default start and end access
+times for group members.
+
+(b) functions returned in hash of hashes.
+Outer hash key is functions.
+Inner hash keys are chat,discussion,email,files,homepage,roster.
+Corresponding values are either on or off, depending on
+whther this type of functionality is available for the group.
+
+=cut
+                                                                                 
+###############################################
+
+sub get_group_settings {
+    my ($groupinfo)=@_;
+    my $parser=HTML::TokeParser->new(\$groupinfo);
+    my $token;
+    my $tool = '';
+    my %content=();
+    while ($token=$parser->get_token) {
+        if ($token->[0] eq 'S')  {
+            my $entry=$token->[1];
+            if ($entry eq 'functions') {
+                %{$content{$entry}} = ();
+                $tool = $entry;
+            } else {
+                my $value=$parser->get_text('/'.$entry);
+                if ($entry eq 'name') {
+                    if ($tool eq 'functions') {
+                        my $function = $token->[2]{id};
+                        $content{$tool}{$function} = $value;
+                    }
+                } elsif ($entry eq 'groupname') {
+                    $content{$entry}=&Apache::lonnet::unescape($value);
+                } else {
+                    $content{$entry}=$value;
+                }
+            }
+        } elsif ($token->[0] eq 'E') {
+            if ($token->[1] eq 'functions') {
+                $tool = '';
+            }
+        }
+    }
+    return %content;
+}
+
+sub check_group_access {
+    my ($group) = @_;
+    my $access = 1;
+    my $now = time;
+    my ($start,$end) = split(/\./,$env{'user.role.gr/'.$env{'request.course,id'}.'/'.$group});
+    if (($end!=0) && ($end<$now)) { $access = 0; }
+    if (($start!=0) && ($start>$now)) { $access=0; }
+    return $access;
+}
+
+###############################################
+
+=pod
                                                                                 
 =item get_course_users
                                                                                 

--raeburn1132092186--