site permissions: add class list by section number, or by external group code
authorgfawcett <gfawcett@6d9bc8c9-1ec2-4278-b937-99fde70a366f>
Tue, 28 Dec 2010 00:40:52 +0000 (00:40 +0000)
committergfawcett <gfawcett@6d9bc8c9-1ec2-4278-b937-99fde70a366f>
Tue, 28 Dec 2010 00:40:52 +0000 (00:40 +0000)
If you provide a hook function, 'derive_group_code_from_section',
mapping section numbers onto external group/class-list codes (in the
context of a given site), then users can easily add sections to course
sites, just by providing the section number(s). If not, then the part
of the UI related to section numbers is not displayed.

Note that external group members are loaded lazily: we don't know who
belongs to an external group until they log into Syrup, and their
external memberships are updated.

git-svn-id: svn://svn.open-ils.org/ILS-Contrib/servres/trunk@1122 6d9bc8c9-1ec2-4278-b937-99fde70a366f

conifer/integration/uwindsor.py
conifer/syrup/integration.py
conifer/syrup/views/sites.py
conifer/templates/edit_site_permissions.xhtml

index e57b9c1..ae511a9 100644 (file)
@@ -265,6 +265,24 @@ if os.path.isfile(FUZZY_LOOKUP_BIN):
         return out
 
 
+def derive_group_code_from_section(site, section):
+    """
+    This function is used to simplify common-case permission setting
+    on course sites. It takes a site and a section number/code, and
+    returns the most likely external group code. (This function will
+    probably check the site's term and course codes, and merge those
+    with the section code, to derive the group code.) Return None if a
+    valid, unambiguous group code cannot be generated.
+    """
+    try:
+        section = int(section)
+    except:
+        return None
+
+    return '%s-%s-%s' % (site.course.code.replace('-', ''),
+                         section, 
+                         site.start_term.code)
+                         
 #--------------------------------------------------
 # proxy server integration
 
index 540a35e..8c5161f 100644 (file)
@@ -132,6 +132,17 @@ def user_needs_decoration(user_obj):
     """
 
 @disable
+def derive_group_code_from_section(site, section):
+    """
+    This function is used to simplify common-case permission setting
+    on course sites. It takes a site and a section number/code, and
+    returns the most likely external group code. (This function will
+    probably check the site's term and course codes, and merge those
+    with the section code, to derive the group code.) Return None if a
+    valid, unambiguous group code cannot be generated.
+    """
+
+@disable
 def proxify_url(url):
     """
     Given a URL, determine whether the URL needs to be passed through
index e8c4c96..6807806 100644 (file)
@@ -94,15 +94,35 @@ def edit_site_permissions(request, site_id):
 
     choose_access = django.forms.RadioSelect(choices=choices)
 
+    show_add_section_panel = gethook('derive_group_code_from_section')
+
     if request.method != 'POST':
         return g.render('edit_site_permissions.xhtml', **locals())
     else:
         POST = request.POST
-        access = POST.get('access')
-        site.access = access
+        message = 'Changes saved.' # default
+
+        if 'action_access_level' in POST:
+            access = POST.get('access')
+            site.access = access
+
+        elif 'action_add_group' in POST:
+            section = POST.get('section', '').strip()
+            groupcode = None
+            if section:
+                groupcode = callhook('derive_group_code_from_section', 
+                                     site, section)
+            if not groupcode:
+                groupcode = POST.get('groupcode','').strip()
+            
+            if not groupcode:
+                message = 'No group code or section number provided.'
+            else:
+                group, created = models.Group.objects.get_or_create(
+                    site=site, external_id=groupcode)
+                
         site.save()
-
-        return HttpResponseRedirect('../../')
+        return g.render('edit_site_permissions.xhtml', **locals())
 
 @instructors_only
 def delete_site(request, site_id):
index 9ce6469..8979498 100644 (file)
@@ -1,8 +1,9 @@
 <?python
+from django.db.models import Count
 title = _('Site permissions')
 instructors = site.get_instructors()
 members = models.Membership.objects.select_related().filter(group__site=site).order_by('user__last_name', 'user__first_name')
-extgroups = site.group_set.filter(external_id__isnull=False)
+extgroups = site.group_set.filter(external_id__isnull=False).annotate(Count('membership')).order_by('external_id')
 ?>
 <html xmlns="http://www.w3.org/1999/xhtml"
       xmlns:xi="http://www.w3.org/2001/XInclude"
@@ -14,18 +15,54 @@ extgroups = site.group_set.filter(external_id__isnull=False)
   <script type="text/javascript" src="${ROOT}/static/edit_site_permissions.js"/>
   <style type="text/css">
     #access_level li { list-style-type: none; }
