Commit f1f6acb5 by cahrens

Updates to admin table view.

Also hooks up the granted and deny events to the course creator group.
parent 2970f242
...@@ -3,7 +3,6 @@ from django.core.exceptions import PermissionDenied ...@@ -3,7 +3,6 @@ from django.core.exceptions import PermissionDenied
from django.conf import settings from django.conf import settings
from xmodule.modulestore import Location from xmodule.modulestore import Location
from course_creators import views
''' '''
This code is somewhat duplicative of access.py in the LMS. We will unify the code as a separate story This code is somewhat duplicative of access.py in the LMS. We will unify the code as a separate story
...@@ -215,13 +214,30 @@ def _grant_instructors_creator_access(caller): ...@@ -215,13 +214,30 @@ def _grant_instructors_creator_access(caller):
This is to be called only by either a command line code path or through an app which has already This is to be called only by either a command line code path or through an app which has already
asserted permissions to do this action. asserted permissions to do this action.
Gives all users with instructor role course creator rights. Gives all users with instructor role course creator rights, and returns the set of
such users.
This is only intended to be run once on a given environment. This is only intended to be run once on a given environment.
""" """
instructors = _get_users_with_role(INSTRUCTOR_ROLE_NAME)
for user in instructors:
add_user_to_creator_group(caller, user)
return instructors
def get_users_with_staff_role():
"""
Returns all users with the role 'staff'
"""
return _get_users_with_role(STAFF_ROLE_NAME)
def _get_users_with_role(role):
"""
Returns all users with the specified role.
"""
users = set()
for group in Group.objects.all(): for group in Group.objects.all():
if group.name.startswith(INSTRUCTOR_ROLE_NAME + "_"): if group.name.startswith(role + "_"):
for user in group.user_set.all(): for user in group.user_set.all():
add_user_to_creator_group(caller, user) users.add(user)
views.add_user_with_status_granted(user) return users
elif group.name.startswith(STAFF_ROLE_NAME + "_"):
views.add_user_with_status_unrequested(user)
...@@ -9,7 +9,8 @@ from django.core.exceptions import PermissionDenied ...@@ -9,7 +9,8 @@ from django.core.exceptions import PermissionDenied
from auth.authz import add_user_to_creator_group, remove_user_from_creator_group, is_user_in_creator_group,\ from auth.authz import add_user_to_creator_group, remove_user_from_creator_group, is_user_in_creator_group,\
create_all_course_groups, add_user_to_course_group, STAFF_ROLE_NAME, INSTRUCTOR_ROLE_NAME,\ create_all_course_groups, add_user_to_course_group, STAFF_ROLE_NAME, INSTRUCTOR_ROLE_NAME,\
is_user_in_course_group_role, remove_user_from_course_group, _grant_instructors_creator_access is_user_in_course_group_role, remove_user_from_course_group, _grant_instructors_creator_access,\
get_users_with_staff_role
class CreatorGroupTest(TestCase): class CreatorGroupTest(TestCase):
...@@ -175,6 +176,18 @@ class CourseGroupTest(TestCase): ...@@ -175,6 +176,18 @@ class CourseGroupTest(TestCase):
with self.assertRaises(PermissionDenied): with self.assertRaises(PermissionDenied):
remove_user_from_course_group(self.staff, self.staff, self.location, STAFF_ROLE_NAME) remove_user_from_course_group(self.staff, self.staff, self.location, STAFF_ROLE_NAME)
def test_get_staff(self):
# Do this test with staff in 2 different classes.
create_all_course_groups(self.creator, self.location)
add_user_to_course_group(self.creator, self.staff, self.location, STAFF_ROLE_NAME)
location2 = 'i4x', 'mitX', '103', 'course2', 'test2'
staff2 = User.objects.create_user('teststaff2', 'teststaff+courses@edx.org', 'foo')
create_all_course_groups(self.creator, location2)
add_user_to_course_group(self.creator, staff2, location2, STAFF_ROLE_NAME)
self.assertSetEqual({self.staff, staff2, self.creator}, get_users_with_staff_role())
class GrantInstructorsCreatorAccessTest(TestCase): class GrantInstructorsCreatorAccessTest(TestCase):
""" """
...@@ -206,7 +219,8 @@ class GrantInstructorsCreatorAccessTest(TestCase): ...@@ -206,7 +219,8 @@ class GrantInstructorsCreatorAccessTest(TestCase):
admin = User.objects.create_user('populate_creators_command', 'grant+creator+access@edx.org', 'foo') admin = User.objects.create_user('populate_creators_command', 'grant+creator+access@edx.org', 'foo')
admin.is_staff = True admin.is_staff = True
_grant_instructors_creator_access(admin) instructors = _grant_instructors_creator_access(admin)
self.assertSetEqual({creator1, creator2}, instructors)
# Now instructors only are creators. # Now instructors only are creators.
self.assertTrue(is_user_in_creator_group(creator1)) self.assertTrue(is_user_in_creator_group(creator1))
......
...@@ -3,7 +3,8 @@ Script for granting existing course instructors course creator privileges. ...@@ -3,7 +3,8 @@ Script for granting existing course instructors course creator privileges.
This script is only intended to be run once on a given environment. This script is only intended to be run once on a given environment.
""" """
from auth.authz import _grant_instructors_creator_access from auth.authz import _grant_instructors_creator_access, get_users_with_staff_role
from course_creators.views import add_user_with_status_granted, add_user_with_status_unrequested
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.contrib.auth.models import User from django.contrib.auth.models import User
...@@ -31,5 +32,18 @@ class Command(BaseCommand): ...@@ -31,5 +32,18 @@ class Command(BaseCommand):
# the admin user will already exist. # the admin user will already exist.
admin = User.objects.get(username=username, email=email) admin = User.objects.get(username=username, email=email)
_grant_instructors_creator_access(admin) creators = _grant_instructors_creator_access(admin)
for user in creators:
add_user_with_status_granted(user)
# Some users will be both staff and instructors. Those folks have been
# added with status granted above, and add_user_with_status_unrequested
# will not try to add them again if they already exist in the course creator database.
for user in get_users_with_staff_role():
add_user_with_status_unrequested(user)
# There could be users who are not in either staff or instructor (they've
# never actually done anything in Studio). I plan to add those as unrequested
# when they first go to their dashboard.
admin.delete() admin.delete()
...@@ -7,7 +7,22 @@ from django.contrib import admin ...@@ -7,7 +7,22 @@ from django.contrib import admin
class CourseCreatorAdmin(admin.ModelAdmin): class CourseCreatorAdmin(admin.ModelAdmin):
list_display = ('username', 'state', 'state_changed', 'note') # Fields to display on the overview page.
list_display = ('username', 'email', 'state', 'state_changed')
readonly_fields = ('username', 'email', 'state_changed')
# Controls the order on the edit form (without this, read-only fields appear at the end).
fieldsets = (
(None, {
'fields': list_display
}),
)
# Fields that filtering support
list_filter = list_display
# Fields that search supports. Note that the search term for state has to be
# its key (ie, 'g' instead of 'granted').
search_fields = ['username', 'email', 'state']
# Turn off the action bar (we have no bulk actions)
actions = None
def has_add_permission(self, request): def has_add_permission(self, request):
return False return False
...@@ -18,5 +33,10 @@ class CourseCreatorAdmin(admin.ModelAdmin): ...@@ -18,5 +33,10 @@ class CourseCreatorAdmin(admin.ModelAdmin):
def has_change_permission(self, request, obj=None): def has_change_permission(self, request, obj=None):
return request.user.is_staff return request.user.is_staff
def save_model(self, request, obj, form, change):
# Store who is making the request.
obj.admin = request.user
obj.save()
admin.site.register(CourseCreator, CourseCreatorAdmin) admin.site.register(CourseCreator, CourseCreatorAdmin)
...@@ -11,6 +11,7 @@ class Migration(SchemaMigration): ...@@ -11,6 +11,7 @@ class Migration(SchemaMigration):
# Adding model 'CourseCreator' # Adding model 'CourseCreator'
db.create_table('course_creators_coursecreator', ( db.create_table('course_creators_coursecreator', (
('username', self.gf('django.db.models.fields.CharField')(unique=True, max_length=64, primary_key=True)), ('username', self.gf('django.db.models.fields.CharField')(unique=True, max_length=64, primary_key=True)),
('email', self.gf('django.db.models.fields.CharField')(max_length=128)),
('state_changed', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), ('state_changed', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
('state', self.gf('django.db.models.fields.CharField')(default='u', max_length=1)), ('state', self.gf('django.db.models.fields.CharField')(default='u', max_length=1)),
('note', self.gf('django.db.models.fields.CharField')(max_length=512, blank=True)), ('note', self.gf('django.db.models.fields.CharField')(max_length=512, blank=True)),
...@@ -26,6 +27,7 @@ class Migration(SchemaMigration): ...@@ -26,6 +27,7 @@ class Migration(SchemaMigration):
models = { models = {
'course_creators.coursecreator': { 'course_creators.coursecreator': {
'Meta': {'object_name': 'CourseCreator'}, 'Meta': {'object_name': 'CourseCreator'},
'email': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'note': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}), 'note': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'state': ('django.db.models.fields.CharField', [], {'default': "'u'", 'max_length': '1'}), 'state': ('django.db.models.fields.CharField', [], {'default': "'u'", 'max_length': '1'}),
'state_changed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), 'state_changed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
......
...@@ -4,7 +4,7 @@ Table for storing information about whether or not Studio users have course crea ...@@ -4,7 +4,7 @@ Table for storing information about whether or not Studio users have course crea
from django.db import models from django.db import models
from django.db.models.signals import post_init, post_save from django.db.models.signals import post_init, post_save
from django.dispatch import receiver from django.dispatch import receiver
from auth.authz import add_user_to_creator_group, remove_user_from_creator_group from auth.authz import add_user_to_creator_group, remove_user_from_creator_group, get_user_by_email
import datetime import datetime
...@@ -21,6 +21,7 @@ class CourseCreator(models.Model): ...@@ -21,6 +21,7 @@ class CourseCreator(models.Model):
) )
username = models.CharField(max_length=64, blank=False, help_text="Studio username", primary_key=True, unique=True) username = models.CharField(max_length=64, blank=False, help_text="Studio username", primary_key=True, unique=True)
email = models.CharField(max_length=128, blank=False, help_text="Registered e-mail address")
state_changed = models.DateTimeField('state last updated', auto_now_add=True, state_changed = models.DateTimeField('state last updated', auto_now_add=True,
help_text='The date when state was last updated') help_text='The date when state was last updated')
state = models.CharField(max_length=1, blank=False, choices=STATES, default='u', state = models.CharField(max_length=1, blank=False, choices=STATES, default='u',
...@@ -30,14 +31,20 @@ class CourseCreator(models.Model): ...@@ -30,14 +31,20 @@ class CourseCreator(models.Model):
def __unicode__(self): def __unicode__(self):
s = "%s | %s [%s] | %s" % (self.username, self.state, self.state_changed, self.note) s = "%s %s | %s [%s] | %s" % (self.username, self.email, self.state, self.state_changed, self.note)
return s return s
@receiver(post_init, sender=CourseCreator) @receiver(post_init, sender=CourseCreator)
def post_init_callback(sender, **kwargs): def post_init_callback(sender, **kwargs):
instance = kwargs['instance'] instance = kwargs['instance']
instance.orig_state = instance.state user = get_user_by_email(instance.email)
if user is None:
# User has been removed, delete from this table.
instance.delete()
else:
instance.orig_state = instance.state
@receiver(post_save, sender=CourseCreator) @receiver(post_save, sender=CourseCreator)
def post_save_callback(sender, **kwargs): def post_save_callback(sender, **kwargs):
...@@ -46,12 +53,13 @@ def post_save_callback(sender, **kwargs): ...@@ -46,12 +53,13 @@ def post_save_callback(sender, **kwargs):
# modify it for changes to the notes field. # modify it for changes to the notes field.
if instance.state != instance.orig_state: if instance.state != instance.orig_state:
instance.state_changed = datetime.datetime.now() instance.state_changed = datetime.datetime.now()
# We removed bad users in post_init.
user = get_user_by_email(instance.email)
if instance.state == 'g': if instance.state == 'g':
# add to course group # We have granted access, add to course group
instance.state = 'g' add_user_to_creator_group(instance.admin, user)
else: else:
# remove from course group remove_user_from_creator_group(instance.admin, user)
instance.state = instance.state
instance.orig_state = instance.state instance.orig_state = instance.state
instance.save() instance.save()
"""
Methods for interacting programmatically with the user creator table.
"""
from course_creators.models import CourseCreator from course_creators.models import CourseCreator
...@@ -14,7 +17,11 @@ def add_user_with_status_granted(user): ...@@ -14,7 +17,11 @@ def add_user_with_status_granted(user):
""" """
_add_user(user, 'g') _add_user(user, 'g')
def _add_user(user, state): def _add_user(user, state):
"""
Adds a user to the course creator table with the specified state.
"""
if CourseCreator.objects.filter(username=user.username).count() == 0: if CourseCreator.objects.filter(username=user.username).count() == 0:
entry = CourseCreator(username=user.username, state=state) entry = CourseCreator(username=user.username, email=user.email, state=state)
entry.save() entry.save()
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment