[open-ils-commits] r927 - in servres/trunk/conifer: syrup syrup/migrations syrup/views templates (gfawcett)
svn at svn.open-ils.org
svn at svn.open-ils.org
Fri Jul 16 13:38:27 EDT 2010
Author: gfawcett
Date: 2010-07-16 13:38:23 -0400 (Fri, 16 Jul 2010)
New Revision: 927
Added:
servres/trunk/conifer/syrup/migrations/0003_auto__del_field_site_passkey.py
Modified:
servres/trunk/conifer/syrup/models.py
servres/trunk/conifer/syrup/views/sites.py
servres/trunk/conifer/templates/edit_site_permissions.xhtml
Log:
Access-control model changes. Simplistic site-permisisons screen. Schema changes.
The site-permissions screen is unfinished; need ways to add
individuals and external groups. Right now it's mainly informational.
I've taken out the passkey (invitation-code) system for now; it could
easily be reimplemented in terms of a Group with a 'passkey:NNN'
external ID.
Added: servres/trunk/conifer/syrup/migrations/0003_auto__del_field_site_passkey.py
===================================================================
--- servres/trunk/conifer/syrup/migrations/0003_auto__del_field_site_passkey.py (rev 0)
+++ servres/trunk/conifer/syrup/migrations/0003_auto__del_field_site_passkey.py 2010-07-16 17:38:23 UTC (rev 927)
@@ -0,0 +1,170 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Deleting field 'Site.passkey'
+ db.delete_column('syrup_site', 'passkey')
+
+
+ def backwards(self, orm):
+
+ # Adding field 'Site.passkey'
+ db.add_column('syrup_site', 'passkey', self.gf('django.db.models.fields.CharField')(blank=True, max_length=256, null=True, db_index=True), keep_default=False)
+
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'syrup.config': {
+ 'Meta': {'object_name': 'Config'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '256'}),
+ 'value': ('django.db.models.fields.CharField', [], {'max_length': '8192'})
+ },
+ 'syrup.course': {
+ 'Meta': {'object_name': 'Course'},
+ 'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'department': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['syrup.Department']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'last_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '1024'})
+ },
+ 'syrup.department': {
+ 'Meta': {'object_name': 'Department'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'last_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
+ 'service_desk': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['syrup.ServiceDesk']"})
+ },
+ 'syrup.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'external_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'last_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['syrup.Site']"})
+ },
+ 'syrup.item': {
+ 'Meta': {'object_name': 'Item'},
+ 'author': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '8192', 'null': 'True', 'blank': 'True'}),
+ 'bib_id': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'fileobj': ('django.db.models.fields.files.FileField', [], {'default': 'None', 'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'fileobj_mimetype': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'item_type': ('django.db.models.fields.CharField', [], {'max_length': '7'}),
+ 'itemtype': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '1', 'null': 'True', 'blank': 'True'}),
+ 'last_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'marcxml': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'parent_heading': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['syrup.Item']", 'null': 'True', 'blank': 'True'}),
+ 'published': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
+ 'publisher': ('django.db.models.fields.CharField', [], {'max_length': '8192', 'null': 'True', 'blank': 'True'}),
+ 'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['syrup.Site']"}),
+ 'title': ('django.db.models.fields.CharField', [], {'max_length': '8192', 'db_index': 'True'}),
+ 'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
+ },
+ 'syrup.membership': {
+ 'Meta': {'unique_together': "(('group', 'user'),)", 'object_name': 'Membership'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['syrup.Group']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'last_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'role': ('django.db.models.fields.CharField', [], {'default': "'STUDT'", 'max_length': '6'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'syrup.servicedesk': {
+ 'Meta': {'object_name': 'ServiceDesk'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'external_id': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'last_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'})
+ },
+ 'syrup.site': {
+ 'Meta': {'unique_together': "(('course', 'term', 'owner'),)", 'object_name': 'Site'},
+ 'access': ('django.db.models.fields.CharField', [], {'default': "'LOGIN'", 'max_length': '5'}),
+ 'course': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['syrup.Course']"}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'last_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'service_desk': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['syrup.ServiceDesk']"}),
+ 'term': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['syrup.Term']"})
+ },
+ 'syrup.term': {
+ 'Meta': {'object_name': 'Term'},
+ 'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64'}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'finish': ('django.db.models.fields.DateField', [], {}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'last_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '256'}),
+ 'start': ('django.db.models.fields.DateField', [], {})
+ },
+ 'syrup.userprofile': {
+ 'Meta': {'object_name': 'UserProfile'},
+ 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ils_userid': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
+ 'last_email_notice': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'null': 'True', 'blank': 'True'}),
+ 'last_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
+ 'wants_email_notices': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'})
+ },
+ 'syrup.z3950target': {
+ 'Meta': {'object_name': 'Z3950Target'},
+ 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+ 'database': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'host': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'port': ('django.db.models.fields.IntegerField', [], {'default': '210'}),
+ 'syntax': ('django.db.models.fields.CharField', [], {'default': "'USMARC'", 'max_length': '10'})
+ }
+ }
+
+ complete_apps = ['syrup']
Modified: servres/trunk/conifer/syrup/models.py
===================================================================
--- servres/trunk/conifer/syrup/models.py 2010-07-16 17:38:20 UTC (rev 926)
+++ servres/trunk/conifer/syrup/models.py 2010-07-16 17:38:23 UTC (rev 927)
@@ -48,7 +48,7 @@
bool(callhook('can_create_sites', self))
def get_list_name(self):
- return '%s, %s (%s)' % (self.last_name, self.first_name, self.username)
+ return '%s, %s' % (self.last_name, self.first_name)
@classmethod
def active_instructors(cls):
@@ -164,19 +164,23 @@
choices = [
('ANON', _('World-accessible')),
('LOGIN', _('Accessible to all logged-in users')),
- ('STUDT', _('Accessible to course students (by section)')),
- ('INVIT', _('Accessible to course students (by invitation code)')),
+ ('MEMBR', _('Accessible to course-site members')),
('CLOSE', _('Accessible only to course-site owners'))])
- # For sites that use a passkey as an invitation (INVIT access).
- # Note: only set this value using 'generate_new_passkey'.
- # TODO: for postgres, add UNIQUE constraint on 'passkey'.
- passkey = m.CharField(db_index=True, blank=True, null=True, max_length=256)
-
class Meta:
unique_together = (('course', 'term', 'owner'))
ordering = ['-term__start', 'course__code']
+ def save(self, *args, **kwargs):
+ # Ensure there is always an internal Group.
+ super(Site, self).save(*args, **kwargs)
+ internal, just_created = Group.objects.get_or_create(
+ site=self, external_id=None)
+ # ..and that the owner is an instructor in the site.
+ Membership.objects.get_or_create(group = internal,
+ user = self.owner,
+ defaults = {'role':'INSTR'})
+
def __unicode__(self):
return u'%s: %s (%s, %s)' % (
self.course.code, self.course.name,
@@ -184,7 +188,8 @@
self.term.name)
def list_display(self):
- return '%s [%s, %s]' % (self.course.name, self.course.code, self.term.name)
+ return '%s [%s, %s]' % (self.course.name, self.course.code,
+ self.term.name)
def items(self):
return self.item_set.all()
@@ -293,7 +298,6 @@
or user.is_staff \
or self.is_member(user)
-
#--------------------------------------------------
@classmethod
Modified: servres/trunk/conifer/syrup/views/sites.py
===================================================================
--- servres/trunk/conifer/syrup/views/sites.py 2010-07-16 17:38:20 UTC (rev 926)
+++ servres/trunk/conifer/syrup/views/sites.py 2010-07-16 17:38:23 UTC (rev 927)
@@ -7,7 +7,7 @@
class NewSiteForm(ModelForm):
class Meta:
model = models.Site
- exclude = ('passkey','access')
+ exclude = ('access',)
def clean_code(self):
v = (self.cleaned_data.get('code') or '').strip()
@@ -53,9 +53,6 @@
else:
form.save()
site = form.instance
- if site.access == u'INVIT' and not site.passkey:
- site.generate_new_passkey()
- site.save()
assert site.id
if is_add or (current_access_level != site.access):
@@ -70,31 +67,20 @@
# choices: make the access-choice labels more personalized than
# the ones in 'models'.
choices = [
- # note: I'm leaving ANON out for now, until we discuss it further.
- (u'ANON', _(u'Anyone on the planet may access this site.')),
- (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'))]
- # TODO: fixme, campus module no longer exists.
- if models.campus.sections_tuple_delimiter is None:
- # no course-sections support? Then STUDT cannot be an option.
- del choices[1]
- choose_access = django.forms.Select(choices=choices)
+ (u'ANON', _(u'Everyone: no login required.')),
+ (u'LOGIN', _(u'Members and non-members: login required.')),
+ (u'MEMBR', _(u'Members only.')),
+ (u'CLOSE', _(u'Instructors only: this site is closed.')),
+ ]
+
+ choose_access = django.forms.RadioSelect(choices=choices)
if request.method != 'POST':
return g.render('edit_site_permissions.xhtml', **locals())
else:
POST = request.POST
- if 'action_change_code' in POST:
- # update invitation code -------------------------------------
- site.generate_new_passkey()
- site.access = u'INVIT'
- site.save()
- return HttpResponseRedirect('.#student_access')
-
- elif 'action_save_instructor' in POST:
+ if 'action_save_instructor' in POST:
# update instructor details ----------------------------------
iname = POST.get('new_instructor_name','').strip()
irole = POST.get('new_instructor_role')
@@ -181,32 +167,8 @@
@login_required # must be, to avoid/audit brute force attacks.
def site_invitation(request):
- if request.method != 'POST':
- return g.render('site_invitation.xhtml', code='', error='',
- **locals())
- else:
- code = request.POST.get('code', '').strip()
- # todo, a pluggable passkey implementation would normalize the code here.
- if not code:
- return HttpResponseRedirect('.')
- try:
- # note, we only allow the passkey if access='INVIT'.
- crs = models.Site.objects.filter(access='INVIT').get(passkey=code)
- except models.Site.DoesNotExist:
- # todo, do we need a formal logging system? Or a table for
- # invitation failures? They should be captured somehow, I
- # think. Should we temporarily disable accounts after
- # multiple failures?
- error = _('The code you provided is not valid.')
- return g.render('site_invitation.xhtml', **locals())
+ raise NotImplementedError
- # the passkey is good; add the user if not already a member.
- if not models.Membership.objects.filter(user=request.user, site=crs):
- mbr = models.Membership.objects.create(user=request.user, site=crs,
- role='STUDT')
- mbr.save()
- return HttpResponseRedirect(crs.site_url())
-
#-----------------------------------------------------------------------------
# Site-instance handlers
Modified: servres/trunk/conifer/templates/edit_site_permissions.xhtml
===================================================================
--- servres/trunk/conifer/templates/edit_site_permissions.xhtml 2010-07-16 17:38:20 UTC (rev 926)
+++ servres/trunk/conifer/templates/edit_site_permissions.xhtml 2010-07-16 17:38:23 UTC (rev 927)
@@ -1,6 +1,8 @@
<?python
title = _('Edit 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)
?>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xi="http://www.w3.org/2001/XInclude"
@@ -10,72 +12,66 @@
<head>
<title>${title}</title>
<script type="text/javascript" src="${ROOT}/static/edit_site_permissions.js"/>
+ <style type="text/css">
+ #access_level li { list-style-type: none; }
+ </style>
</head>
<body>
${site_banner(site)}
<h1>${title}</h1>
<p><a href="../">Edit site details</a> • <a href="${site.site_url()}">Return to site 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">
+ <h2>Site Access Level</h2>
+ <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_save_student" value="Save changes to student access"/></p>
+
+ <h2>Current Membership</h2>
+ <table class="pagetable" style="width: 100%;">
<thead>
- <tr><th>Person</th><th>Role</th><th>Remove?</th></tr>
+ <tr><th>Name</th><th>Role</th><th>User ID</th><th>Group</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 py:for="member in members"
+ style="${'' if member.user.is_active else 'text-decoration: line-through;'}">
+ <td>${member.user.get_list_name()}</td>
+ <td>${member.get_role_display()}</td>
+ <td>${member.user.username}</td>
+ <td>${member.group.external_id or '(internal)'}</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 id="student_access">Student Access</h2>
- <p>Who will have student-level access to this site?
- <span style="margin-left: 12;"/>${Markup(choose_access.render('access', site.access, {'id':'id_access'}, []))}</p>
- <div id="INVIT_panel" class="specific">
- <h3>Site Invitation Code</h3>
- <p style="font-size: larger;">Your Site Invitation Code is: <strong>${site.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
- site. Share it only with your students: anyone who has
- the code can join your site.</p>
- <p>You may change the code at any time. This will not block
- students who have already joined, but will prevent new students
- from joining with the old code.</p>
+ <div py:if="extgroups">
+ <h2>External Groups</h2>
+ <p py:for="eg in extgroups">${eg.external_id}</p>
</div>
- <div id="STUDT_panel" class="specific">
- <h3>Course section numbers</h3>
- <p>Not implemented yet.</p>
- </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 site.</p>
- <ol>
- <li py:for="student in site.get_students()">
- ${student.get_full_name()} (${student.email})
- </li>
- </ol>
+ <!-- <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> -->
</form>
<div class="gap"/>
</body>
More information about the open-ils-commits
mailing list