+       .popup { background-color: #ddd; padding: 24; margin: 0 24; }
+       #message { background-color: #fdd; padding: 12; display: inline-block; }
   </style>
 </head>
 <body>
   ${site_banner(site)}
   <h1>${title}</h1>
+  <div py:if="defined('message')" id="message">${message}</div>
   <form action="." method="POST">
-    <p>Who has permission to view resources in this site?</p>
-    <div id="access_level" style="margin-left: 12;">${Markup(choose_access.render('access', site.access, {'id':'id_access'}, []))}</div>
-    <p><input type="submit" name="action_access_level" value="Change access level"/>
-    ${go_back_link()}</p>
+       <div>
+         <h2>Security level</h2>
+         <div>Who has permission to view resources in this site?</div>
+         <div id="access_level" style="margin: 8;">${Markup(choose_access.render('access', site.access, {'id':'id_access'}, []))}</div>
+         <p><input type="submit" name="action_access_level" value="Change security level"/>
+         ${go_back_link()}</p>
+       </div>
 
-    <h2>Current Membership</h2>
+       <div>
+      <h2>Class lists and groups</h2>
+         <div py:if="not extgroups">No external lists are currently associated.</div>
+
+         <table class="pagetable" py:if="extgroups">
+               <thead>
+                 <tr><th>Class/group code</th><th>Known members</th></tr>
+               </thead>
+               <tbody>
+                 <tr py:for="eg in extgroups">
+                       <td>${eg.external_id}</td>
+                       <td>${eg.membership__count}</td>
+                 </tr>
+               </tbody>
+         </table>
+         <div>
+               <p><input type="button" value="Add external group" onclick="$('#addExternalGroupPopup').fadeIn();"/></p>
+               <div class="popup" id="addExternalGroupPopup" style="display: none;">
+                 <li py:if="show_add_section_panel">
+                       Add the class list for ${site.course.code}, section <input name="section" style="width: 2em;"/>, ${site.start_term.name}
+                 </li>
+                 <li>
+                       Add a class list or group by its group code: <input name="groupcode" style="width: 15em;"/>
+                 </li>
+                 <p><input type="submit" name="action_add_group" value="Add class list or group"/></p>
+               </div>
+         </div>
+         
+    </div>
+    <div>
+    <h2>Current membership</h2>
     <table class="pagetable" style="width: 100%;">
       <thead>
        <tr><th>Name</th><th>Role</th><th>User ID</th><th>Group</th></tr>
@@ -38,39 +75,7 @@ extgroups = site.group_set.filter(external_id__isnull=False)
        <td>${member.group.external_id or '(internal)'}</td>
       </tr>
     </table>
-    <div py:if="extgroups">
-      <h2>External Groups</h2>
-      <p py:for="eg in extgroups">${eg.external_id}</p>
-    </div>
-
-    <!-- <h2>Instructor Access</h2> -->
-    <!-- <div py:if="defined('instructor_error')" class="errors">${instructor_error}</div> -->
-    <!-- <table class="pagetable"> -->
-    <!--   <thead> -->
-    <!--       <tr><th>Person</th><th>Role</th><th>Remove?</th></tr> -->
-    <!--   </thead> -->
-    <!--   <tbody> -->
-    <!--       <select py:def="select_role(mbr)"  -->
-    <!--               py:replace="Markup(django.forms.Select(choices= -->
-    <!--                           [(u'INSTR',_('Instructor')), (u'PROXY', _('Proxy instructor'))]) -->
-    <!--                           .render('instructor_role_%d' % mbr.id, mbr.role))"/> -->
-    <!--   <tr py:for="mbr in instructors"> -->
-    <!--       <td>${mbr.user.get_full_name() or ''} <code>(${mbr.user.username})</code></td> -->
-    <!--       <td>${select_role(mbr)}</td> -->
-    <!--       <td><input type="checkbox" name="instructor_remove_${mbr.id}"/></td> -->
-    <!--   </tr> -->
-    <!--   <tr style="vertical-align: bottom;"> -->
-    <!--       <td><p>Username of the new instructor.</p><input type="text" name="new_instructor_name"/></td> -->
-    <!--       <td><select name="new_instructor_role"> -->
-    <!--         <option value="INSTR">Instructor</option> -->
-    <!--         <option value="PROXY">Proxy instructor</option> -->
-    <!--         </select> -->
-    <!--       </td> -->
-    <!--       <td/> -->
-    <!--   </tr> -->
-    <!--   </tbody> -->
-    <!-- </table> -->
-    <!-- <p>     <input type="submit" name="action_save_instructor" value="Save changes to instructors"/></p> -->
+       </div>
   </form>
   <div class="gap"/>
 </body>