better edit-course-permissions screen, separated from edit-course-details.
authorgfawcett <gfawcett@6d9bc8c9-1ec2-4278-b937-99fde70a366f>
Sun, 8 Mar 2009 19:08:31 +0000 (19:08 +0000)
committergfawcett <gfawcett@6d9bc8c9-1ec2-4278-b937-99fde70a366f>
Sun, 8 Mar 2009 19:08:31 +0000 (19:08 +0000)
Still no support for course-sections; coming soon.

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

conifer/genshi_namespace.py
conifer/static/edit_course_permissions.js [new file with mode: 0644]
conifer/static/main.css
conifer/syrup/views.py
conifer/templates/add_new_course.xhtml
conifer/templates/edit_course_permissions.xhtml

index 1022c05..bb003e5 100644 (file)
@@ -5,6 +5,7 @@
 
 from itertools import cycle
 from conifer.syrup import models
+import django.forms
 
 # this probably ought to be a method on User, or another model class.
 def instructor_url(instructor, suffix=''):
diff --git a/conifer/static/edit_course_permissions.js b/conifer/static/edit_course_permissions.js
new file mode 100644 (file)
index 0000000..92bd861
--- /dev/null
@@ -0,0 +1,12 @@
+function do_init() {
+    show_specific_only();
+    $('#id_access').change(show_specific_only);
+}
+
+function show_specific_only() {
+    $('.specific').hide();
+    var cur = $('#id_access').val();
+    $('#' + cur + '_panel').fadeIn();
+}
+
+$(do_init);
\ No newline at end of file
index 0699b7f..7560fec 100644 (file)
@@ -49,10 +49,15 @@ min-height: 300;
     border-bottom: white 10px solid;
 }
 
+/* heading sizes and colours. */
+
 h1 { font-size: 150%;  }
 h2 { font-size: 135%; }
-h1, h2, h3, h4 { color: navy; }
+h3 { font-size: 120%; }
 
+h1 { color: navy; }
+h2 { color: #066; }
+h3, h4 { color: darkgreen; }
 h1 a, h2 a { color: navy; }
 
 a { color: blue; text-decoration: none; }
@@ -209,3 +214,7 @@ p.todo, div.todo { background-color: #fdd; padding: 6; margin: 12; border-left:
     font-weight: normal; 
 }
 
+.gap { height: 24; }
+
+/* panels that appear when specific OPTIONs or radio-buttons are selected. */
+.specific { padding: 8; margin: 0 16; background-color: #eef; }
index cb9693e..7f2da57 100644 (file)
@@ -9,6 +9,7 @@ import re
 from conifer.syrup import models
 from django.contrib.auth.models import User
 from django.db.models import Q
+import django.forms
 from datetime import datetime
 from generics import *
 from gettext import gettext as _ # fixme, is this the right function to import?
@@ -136,7 +137,7 @@ def course_search(request, course_id):
 class NewCourseForm(ModelForm):
     class Meta:
         model = models.Course
-        exclude = ('passkey',)
+        exclude = ('passkey','access')
 
     def clean_code(self):
         v = (self.cleaned_data.get('code') or '').strip()
@@ -146,14 +147,6 @@ class NewCourseForm(ModelForm):
         else:
             raise ValidationError, _('invalid course code')
 
-# I want different choice labels on the access screen than are given
-# in the model.
-NewCourseForm.base_fields['access'].widget.choices = [
-    (u'CLOSE', _(u'For now, just myself and designated colleagues')),
-    (u'STUDT', _(u'Students in my course (I will provide section numbers)')),
-    (u'INVIT', _(u'Students in my course (I will share an Invitation Code with them)')),
-    (u'LOGIN', _(u'All Reserves patrons'))]
-
 
 # hack the new-course form if we have course-code lookup
 COURSE_CODE_LIST = bool(models.course_codes.course_code_list)
@@ -217,14 +210,73 @@ def add_new_course_ajax_title(request):
 
 def edit_course_permissions(request, course_id):
     course = get_object_or_404(models.Course, pk=course_id)
+    choose_access = django.forms.Select(choices=[
+        (u'CLOSE', _(u'No students: this site is closed.')),
+        (u'STUDT', _(u'Students in my course -- I will provide section numbers')),
+        (u'INVIT', _(u'Students in my course -- I will share an Invitation Code with them')),
+        (u'LOGIN', _(u'All Reserves patrons'))])
     if request.method != 'POST':
         return g.render('edit_course_permissions.xhtml', **locals())
     else:
-        if request.POST.get('action') == 'change_code':
+        POST = request.POST
+
+        if 'action_change_code' in POST:
+            # update invitation code -------------------------------------
             course.generate_new_passkey()
+            course.access = u'INVIT'
             course.save()
             return HttpResponseRedirect('.')
 
+        elif 'action_save_instructor' in POST:
+            # update instructor details ----------------------------------
+            iname = POST.get('new_instructor_name','').strip()
+            irole = POST.get('new_instructor_role')
+
+            def get_record_for(username):
+                instr = models.maybe_initialize_user(iname)
+                if instr:
+                    try:
+                        return models.Member.objects.get(user=instr, course=course)
+                    except models.Member.DoesNotExist:
+                        return models.Member.objects.create(user=instr, course=course)
+
+            # add a new instructor
+            if iname:
+                instr = get_record_for(iname)
+                if instr:       # else? should have an error.
+                    instr.role = irole
+                    instr.save()
+                else:
+                    instructor_error = 'No such user: %s' % iname
+                    return g.render('edit_course_permissions.xhtml', **locals())
+
+
+            # removing and changing roles of instructors
+            to_change_role = [(int(name.rsplit('_', 1)[-1]), POST[name]) \
+                                  for name in POST if name.startswith('instructor_role_')]
+            to_remove = [int(name.rsplit('_', 1)[-1]) \
+                             for name in POST if name.startswith('instructor_remove_')]
+            for instr_id, newrole in to_change_role:
+                if not instr_id in to_remove:
+                    instr = models.Member.objects.get(pk=instr_id, course=course)
+                    instr.role = newrole
+                    instr.save()
+            for instr_id in to_remove:
+                # todo, should warn if deleting yourself!
+                instr = models.Member.objects.get(pk=instr_id, course=course)
+                instr.delete()
+            # todo, should have some error-reporting.
+            return HttpResponseRedirect('.')
+
+        elif 'action_save_student' in POST:
+            # update student details ------------------------------------
+            access = POST.get('access')
+            course.access = access
+            course.save()
+            if course.access == u'STUDT':
+                raise NotImplementedError, 'No course sections yet! Coming soon.'
+            return HttpResponseRedirect('.')
+            
 #------------------------------------------------------------
 
 @login_required                 # must be, to avoid/audit brute force attacks.
index bf6bf7d..19d83de 100644 (file)
@@ -8,15 +8,15 @@ else:
       xmlns:xi="http://www.w3.org/2001/XInclude"
       xmlns:py="http://genshi.edgewall.org/">
 <xi:include href="master.xhtml"/>
+<xi:include href="components/course.xhtml"/>
 <head>
   <title>${title}</title>
   <script type="text/javascript" src="/static/add_new_course.js"/>
 </head>
 <body>
+  <div py:if="instance.id">${course_banner(instance)}</div>
   <h1>${title}</h1>
-  <ul py:for="fld in form" py:if="fld.errors">
-    <li py:for="err in fld.errors">${fld.name}: ${err}</li>
-  </ul>
+  <p py:if="instance.id"><a href="permission/">Edit course permissions</a> &bull; <a href="${instance.course_url()}">Return to course page</a></p>
   <form action="." method="POST">
     <tr py:def="field_row(field, example=None)">
       <th>${field.label}</th>
@@ -28,8 +28,7 @@ else:
       </td>
       <td class="example" py:if="example">e.g., ${example}</td>
     </tr>
-  <fieldset>
-    <legend>General description</legend>
+    <h2>General description</h2>
     <table class="formtable">
     ${field_row(form.code, example)}
     ${field_row(form.title)}
@@ -37,12 +36,6 @@ else:
     ${field_row(form.department)}
     <!-- <tr><th>Department</th><td>${Markup(form.department)} ${errorlist(form.department)}</td></tr> -->
   </table>
-  </fieldset>
-  <fieldset>
-    <legend>Access controls</legend>
-    <p>This site will be available to:</p>
-    ${Markup(form.access)}
-  </fieldset>
   <p><input type="submit" value="Continue"/></p>
   </form>
 
index 21bc498..06b4b8d 100644 (file)
@@ -1,27 +1,60 @@
 <?python
 title = _('Edit course permissions')
+instructors = [m for m in models.Member.objects.filter(course=course) if m.role in ('PROXY', 'INSTR')]
 ?>
 <html xmlns="http://www.w3.org/1999/xhtml"
       xmlns:xi="http://www.w3.org/2001/XInclude"
       xmlns:py="http://genshi.edgewall.org/">
 <xi:include href="master.xhtml"/>
+  <xi:include href="components/course.xhtml"/>
 <head>
   <title>${title}</title>
+  <script type="text/javascript" src="/static/edit_course_permissions.js"/>
 </head>
 <body>
+  ${course_banner(course)}
   <h1>${title}</h1>
-  <h2>Add Co-Instructors and Proxies</h2>
-  <p>blah blah...</p>    
-  <div py:choose="course.access">
-    <div py:when="'INVIT'">
-      <h2>Course Invitation Code</h2>
-      <p style="font-size: larger;">Your Course Invitation Code is: <strong>${course.passkey}</strong></p>
-      <form action="." method="POST">
-       <p>
-         <input type="hidden" name="action" value="change_code"/>
-         <input type="submit" value="Select a new code (this will invalidate the old code!)"/>
-       </p>
-       </form>
+  <p><a href="../">Edit course details</a> &bull; <a href="${course.course_url()}">Return to course page</a></p>
+  <form action="." method="POST">
+    <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',_(u'Instructor')), (u'PROXY', _(u'Proxy instructor'))])
+                           .render('instructor_role_%d' % mbr.id, mbr.role))"/>
+      <tr py:for="mbr in instructors">
+       <td>${mbr.user.get_full_name() or mbr.user}</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 class="gap"/>
+    <h2>Student Access</h2>
+    <p>Who will have student-level access to this site?
+    <span style="margin-left: 12;"/>${Markup(choose_access.render('access', course.access, {'id':'id_access'}, []))}</p>
+    <div id="INVIT_panel" class="specific">
+      <h3>Course Invitation Code</h3>
+      <p style="font-size: larger;">Your Course Invitation Code is: <strong>${course.passkey}</strong>
+      <span style="margin-left: 16;">
+         <input type="submit" name="action_change_code" value="Select a new code"/>
+      </span>
+      </p>
       <p>This invitation code will enable your students to join this
       course site. Share it only with your students: anyone who has
       the code can join your site.</p>
@@ -29,10 +62,19 @@ title = _('Edit course permissions')
       students who have already joined, but will prevent new students
       from joining with the old code.</p>
     </div>
-    <div py:when="'STUDT'">
-      <h2>Course section numbers</h2>
+    <div id="STUDT_panel" class="specific">
+      <h3>Course section numbers</h3>
+      <table>
+       <tr py:for="x in range(3)"><td><input type="text"/></td></tr>
+      </table>
     </div>
-  </div>
+    <p><input type="submit" name="action_save_student" value="Save changes to student access"/></p>
+
+    <div class="gap"/>
+    <h2>Class List</h2>
+    <p>The following users have student-level access in this course site.</p>
+
+  </form>
   <div class="gap"/>
 </body>
 </html>