Commit bd966321 by Christina Roberts

Merge pull request #345 from edx/christina/course-creator-table

Admin table for course creators.
parents 1dbbbb10 1fd04511
...@@ -5,6 +5,10 @@ These are notable changes in edx-platform. This is a rolling list of changes, ...@@ -5,6 +5,10 @@ These are notable changes in edx-platform. This is a rolling list of changes,
in roughly chronological order, most recent first. Add your entries at or near in roughly chronological order, most recent first. Add your entries at or near
the top. Include a label indicating the component affected. the top. Include a label indicating the component affected.
Studio: Add table for tracking course creator permissions (not yet used).
Update rake django-admin[syncdb] and rake django-admin[migrate] so they
run for both LMS and CMS.
Common: Student information is now passed to the tracking log via POST instead of GET. Common: Student information is now passed to the tracking log via POST instead of GET.
Common: Add tests for documentation generation to test suite Common: Add tests for documentation generation to test suite
......
...@@ -209,15 +209,27 @@ def is_user_in_creator_group(user): ...@@ -209,15 +209,27 @@ def is_user_in_creator_group(user):
return True return True
def _grant_instructors_creator_access(caller): def get_users_with_instructor_role():
""" """
This is to be called only by either a command line code path or through an app which has already Returns all users with the role 'instructor'
asserted permissions to do this action. """
return _get_users_with_role(INSTRUCTOR_ROLE_NAME)
def get_users_with_staff_role():
"""
Returns all users with the role 'staff'
"""
return _get_users_with_role(STAFF_ROLE_NAME)
Gives all users with instructor role course creator rights.
This is only intended to be run once on a given environment. 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)
return users
...@@ -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, get_users_with_staff_role,\
get_users_with_instructor_role
class CreatorGroupTest(TestCase): class CreatorGroupTest(TestCase):
...@@ -175,41 +176,27 @@ class CourseGroupTest(TestCase): ...@@ -175,41 +176,27 @@ 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)
class GrantInstructorsCreatorAccessTest(TestCase): location2 = 'i4x', 'mitX', '103', 'course2', 'test2'
""" staff2 = User.objects.create_user('teststaff2', 'teststaff2+courses@edx.org', 'foo')
Tests granting existing instructors course creator rights. create_all_course_groups(self.creator, location2)
""" add_user_to_course_group(self.creator, staff2, location2, STAFF_ROLE_NAME)
def create_course(self, index):
"""
Creates a course with one instructor and one staff member.
"""
creator = User.objects.create_user('testcreator' + str(index), 'testcreator+courses@edx.org', 'foo')
staff = User.objects.create_user('teststaff' + str(index), 'teststaff+courses@edx.org', 'foo')
location = 'i4x', 'mitX', str(index), 'course', 'test'
create_all_course_groups(creator, location)
add_user_to_course_group(creator, staff, location, STAFF_ROLE_NAME)
return [creator, staff]
def test_grant_creator_access(self): self.assertSetEqual({self.staff, staff2, self.creator}, get_users_with_staff_role())
"""
Test for _grant_instructors_creator_access. def test_get_instructor(self):
""" # Do this test with creators in 2 different classes.
[creator1, staff1] = self.create_course(1) create_all_course_groups(self.creator, self.location)
[creator2, staff2] = self.create_course(2) add_user_to_course_group(self.creator, self.staff, self.location, STAFF_ROLE_NAME)
with mock.patch.dict('django.conf.settings.MITX_FEATURES', {"ENABLE_CREATOR_GROUP": True}):
# Initially no creators. location2 = 'i4x', 'mitX', '103', 'course2', 'test2'
self.assertFalse(is_user_in_creator_group(creator1)) creator2 = User.objects.create_user('testcreator2', 'testcreator2+courses@edx.org', 'foo')
self.assertFalse(is_user_in_creator_group(creator2)) staff2 = User.objects.create_user('teststaff2', 'teststaff2+courses@edx.org', 'foo')
self.assertFalse(is_user_in_creator_group(staff1)) create_all_course_groups(creator2, location2)
self.assertFalse(is_user_in_creator_group(staff2)) add_user_to_course_group(creator2, staff2, location2, STAFF_ROLE_NAME)
admin = User.objects.create_user('populate_creators_command', 'grant+creator+access@edx.org', 'foo') self.assertSetEqual({self.creator, creator2}, get_users_with_instructor_role())
admin.is_staff = True
_grant_instructors_creator_access(admin)
# Now instructors only are creators.
self.assertTrue(is_user_in_creator_group(creator1))
self.assertTrue(is_user_in_creator_group(creator2))
self.assertFalse(is_user_in_creator_group(staff1))
self.assertFalse(is_user_in_creator_group(staff2))
...@@ -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 get_users_with_instructor_role, 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,17 @@ class Command(BaseCommand): ...@@ -31,5 +32,17 @@ 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) for user in get_users_with_instructor_role():
add_user_with_status_granted(admin, 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(admin, 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()
"""
django admin page for the course creators table
"""
from course_creators.models import CourseCreator, update_creator_state
from course_creators.views import update_course_creator_group
from django.contrib import admin
from django.dispatch import receiver
def get_email(obj):
""" Returns the email address for a user """
return obj.user.email
get_email.short_description = 'email'
class CourseCreatorAdmin(admin.ModelAdmin):
"""
Admin for the course creator table.
"""
# Fields to display on the overview page.
list_display = ['user', get_email, 'state', 'state_changed', 'note']
readonly_fields = ['user', 'state_changed']
# Controls the order on the edit form (without this, read-only fields appear at the end).
fieldsets = (
(None, {
'fields': ['user', 'state', 'state_changed', 'note']
}),
)
# Fields that filtering support
list_filter = ['state', 'state_changed']
# Fields that search supports.
search_fields = ['user__username', 'user__email', 'state', 'note']
# Turn off the action bar (we have no bulk actions)
actions = None
def has_add_permission(self, request):
return False
def has_delete_permission(self, request, obj=None):
return False
def has_change_permission(self, request, obj=None):
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)
@receiver(update_creator_state, sender=CourseCreator)
def update_creator_group_callback(sender, **kwargs):
"""
Callback for when the model's creator status has changed.
"""
update_course_creator_group(kwargs['caller'], kwargs['user'], kwargs['add'])
# -*- coding: 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):
# Adding model 'CourseCreator'
db.create_table('course_creators_coursecreator', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], unique=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='unrequested', max_length=24)),
('note', self.gf('django.db.models.fields.CharField')(max_length=512, blank=True)),
))
db.send_create_signal('course_creators', ['CourseCreator'])
def backwards(self, orm):
# Deleting model 'CourseCreator'
db.delete_table('course_creators_coursecreator')
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']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", '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']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'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']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", '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'})
},
'course_creators.coursecreator': {
'Meta': {'object_name': 'CourseCreator'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'note': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'state': ('django.db.models.fields.CharField', [], {'default': "'unrequested'", 'max_length': '24'}),
'state_changed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
}
}
complete_apps = ['course_creators']
\ No newline at end of file
"""
Table for storing information about whether or not Studio users have course creation privileges.
"""
from django.db import models
from django.db.models.signals import post_init, post_save
from django.dispatch import receiver, Signal
from django.contrib.auth.models import User
from django.utils import timezone
from django.utils.translation import ugettext as _
# A signal that will be sent when users should be added or removed from the creator group
update_creator_state = Signal(providing_args=["caller", "user", "add"])
class CourseCreator(models.Model):
"""
Creates the database table model.
"""
UNREQUESTED = 'unrequested'
PENDING = 'pending'
GRANTED = 'granted'
DENIED = 'denied'
# Second value is the "human-readable" version.
STATES = (
(UNREQUESTED, _(u'unrequested')),
(PENDING, _(u'pending')),
(GRANTED, _(u'granted')),
(DENIED, _(u'denied')),
)
user = models.ForeignKey(User, help_text=_("Studio user"), unique=True)
state_changed = models.DateTimeField('state last updated', auto_now_add=True,
help_text=_("The date when state was last updated"))
state = models.CharField(max_length=24, blank=False, choices=STATES, default=UNREQUESTED,
help_text=_("Current course creator state"))
note = models.CharField(max_length=512, blank=True, help_text=_("Optional notes about this user (for example, "
"why course creation access was denied)"))
def __unicode__(self):
return u'%str | %str [%str] | %str' % (self.user, self.state, self.state_changed, self.note)
@receiver(post_init, sender=CourseCreator)
def post_init_callback(sender, **kwargs):
"""
Extend to store previous state.
"""
instance = kwargs['instance']
instance.orig_state = instance.state
@receiver(post_save, sender=CourseCreator)
def post_save_callback(sender, **kwargs):
"""
Extend to update state_changed time and modify the course creator group in authz.py.
"""
instance = kwargs['instance']
# We only wish to modify the state_changed time if the state has been modified. We don't wish to
# modify it for changes to the notes field.
if instance.state != instance.orig_state:
update_creator_state.send(
sender=sender,
caller=instance.admin,
user=instance.user,
add=instance.state == CourseCreator.GRANTED
)
instance.state_changed = timezone.now()
instance.orig_state = instance.state
instance.save()
"""
Tests course_creators.admin.py.
"""
from django.test import TestCase
from django.contrib.auth.models import User
from django.contrib.admin.sites import AdminSite
from django.http import HttpRequest
import mock
from course_creators.admin import CourseCreatorAdmin
from course_creators.models import CourseCreator
from auth.authz import is_user_in_creator_group
class CourseCreatorAdminTest(TestCase):
"""
Tests for course creator admin.
"""
def setUp(self):
""" Test case setup """
self.user = User.objects.create_user('test_user', 'test_user+courses@edx.org', 'foo')
self.table_entry = CourseCreator(user=self.user)
self.table_entry.save()
self.admin = User.objects.create_user('Mark', 'admin+courses@edx.org', 'foo')
self.admin.is_staff = True
self.request = HttpRequest()
self.request.user = self.admin
self.creator_admin = CourseCreatorAdmin(self.table_entry, AdminSite())
def test_change_status(self):
"""
Tests that updates to state impact the creator group maintained in authz.py.
"""
def change_state(state, is_creator):
""" Helper method for changing state """
self.table_entry.state = state
self.creator_admin.save_model(self.request, self.table_entry, None, True)
self.assertEqual(is_creator, is_user_in_creator_group(self.user))
with mock.patch.dict('django.conf.settings.MITX_FEATURES', {"ENABLE_CREATOR_GROUP": True}):
# User is initially unrequested.
self.assertFalse(is_user_in_creator_group(self.user))
change_state(CourseCreator.GRANTED, True)
change_state(CourseCreator.DENIED, False)
change_state(CourseCreator.GRANTED, True)
change_state(CourseCreator.PENDING, False)
change_state(CourseCreator.GRANTED, True)
change_state(CourseCreator.UNREQUESTED, False)
def test_add_permission(self):
"""
Tests that staff cannot add entries
"""
self.assertFalse(self.creator_admin.has_add_permission(self.request))
def test_delete_permission(self):
"""
Tests that staff cannot delete entries
"""
self.assertFalse(self.creator_admin.has_delete_permission(self.request))
def test_change_permission(self):
"""
Tests that only staff can change entries
"""
self.assertTrue(self.creator_admin.has_change_permission(self.request))
self.request.user = self.user
self.assertFalse(self.creator_admin.has_change_permission(self.request))
"""
Tests course_creators.views.py.
"""
from django.test import TestCase
from django.contrib.auth.models import User
from django.core.exceptions import PermissionDenied
from course_creators.views import add_user_with_status_unrequested, add_user_with_status_granted
from course_creators.views import get_course_creator_status, update_course_creator_group
from course_creators.models import CourseCreator
from auth.authz import is_user_in_creator_group
import mock
class CourseCreatorView(TestCase):
"""
Tests for modifying the course creator table.
"""
def setUp(self):
""" Test case setup """
self.user = User.objects.create_user('test_user', 'test_user+courses@edx.org', 'foo')
self.admin = User.objects.create_user('Mark', 'admin+courses@edx.org', 'foo')
self.admin.is_staff = True
def test_staff_permission_required(self):
"""
Tests that add methods and course creator group method must be called with staff permissions.
"""
with self.assertRaises(PermissionDenied):
add_user_with_status_granted(self.user, self.user)
with self.assertRaises(PermissionDenied):
add_user_with_status_unrequested(self.user, self.user)
with self.assertRaises(PermissionDenied):
update_course_creator_group(self.user, self.user, True)
def test_table_initially_empty(self):
self.assertIsNone(get_course_creator_status(self.user))
def test_add_unrequested(self):
add_user_with_status_unrequested(self.admin, self.user)
self.assertEqual('unrequested', get_course_creator_status(self.user))
# Calling add again will be a no-op (even if state is different).
add_user_with_status_granted(self.admin, self.user)
self.assertEqual('unrequested', get_course_creator_status(self.user))
def test_add_granted(self):
with mock.patch.dict('django.conf.settings.MITX_FEATURES', {"ENABLE_CREATOR_GROUP": True}):
# Calling add_user_with_status_granted impacts is_user_in_course_group_role.
self.assertFalse(is_user_in_creator_group(self.user))
add_user_with_status_granted(self.admin, self.user)
self.assertEqual('granted', get_course_creator_status(self.user))
# Calling add again will be a no-op (even if state is different).
add_user_with_status_unrequested(self.admin, self.user)
self.assertEqual('granted', get_course_creator_status(self.user))
self.assertTrue(is_user_in_creator_group(self.user))
def test_update_creator_group(self):
with mock.patch.dict('django.conf.settings.MITX_FEATURES', {"ENABLE_CREATOR_GROUP": True}):
self.assertFalse(is_user_in_creator_group(self.user))
update_course_creator_group(self.admin, self.user, True)
self.assertTrue(is_user_in_creator_group(self.user))
update_course_creator_group(self.admin, self.user, False)
self.assertFalse(is_user_in_creator_group(self.user))
"""
Methods for interacting programmatically with the user creator table.
"""
from course_creators.models import CourseCreator
from django.core.exceptions import PermissionDenied
from auth.authz import add_user_to_creator_group, remove_user_from_creator_group
def add_user_with_status_unrequested(caller, user):
"""
Adds a user to the course creator table with status 'unrequested'.
If the user is already in the table, this method is a no-op
(state will not be changed). Caller must have staff permissions.
"""
_add_user(caller, user, CourseCreator.UNREQUESTED)
def add_user_with_status_granted(caller, user):
"""
Adds a user to the course creator table with status 'granted'.
If the user is already in the table, this method is a no-op
(state will not be changed). Caller must have staff permissions.
This method also adds the user to the course creator group maintained by authz.py.
"""
_add_user(caller, user, CourseCreator.GRANTED)
update_course_creator_group(caller, user, True)
def update_course_creator_group(caller, user, add):
"""
Method for adding and removing users from the creator group.
Caller must have staff permissions.
"""
if add:
add_user_to_creator_group(caller, user)
else:
remove_user_from_creator_group(caller, user)
def get_course_creator_status(user):
"""
Returns the status for a particular user, or None if user is not in the table.
Possible return values are:
'unrequested' = user has not requested course creation rights
'pending' = user has requested course creation rights
'granted' = user has been granted course creation rights
'denied' = user has been denied course creation rights
None = user does not exist in the table
"""
user = CourseCreator.objects.filter(user=user)
if user.count() == 0:
return None
else:
# User is defined to be unique, can assume a single entry.
return user[0].state
def _add_user(caller, user, state):
"""
Adds a user to the course creator table with the specified state.
If the user is already in the table, this method is a no-op
(state will not be changed).
"""
if not caller.is_active or not caller.is_authenticated or not caller.is_staff:
raise PermissionDenied
if CourseCreator.objects.filter(user=user).count() == 0:
entry = CourseCreator(user=user, state=state)
entry.save()
...@@ -331,6 +331,7 @@ INSTALLED_APPS = ( ...@@ -331,6 +331,7 @@ INSTALLED_APPS = (
# For CMS # For CMS
'contentstore', 'contentstore',
'auth', 'auth',
'course_creators',
'student', # misleading name due to sharing with lms 'student', # misleading name due to sharing with lms
'course_groups', # not used in cms (yet), but tests run 'course_groups', # not used in cms (yet), but tests run
...@@ -345,6 +346,9 @@ INSTALLED_APPS = ( ...@@ -345,6 +346,9 @@ INSTALLED_APPS = (
# comment common # comment common
'django_comment_common', 'django_comment_common',
# for course creator table
'django.contrib.admin'
) )
################# EDX MARKETING SITE ################################## ################# EDX MARKETING SITE ##################################
......
...@@ -143,3 +143,6 @@ MITX_FEATURES['ENABLE_SERVICE_STATUS'] = True ...@@ -143,3 +143,6 @@ MITX_FEATURES['ENABLE_SERVICE_STATUS'] = True
# Enabling SQL tracking logs for testing on common/djangoapps/track # Enabling SQL tracking logs for testing on common/djangoapps/track
MITX_FEATURES['ENABLE_SQL_TRACKING_LOGS'] = True MITX_FEATURES['ENABLE_SQL_TRACKING_LOGS'] = True
# This is to disable a test under the common directory that will not pass when run under CMS
MITX_FEATURES['DISABLE_PASSWORD_RESET_EMAIL_TEST'] = True
...@@ -5,9 +5,9 @@ from django.conf.urls import patterns, include, url ...@@ -5,9 +5,9 @@ from django.conf.urls import patterns, include, url
# pylint: disable=W0611 # pylint: disable=W0611
from . import one_time_startup from . import one_time_startup
# Uncomment the next two lines to enable the admin: # There is a course creators admin table.
# from django.contrib import admin from django.contrib import admin
# admin.autodiscover() admin.autodiscover()
urlpatterns = ('', # nopep8 urlpatterns = ('', # nopep8
url(r'^$', 'contentstore.views.howitworks', name='homepage'), url(r'^$', 'contentstore.views.howitworks', name='homepage'),
...@@ -146,6 +146,8 @@ if settings.MITX_FEATURES.get('ENABLE_SERVICE_STATUS'): ...@@ -146,6 +146,8 @@ if settings.MITX_FEATURES.get('ENABLE_SERVICE_STATUS'):
url(r'^status/', include('service_status.urls')), url(r'^status/', include('service_status.urls')),
) )
urlpatterns += (url(r'^admin/', include(admin.site.urls)),)
urlpatterns = patterns(*urlpatterns) urlpatterns = patterns(*urlpatterns)
# Custom error pages # Custom error pages
......
...@@ -9,15 +9,12 @@ import json ...@@ -9,15 +9,12 @@ import json
import re import re
import unittest import unittest
from django import forms
from django.conf import settings from django.conf import settings
from django.test import TestCase from django.test import TestCase
from django.test.client import RequestFactory from django.test.client import RequestFactory
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.auth.hashers import UNUSABLE_PASSWORD from django.contrib.auth.hashers import UNUSABLE_PASSWORD
from django.contrib.auth.tokens import default_token_generator from django.contrib.auth.tokens import default_token_generator
from django.template.loader import render_to_string, get_template, TemplateDoesNotExist
from django.core.urlresolvers import is_valid_path
from django.utils.http import int_to_base36 from django.utils.http import int_to_base36
...@@ -33,12 +30,6 @@ COURSE_2 = 'edx/full/6.002_Spring_2012' ...@@ -33,12 +30,6 @@ COURSE_2 = 'edx/full/6.002_Spring_2012'
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
try:
get_template('registration/password_reset_email.html')
project_uses_password_reset = True
except TemplateDoesNotExist:
project_uses_password_reset = False
class ResetPasswordTests(TestCase): class ResetPasswordTests(TestCase):
""" Tests that clicking reset password sends email, and doesn't activate the user """ Tests that clicking reset password sends email, and doesn't activate the user
...@@ -75,7 +66,7 @@ class ResetPasswordTests(TestCase): ...@@ -75,7 +66,7 @@ class ResetPasswordTests(TestCase):
self.assertEquals(bad_email_resp.content, json.dumps({'success': False, self.assertEquals(bad_email_resp.content, json.dumps({'success': False,
'error': 'Invalid e-mail or user'})) 'error': 'Invalid e-mail or user'}))
@unittest.skipUnless(project_uses_password_reset, @unittest.skipUnless(not settings.MITX_FEATURES.get('DISABLE_PASSWORD_RESET_EMAIL_TEST', False),
dedent("""Skipping Test because CMS has not provided necessary templates for password reset. dedent("""Skipping Test because CMS has not provided necessary templates for password reset.
If LMS tests print this message, that needs to be fixed.""")) If LMS tests print this message, that needs to be fixed."""))
@patch('django.core.mail.send_mail') @patch('django.core.mail.send_mail')
......
...@@ -57,18 +57,17 @@ task :resetdb, [:env] do |t, args| ...@@ -57,18 +57,17 @@ task :resetdb, [:env] do |t, args|
sh(django_admin(:lms, args.env, 'migrate')) sh(django_admin(:lms, args.env, 'migrate'))
end end
desc "Update the relational database to the latest migration"
task :migrate, [:env] do |t, args|
args.with_defaults(:env => 'dev')
sh(django_admin(:lms, args.env, 'migrate'))
end
task :runserver => :lms task :runserver => :lms
desc "Run django-admin <action> against the specified system and environment" desc "Run django-admin <action> against the specified system and environment"
task "django-admin", [:action, :system, :env, :options] do |t, args| task "django-admin", [:action, :system, :env, :options] do |t, args|
# If no system was explicitly set, we want to run both CMS and LMS for migrate and syncdb.
no_system_set = !args.system
args.with_defaults(:env => 'dev', :system => 'lms', :options => '') args.with_defaults(:env => 'dev', :system => 'lms', :options => '')
sh(django_admin(args.system, args.env, args.action, args.options)) sh(django_admin(args.system, args.env, args.action, args.options))
if no_system_set and (args.action == 'migrate' or args.action == 'syncdb')
sh(django_admin('cms', args.env, args.action, args.options))
end
end end
desc "Set the staff bit for a user" desc "Set the staff bit for a user"
......
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