Merge remote-tracking branch 'origin/release' into dj18-release-merge
Conflicts: common/djangoapps/util/testing.py lms/djangoapps/instructor/views/api.py lms/djangoapps/teams/tests/test_views.py openedx/core/djangoapps/programs/models.py openedx/core/djangoapps/user_api/accounts/tests/test_views.py requirements/edx/github.txt
This commit is contained in:
@@ -1,2 +0,0 @@
|
||||
""" module init will register signal handlers """
|
||||
import contentstore.signals
|
||||
|
||||
@@ -15,7 +15,7 @@ config.readfp(config_file)
|
||||
|
||||
def doc_url(request=None): # pylint: disable=unused-argument
|
||||
"""
|
||||
This function is added in the list of TEMPLATE_CONTEXT_PROCESSORS, which is a django setting for
|
||||
This function is added in the list of TEMPLATES 'context_processors' OPTION, which is a django setting for
|
||||
a tuple of callables that take a request object as their argument and return a dictionary of items
|
||||
to be merged into the RequestContext.
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import re
|
||||
from six import add_metaclass
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy, ugettext as _
|
||||
from django.core.urlresolvers import resolve
|
||||
|
||||
from contentstore.utils import course_image_url
|
||||
@@ -350,7 +350,7 @@ class CoursewareSearchIndexer(SearchIndexerBase):
|
||||
'category': 'courseware_index'
|
||||
}
|
||||
|
||||
UNNAMED_MODULE_NAME = _("(Unnamed)")
|
||||
UNNAMED_MODULE_NAME = ugettext_lazy("(Unnamed)")
|
||||
|
||||
@classmethod
|
||||
def normalize_structure_key(cls, structure_key):
|
||||
@@ -417,7 +417,7 @@ class CoursewareSearchIndexer(SearchIndexerBase):
|
||||
while parent is not None:
|
||||
path_component_name = parent.display_name
|
||||
if not path_component_name:
|
||||
path_component_name = cls.UNNAMED_MODULE_NAME
|
||||
path_component_name = unicode(cls.UNNAMED_MODULE_NAME)
|
||||
location_path.append(path_component_name)
|
||||
parent = parent.get_parent()
|
||||
location_path.reverse()
|
||||
|
||||
@@ -14,18 +14,19 @@ class Command(BaseCommand):
|
||||
|commit|: optional argument. If not provided, will not run task.
|
||||
'''
|
||||
|
||||
def handle(self, *args, **options):
|
||||
if len(args) not in {1, 2}:
|
||||
raise CommandError("delete_orphans requires one or more arguments: <course_id> |commit|")
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('course_id')
|
||||
parser.add_argument('--commit', action='store')
|
||||
|
||||
def handle(self, *args, **options):
|
||||
try:
|
||||
course_key = CourseKey.from_string(args[0])
|
||||
course_key = CourseKey.from_string(options['course_id'])
|
||||
except InvalidKeyError:
|
||||
raise CommandError("Invalid course key.")
|
||||
|
||||
commit = False
|
||||
if len(args) == 2:
|
||||
commit = args[1] == 'commit'
|
||||
if options['commit']:
|
||||
commit = options['commit'] == 'commit'
|
||||
|
||||
if commit:
|
||||
print 'Deleting orphans from the course:'
|
||||
|
||||
@@ -18,23 +18,24 @@ class Command(BaseCommand):
|
||||
"""
|
||||
help = 'Export the specified data directory into the default ModuleStore'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
"Execute the command"
|
||||
if len(args) != 2:
|
||||
raise CommandError("export requires two arguments: <course id> <output path>")
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('course_id')
|
||||
parser.add_argument('output_path')
|
||||
|
||||
def handle(self, *args, **options):
|
||||
"""Execute the command"""
|
||||
try:
|
||||
course_key = CourseKey.from_string(args[0])
|
||||
course_key = CourseKey.from_string(options['course_id'])
|
||||
except InvalidKeyError:
|
||||
try:
|
||||
course_key = SlashSeparatedCourseKey.from_deprecated_string(args[0])
|
||||
course_key = SlashSeparatedCourseKey.from_deprecated_string(options['course_id'])
|
||||
except InvalidKeyError:
|
||||
raise CommandError("Invalid course_key: '%s'. " % args[0])
|
||||
raise CommandError("Invalid course_key: '%s'." % options['course_id'])
|
||||
|
||||
if not modulestore().get_course(course_key):
|
||||
raise CommandError("Course with %s key not found." % args[0])
|
||||
raise CommandError("Course with %s key not found." % options['course_id'])
|
||||
|
||||
output_path = args[1]
|
||||
output_path = options['output_path']
|
||||
|
||||
print "Exporting course id = {0} to {1}".format(course_key, output_path)
|
||||
|
||||
|
||||
@@ -13,12 +13,14 @@ class Command(BaseCommand):
|
||||
"""Fix a course's item not found errors"""
|
||||
help = "Fix a course's ItemNotFound errors"
|
||||
|
||||
def handle(self, *args, **options):
|
||||
"Execute the command"
|
||||
if len(args) != 1:
|
||||
raise CommandError("requires 1 argument: <course_id>")
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('course_id')
|
||||
|
||||
course_key = CourseKey.from_string(args[0])
|
||||
def handle(self, *args, **options):
|
||||
"""Execute the command"""
|
||||
course_id = options.get('course_id', None)
|
||||
|
||||
course_key = CourseKey.from_string(course_id)
|
||||
# for now only support on split mongo
|
||||
# pylint: disable=protected-access
|
||||
owning_store = modulestore()._get_modulestore_for_courselike(course_key)
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
"""
|
||||
Script for importing courseware from XML format
|
||||
"""
|
||||
from optparse import make_option
|
||||
|
||||
from django.core.management.base import BaseCommand, CommandError, make_option
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django_comment_common.utils import (seed_permissions_roles,
|
||||
are_permissions_roles_seeded)
|
||||
from xmodule.modulestore.xml_importer import import_course_from_xml
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Tests running the delete_orphan command"""
|
||||
|
||||
import ddt
|
||||
from django.core.management import call_command
|
||||
from django.core.management import call_command, CommandError
|
||||
from contentstore.tests.test_orphan import TestOrphanBase
|
||||
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
@@ -14,6 +14,13 @@ class TestDeleteOrphan(TestOrphanBase):
|
||||
Tests for running the delete_orphan management command.
|
||||
Inherits from TestOrphan in order to use its setUp method.
|
||||
"""
|
||||
def test_no_args(self):
|
||||
"""
|
||||
Test delete_orphans command with no arguments
|
||||
"""
|
||||
with self.assertRaisesRegexp(CommandError, 'Error: too few arguments'):
|
||||
call_command('delete_orphans')
|
||||
|
||||
@ddt.data(ModuleStoreEnum.Type.split, ModuleStoreEnum.Type.mongo)
|
||||
def test_delete_orphans_no_commit(self, default_store):
|
||||
"""
|
||||
@@ -35,7 +42,7 @@ class TestDeleteOrphan(TestOrphanBase):
|
||||
"""
|
||||
course = self.create_course_with_orphans(default_store)
|
||||
|
||||
call_command('delete_orphans', unicode(course.id), 'commit')
|
||||
call_command('delete_orphans', unicode(course.id), commit='commit')
|
||||
|
||||
# make sure this module wasn't deleted
|
||||
self.assertTrue(self.store.has_item(course.id.make_usage_key('html', 'multi_parent_html')))
|
||||
@@ -59,7 +66,7 @@ class TestDeleteOrphan(TestOrphanBase):
|
||||
|
||||
# call delete orphans, specifying the published branch
|
||||
# of the course
|
||||
call_command('delete_orphans', unicode(published_branch), 'commit')
|
||||
call_command('delete_orphans', unicode(published_branch), commit='commit')
|
||||
|
||||
# now all orphans should be deleted
|
||||
self.assertOrphanCount(course.id, 0)
|
||||
|
||||
@@ -24,11 +24,9 @@ class TestArgParsingCourseExport(unittest.TestCase):
|
||||
"""
|
||||
Test export command with no arguments
|
||||
"""
|
||||
errstring = "export requires two arguments: <course id> <output path>"
|
||||
with self.assertRaises(SystemExit) as ex:
|
||||
with self.assertRaisesRegexp(CommandError, errstring):
|
||||
call_command('export')
|
||||
self.assertEqual(ex.exception.code, 1)
|
||||
errstring = "Error: too few arguments"
|
||||
with self.assertRaisesRegexp(CommandError, errstring):
|
||||
call_command('export')
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@@ -59,11 +57,9 @@ class TestCourseExport(ModuleStoreTestCase):
|
||||
"Could not find course in {}".format(store)
|
||||
)
|
||||
# Test `export` management command with invalid course_id
|
||||
errstring = "Invalid course_key 'InvalidCourseID'."
|
||||
with self.assertRaises(SystemExit) as ex:
|
||||
with self.assertRaisesRegexp(CommandError, errstring):
|
||||
call_command('export', "InvalidCourseID", self.temp_dir_1)
|
||||
self.assertEqual(ex.exception.code, 1)
|
||||
errstring = "Invalid course_key: 'InvalidCourseID'."
|
||||
with self.assertRaisesRegexp(CommandError, errstring):
|
||||
call_command('export', "InvalidCourseID", self.temp_dir_1)
|
||||
|
||||
# Test `export` management command with correct course_id
|
||||
for output_dir in [self.temp_dir_1, self.temp_dir_2]:
|
||||
@@ -74,7 +70,5 @@ class TestCourseExport(ModuleStoreTestCase):
|
||||
Test export command with a valid course key that doesn't exist
|
||||
"""
|
||||
errstring = "Course with x/y/z key not found."
|
||||
with self.assertRaises(SystemExit) as ex:
|
||||
with self.assertRaisesRegexp(CommandError, errstring):
|
||||
call_command('export', "x/y/z", self.temp_dir_1)
|
||||
self.assertEqual(ex.exception.code, 1)
|
||||
with self.assertRaisesRegexp(CommandError, errstring):
|
||||
call_command('export', "x/y/z", self.temp_dir_1)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Tests for the fix_not_found management command
|
||||
"""
|
||||
|
||||
from django.core.management import call_command
|
||||
from django.core.management import CommandError, call_command
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
|
||||
@@ -12,12 +12,19 @@ class TestFixNotFound(ModuleStoreTestCase):
|
||||
"""
|
||||
Tests for the fix_not_found management command
|
||||
"""
|
||||
def test_no_args(self):
|
||||
"""
|
||||
Test fix_not_found command with no arguments
|
||||
"""
|
||||
with self.assertRaisesRegexp(CommandError, "Error: too few arguments"):
|
||||
call_command('fix_not_found')
|
||||
|
||||
def test_fix_not_found_non_split(self):
|
||||
"""
|
||||
The management command doesn't work on non split courses
|
||||
"""
|
||||
course = CourseFactory.create(default_store=ModuleStoreEnum.Type.mongo)
|
||||
with self.assertRaises(SystemExit):
|
||||
with self.assertRaisesRegexp(CommandError, "The owning modulestore does not support this command."):
|
||||
call_command("fix_not_found", unicode(course.id))
|
||||
|
||||
def test_fix_not_found(self):
|
||||
|
||||
@@ -56,57 +56,33 @@ class TestGitExport(CourseTestCase):
|
||||
Test that the command interface works. Ignore stderr for clean
|
||||
test output.
|
||||
"""
|
||||
with self.assertRaises(SystemExit) as ex:
|
||||
with self.assertRaisesRegexp(CommandError, 'This script requires.*'):
|
||||
call_command('git_export', 'blah', 'blah', 'blah',
|
||||
stderr=StringIO.StringIO())
|
||||
self.assertEqual(ex.exception.code, 1)
|
||||
with self.assertRaisesRegexp(CommandError, 'This script requires.*'):
|
||||
call_command('git_export', 'blah', 'blah', 'blah', stderr=StringIO.StringIO())
|
||||
|
||||
with self.assertRaises(SystemExit) as ex:
|
||||
with self.assertRaisesRegexp(CommandError, 'This script requires.*'):
|
||||
call_command('git_export', stderr=StringIO.StringIO())
|
||||
self.assertEqual(ex.exception.code, 1)
|
||||
with self.assertRaisesRegexp(CommandError, 'This script requires.*'):
|
||||
call_command('git_export', stderr=StringIO.StringIO())
|
||||
|
||||
# Send bad url to get course not exported
|
||||
with self.assertRaises(SystemExit) as ex:
|
||||
with self.assertRaisesRegexp(CommandError, unicode(GitExportError.URL_BAD)):
|
||||
call_command('git_export', 'foo/bar/baz', 'silly',
|
||||
stderr=StringIO.StringIO())
|
||||
self.assertEqual(ex.exception.code, 1)
|
||||
with self.assertRaisesRegexp(CommandError, unicode(GitExportError.URL_BAD)):
|
||||
call_command('git_export', 'foo/bar/baz', 'silly', stderr=StringIO.StringIO())
|
||||
|
||||
# Send bad course_id to get course not exported
|
||||
with self.assertRaises(SystemExit) as ex:
|
||||
with self.assertRaisesRegexp(CommandError, unicode(GitExportError.BAD_COURSE)):
|
||||
call_command('git_export', 'foo/bar:baz', 'silly',
|
||||
stderr=StringIO.StringIO())
|
||||
self.assertEqual(ex.exception.code, 1)
|
||||
with self.assertRaisesRegexp(CommandError, unicode(GitExportError.BAD_COURSE)):
|
||||
call_command('git_export', 'foo/bar:baz', 'silly', stderr=StringIO.StringIO())
|
||||
|
||||
def test_error_output(self):
|
||||
"""
|
||||
Verify that error output is actually resolved as the correct string
|
||||
"""
|
||||
output = StringIO.StringIO()
|
||||
with self.assertRaises(SystemExit):
|
||||
with self.assertRaisesRegexp(CommandError, unicode(GitExportError.BAD_COURSE)):
|
||||
call_command(
|
||||
'git_export', 'foo/bar:baz', 'silly',
|
||||
stdout=output, stderr=output
|
||||
)
|
||||
self.assertIn('Bad course location provided', output.getvalue())
|
||||
output.close()
|
||||
with self.assertRaisesRegexp(CommandError, unicode(GitExportError.BAD_COURSE)):
|
||||
call_command(
|
||||
'git_export', 'foo/bar:baz', 'silly'
|
||||
)
|
||||
|
||||
output = StringIO.StringIO()
|
||||
with self.assertRaises(SystemExit):
|
||||
with self.assertRaisesRegexp(CommandError, unicode(GitExportError.URL_BAD)):
|
||||
call_command(
|
||||
'git_export', 'foo/bar/baz', 'silly',
|
||||
stdout=output, stderr=output
|
||||
)
|
||||
self.assertIn(
|
||||
'Non writable git url provided. Expecting something like:'
|
||||
' git@github.com:mitocw/edx4edx_lite.git',
|
||||
output.getvalue()
|
||||
)
|
||||
output.close()
|
||||
with self.assertRaisesRegexp(CommandError, unicode(GitExportError.URL_BAD)):
|
||||
call_command(
|
||||
'git_export', 'foo/bar/baz', 'silly'
|
||||
)
|
||||
|
||||
def test_bad_git_url(self):
|
||||
"""
|
||||
|
||||
@@ -6,7 +6,6 @@ import mock
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from common.test.utils import nostderr
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, LibraryFactory
|
||||
|
||||
from contentstore.management.commands.reindex_course import Command as ReindexCommand
|
||||
@@ -48,36 +47,30 @@ class TestReindexCourse(ModuleStoreTestCase):
|
||||
|
||||
def test_given_no_arguments_raises_command_error(self):
|
||||
""" Test that raises CommandError for incorrect arguments """
|
||||
with self.assertRaises(SystemExit), nostderr():
|
||||
with self.assertRaisesRegexp(CommandError, ".* requires one or more arguments .*"):
|
||||
call_command('reindex_course')
|
||||
with self.assertRaisesRegexp(CommandError, ".* requires one or more arguments.*"):
|
||||
call_command('reindex_course')
|
||||
|
||||
@ddt.data('qwerty', 'invalid_key', 'xblock-v1:qwe+rty')
|
||||
@ddt.data('qwerty', 'invalid_key', 'xblockv1:qwerty')
|
||||
def test_given_invalid_course_key_raises_not_found(self, invalid_key):
|
||||
""" Test that raises InvalidKeyError for invalid keys """
|
||||
errstring = "Invalid course_key: '%s'." % invalid_key
|
||||
with self.assertRaises(SystemExit) as ex:
|
||||
with self.assertRaisesRegexp(CommandError, errstring):
|
||||
call_command('reindex_course', invalid_key)
|
||||
self.assertEqual(ex.exception.code, 1)
|
||||
err_string = "Invalid course_key: '{0}'".format(invalid_key)
|
||||
with self.assertRaisesRegexp(CommandError, err_string):
|
||||
call_command('reindex_course', invalid_key)
|
||||
|
||||
def test_given_library_key_raises_command_error(self):
|
||||
""" Test that raises CommandError if library key is passed """
|
||||
with self.assertRaises(SystemExit), nostderr():
|
||||
with self.assertRaisesRegexp(SearchIndexingError, ".* is not a course key"):
|
||||
call_command('reindex_course', unicode(self._get_lib_key(self.first_lib)))
|
||||
with self.assertRaisesRegexp(CommandError, ".* is not a course key"):
|
||||
call_command('reindex_course', unicode(self._get_lib_key(self.first_lib)))
|
||||
|
||||
with self.assertRaises(SystemExit), nostderr():
|
||||
with self.assertRaisesRegexp(SearchIndexingError, ".* is not a course key"):
|
||||
call_command('reindex_course', unicode(self._get_lib_key(self.second_lib)))
|
||||
with self.assertRaisesRegexp(CommandError, ".* is not a course key"):
|
||||
call_command('reindex_course', unicode(self._get_lib_key(self.second_lib)))
|
||||
|
||||
with self.assertRaises(SystemExit), nostderr():
|
||||
with self.assertRaisesRegexp(SearchIndexingError, ".* is not a course key"):
|
||||
call_command(
|
||||
'reindex_course',
|
||||
unicode(self.second_course.id),
|
||||
unicode(self._get_lib_key(self.first_lib))
|
||||
)
|
||||
with self.assertRaisesRegexp(CommandError, ".* is not a course key"):
|
||||
call_command(
|
||||
'reindex_course',
|
||||
unicode(self.second_course.id),
|
||||
unicode(self._get_lib_key(self.first_lib))
|
||||
)
|
||||
|
||||
def test_given_id_list_indexes_courses(self):
|
||||
""" Test that reindexes courses when given single course key or a list of course keys """
|
||||
|
||||
@@ -6,7 +6,6 @@ import mock
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.modulestore.django import modulestore
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from common.test.utils import nostderr
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, LibraryFactory
|
||||
|
||||
from opaque_keys import InvalidKeyError
|
||||
@@ -50,9 +49,8 @@ class TestReindexLibrary(ModuleStoreTestCase):
|
||||
|
||||
def test_given_no_arguments_raises_command_error(self):
|
||||
""" Test that raises CommandError for incorrect arguments """
|
||||
with self.assertRaises(SystemExit), nostderr():
|
||||
with self.assertRaisesRegexp(CommandError, ".* requires one or more arguments .*"):
|
||||
call_command('reindex_library')
|
||||
with self.assertRaisesRegexp(CommandError, ".* requires one or more arguments.*"):
|
||||
call_command('reindex_library')
|
||||
|
||||
@ddt.data('qwerty', 'invalid_key', 'xblock-v1:qwe+rty')
|
||||
def test_given_invalid_lib_key_raises_not_found(self, invalid_key):
|
||||
@@ -62,21 +60,18 @@ class TestReindexLibrary(ModuleStoreTestCase):
|
||||
|
||||
def test_given_course_key_raises_command_error(self):
|
||||
""" Test that raises CommandError if course key is passed """
|
||||
with self.assertRaises(SystemExit), nostderr():
|
||||
with self.assertRaisesRegexp(CommandError, ".* is not a library key"):
|
||||
call_command('reindex_library', unicode(self.first_course.id))
|
||||
with self.assertRaisesRegexp(CommandError, ".* is not a library key"):
|
||||
call_command('reindex_library', unicode(self.first_course.id))
|
||||
|
||||
with self.assertRaises(SystemExit), nostderr():
|
||||
with self.assertRaisesRegexp(CommandError, ".* is not a library key"):
|
||||
call_command('reindex_library', unicode(self.second_course.id))
|
||||
with self.assertRaisesRegexp(CommandError, ".* is not a library key"):
|
||||
call_command('reindex_library', unicode(self.second_course.id))
|
||||
|
||||
with self.assertRaises(SystemExit), nostderr():
|
||||
with self.assertRaisesRegexp(CommandError, ".* is not a library key"):
|
||||
call_command(
|
||||
'reindex_library',
|
||||
unicode(self.second_course.id),
|
||||
unicode(self._get_lib_key(self.first_lib))
|
||||
)
|
||||
with self.assertRaisesRegexp(CommandError, ".* is not a library key"):
|
||||
call_command(
|
||||
'reindex_library',
|
||||
unicode(self.second_course.id),
|
||||
unicode(self._get_lib_key(self.first_lib))
|
||||
)
|
||||
|
||||
def test_given_id_list_indexes_libraries(self):
|
||||
""" Test that reindexes libraries when given single library key or a list of library keys """
|
||||
|
||||
@@ -1,80 +1,43 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding model 'VideoUploadConfig'
|
||||
db.create_table('contentstore_videouploadconfig', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('change_date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
|
||||
('changed_by', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, on_delete=models.PROTECT)),
|
||||
('enabled', self.gf('django.db.models.fields.BooleanField')(default=False)),
|
||||
('profile_whitelist', self.gf('django.db.models.fields.TextField')(blank=True)),
|
||||
('status_whitelist', self.gf('django.db.models.fields.TextField')(blank=True)),
|
||||
))
|
||||
db.send_create_signal('contentstore', ['VideoUploadConfig'])
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
if not db.dry_run:
|
||||
orm.VideoUploadConfig.objects.create(
|
||||
profile_whitelist="desktop_mp4,desktop_webm,mobile_low,youtube",
|
||||
status_whitelist="Uploading,In Progress,Complete,Failed,Invalid Token,Unknown"
|
||||
)
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting model 'VideoUploadConfig'
|
||||
db.delete_table('contentstore_videouploadconfig')
|
||||
|
||||
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'})
|
||||
},
|
||||
'contentstore.videouploadconfig': {
|
||||
'Meta': {'object_name': 'VideoUploadConfig'},
|
||||
'change_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.PROTECT'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'profile_whitelist': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'status_whitelist': ('django.db.models.fields.TextField', [], {'blank': 'True'})
|
||||
},
|
||||
'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'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['contentstore']
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='PushNotificationConfig',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('change_date', models.DateTimeField(auto_now_add=True, verbose_name='Change date')),
|
||||
('enabled', models.BooleanField(default=False, verbose_name='Enabled')),
|
||||
('changed_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, editable=False, to=settings.AUTH_USER_MODEL, null=True, verbose_name='Changed by')),
|
||||
],
|
||||
options={
|
||||
'ordering': ('-change_date',),
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='VideoUploadConfig',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('change_date', models.DateTimeField(auto_now_add=True, verbose_name='Change date')),
|
||||
('enabled', models.BooleanField(default=False, verbose_name='Enabled')),
|
||||
('profile_whitelist', models.TextField(help_text=b'A comma-separated list of names of profiles to include in video encoding downloads.', blank=True)),
|
||||
('changed_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, editable=False, to=settings.AUTH_USER_MODEL, null=True, verbose_name='Changed by')),
|
||||
],
|
||||
options={
|
||||
'ordering': ('-change_date',),
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
# -*- 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):
|
||||
# Deleting field 'VideoUploadConfig.status_whitelist'
|
||||
db.delete_column('contentstore_videouploadconfig', 'status_whitelist')
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Adding field 'VideoUploadConfig.status_whitelist'
|
||||
db.add_column('contentstore_videouploadconfig', 'status_whitelist',
|
||||
self.gf('django.db.models.fields.TextField')(default='', blank=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']", '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'})
|
||||
},
|
||||
'contentstore.videouploadconfig': {
|
||||
'Meta': {'object_name': 'VideoUploadConfig'},
|
||||
'change_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.PROTECT'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'profile_whitelist': ('django.db.models.fields.TextField', [], {'blank': 'True'})
|
||||
},
|
||||
'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'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['contentstore']
|
||||
@@ -1,80 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from south.utils import datetime_utils as 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 'PushNotificationConfig'
|
||||
db.create_table('contentstore_pushnotificationconfig', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('change_date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
|
||||
('changed_by', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, on_delete=models.PROTECT)),
|
||||
('enabled', self.gf('django.db.models.fields.BooleanField')(default=False)),
|
||||
))
|
||||
db.send_create_signal('contentstore', ['PushNotificationConfig'])
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting model 'PushNotificationConfig'
|
||||
db.delete_table('contentstore_pushnotificationconfig')
|
||||
|
||||
|
||||
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'})
|
||||
},
|
||||
'contentstore.pushnotificationconfig': {
|
||||
'Meta': {'object_name': 'PushNotificationConfig'},
|
||||
'change_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.PROTECT'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||
},
|
||||
'contentstore.videouploadconfig': {
|
||||
'Meta': {'object_name': 'VideoUploadConfig'},
|
||||
'change_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.PROTECT'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'profile_whitelist': ('django.db.models.fields.TextField', [], {'blank': 'True'})
|
||||
},
|
||||
'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'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['contentstore']
|
||||
2
cms/djangoapps/contentstore/startup.py
Normal file
2
cms/djangoapps/contentstore/startup.py
Normal file
@@ -0,0 +1,2 @@
|
||||
""" will register signal handlers, moved out of __init__.py to ensure correct loading order post Django 1.7 """
|
||||
from . import signals # pylint: disable=unused-import
|
||||
@@ -187,7 +187,7 @@ def _get_entrance_exam(request, course_key): # pylint: disable=W0613
|
||||
exam_descriptor = modulestore().get_item(exam_key)
|
||||
return HttpResponse(
|
||||
escape_json_dumps({'locator': unicode(exam_descriptor.location)}),
|
||||
status=200, mimetype='application/json')
|
||||
status=200, content_type='application/json')
|
||||
except ItemNotFoundError:
|
||||
return HttpResponse(status=404)
|
||||
|
||||
|
||||
@@ -301,7 +301,7 @@ def xblock_view_handler(request, usage_key_string, view_name):
|
||||
|
||||
hashed_resources = OrderedDict()
|
||||
for resource in fragment.resources:
|
||||
hashed_resources[hash_resource(resource)] = resource
|
||||
hashed_resources[hash_resource(resource)] = resource._asdict()
|
||||
|
||||
return JsonResponse({
|
||||
'html': fragment.content,
|
||||
|
||||
@@ -38,7 +38,7 @@ from .session_kv_store import SessionKeyValueStore
|
||||
from .helpers import render_from_lms
|
||||
|
||||
from contentstore.views.access import get_user_role
|
||||
from cms.djangoapps.xblock_config.models import StudioConfig
|
||||
from xblock_config.models import StudioConfig
|
||||
|
||||
__all__ = ['preview_handler']
|
||||
|
||||
|
||||
@@ -610,12 +610,12 @@ class TestCourseReIndex(CourseTestCase):
|
||||
response = non_staff_client.get(index_url, {}, HTTP_ACCEPT='application/json')
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
def test_content_type_none(self):
|
||||
def test_empty_content_type(self):
|
||||
"""
|
||||
Test json content type is set if none is selected
|
||||
Test json content type is set if '' is selected
|
||||
"""
|
||||
index_url = reverse_course_url('course_search_index_handler', self.course.id)
|
||||
response = self.client.get(index_url, {}, CONTENT_TYPE=None)
|
||||
response = self.client.get(index_url, {}, CONTENT_TYPE='')
|
||||
|
||||
# A course with the default release date should display as "Unscheduled"
|
||||
self.assertIn(self.SUCCESSFUL_RESPONSE, response.content)
|
||||
|
||||
@@ -17,7 +17,7 @@ from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from contentstore.views.preview import get_preview_fragment, _preview_module_system
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.modulestore.tests.test_asides import AsideTestType
|
||||
from cms.djangoapps.xblock_config.models import StudioConfig
|
||||
from xblock_config.models import StudioConfig
|
||||
from xmodule.modulestore.django import modulestore
|
||||
|
||||
|
||||
|
||||
@@ -1,74 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
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'])
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
|
||||
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']
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CourseCreator',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('state_changed', models.DateTimeField(help_text='The date when state was last updated', verbose_name=b'state last updated', auto_now_add=True)),
|
||||
('state', models.CharField(default=b'unrequested', help_text='Current course creator state', max_length=24, choices=[(b'unrequested', 'unrequested'), (b'pending', 'pending'), (b'granted', 'granted'), (b'denied', 'denied')])),
|
||||
('note', models.CharField(help_text='Optional notes about this user (for example, why course creation access was denied)', max_length=512, blank=True)),
|
||||
('user', models.OneToOneField(to=settings.AUTH_USER_MODEL, help_text='Studio user')),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
||||
@@ -36,7 +36,7 @@ class CourseCreator(models.Model):
|
||||
(DENIED, _(u'denied')),
|
||||
)
|
||||
|
||||
user = models.ForeignKey(User, help_text=_("Studio user"), unique=True)
|
||||
user = models.OneToOneField(User, help_text=_("Studio user"))
|
||||
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,
|
||||
|
||||
@@ -105,7 +105,7 @@ class CourseCreatorAdminTest(TestCase):
|
||||
# message sent. Admin message will follow.
|
||||
base_num_emails = 1 if expect_sent_to_user else 0
|
||||
if expect_sent_to_admin:
|
||||
context = {'user_name': "test_user", 'user_email': 'test_user+courses@edx.org'}
|
||||
context = {'user_name': "test_user", 'user_email': u'test_user+courses@edx.org'}
|
||||
self.assertEquals(base_num_emails + 1, len(mail.outbox), 'Expected admin message to be sent')
|
||||
sent_mail = mail.outbox[base_num_emails]
|
||||
self.assertEquals(
|
||||
@@ -166,10 +166,10 @@ class CourseCreatorAdminTest(TestCase):
|
||||
# try logging in 30 times, the default limit in the number of failed
|
||||
# login attempts in one 5 minute period before the rate gets limited
|
||||
for _ in xrange(30):
|
||||
response = self.client.post('/admin/', post_params)
|
||||
response = self.client.post('/admin/login/', post_params)
|
||||
self.assertEquals(response.status_code, 200)
|
||||
|
||||
response = self.client.post('/admin/', post_params)
|
||||
response = self.client.post('/admin/login/', post_params)
|
||||
# Since we are using the default rate limit behavior, we are
|
||||
# expecting this to return a 403 error to indicate that there have
|
||||
# been too many attempts
|
||||
|
||||
@@ -4,6 +4,6 @@ Django admin dashboard configuration for LMS XBlock infrastructure.
|
||||
|
||||
from django.contrib import admin
|
||||
from config_models.admin import ConfigurationModelAdmin
|
||||
from cms.djangoapps.xblock_config.models import StudioConfig
|
||||
from xblock_config.models import StudioConfig
|
||||
|
||||
admin.site.register(StudioConfig, ConfigurationModelAdmin)
|
||||
|
||||
@@ -1,74 +1,30 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding model 'StudioConfig'
|
||||
db.create_table('xblock_config_studioconfig', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('change_date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
|
||||
('changed_by', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, on_delete=models.PROTECT)),
|
||||
('enabled', self.gf('django.db.models.fields.BooleanField')(default=False)),
|
||||
('disabled_blocks', self.gf('django.db.models.fields.TextField')(default='about course_info static_tab')),
|
||||
))
|
||||
db.send_create_signal('xblock_config', ['StudioConfig'])
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting model 'StudioConfig'
|
||||
db.delete_table('xblock_config_studioconfig')
|
||||
|
||||
|
||||
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'})
|
||||
},
|
||||
'xblock_config.studioconfig': {
|
||||
'Meta': {'object_name': 'StudioConfig'},
|
||||
'change_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.PROTECT'}),
|
||||
'disabled_blocks': ('django.db.models.fields.TextField', [], {'default': "'about course_info static_tab'"}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['xblock_config']
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='StudioConfig',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('change_date', models.DateTimeField(auto_now_add=True, verbose_name='Change date')),
|
||||
('enabled', models.BooleanField(default=False, verbose_name='Enabled')),
|
||||
('disabled_blocks', models.TextField(default=b'about course_info static_tab', help_text=b'Space-separated list of XBlocks on which XBlockAsides should never render in studio')),
|
||||
('changed_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, editable=False, to=settings.AUTH_USER_MODEL, null=True, verbose_name='Changed by')),
|
||||
],
|
||||
options={
|
||||
'ordering': ('-change_date',),
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
@@ -79,6 +79,7 @@ DATABASES = {
|
||||
'OPTIONS': {
|
||||
'timeout': 30,
|
||||
},
|
||||
'ATOMIC_REQUESTS': True,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +101,7 @@ USE_I18N = True
|
||||
# Include the lettuce app for acceptance testing, including the 'harvest' django-admin command
|
||||
# django.contrib.staticfiles used to be loaded by lettuce, now we must add it ourselves
|
||||
# django.contrib.staticfiles is not added to lms as there is a ^/static$ route built in to the app
|
||||
INSTALLED_APPS += ('lettuce.django', 'django.contrib.staticfiles')
|
||||
INSTALLED_APPS += ('lettuce.django',)
|
||||
LETTUCE_APPS = ('contentstore',)
|
||||
LETTUCE_BROWSER = os.environ.get('LETTUCE_BROWSER', 'chrome')
|
||||
|
||||
|
||||
@@ -39,7 +39,6 @@ CONFIG_PREFIX = SERVICE_VARIANT + "." if SERVICE_VARIANT else ""
|
||||
############### ALWAYS THE SAME ################################
|
||||
|
||||
DEBUG = False
|
||||
TEMPLATE_DEBUG = False
|
||||
|
||||
EMAIL_BACKEND = 'django_ses.SESBackend'
|
||||
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
|
||||
@@ -69,11 +68,6 @@ BROKER_HEARTBEAT_CHECKRATE = 2
|
||||
# Each worker should only fetch one message at a time
|
||||
CELERYD_PREFETCH_MULTIPLIER = 1
|
||||
|
||||
# Skip djcelery migrations, since we don't use the database as the broker
|
||||
SOUTH_MIGRATION_MODULES = {
|
||||
'djcelery': 'ignore',
|
||||
}
|
||||
|
||||
# Rename the exchange and queues for each variant
|
||||
|
||||
QUEUE_VARIANT = CONFIG_PREFIX.lower()
|
||||
@@ -130,6 +124,12 @@ LMS_BASE = ENV_TOKENS.get('LMS_BASE')
|
||||
|
||||
SITE_NAME = ENV_TOKENS['SITE_NAME']
|
||||
|
||||
ALLOWED_HOSTS = [
|
||||
# TODO: bbeggs remove this before prod, temp fix to get load testing running
|
||||
"*",
|
||||
ENV_TOKENS.get('CMS_BASE')
|
||||
]
|
||||
|
||||
LOG_DIR = ENV_TOKENS['LOG_DIR']
|
||||
|
||||
CACHES = ENV_TOKENS['CACHES']
|
||||
@@ -249,6 +249,8 @@ EMAIL_HOST_PASSWORD = AUTH_TOKENS.get('EMAIL_HOST_PASSWORD', EMAIL_HOST_PASSWORD
|
||||
# Note that this is the Studio key for Segment. There is a separate key for the LMS.
|
||||
CMS_SEGMENT_KEY = AUTH_TOKENS.get('SEGMENT_KEY')
|
||||
|
||||
SECRET_KEY = AUTH_TOKENS['SECRET_KEY']
|
||||
|
||||
AWS_ACCESS_KEY_ID = AUTH_TOKENS["AWS_ACCESS_KEY_ID"]
|
||||
if AWS_ACCESS_KEY_ID == "":
|
||||
AWS_ACCESS_KEY_ID = None
|
||||
@@ -265,6 +267,11 @@ else:
|
||||
DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
|
||||
|
||||
DATABASES = AUTH_TOKENS['DATABASES']
|
||||
|
||||
# Enable automatic transaction management on all databases
|
||||
for database_name in DATABASES:
|
||||
DATABASES[database_name]['ATOMIC_REQUESTS'] = True
|
||||
|
||||
MODULESTORE = convert_module_store_setting_if_needed(AUTH_TOKENS.get('MODULESTORE', MODULESTORE))
|
||||
CONTENTSTORE = AUTH_TOKENS['CONTENTSTORE']
|
||||
DOC_STORE_CONFIG = AUTH_TOKENS['DOC_STORE_CONFIG']
|
||||
|
||||
@@ -59,6 +59,11 @@ from xmodule.modulestore.edit_info import EditInfoMixin
|
||||
from xmodule.mixin import LicenseMixin
|
||||
|
||||
############################ FEATURE CONFIGURATION #############################
|
||||
|
||||
|
||||
# Dummy secret key for dev/test
|
||||
SECRET_KEY = '85920908f28904ed733fe576320db18cabd7b6cd'
|
||||
|
||||
STUDIO_NAME = "Studio"
|
||||
STUDIO_SHORT_NAME = "Studio"
|
||||
FEATURES = {
|
||||
@@ -206,8 +211,9 @@ sys.path.append(COMMON_ROOT / 'djangoapps')
|
||||
GEOIP_PATH = REPO_ROOT / "common/static/data/geoip/GeoIP.dat"
|
||||
GEOIPV6_PATH = REPO_ROOT / "common/static/data/geoip/GeoIPv6.dat"
|
||||
|
||||
############################# WEB CONFIGURATION #############################
|
||||
# This is where we stick our compiled template files.
|
||||
############################# TEMPLATE CONFIGURATION #############################
|
||||
# Mako templating
|
||||
# TODO: Move the Mako templating into a different engine in TEMPLATES below.
|
||||
import tempfile
|
||||
MAKO_MODULE_DIR = os.path.join(tempfile.gettempdir(), 'mako_cms')
|
||||
MAKO_TEMPLATES = {}
|
||||
@@ -222,25 +228,44 @@ MAKO_TEMPLATES['main'] = [
|
||||
for namespace, template_dirs in lms.envs.common.MAKO_TEMPLATES.iteritems():
|
||||
MAKO_TEMPLATES['lms.' + namespace] = template_dirs
|
||||
|
||||
TEMPLATE_DIRS = MAKO_TEMPLATES['main']
|
||||
# Django templating
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
# Don't look for template source files inside installed applications.
|
||||
'APP_DIRS': False,
|
||||
# Instead, look for template source files in these dirs.
|
||||
'DIRS': MAKO_TEMPLATES['main'],
|
||||
# Options specific to this backend.
|
||||
'OPTIONS': {
|
||||
'loaders': (
|
||||
'django.template.loaders.filesystem.Loader',
|
||||
'django.template.loaders.app_directories.Loader',
|
||||
),
|
||||
'context_processors': (
|
||||
'django.template.context_processors.request',
|
||||
'django.template.context_processors.static',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
'django.template.context_processors.i18n',
|
||||
'django.contrib.auth.context_processors.auth', # this is required for admin
|
||||
'django.template.context_processors.csrf',
|
||||
'dealer.contrib.django.staff.context_processor', # access git revision
|
||||
'contentstore.context_processors.doc_url',
|
||||
),
|
||||
# Change 'debug' in your environment settings files - not here.
|
||||
'debug': False
|
||||
}
|
||||
}
|
||||
]
|
||||
DEFAULT_TEMPLATE_ENGINE = TEMPLATES[0]
|
||||
|
||||
##############################################################################
|
||||
|
||||
EDX_ROOT_URL = ''
|
||||
|
||||
LOGIN_REDIRECT_URL = EDX_ROOT_URL + '/signin'
|
||||
LOGIN_URL = EDX_ROOT_URL + '/signin'
|
||||
|
||||
|
||||
TEMPLATE_CONTEXT_PROCESSORS = (
|
||||
'django.core.context_processors.request',
|
||||
'django.core.context_processors.static',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
'django.core.context_processors.i18n',
|
||||
'django.contrib.auth.context_processors.auth', # this is required for admin
|
||||
'django.core.context_processors.csrf',
|
||||
'dealer.contrib.django.staff.context_processor', # access git revision
|
||||
'contentstore.context_processors.doc_url',
|
||||
)
|
||||
|
||||
# use the ratelimit backend to prevent brute force attacks
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
'ratelimitbackend.backends.RateLimitModelBackend',
|
||||
@@ -275,12 +300,6 @@ simplefilter('ignore')
|
||||
|
||||
################################# Middleware ###################################
|
||||
|
||||
# List of callables that know how to import templates from various sources.
|
||||
TEMPLATE_LOADERS = (
|
||||
'django.template.loaders.filesystem.Loader',
|
||||
'django.template.loaders.app_directories.Loader',
|
||||
)
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
'request_cache.middleware.RequestCache',
|
||||
'django.middleware.cache.UpdateCacheMiddleware',
|
||||
@@ -304,11 +323,8 @@ MIDDLEWARE_CLASSES = (
|
||||
'embargo.middleware.EmbargoMiddleware',
|
||||
|
||||
# Detects user-requested locale from 'accept-language' header in http request
|
||||
# TODO: Re-import the Django version once we upgrade to Django 1.8 [PLAT-671]
|
||||
# 'django.middleware.locale.LocaleMiddleware',
|
||||
'django_locale.middleware.LocaleMiddleware',
|
||||
'django.middleware.locale.LocaleMiddleware',
|
||||
|
||||
'django.middleware.transaction.TransactionMiddleware',
|
||||
# needs to run after locale middleware (or anything that modifies the request context)
|
||||
'edxmako.middleware.MakoMiddleware',
|
||||
|
||||
@@ -383,11 +399,12 @@ MODULESTORE = {
|
||||
}
|
||||
|
||||
############################ DJANGO_BUILTINS ################################
|
||||
# Change DEBUG/TEMPLATE_DEBUG in your environment settings files, not here
|
||||
# Change DEBUG in your environment settings files, not here
|
||||
DEBUG = False
|
||||
TEMPLATE_DEBUG = False
|
||||
SESSION_COOKIE_SECURE = False
|
||||
SESSION_SAVE_EVERY_REQUEST = False
|
||||
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
|
||||
|
||||
|
||||
# Site info
|
||||
SITE_ID = 1
|
||||
@@ -704,8 +721,8 @@ INSTALLED_APPS = (
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.sites',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'djcelery',
|
||||
'south',
|
||||
'method_override',
|
||||
|
||||
# Common views
|
||||
@@ -726,13 +743,14 @@ INSTALLED_APPS = (
|
||||
# For CMS
|
||||
'contentstore',
|
||||
'course_creators',
|
||||
'external_auth',
|
||||
'student', # misleading name due to sharing with lms
|
||||
'openedx.core.djangoapps.course_groups', # not used in cms (yet), but tests run
|
||||
'xblock_config',
|
||||
|
||||
# Tracking
|
||||
'track',
|
||||
'eventtracking.django',
|
||||
'eventtracking.django.apps.EventTrackingConfig',
|
||||
|
||||
# Monitoring
|
||||
'datadog',
|
||||
@@ -740,7 +758,6 @@ INSTALLED_APPS = (
|
||||
# For asset pipelining
|
||||
'edxmako',
|
||||
'pipeline',
|
||||
'django.contrib.staticfiles',
|
||||
'static_replace',
|
||||
'require',
|
||||
|
||||
@@ -793,6 +810,11 @@ INSTALLED_APPS = (
|
||||
|
||||
# Self-paced course configuration
|
||||
'openedx.core.djangoapps.self_paced',
|
||||
|
||||
# These are apps that aren't strictly needed by Studio, but are imported by
|
||||
# other apps that are. Django 1.8 wants to have imported models supported
|
||||
# by installed apps.
|
||||
'lms.djangoapps.verify_student',
|
||||
)
|
||||
|
||||
|
||||
@@ -901,9 +923,6 @@ OPTIONAL_APPS = (
|
||||
|
||||
# milestones
|
||||
'milestones',
|
||||
|
||||
# edX Proctoring
|
||||
'edx_proctoring',
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': ENV_ROOT / "db" / "edx.db",
|
||||
'ATOMIC_REQUESTS': True,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,11 +29,5 @@ DJKOMBU_POLLING_INTERVAL = 1.0
|
||||
|
||||
# Disable transaction management because we are using a worker. Views
|
||||
# that request a task and wait for the result will deadlock otherwise.
|
||||
|
||||
MIDDLEWARE_CLASSES = tuple(
|
||||
c for c in MIDDLEWARE_CLASSES
|
||||
if c != 'django.middleware.transaction.TransactionMiddleware')
|
||||
|
||||
# Note: other alternatives for disabling transactions don't work in 1.4
|
||||
# https://code.djangoproject.com/ticket/2304
|
||||
# https://code.djangoproject.com/ticket/16039
|
||||
for database_name in DATABASES:
|
||||
DATABASES[database_name]['ATOMIC_REQUESTS'] = False
|
||||
|
||||
@@ -12,7 +12,7 @@ MEDIA_ROOT = "/edx/var/edxapp/uploads"
|
||||
|
||||
DEBUG = True
|
||||
USE_I18N = True
|
||||
TEMPLATE_DEBUG = DEBUG
|
||||
DEFAULT_TEMPLATE_ENGINE['OPTIONS']['debug'] = DEBUG
|
||||
HTTPS = 'off'
|
||||
|
||||
################################ LOGGERS ######################################
|
||||
|
||||
@@ -128,9 +128,14 @@ DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': TEST_ROOT / "db" / "cms.db",
|
||||
'ATOMIC_REQUESTS': True,
|
||||
},
|
||||
}
|
||||
|
||||
# This hack disables migrations during tests. We want to create tables directly from the models for speed.
|
||||
# See https://groups.google.com/d/msg/django-developers/PWPj3etj3-U/kCl6pMsQYYoJ.
|
||||
MIGRATION_MODULES = {app: "app.migrations_not_used_in_tests" for app in INSTALLED_APPS}
|
||||
|
||||
LMS_BASE = "localhost:8000"
|
||||
FEATURES['PREVIEW_LMS_BASE'] = "preview"
|
||||
|
||||
@@ -171,11 +176,8 @@ CACHES = {
|
||||
},
|
||||
}
|
||||
|
||||
# Add external_auth to Installed apps for testing
|
||||
INSTALLED_APPS += ('external_auth', )
|
||||
|
||||
# Add milestones to Installed apps for testing
|
||||
INSTALLED_APPS += ('milestones', 'openedx.core.djangoapps.call_stack_manager')
|
||||
# Add apps to Installed apps for testing
|
||||
INSTALLED_APPS += ('openedx.core.djangoapps.call_stack_manager',)
|
||||
|
||||
# hide ratelimit warnings while running tests
|
||||
filterwarnings('ignore', message='No request passed to the backend, unable to rate-limit')
|
||||
|
||||
@@ -17,6 +17,7 @@ from .common import * # pylint: disable=wildcard-import, unused-wildcard-import
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'ATOMIC_REQUESTS': True,
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
@@ -85,11 +85,6 @@ BROKER_HEARTBEAT_CHECKRATE = 2
|
||||
# Each worker should only fetch one message at a time
|
||||
CELERYD_PREFETCH_MULTIPLIER = 1
|
||||
|
||||
# Skip djcelery migrations, since we don't use the database as the broker
|
||||
SOUTH_MIGRATION_MODULES = {
|
||||
'djcelery': 'ignore',
|
||||
}
|
||||
|
||||
# Rename the exchange and queues for each variant
|
||||
|
||||
QUEUE_VARIANT = CONFIG_PREFIX.lower()
|
||||
|
||||
@@ -8,7 +8,8 @@ from django.conf import settings
|
||||
settings.INSTALLED_APPS # pylint: disable=pointless-statement
|
||||
|
||||
from openedx.core.lib.django_startup import autostartup
|
||||
from monkey_patch import django_utils_translation
|
||||
import django
|
||||
from monkey_patch import third_party_auth
|
||||
|
||||
import xmodule.x_module
|
||||
import cms.lib.xblock.runtime
|
||||
@@ -18,7 +19,9 @@ def run():
|
||||
"""
|
||||
Executed during django startup
|
||||
"""
|
||||
django_utils_translation.patch()
|
||||
third_party_auth.patch()
|
||||
|
||||
django.setup()
|
||||
|
||||
autostartup()
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ from openedx.core.lib.js_utils import (
|
||||
url_name: "${context_course.location.name | h}",
|
||||
org: "${context_course.location.org | h}",
|
||||
num: "${context_course.location.course | h}",
|
||||
display_course_number: "${_(context_course.display_coursenumber)}",
|
||||
display_course_number: "${_(context_course.display_number_with_default)}",
|
||||
revision: "${context_course.location.revision | h}",
|
||||
self_paced: ${escape_json_dumps(context_course.self_paced) | n}
|
||||
});
|
||||
|
||||
@@ -130,14 +130,15 @@ class TestLoginWithAccessTokenView(TestCase):
|
||||
self.user = UserFactory()
|
||||
self.oauth2_client = Client.objects.create(client_type=provider.constants.CONFIDENTIAL)
|
||||
|
||||
def _verify_response(self, access_token, expected_status_code, expected_num_cookies):
|
||||
def _verify_response(self, access_token, expected_status_code, expected_cookie_name=None):
|
||||
"""
|
||||
Calls the login_with_access_token endpoint and verifies the response given the expected values.
|
||||
"""
|
||||
url = reverse("login_with_access_token")
|
||||
response = self.client.post(url, HTTP_AUTHORIZATION="Bearer {0}".format(access_token))
|
||||
self.assertEqual(response.status_code, expected_status_code)
|
||||
self.assertEqual(len(response.cookies), expected_num_cookies)
|
||||
if expected_cookie_name:
|
||||
self.assertIn(expected_cookie_name, response.cookies)
|
||||
|
||||
def test_success(self):
|
||||
access_token = AccessToken.objects.create(
|
||||
@@ -145,11 +146,9 @@ class TestLoginWithAccessTokenView(TestCase):
|
||||
client=self.oauth2_client,
|
||||
user=self.user,
|
||||
)
|
||||
self._verify_response(access_token, expected_status_code=204, expected_num_cookies=1)
|
||||
self.assertEqual(len(self.client.cookies), 1)
|
||||
self.assertEqual(self.client.session['_auth_user_id'], self.user.id)
|
||||
self._verify_response(access_token, expected_status_code=204, expected_cookie_name='sessionid')
|
||||
self.assertEqual(int(self.client.session['_auth_user_id']), self.user.id)
|
||||
|
||||
def test_unauthenticated(self):
|
||||
self._verify_response("invalid_token", expected_status_code=401, expected_num_cookies=0)
|
||||
self.assertEqual(len(self.client.cookies), 0)
|
||||
self._verify_response("invalid_token", expected_status_code=401)
|
||||
self.assertNotIn("session_key", self.client.session)
|
||||
|
||||
@@ -33,8 +33,8 @@ def get_instance(model, instance_or_pk, timeout=None, using=None):
|
||||
<User: lamby>
|
||||
>>> User.objects.get(pk=1) == get_instance(User, 1)
|
||||
True
|
||||
"""
|
||||
|
||||
"""
|
||||
pk = getattr(instance_or_pk, 'pk', instance_or_pk)
|
||||
key = instance_key(model, instance_or_pk)
|
||||
data = cache.get(key)
|
||||
@@ -59,7 +59,7 @@ def get_instance(model, instance_or_pk, timeout=None, using=None):
|
||||
# fallback and return the underlying instance
|
||||
cache.delete(key)
|
||||
|
||||
# Use the default manager so we are never filtered by a .get_query_set()
|
||||
# Use the default manager so we are never filtered by a .get_queryset()
|
||||
|
||||
# import logging
|
||||
# log = logging.getLogger("tracking")
|
||||
@@ -94,7 +94,6 @@ def delete_instance(model, *instance_or_pk):
|
||||
"""
|
||||
Purges the cache keys for the instances of this model.
|
||||
"""
|
||||
|
||||
cache.delete_many([instance_key(model, x) for x in instance_or_pk])
|
||||
|
||||
|
||||
@@ -102,10 +101,10 @@ def instance_key(model, instance_or_pk):
|
||||
"""
|
||||
Returns the cache key for this (model, instance) pair.
|
||||
"""
|
||||
|
||||
# pylint: disable=protected-access
|
||||
return '%s.%s:%d' % (
|
||||
model._meta.app_label,
|
||||
model._meta.module_name,
|
||||
model._meta.model_name,
|
||||
getattr(instance_or_pk, 'pk', instance_or_pk),
|
||||
)
|
||||
|
||||
|
||||
@@ -5,14 +5,14 @@ Admin site models for managing :class:`.ConfigurationModel` subclasses
|
||||
from django.forms import models
|
||||
from django.contrib import admin
|
||||
from django.contrib.admin import ListFilter
|
||||
from django.core.cache import get_cache, InvalidCacheBackendError
|
||||
from django.core.cache import caches, InvalidCacheBackendError
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
try:
|
||||
cache = get_cache('configuration') # pylint: disable=invalid-name
|
||||
cache = caches['configuration'] # pylint: disable=invalid-name
|
||||
except InvalidCacheBackendError:
|
||||
from django.core.cache import cache
|
||||
|
||||
@@ -84,7 +84,7 @@ class ConfigurationModelAdmin(admin.ModelAdmin):
|
||||
reverse(
|
||||
'admin:{}_{}_change'.format(
|
||||
self.model._meta.app_label,
|
||||
self.model._meta.module_name,
|
||||
self.model._meta.model_name,
|
||||
),
|
||||
args=(target.id,),
|
||||
)
|
||||
@@ -142,7 +142,7 @@ class KeyedConfigurationModelAdmin(ConfigurationModelAdmin):
|
||||
date_hierarchy = None
|
||||
list_filter = (ShowHistoryFilter, )
|
||||
|
||||
def queryset(self, request):
|
||||
def get_queryset(self, request):
|
||||
"""
|
||||
Annote the queryset with an 'is_active' property that's true iff that row is the most
|
||||
recently added row for that particular set of KEY_FIELDS values.
|
||||
@@ -180,7 +180,7 @@ class KeyedConfigurationModelAdmin(ConfigurationModelAdmin):
|
||||
""" Edit link for the change view """
|
||||
if not inst.is_active:
|
||||
return u'--'
|
||||
update_url = reverse('admin:{}_{}_add'.format(self.model._meta.app_label, self.model._meta.module_name))
|
||||
update_url = reverse('admin:{}_{}_add'.format(self.model._meta.app_label, self.model._meta.model_name))
|
||||
update_url += "?source={}".format(inst.pk)
|
||||
return u'<a href="{}">{}</a>'.format(update_url, _('Update'))
|
||||
edit_link.allow_tags = True
|
||||
|
||||
@@ -3,11 +3,11 @@ Django Model baseclass for database-backed configuration.
|
||||
"""
|
||||
from django.db import connection, models
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.cache import get_cache, InvalidCacheBackendError
|
||||
from django.core.cache import caches, InvalidCacheBackendError
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
try:
|
||||
cache = get_cache('configuration') # pylint: disable=invalid-name
|
||||
cache = caches['configuration'] # pylint: disable=invalid-name
|
||||
except InvalidCacheBackendError:
|
||||
from django.core.cache import cache
|
||||
|
||||
@@ -37,7 +37,7 @@ class ConfigurationModelManager(models.Manager):
|
||||
necessaryily mean enbled.
|
||||
"""
|
||||
assert self.model.KEY_FIELDS != (), "Just use model.current() if there are no KEY_FIELDS"
|
||||
return self.get_query_set().extra(
|
||||
return self.get_queryset().extra( # pylint: disable=no-member
|
||||
where=["id IN ({subquery})".format(subquery=self._current_ids_subquery())],
|
||||
select={'is_active': 1}, # This annotation is used by the admin changelist. sqlite requires '1', not 'True'
|
||||
)
|
||||
@@ -49,11 +49,11 @@ class ConfigurationModelManager(models.Manager):
|
||||
"""
|
||||
if self.model.KEY_FIELDS:
|
||||
subquery = self._current_ids_subquery()
|
||||
return self.get_query_set().extra(
|
||||
return self.get_queryset().extra( # pylint: disable=no-member
|
||||
select={'is_active': "id IN ({subquery})".format(subquery=subquery)}
|
||||
)
|
||||
else:
|
||||
return self.get_query_set().extra(
|
||||
return self.get_queryset().extra( # pylint: disable=no-member
|
||||
select={'is_active': "id = {pk}".format(pk=self.model.current().pk)}
|
||||
)
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ from rest_framework.generics import CreateAPIView, RetrieveAPIView
|
||||
from rest_framework.permissions import DjangoModelPermissions
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
from rest_framework.serializers import ModelSerializer
|
||||
from django.db import transaction
|
||||
|
||||
|
||||
class ReadableOnlyByAuthors(DjangoModelPermissions):
|
||||
@@ -13,7 +14,29 @@ class ReadableOnlyByAuthors(DjangoModelPermissions):
|
||||
perms_map['GET'] = perms_map['OPTIONS'] = perms_map['HEAD'] = perms_map['POST']
|
||||
|
||||
|
||||
class ConfigurationModelCurrentAPIView(CreateAPIView, RetrieveAPIView):
|
||||
class AtomicMixin(object):
|
||||
"""Mixin to provide atomic transaction for as_view."""
|
||||
@classmethod
|
||||
def create_atomic_wrapper(cls, wrapped_func):
|
||||
"""Returns a wrapped function."""
|
||||
def _create_atomic_wrapper(*args, **kwargs):
|
||||
"""Actual wrapper."""
|
||||
# When a view call fails due to a permissions error, it raises an exception.
|
||||
# An uncaught exception breaks the DB transaction for any following DB operations
|
||||
# unless it's wrapped in a atomic() decorator or context manager.
|
||||
with transaction.atomic():
|
||||
return wrapped_func(*args, **kwargs)
|
||||
|
||||
return _create_atomic_wrapper
|
||||
|
||||
@classmethod
|
||||
def as_view(cls, **initkwargs):
|
||||
"""Overrides as_view to add atomic transaction."""
|
||||
view = super(AtomicMixin, cls).as_view(**initkwargs)
|
||||
return cls.create_atomic_wrapper(view)
|
||||
|
||||
|
||||
class ConfigurationModelCurrentAPIView(AtomicMixin, CreateAPIView, RetrieveAPIView):
|
||||
"""
|
||||
This view allows an authenticated user with the appropriate model permissions
|
||||
to read and write the current configuration for the specified `model`.
|
||||
|
||||
@@ -1,74 +1,30 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from south.utils import datetime_utils as datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding model 'XDomainProxyConfiguration'
|
||||
db.create_table('cors_csrf_xdomainproxyconfiguration', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('change_date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
|
||||
('changed_by', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, on_delete=models.PROTECT)),
|
||||
('enabled', self.gf('django.db.models.fields.BooleanField')(default=False)),
|
||||
('whitelist', self.gf('django.db.models.fields.TextField')()),
|
||||
))
|
||||
db.send_create_signal('cors_csrf', ['XDomainProxyConfiguration'])
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting model 'XDomainProxyConfiguration'
|
||||
db.delete_table('cors_csrf_xdomainproxyconfiguration')
|
||||
|
||||
|
||||
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'})
|
||||
},
|
||||
'cors_csrf.xdomainproxyconfiguration': {
|
||||
'Meta': {'object_name': 'XDomainProxyConfiguration'},
|
||||
'change_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.PROTECT'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'whitelist': ('django.db.models.fields.TextField', [], {})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['cors_csrf']
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='XDomainProxyConfiguration',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('change_date', models.DateTimeField(auto_now_add=True, verbose_name='Change date')),
|
||||
('enabled', models.BooleanField(default=False, verbose_name='Enabled')),
|
||||
('whitelist', models.TextField(help_text='List of domains that are allowed to make cross-domain requests to this site. Please list each domain on its own line.')),
|
||||
('changed_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, editable=False, to=settings.AUTH_USER_MODEL, null=True, verbose_name='Changed by')),
|
||||
],
|
||||
options={
|
||||
'ordering': ('-change_date',),
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
@@ -55,7 +55,7 @@ class CourseActionUIStateManager(CourseActionStateManager):
|
||||
"""
|
||||
|
||||
# add transaction protection to revert changes by get_or_create if an exception is raised before the final save.
|
||||
@transaction.commit_on_success
|
||||
@transaction.atomic
|
||||
def update_state(
|
||||
self, course_key, new_state, should_display=True, message="", user=None, allow_not_found=False, **kwargs
|
||||
):
|
||||
|
||||
@@ -1,92 +1,38 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
import xmodule_django.models
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding model 'CourseRerunState'
|
||||
db.create_table('course_action_state_coursererunstate', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('created_time', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
|
||||
('updated_time', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)),
|
||||
('created_user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='created_by_user+', null=True, on_delete=models.SET_NULL, to=orm['auth.User'])),
|
||||
('updated_user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='updated_by_user+', null=True, on_delete=models.SET_NULL, to=orm['auth.User'])),
|
||||
('course_key', self.gf('xmodule_django.models.CourseKeyField')(max_length=255, db_index=True)),
|
||||
('action', self.gf('django.db.models.fields.CharField')(max_length=100, db_index=True)),
|
||||
('state', self.gf('django.db.models.fields.CharField')(max_length=50)),
|
||||
('should_display', self.gf('django.db.models.fields.BooleanField')(default=False)),
|
||||
('message', self.gf('django.db.models.fields.CharField')(max_length=1000)),
|
||||
('source_course_key', self.gf('xmodule_django.models.CourseKeyField')(max_length=255, db_index=True)),
|
||||
))
|
||||
db.send_create_signal('course_action_state', ['CourseRerunState'])
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
# Adding unique constraint on 'CourseRerunState', fields ['course_key', 'action']
|
||||
db.create_unique('course_action_state_coursererunstate', ['course_key', 'action'])
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Removing unique constraint on 'CourseRerunState', fields ['course_key', 'action']
|
||||
db.delete_unique('course_action_state_coursererunstate', ['course_key', 'action'])
|
||||
|
||||
# Deleting model 'CourseRerunState'
|
||||
db.delete_table('course_action_state_coursererunstate')
|
||||
|
||||
|
||||
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_action_state.coursererunstate': {
|
||||
'Meta': {'unique_together': "(('course_key', 'action'),)", 'object_name': 'CourseRerunState'},
|
||||
'action': ('django.db.models.fields.CharField', [], {'max_length': '100', 'db_index': 'True'}),
|
||||
'course_key': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'created_time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'created_user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_by_user+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'message': ('django.db.models.fields.CharField', [], {'max_length': '1000'}),
|
||||
'should_display': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'source_course_key': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'state': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
|
||||
'updated_time': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||
'updated_user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'updated_by_user+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['course_action_state']
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CourseRerunState',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('created_time', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_time', models.DateTimeField(auto_now=True)),
|
||||
('course_key', xmodule_django.models.CourseKeyField(max_length=255, db_index=True)),
|
||||
('action', models.CharField(max_length=100, db_index=True)),
|
||||
('state', models.CharField(max_length=50)),
|
||||
('should_display', models.BooleanField(default=False)),
|
||||
('message', models.CharField(max_length=1000)),
|
||||
('source_course_key', xmodule_django.models.CourseKeyField(max_length=255, db_index=True)),
|
||||
('display_name', models.CharField(default=b'', max_length=255, blank=True)),
|
||||
('created_user', models.ForeignKey(related_name='created_by_user+', on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, null=True)),
|
||||
('updated_user', models.ForeignKey(related_name='updated_by_user+', on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, null=True)),
|
||||
],
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='coursererunstate',
|
||||
unique_together=set([('course_key', 'action')]),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
# -*- 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 field 'CourseRerunState.display_name'
|
||||
db.add_column('course_action_state_coursererunstate', 'display_name',
|
||||
self.gf('django.db.models.fields.CharField')(default='', max_length=255, blank=True),
|
||||
keep_default=False)
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting field 'CourseRerunState.display_name'
|
||||
db.delete_column('course_action_state_coursererunstate', 'display_name')
|
||||
|
||||
|
||||
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_action_state.coursererunstate': {
|
||||
'Meta': {'unique_together': "(('course_key', 'action'),)", 'object_name': 'CourseRerunState'},
|
||||
'action': ('django.db.models.fields.CharField', [], {'max_length': '100', 'db_index': 'True'}),
|
||||
'course_key': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'created_time': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'created_user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_by_user+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"}),
|
||||
'display_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'message': ('django.db.models.fields.CharField', [], {'max_length': '1000'}),
|
||||
'should_display': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'source_course_key': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'state': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
|
||||
'updated_time': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||
'updated_user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'updated_by_user+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['auth.User']"})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['course_action_state']
|
||||
@@ -90,7 +90,7 @@ class CourseActionUIState(CourseActionState):
|
||||
# FIELDS
|
||||
|
||||
# Whether or not the status should be displayed to users
|
||||
should_display = models.BooleanField()
|
||||
should_display = models.BooleanField(default=False)
|
||||
|
||||
# Message related to the status
|
||||
message = models.CharField(max_length=MAX_MESSAGE_LENGTH)
|
||||
|
||||
@@ -26,13 +26,14 @@ from course_modes.models import CourseMode
|
||||
# The admin page will work in both LMS and Studio,
|
||||
# but the test suite for Studio will fail because
|
||||
# the verification deadline table won't exist.
|
||||
from verify_student import models as verification_models # pylint: disable=import-error
|
||||
from lms.djangoapps.verify_student import models as verification_models
|
||||
|
||||
|
||||
class CourseModeForm(forms.ModelForm):
|
||||
|
||||
class Meta(object):
|
||||
model = CourseMode
|
||||
fields = '__all__'
|
||||
|
||||
COURSE_MODE_SLUG_CHOICES = (
|
||||
[(CourseMode.DEFAULT_MODE_SLUG, CourseMode.DEFAULT_MODE_SLUG)] +
|
||||
|
||||
@@ -1,40 +1,48 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import xmodule_django.models
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding model 'CourseMode'
|
||||
db.create_table('course_modes_coursemode', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('course_id', self.gf('django.db.models.fields.CharField')(max_length=255, db_index=True)),
|
||||
('mode_slug', self.gf('django.db.models.fields.CharField')(max_length=100)),
|
||||
('mode_display_name', self.gf('django.db.models.fields.CharField')(max_length=255)),
|
||||
('min_price', self.gf('django.db.models.fields.IntegerField')(default=0)),
|
||||
('suggested_prices', self.gf('django.db.models.fields.CommaSeparatedIntegerField')(default='', max_length=255, blank=True)),
|
||||
))
|
||||
db.send_create_signal('course_modes', ['CourseMode'])
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting model 'CourseMode'
|
||||
db.delete_table('course_modes_coursemode')
|
||||
|
||||
|
||||
models = {
|
||||
'course_modes.coursemode': {
|
||||
'Meta': {'object_name': 'CourseMode'},
|
||||
'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'min_price': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'mode_display_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'mode_slug': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'suggested_prices': ('django.db.models.fields.CommaSeparatedIntegerField', [], {'default': "''", 'max_length': '255', 'blank': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['course_modes']
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CourseMode',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('course_id', xmodule_django.models.CourseKeyField(max_length=255, verbose_name='Course', db_index=True)),
|
||||
('mode_slug', models.CharField(max_length=100, verbose_name='Mode')),
|
||||
('mode_display_name', models.CharField(max_length=255, verbose_name='Display Name')),
|
||||
('min_price', models.IntegerField(default=0, verbose_name='Price')),
|
||||
('currency', models.CharField(default=b'usd', max_length=8)),
|
||||
('expiration_datetime', models.DateTimeField(default=None, help_text='OPTIONAL: After this date/time, users will no longer be able to enroll in this mode. Leave this blank if users can enroll in this mode until enrollment closes for the course.', null=True, verbose_name='Upgrade Deadline', blank=True)),
|
||||
('expiration_date', models.DateField(default=None, null=True, blank=True)),
|
||||
('suggested_prices', models.CommaSeparatedIntegerField(default=b'', max_length=255, blank=True)),
|
||||
('description', models.TextField(null=True, blank=True)),
|
||||
('sku', models.CharField(help_text='OPTIONAL: This is the SKU (stock keeping unit) of this mode in the external ecommerce service. Leave this blank if the course has not yet been migrated to the ecommerce service.', max_length=255, null=True, verbose_name=b'SKU', blank=True)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CourseModesArchive',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('course_id', xmodule_django.models.CourseKeyField(max_length=255, db_index=True)),
|
||||
('mode_slug', models.CharField(max_length=100)),
|
||||
('mode_display_name', models.CharField(max_length=255)),
|
||||
('min_price', models.IntegerField(default=0)),
|
||||
('suggested_prices', models.CommaSeparatedIntegerField(default=b'', max_length=255, blank=True)),
|
||||
('currency', models.CharField(default=b'usd', max_length=8)),
|
||||
('expiration_date', models.DateField(default=None, null=True, blank=True)),
|
||||
('expiration_datetime', models.DateTimeField(default=None, null=True, blank=True)),
|
||||
],
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='coursemode',
|
||||
unique_together=set([('course_id', 'mode_slug', 'currency')]),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
# -*- 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 field 'CourseMode.currency'
|
||||
db.add_column('course_modes_coursemode', 'currency',
|
||||
self.gf('django.db.models.fields.CharField')(default='usd', max_length=8),
|
||||
keep_default=False)
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting field 'CourseMode.currency'
|
||||
db.delete_column('course_modes_coursemode', 'currency')
|
||||
|
||||
|
||||
models = {
|
||||
'course_modes.coursemode': {
|
||||
'Meta': {'object_name': 'CourseMode'},
|
||||
'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'currency': ('django.db.models.fields.CharField', [], {'default': "'usd'", 'max_length': '8'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'min_price': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'mode_display_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'mode_slug': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'suggested_prices': ('django.db.models.fields.CommaSeparatedIntegerField', [], {'default': "''", 'max_length': '255', 'blank': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['course_modes']
|
||||
@@ -1,33 +0,0 @@
|
||||
# -*- 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 unique constraint on 'CourseMode', fields ['course_id', 'currency', 'mode_slug']
|
||||
db.create_unique('course_modes_coursemode', ['course_id', 'currency', 'mode_slug'])
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Removing unique constraint on 'CourseMode', fields ['course_id', 'currency', 'mode_slug']
|
||||
db.delete_unique('course_modes_coursemode', ['course_id', 'currency', 'mode_slug'])
|
||||
|
||||
|
||||
models = {
|
||||
'course_modes.coursemode': {
|
||||
'Meta': {'unique_together': "(('course_id', 'mode_slug', 'currency'),)", 'object_name': 'CourseMode'},
|
||||
'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'currency': ('django.db.models.fields.CharField', [], {'default': "'usd'", 'max_length': '8'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'min_price': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'mode_display_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'mode_slug': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'suggested_prices': ('django.db.models.fields.CommaSeparatedIntegerField', [], {'default': "''", 'max_length': '255', 'blank': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['course_modes']
|
||||
@@ -1,36 +0,0 @@
|
||||
# -*- 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 field 'CourseMode.expiration_date'
|
||||
db.add_column('course_modes_coursemode', 'expiration_date',
|
||||
self.gf('django.db.models.fields.DateField')(default=None, null=True),
|
||||
keep_default=False)
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting field 'CourseMode.expiration_date'
|
||||
db.delete_column('course_modes_coursemode', 'expiration_date')
|
||||
|
||||
|
||||
models = {
|
||||
'course_modes.coursemode': {
|
||||
'Meta': {'unique_together': "(('course_id', 'mode_slug', 'currency'),)", 'object_name': 'CourseMode'},
|
||||
'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'currency': ('django.db.models.fields.CharField', [], {'default': "'usd'", 'max_length': '8'}),
|
||||
'expiration_date': ('django.db.models.fields.DateField', [], {'default': 'None', 'null': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'min_price': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'mode_display_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'mode_slug': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'suggested_prices': ('django.db.models.fields.CommaSeparatedIntegerField', [], {'default': "''", 'max_length': '255', 'blank': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['course_modes']
|
||||
@@ -1,37 +0,0 @@
|
||||
# -*- 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 field 'CourseMode.expiration_datetime'
|
||||
db.add_column('course_modes_coursemode', 'expiration_datetime',
|
||||
self.gf('django.db.models.fields.DateTimeField')(default=None, null=True, blank=True),
|
||||
keep_default=False)
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting field 'CourseMode.expiration_datetime'
|
||||
db.delete_column('course_modes_coursemode', 'expiration_datetime')
|
||||
|
||||
|
||||
models = {
|
||||
'course_modes.coursemode': {
|
||||
'Meta': {'unique_together': "(('course_id', 'mode_slug', 'currency'),)", 'object_name': 'CourseMode'},
|
||||
'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'currency': ('django.db.models.fields.CharField', [], {'default': "'usd'", 'max_length': '8'}),
|
||||
'expiration_date': ('django.db.models.fields.DateField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
|
||||
'expiration_datetime': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'min_price': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'mode_display_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'mode_slug': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'suggested_prices': ('django.db.models.fields.CommaSeparatedIntegerField', [], {'default': "''", 'max_length': '255', 'blank': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['course_modes']
|
||||
@@ -1,40 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import DataMigration
|
||||
from django.db import models
|
||||
|
||||
class Migration(DataMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
from datetime import datetime
|
||||
for course_mode in orm.CourseMode.objects.all():
|
||||
if course_mode.expiration_date is None:
|
||||
course_mode.expiration_datetime = None
|
||||
course_mode.save()
|
||||
else:
|
||||
course_mode.expiration_datetime = datetime.combine(course_mode.expiration_date, datetime.min.time())
|
||||
course_mode.save()
|
||||
|
||||
def backwards(self, orm):
|
||||
for course_mode in orm.CourseMode.objects.all():
|
||||
course_mode.expiration_datetime = None
|
||||
course_mode.save()
|
||||
|
||||
models = {
|
||||
'course_modes.coursemode': {
|
||||
'Meta': {'unique_together': "(('course_id', 'mode_slug', 'currency'),)", 'object_name': 'CourseMode'},
|
||||
'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'currency': ('django.db.models.fields.CharField', [], {'default': "'usd'", 'max_length': '8'}),
|
||||
'expiration_date': ('django.db.models.fields.DateField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
|
||||
'expiration_datetime': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'min_price': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'mode_display_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'mode_slug': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'suggested_prices': ('django.db.models.fields.CommaSeparatedIntegerField', [], {'default': "''", 'max_length': '255', 'blank': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['course_modes']
|
||||
symmetrical = True
|
||||
@@ -1,44 +0,0 @@
|
||||
# -*- 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 field 'CourseMode.description'
|
||||
db.add_column('course_modes_coursemode', 'description',
|
||||
self.gf('django.db.models.fields.TextField')(null=True, blank=True),
|
||||
keep_default=False)
|
||||
|
||||
|
||||
# Changing field 'CourseMode.course_id'
|
||||
db.alter_column('course_modes_coursemode', 'course_id', self.gf('xmodule_django.models.CourseKeyField')(max_length=255))
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting field 'CourseMode.description'
|
||||
db.delete_column('course_modes_coursemode', 'description')
|
||||
|
||||
|
||||
# Changing field 'CourseMode.course_id'
|
||||
db.alter_column('course_modes_coursemode', 'course_id', self.gf('django.db.models.fields.CharField')(max_length=255))
|
||||
|
||||
models = {
|
||||
'course_modes.coursemode': {
|
||||
'Meta': {'unique_together': "(('course_id', 'mode_slug', 'currency'),)", 'object_name': 'CourseMode'},
|
||||
'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'currency': ('django.db.models.fields.CharField', [], {'default': "'usd'", 'max_length': '8'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'expiration_date': ('django.db.models.fields.DateField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
|
||||
'expiration_datetime': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'min_price': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'mode_display_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'mode_slug': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'suggested_prices': ('django.db.models.fields.CommaSeparatedIntegerField', [], {'default': "''", 'max_length': '255', 'blank': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['course_modes']
|
||||
@@ -1,66 +0,0 @@
|
||||
# -*- 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 'CourseModesArchive'
|
||||
db.create_table('course_modes_coursemodesarchive', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('course_id', self.gf('xmodule_django.models.CourseKeyField')(max_length=255, db_index=True)),
|
||||
('mode_slug', self.gf('django.db.models.fields.CharField')(max_length=100)),
|
||||
('mode_display_name', self.gf('django.db.models.fields.CharField')(max_length=255)),
|
||||
('min_price', self.gf('django.db.models.fields.IntegerField')(default=0)),
|
||||
('suggested_prices', self.gf('django.db.models.fields.CommaSeparatedIntegerField')(default='', max_length=255, blank=True)),
|
||||
('currency', self.gf('django.db.models.fields.CharField')(default='usd', max_length=8)),
|
||||
('expiration_date', self.gf('django.db.models.fields.DateField')(default=None, null=True, blank=True)),
|
||||
('expiration_datetime', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True, blank=True)),
|
||||
))
|
||||
db.send_create_signal('course_modes', ['CourseModesArchive'])
|
||||
|
||||
|
||||
# Changing field 'CourseMode.course_id'
|
||||
db.alter_column('course_modes_coursemode', 'course_id', self.gf('xmodule_django.models.CourseKeyField')(max_length=255))
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting model 'CourseModesArchive'
|
||||
db.delete_table('course_modes_coursemodesarchive')
|
||||
|
||||
|
||||
# Changing field 'CourseMode.course_id'
|
||||
db.alter_column('course_modes_coursemode', 'course_id', self.gf('django.db.models.fields.CharField')(max_length=255))
|
||||
|
||||
models = {
|
||||
'course_modes.coursemode': {
|
||||
'Meta': {'unique_together': "(('course_id', 'mode_slug', 'currency'),)", 'object_name': 'CourseMode'},
|
||||
'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'currency': ('django.db.models.fields.CharField', [], {'default': "'usd'", 'max_length': '8'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'expiration_date': ('django.db.models.fields.DateField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
|
||||
'expiration_datetime': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'min_price': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'mode_display_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'mode_slug': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'suggested_prices': ('django.db.models.fields.CommaSeparatedIntegerField', [], {'default': "''", 'max_length': '255', 'blank': 'True'})
|
||||
},
|
||||
'course_modes.coursemodesarchive': {
|
||||
'Meta': {'object_name': 'CourseModesArchive'},
|
||||
'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'currency': ('django.db.models.fields.CharField', [], {'default': "'usd'", 'max_length': '8'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'expiration_date': ('django.db.models.fields.DateField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
|
||||
'expiration_datetime': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'min_price': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'mode_display_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'mode_slug': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'suggested_prices': ('django.db.models.fields.CommaSeparatedIntegerField', [], {'default': "''", 'max_length': '255', 'blank': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['course_modes']
|
||||
@@ -1,49 +0,0 @@
|
||||
# -*- 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 field 'CourseMode.sku'
|
||||
db.add_column('course_modes_coursemode', 'sku',
|
||||
self.gf('django.db.models.fields.CharField')(max_length=255, null=True),
|
||||
keep_default=False)
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting field 'CourseMode.sku'
|
||||
db.delete_column('course_modes_coursemode', 'sku')
|
||||
|
||||
models = {
|
||||
'course_modes.coursemode': {
|
||||
'Meta': {'unique_together': "(('course_id', 'mode_slug', 'currency'),)", 'object_name': 'CourseMode'},
|
||||
'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'currency': ('django.db.models.fields.CharField', [], {'default': "'usd'", 'max_length': '8'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'expiration_date': ('django.db.models.fields.DateField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
|
||||
'expiration_datetime': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'min_price': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'mode_display_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'mode_slug': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'sku': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'suggested_prices': ('django.db.models.fields.CommaSeparatedIntegerField', [], {'default': "''", 'max_length': '255', 'blank': 'True'})
|
||||
},
|
||||
'course_modes.coursemodesarchive': {
|
||||
'Meta': {'object_name': 'CourseModesArchive'},
|
||||
'course_id': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'currency': ('django.db.models.fields.CharField', [], {'default': "'usd'", 'max_length': '8'}),
|
||||
'expiration_date': ('django.db.models.fields.DateField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
|
||||
'expiration_datetime': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'min_price': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'mode_display_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'mode_slug': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
|
||||
'suggested_prices': ('django.db.models.fields.CommaSeparatedIntegerField', [], {'default': "''", 'max_length': '255', 'blank': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['course_modes']
|
||||
@@ -21,7 +21,7 @@ from course_modes.admin import CourseModeForm
|
||||
# defined in the LMS and course_modes is in common. However, the benefits
|
||||
# of putting all this configuration in one place outweigh the downsides.
|
||||
# Once the course admin tool is deployed, we can remove this dependency.
|
||||
from verify_student.models import VerificationDeadline # pylint: disable=import-error
|
||||
from lms.djangoapps.verify_student.models import VerificationDeadline
|
||||
|
||||
|
||||
# We can only test this in the LMS because the course modes admin relies
|
||||
|
||||
@@ -6,6 +6,7 @@ import decimal
|
||||
from ipware.ip import get_ip
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db import transaction
|
||||
from django.http import HttpResponse, HttpResponseBadRequest
|
||||
from django.shortcuts import redirect
|
||||
from django.views.generic.base import View
|
||||
@@ -20,7 +21,7 @@ from courseware.access import has_access
|
||||
from student.models import CourseEnrollment
|
||||
from opaque_keys.edx.locations import SlashSeparatedCourseKey
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from util.db import commit_on_success_with_read_committed
|
||||
from util.db import outer_atomic
|
||||
from xmodule.modulestore.django import modulestore
|
||||
|
||||
from embargo import api as embargo_api
|
||||
@@ -37,7 +38,12 @@ class ChooseModeView(View):
|
||||
|
||||
"""
|
||||
|
||||
@method_decorator(transaction.non_atomic_requests)
|
||||
def dispatch(self, *args, **kwargs): # pylint: disable=missing-docstring
|
||||
return super(ChooseModeView, self).dispatch(*args, **kwargs)
|
||||
|
||||
@method_decorator(login_required)
|
||||
@method_decorator(transaction.atomic)
|
||||
def get(self, request, course_id, error=None):
|
||||
"""Displays the course mode choice page.
|
||||
|
||||
@@ -135,8 +141,9 @@ class ChooseModeView(View):
|
||||
|
||||
return render_to_response("course_modes/choose.html", context)
|
||||
|
||||
@method_decorator(transaction.non_atomic_requests)
|
||||
@method_decorator(login_required)
|
||||
@method_decorator(commit_on_success_with_read_committed)
|
||||
@method_decorator(outer_atomic(read_committed=True))
|
||||
def post(self, request, course_id):
|
||||
"""Takes the form submission from the page and parses it.
|
||||
|
||||
|
||||
@@ -19,10 +19,8 @@ from openedx.core.djangoapps.user_api.preferences.api import (
|
||||
)
|
||||
from lang_pref import LANGUAGE_KEY
|
||||
|
||||
# TODO re-import this once we're on Django 1.5 or greater. [PLAT-671]
|
||||
# from django.utils.translation.trans_real import parse_accept_lang_header
|
||||
# from django.utils.translation import LANGUAGE_SESSION_KEY
|
||||
from django_locale.trans_real import parse_accept_lang_header, LANGUAGE_SESSION_KEY
|
||||
from django.utils.translation.trans_real import parse_accept_lang_header
|
||||
from django.utils.translation import LANGUAGE_SESSION_KEY
|
||||
|
||||
|
||||
def dark_parse_accept_lang_header(accept):
|
||||
|
||||
@@ -1,74 +1,30 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding model 'DarkLangConfig'
|
||||
db.create_table('dark_lang_darklangconfig', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('change_date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
|
||||
('changed_by', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, on_delete=models.PROTECT)),
|
||||
('enabled', self.gf('django.db.models.fields.BooleanField')(default=False)),
|
||||
('released_languages', self.gf('django.db.models.fields.TextField')(blank=True)),
|
||||
))
|
||||
db.send_create_signal('dark_lang', ['DarkLangConfig'])
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting model 'DarkLangConfig'
|
||||
db.delete_table('dark_lang_darklangconfig')
|
||||
|
||||
|
||||
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'})
|
||||
},
|
||||
'dark_lang.darklangconfig': {
|
||||
'Meta': {'object_name': 'DarkLangConfig'},
|
||||
'change_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.PROTECT'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'released_languages': ('django.db.models.fields.TextField', [], {'blank': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['dark_lang']
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='DarkLangConfig',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('change_date', models.DateTimeField(auto_now_add=True, verbose_name='Change date')),
|
||||
('enabled', models.BooleanField(default=False, verbose_name='Enabled')),
|
||||
('released_languages', models.TextField(help_text=b'A comma-separated list of language codes to release to the public.', blank=True)),
|
||||
('changed_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, editable=False, to=settings.AUTH_USER_MODEL, null=True, verbose_name='Changed by')),
|
||||
],
|
||||
options={
|
||||
'ordering': ('-change_date',),
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# Converted from the original South migration 0002_enable_on_install.py
|
||||
#
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def create_dark_lang_config(apps, schema_editor):
|
||||
"""
|
||||
Enable DarkLang by default when it is installed, to prevent accidental
|
||||
release of testing languages.
|
||||
"""
|
||||
DarkLangConfig = apps.get_model("dark_lang", "DarkLangConfig")
|
||||
db_alias = schema_editor.connection.alias
|
||||
|
||||
objects = DarkLangConfig.objects.using(db_alias)
|
||||
if not objects.exists():
|
||||
objects.create(enabled=True)
|
||||
|
||||
def remove_dark_lang_config(apps, schema_editor):
|
||||
"""Write your backwards methods here."""
|
||||
raise RuntimeError("Cannot reverse this migration.")
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('dark_lang', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(create_dark_lang_config, remove_dark_lang_config),
|
||||
]
|
||||
@@ -1,68 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import DataMigration
|
||||
from django.db import models
|
||||
|
||||
class Migration(DataMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
"""
|
||||
Enable DarkLang by default when it is installed, to prevent accidental
|
||||
release of testing languages.
|
||||
"""
|
||||
orm.DarkLangConfig(enabled=True).save()
|
||||
|
||||
def backwards(self, orm):
|
||||
"Write your backwards methods here."
|
||||
raise RuntimeError("Cannot reverse this migration.")
|
||||
|
||||
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'})
|
||||
},
|
||||
'dark_lang.darklangconfig': {
|
||||
'Meta': {'object_name': 'DarkLangConfig'},
|
||||
'change_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.PROTECT'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'released_languages': ('django.db.models.fields.TextField', [], {'blank': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['dark_lang']
|
||||
symmetrical = True
|
||||
@@ -11,9 +11,7 @@ import unittest
|
||||
|
||||
from dark_lang.middleware import DarkLangMiddleware
|
||||
from dark_lang.models import DarkLangConfig
|
||||
# TODO PLAT-671 Import from Django 1.8
|
||||
# from django.utils.translation import LANGUAGE_SESSION_KEY
|
||||
from django_locale.trans_real import LANGUAGE_SESSION_KEY
|
||||
from django.utils.translation import LANGUAGE_SESSION_KEY
|
||||
from student.tests.factories import UserFactory
|
||||
|
||||
|
||||
|
||||
@@ -1,92 +1,42 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from south.v2 import SchemaMigration
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
from django.conf import settings
|
||||
import xmodule_django.models
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
#
|
||||
# cdodge: This is basically an empty migration since everything has - up to now - managed in the django_comment_client app
|
||||
# But going forward we should be using this migration
|
||||
#
|
||||
def forwards(self, orm):
|
||||
pass
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
def backwards(self, orm):
|
||||
pass
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
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'},
|
||||
'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}),
|
||||
'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
|
||||
'consecutive_days_visit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}),
|
||||
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'display_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
|
||||
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
|
||||
'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
|
||||
'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
|
||||
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
|
||||
'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
|
||||
'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
|
||||
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': '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'}),
|
||||
'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
|
||||
'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
|
||||
'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
|
||||
'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}),
|
||||
'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
|
||||
'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
|
||||
'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
|
||||
'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
|
||||
'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}),
|
||||
'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'}),
|
||||
'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
|
||||
},
|
||||
'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'})
|
||||
},
|
||||
'django_comment_common.permission': {
|
||||
'Meta': {'object_name': 'Permission'},
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'}),
|
||||
'roles': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'permissions'", 'symmetrical': 'False', 'to': "orm['django_comment_common.Role']"})
|
||||
},
|
||||
'django_comment_common.role': {
|
||||
'Meta': {'object_name': 'Role'},
|
||||
'course_id': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'name': ('django.db.models.fields.CharField', [], {'max_length': '30'}),
|
||||
'users': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'roles'", 'symmetrical': 'False', 'to': "orm['auth.User']"})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['django_comment_common']
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Permission',
|
||||
fields=[
|
||||
('name', models.CharField(max_length=30, serialize=False, primary_key=True)),
|
||||
],
|
||||
options={
|
||||
'db_table': 'django_comment_client_permission',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Role',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('name', models.CharField(max_length=30)),
|
||||
('course_id', xmodule_django.models.CourseKeyField(db_index=True, max_length=255, blank=True)),
|
||||
('users', models.ManyToManyField(related_name='roles', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'db_table': 'django_comment_client_role',
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='permission',
|
||||
name='roles',
|
||||
field=models.ManyToManyField(related_name='permissions', to='django_comment_common.Role'),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
"""
|
||||
TODO: This module is imported from the stable Django 1.8 branch, as a
|
||||
copy of https://github.com/django/django/blob/stable/1.8.x/django/middleware/locale.py.
|
||||
|
||||
Remove this file and re-import this middleware from Django once the
|
||||
codebase is upgraded with a modern version of Django. [PLAT-671]
|
||||
"""
|
||||
@@ -1,83 +0,0 @@
|
||||
# TODO: This file is imported from the stable Django 1.8 branch. Remove this file
|
||||
# and re-import this middleware from Django once the codebase is upgraded. [PLAT-671]
|
||||
# pylint: disable=invalid-name, missing-docstring
|
||||
"This is the locale selecting middleware that will look at accept headers"
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import (
|
||||
LocaleRegexURLResolver, get_resolver, get_script_prefix, is_valid_path,
|
||||
)
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.utils import translation
|
||||
from django.utils.cache import patch_vary_headers
|
||||
# Override the Django 1.4 implementation with the 1.8 implementation
|
||||
from django_locale.trans_real import get_language_from_request
|
||||
|
||||
|
||||
class LocaleMiddleware(object):
|
||||
"""
|
||||
This is a very simple middleware that parses a request
|
||||
and decides what translation object to install in the current
|
||||
thread context. This allows pages to be dynamically
|
||||
translated to the language the user desires (if the language
|
||||
is available, of course).
|
||||
"""
|
||||
response_redirect_class = HttpResponseRedirect
|
||||
|
||||
def __init__(self):
|
||||
self._is_language_prefix_patterns_used = False
|
||||
for url_pattern in get_resolver(None).url_patterns:
|
||||
if isinstance(url_pattern, LocaleRegexURLResolver):
|
||||
self._is_language_prefix_patterns_used = True
|
||||
break
|
||||
|
||||
def process_request(self, request):
|
||||
check_path = self.is_language_prefix_patterns_used()
|
||||
# This call is broken in Django 1.4:
|
||||
# https://github.com/django/django/blob/stable/1.4.x/django/utils/translation/trans_real.py#L399
|
||||
# (we override parse_accept_lang_header to a fixed version in dark_lang.middleware)
|
||||
language = get_language_from_request(
|
||||
request, check_path=check_path)
|
||||
translation.activate(language)
|
||||
request.LANGUAGE_CODE = translation.get_language()
|
||||
|
||||
def process_response(self, request, response):
|
||||
language = translation.get_language()
|
||||
language_from_path = translation.get_language_from_path(request.path_info)
|
||||
if (response.status_code == 404 and not language_from_path
|
||||
and self.is_language_prefix_patterns_used()):
|
||||
urlconf = getattr(request, 'urlconf', None)
|
||||
language_path = '/%s%s' % (language, request.path_info)
|
||||
path_valid = is_valid_path(language_path, urlconf)
|
||||
if (not path_valid and settings.APPEND_SLASH
|
||||
and not language_path.endswith('/')):
|
||||
path_valid = is_valid_path("%s/" % language_path, urlconf)
|
||||
|
||||
if path_valid:
|
||||
script_prefix = get_script_prefix()
|
||||
language_url = "%s://%s%s" % (
|
||||
request.scheme,
|
||||
request.get_host(),
|
||||
# insert language after the script prefix and before the
|
||||
# rest of the URL
|
||||
request.get_full_path().replace(
|
||||
script_prefix,
|
||||
'%s%s/' % (script_prefix, language),
|
||||
1
|
||||
)
|
||||
)
|
||||
return self.response_redirect_class(language_url)
|
||||
|
||||
if not (self.is_language_prefix_patterns_used()
|
||||
and language_from_path):
|
||||
patch_vary_headers(response, ('Accept-Language',))
|
||||
if 'Content-Language' not in response:
|
||||
response['Content-Language'] = language
|
||||
return response
|
||||
|
||||
def is_language_prefix_patterns_used(self):
|
||||
"""
|
||||
Returns `True` if the `LocaleRegexURLResolver` is used
|
||||
at root level of the urlpatterns, else it returns `False`.
|
||||
"""
|
||||
return self._is_language_prefix_patterns_used
|
||||
@@ -1,157 +0,0 @@
|
||||
# pylint: disable=invalid-name, line-too-long, super-method-not-called
|
||||
"""
|
||||
Tests taken from Django upstream:
|
||||
https://github.com/django/django/blob/e6b34193c5c7d117ededdab04bb16caf8864f07c/tests/regressiontests/i18n/tests.py
|
||||
"""
|
||||
from django.conf import settings
|
||||
from django.test import TestCase, RequestFactory
|
||||
from django_locale.trans_real import (
|
||||
parse_accept_lang_header, get_language_from_request, LANGUAGE_SESSION_KEY
|
||||
)
|
||||
|
||||
# Added to test middleware around dark lang
|
||||
from django.contrib.auth.models import User
|
||||
from django.test.utils import override_settings
|
||||
from dark_lang.models import DarkLangConfig
|
||||
|
||||
|
||||
# Adding to support test differences between Django and our own settings
|
||||
@override_settings(LANGUAGES=[
|
||||
('pt', 'Portuguese'),
|
||||
('pt-br', 'Portuguese-Brasil'),
|
||||
('es', 'Spanish'),
|
||||
('es-ar', 'Spanish (Argentina)'),
|
||||
('de', 'Deutch'),
|
||||
('zh-cn', 'Chinese (China)'),
|
||||
('ar-sa', 'Arabic (Saudi Arabia)'),
|
||||
])
|
||||
class MiscTests(TestCase):
|
||||
"""
|
||||
Tests taken from Django upstream:
|
||||
https://github.com/django/django/blob/e6b34193c5c7d117ededdab04bb16caf8864f07c/tests/regressiontests/i18n/tests.py
|
||||
"""
|
||||
def setUp(self):
|
||||
self.rf = RequestFactory()
|
||||
# Added to test middleware around dark lang
|
||||
user = User()
|
||||
user.save()
|
||||
DarkLangConfig(
|
||||
released_languages='pt, pt-br, es, de, es-ar, zh-cn, ar-sa',
|
||||
changed_by=user,
|
||||
enabled=True
|
||||
).save()
|
||||
|
||||
def test_parse_spec_http_header(self):
|
||||
"""
|
||||
Testing HTTP header parsing. First, we test that we can parse the
|
||||
values according to the spec (and that we extract all the pieces in
|
||||
the right order).
|
||||
"""
|
||||
p = parse_accept_lang_header
|
||||
# Good headers.
|
||||
self.assertEqual([('de', 1.0)], p('de'))
|
||||
self.assertEqual([('en-AU', 1.0)], p('en-AU'))
|
||||
self.assertEqual([('es-419', 1.0)], p('es-419'))
|
||||
self.assertEqual([('*', 1.0)], p('*;q=1.00'))
|
||||
self.assertEqual([('en-AU', 0.123)], p('en-AU;q=0.123'))
|
||||
self.assertEqual([('en-au', 0.5)], p('en-au;q=0.5'))
|
||||
self.assertEqual([('en-au', 1.0)], p('en-au;q=1.0'))
|
||||
self.assertEqual([('da', 1.0), ('en', 0.5), ('en-gb', 0.25)], p('da, en-gb;q=0.25, en;q=0.5'))
|
||||
self.assertEqual([('en-au-xx', 1.0)], p('en-au-xx'))
|
||||
self.assertEqual([('de', 1.0), ('en-au', 0.75), ('en-us', 0.5), ('en', 0.25), ('es', 0.125), ('fa', 0.125)], p('de,en-au;q=0.75,en-us;q=0.5,en;q=0.25,es;q=0.125,fa;q=0.125'))
|
||||
self.assertEqual([('*', 1.0)], p('*'))
|
||||
self.assertEqual([('de', 1.0)], p('de;q=0.'))
|
||||
self.assertEqual([('en', 1.0), ('*', 0.5)], p('en; q=1.0, * ; q=0.5'))
|
||||
self.assertEqual([], p(''))
|
||||
|
||||
# Bad headers; should always return [].
|
||||
self.assertEqual([], p('en-gb;q=1.0000'))
|
||||
self.assertEqual([], p('en;q=0.1234'))
|
||||
self.assertEqual([], p('en;q=.2'))
|
||||
self.assertEqual([], p('abcdefghi-au'))
|
||||
self.assertEqual([], p('**'))
|
||||
self.assertEqual([], p('en,,gb'))
|
||||
self.assertEqual([], p('en-au;q=0.1.0'))
|
||||
self.assertEqual([], p('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXZ,en'))
|
||||
self.assertEqual([], p('da, en-gb;q=0.8, en;q=0.7,#'))
|
||||
self.assertEqual([], p('de;q=2.0'))
|
||||
self.assertEqual([], p('de;q=0.a'))
|
||||
self.assertEqual([], p('12-345'))
|
||||
self.assertEqual([], p(''))
|
||||
|
||||
def test_parse_literal_http_header(self):
|
||||
"""
|
||||
Now test that we parse a literal HTTP header correctly.
|
||||
"""
|
||||
g = get_language_from_request
|
||||
r = self.rf.get('/')
|
||||
r.COOKIES = {}
|
||||
r.META = {'HTTP_ACCEPT_LANGUAGE': 'pt-br'}
|
||||
self.assertEqual('pt-br', g(r))
|
||||
|
||||
r.META = {'HTTP_ACCEPT_LANGUAGE': 'pt'}
|
||||
self.assertEqual('pt', g(r))
|
||||
|
||||
r.META = {'HTTP_ACCEPT_LANGUAGE': 'es,de'}
|
||||
self.assertEqual('es', g(r))
|
||||
|
||||
r.META = {'HTTP_ACCEPT_LANGUAGE': 'es-ar,de'}
|
||||
self.assertEqual('es-ar', g(r))
|
||||
|
||||
# This test assumes there won't be a Django translation to a US
|
||||
# variation of the Spanish language, a safe assumption. When the
|
||||
# user sets it as the preferred language, the main 'es'
|
||||
# translation should be selected instead.
|
||||
r.META = {'HTTP_ACCEPT_LANGUAGE': 'es-us'}
|
||||
self.assertEqual(g(r), 'es')
|
||||
|
||||
# This tests the following scenario: there isn't a main language (zh)
|
||||
# translation of Django but there is a translation to variation (zh_CN)
|
||||
# the user sets zh-cn as the preferred language, it should be selected
|
||||
# by Django without falling back nor ignoring it.
|
||||
r.META = {'HTTP_ACCEPT_LANGUAGE': 'zh-cn,de'}
|
||||
self.assertEqual(g(r), 'zh-cn')
|
||||
|
||||
def test_logic_masked_by_darklang(self):
|
||||
g = get_language_from_request
|
||||
r = self.rf.get('/')
|
||||
r.COOKIES = {}
|
||||
r.META = {'HTTP_ACCEPT_LANGUAGE': 'ar-qa'}
|
||||
self.assertEqual('ar-sa', g(r))
|
||||
|
||||
r.session = {LANGUAGE_SESSION_KEY: 'es'}
|
||||
self.assertEqual('es', g(r))
|
||||
|
||||
def test_parse_language_cookie(self):
|
||||
"""
|
||||
Now test that we parse language preferences stored in a cookie correctly.
|
||||
"""
|
||||
g = get_language_from_request
|
||||
r = self.rf.get('/')
|
||||
r.COOKIES = {settings.LANGUAGE_COOKIE_NAME: 'pt-br'}
|
||||
r.META = {}
|
||||
self.assertEqual('pt-br', g(r))
|
||||
|
||||
r.COOKIES = {settings.LANGUAGE_COOKIE_NAME: 'pt'}
|
||||
r.META = {}
|
||||
self.assertEqual('pt', g(r))
|
||||
|
||||
r.COOKIES = {settings.LANGUAGE_COOKIE_NAME: 'es'}
|
||||
r.META = {'HTTP_ACCEPT_LANGUAGE': 'de'}
|
||||
self.assertEqual('es', g(r))
|
||||
|
||||
# This test assumes there won't be a Django translation to a US
|
||||
# variation of the Spanish language, a safe assumption. When the
|
||||
# user sets it as the preferred language, the main 'es'
|
||||
# translation should be selected instead.
|
||||
r.COOKIES = {settings.LANGUAGE_COOKIE_NAME: 'es-us'}
|
||||
r.META = {}
|
||||
self.assertEqual(g(r), 'es')
|
||||
|
||||
# This tests the following scenario: there isn't a main language (zh)
|
||||
# translation of Django but there is a translation to variation (zh_CN)
|
||||
# the user sets zh-cn as the preferred language, it should be selected
|
||||
# by Django without falling back nor ignoring it.
|
||||
r.COOKIES = {settings.LANGUAGE_COOKIE_NAME: 'zh-cn'}
|
||||
r.META = {'HTTP_ACCEPT_LANGUAGE': 'de'}
|
||||
self.assertEqual(g(r), 'zh-cn')
|
||||
@@ -1,131 +0,0 @@
|
||||
"""Translation helper functions."""
|
||||
# Imported from Django 1.8
|
||||
# pylint: disable=invalid-name
|
||||
import re
|
||||
from django.conf import settings
|
||||
from django.conf.locale import LANG_INFO
|
||||
from django.utils import translation
|
||||
|
||||
|
||||
# Format of Accept-Language header values. From RFC 2616, section 14.4 and 3.9.
|
||||
# and RFC 3066, section 2.1
|
||||
accept_language_re = re.compile(r'''
|
||||
([A-Za-z]{1,8}(?:-[A-Za-z0-9]{1,8})*|\*) # "en", "en-au", "x-y-z", "*"
|
||||
(?:\s*;\s*q=(0(?:\.\d{,3})?|1(?:.0{,3})?))? # Optional "q=1.00", "q=0.8"
|
||||
(?:\s*,\s*|$) # Multiple accepts per header.
|
||||
''', re.VERBOSE)
|
||||
|
||||
|
||||
language_code_re = re.compile(r'^[a-z]{1,8}(?:-[a-z0-9]{1,8})*$', re.IGNORECASE)
|
||||
|
||||
|
||||
LANGUAGE_SESSION_KEY = '_language'
|
||||
|
||||
|
||||
def parse_accept_lang_header(lang_string):
|
||||
"""
|
||||
Parses the lang_string, which is the body of an HTTP Accept-Language
|
||||
header, and returns a list of (lang, q-value), ordered by 'q' values.
|
||||
|
||||
Any format errors in lang_string results in an empty list being returned.
|
||||
"""
|
||||
# parse_accept_lang_header is broken until we are on Django 1.5 or greater
|
||||
# See https://code.djangoproject.com/ticket/19381
|
||||
result = []
|
||||
pieces = accept_language_re.split(lang_string)
|
||||
if pieces[-1]:
|
||||
return []
|
||||
for i in range(0, len(pieces) - 1, 3):
|
||||
first, lang, priority = pieces[i: i + 3]
|
||||
if first:
|
||||
return []
|
||||
priority = priority and float(priority) or 1.0
|
||||
result.append((lang, priority))
|
||||
result.sort(key=lambda k: k[1], reverse=True)
|
||||
return result
|
||||
|
||||
|
||||
def get_supported_language_variant(lang_code, strict=False):
|
||||
"""
|
||||
Returns the language-code that's listed in supported languages, possibly
|
||||
selecting a more generic variant. Raises LookupError if nothing found.
|
||||
If `strict` is False (the default), the function will look for an alternative
|
||||
country-specific variant when the currently checked is not found.
|
||||
lru_cache should have a maxsize to prevent from memory exhaustion attacks,
|
||||
as the provided language codes are taken from the HTTP request. See also
|
||||
<https://www.djangoproject.com/weblog/2007/oct/26/security-fix/>.
|
||||
"""
|
||||
if lang_code:
|
||||
# If 'fr-ca' is not supported, try special fallback or language-only 'fr'.
|
||||
possible_lang_codes = [lang_code]
|
||||
try:
|
||||
# TODO skip this, or import updated LANG_INFO format from __future__
|
||||
# (fallback option wasn't added until
|
||||
# https://github.com/django/django/commit/5dcdbe95c749d36072f527e120a8cb463199ae0d)
|
||||
possible_lang_codes.extend(LANG_INFO[lang_code]['fallback'])
|
||||
except KeyError:
|
||||
pass
|
||||
generic_lang_code = lang_code.split('-')[0]
|
||||
possible_lang_codes.append(generic_lang_code)
|
||||
supported_lang_codes = dict(settings.LANGUAGES)
|
||||
|
||||
for code in possible_lang_codes:
|
||||
# Note: django 1.4 implementation of check_for_language is OK to use
|
||||
if code in supported_lang_codes and translation.check_for_language(code):
|
||||
return code
|
||||
if not strict:
|
||||
# if fr-fr is not supported, try fr-ca.
|
||||
for supported_code in supported_lang_codes:
|
||||
if supported_code.startswith(generic_lang_code + '-'):
|
||||
return supported_code
|
||||
raise LookupError(lang_code)
|
||||
|
||||
|
||||
def get_language_from_request(request, check_path=False):
|
||||
"""
|
||||
Analyzes the request to find what language the user wants the system to
|
||||
show. Only languages listed in settings.LANGUAGES are taken into account.
|
||||
If the user requests a sublanguage where we have a main language, we send
|
||||
out the main language.
|
||||
If check_path is True, the URL path prefix will be checked for a language
|
||||
code, otherwise this is skipped for backwards compatibility.
|
||||
"""
|
||||
if check_path:
|
||||
# Note: django 1.4 implementation of get_language_from_path is OK to use
|
||||
lang_code = translation.get_language_from_path(request.path_info)
|
||||
if lang_code is not None:
|
||||
return lang_code
|
||||
|
||||
supported_lang_codes = dict(settings.LANGUAGES)
|
||||
|
||||
if hasattr(request, 'session'):
|
||||
lang_code = request.session.get(LANGUAGE_SESSION_KEY)
|
||||
# Note: django 1.4 implementation of check_for_language is OK to use
|
||||
if lang_code in supported_lang_codes and lang_code is not None and translation.check_for_language(lang_code):
|
||||
return lang_code
|
||||
|
||||
lang_code = request.COOKIES.get(settings.LANGUAGE_COOKIE_NAME)
|
||||
|
||||
try:
|
||||
return get_supported_language_variant(lang_code)
|
||||
except LookupError:
|
||||
pass
|
||||
|
||||
accept = request.META.get('HTTP_ACCEPT_LANGUAGE', '')
|
||||
# broken in 1.4, so defined above
|
||||
for accept_lang, unused in parse_accept_lang_header(accept):
|
||||
if accept_lang == '*':
|
||||
break
|
||||
|
||||
if not language_code_re.search(accept_lang):
|
||||
continue
|
||||
|
||||
try:
|
||||
return get_supported_language_variant(accept_lang)
|
||||
except LookupError:
|
||||
continue
|
||||
|
||||
try:
|
||||
return get_supported_language_variant(settings.LANGUAGE_CODE)
|
||||
except LookupError:
|
||||
return settings.LANGUAGE_CODE
|
||||
@@ -1,10 +1,11 @@
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.template.base import TemplateDoesNotExist
|
||||
from django.template.loader import make_origin, get_template_from_string
|
||||
from django.template.loaders.filesystem import Loader as FilesystemLoader
|
||||
from django.template.loaders.app_directories import Loader as AppDirectoriesLoader
|
||||
from django.template import Engine
|
||||
|
||||
from edxmako.template import Template
|
||||
|
||||
@@ -50,14 +51,17 @@ class MakoLoader(object):
|
||||
return template, None
|
||||
else:
|
||||
# This is a regular template
|
||||
origin = make_origin(file_path, self.load_template_source, template_name, template_dirs)
|
||||
try:
|
||||
template = get_template_from_string(source, origin, template_name)
|
||||
template = Engine.get_default().from_string(source)
|
||||
return template, None
|
||||
except ImproperlyConfigured:
|
||||
# Either no DjangoTemplates engine was configured -or- multiple engines
|
||||
# were configured, making the get_default() call above fail.
|
||||
raise
|
||||
except TemplateDoesNotExist:
|
||||
# If compiling the template we found raises TemplateDoesNotExist, back off to
|
||||
# returning the source and display name for the template we were asked to load.
|
||||
# This allows for correct identification (later) of the actual template that does
|
||||
# If compiling the loaded template raises TemplateDoesNotExist, back off to
|
||||
# returning the source and display name for the requested template.
|
||||
# This allows for eventual correct identification of the actual template that does
|
||||
# not exist.
|
||||
return source, file_path
|
||||
|
||||
@@ -71,13 +75,15 @@ class MakoLoader(object):
|
||||
|
||||
class MakoFilesystemLoader(MakoLoader):
|
||||
is_usable = True
|
||||
_accepts_engine_in_init = True
|
||||
|
||||
def __init__(self):
|
||||
MakoLoader.__init__(self, FilesystemLoader())
|
||||
def __init__(self, *args):
|
||||
MakoLoader.__init__(self, FilesystemLoader(*args))
|
||||
|
||||
|
||||
class MakoAppDirectoriesLoader(MakoLoader):
|
||||
is_usable = True
|
||||
_accepts_engine_in_init = True
|
||||
|
||||
def __init__(self):
|
||||
MakoLoader.__init__(self, AppDirectoriesLoader())
|
||||
def __init__(self, *args):
|
||||
MakoLoader.__init__(self, AppDirectoriesLoader(*args))
|
||||
|
||||
@@ -23,6 +23,10 @@ class Command(BaseCommand):
|
||||
|
||||
help = "Preprocess asset template files to ready them for compilation."
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('files', type=unicode, nargs='+', help='files to pre-process')
|
||||
parser.add_argument('dest_dir', type=unicode, help='destination directory')
|
||||
|
||||
def handle(self, *args, **options):
|
||||
theme_name = getattr(settings, "THEME_NAME", None)
|
||||
use_custom_theme = settings.FEATURES.get("USE_CUSTOM_THEME", False)
|
||||
@@ -30,8 +34,8 @@ class Command(BaseCommand):
|
||||
# No custom theme, nothing to do!
|
||||
return
|
||||
|
||||
dest_dir = args[-1]
|
||||
for source_file in args[:-1]:
|
||||
dest_dir = options['dest_dir']
|
||||
for source_file in options['files']:
|
||||
self.process_one_file(source_file, dest_dir, theme_name)
|
||||
|
||||
def process_one_file(self, source_file, dest_dir, theme_name):
|
||||
|
||||
@@ -13,7 +13,10 @@
|
||||
# limitations under the License.
|
||||
|
||||
import threading
|
||||
from django.conf import settings
|
||||
from django.template import RequestContext
|
||||
from django.template.context import _builtin_context_processors
|
||||
from django.utils.module_loading import import_string
|
||||
from util.request import safe_get_host
|
||||
|
||||
REQUEST_CONTEXT = threading.local()
|
||||
@@ -31,6 +34,15 @@ class MakoMiddleware(object):
|
||||
return response
|
||||
|
||||
|
||||
def get_template_context_processors():
|
||||
"""
|
||||
Returns the context processors defined in settings.TEMPLATES.
|
||||
"""
|
||||
context_processors = _builtin_context_processors
|
||||
context_processors += tuple(settings.DEFAULT_TEMPLATE_ENGINE['OPTIONS']['context_processors'])
|
||||
return tuple(import_string(path) for path in context_processors)
|
||||
|
||||
|
||||
def get_template_request_context():
|
||||
"""
|
||||
Returns the template processing context to use for the current request,
|
||||
@@ -42,4 +54,12 @@ def get_template_request_context():
|
||||
context = RequestContext(request)
|
||||
context['is_secure'] = request.is_secure()
|
||||
context['site'] = safe_get_host(request)
|
||||
|
||||
# This used to happen when a RequestContext object was initialized but was
|
||||
# moved to a different part of the logic when template engines were introduced.
|
||||
# Since we are not using template engines we do this here.
|
||||
# https://github.com/django/django/commit/37505b6397058bcc3460f23d48a7de9641cd6ef0
|
||||
for processor in get_template_context_processors():
|
||||
context.update(processor(request))
|
||||
|
||||
return context
|
||||
|
||||
@@ -27,6 +27,7 @@ class RestrictedCourseForm(forms.ModelForm):
|
||||
"""
|
||||
class Meta(object):
|
||||
model = RestrictedCourse
|
||||
fields = '__all__'
|
||||
|
||||
def clean_course_key(self):
|
||||
"""Validate the course key.
|
||||
@@ -60,6 +61,7 @@ class IPFilterForm(forms.ModelForm):
|
||||
|
||||
class Meta(object):
|
||||
model = IPFilter
|
||||
fields = '__all__'
|
||||
|
||||
def _is_valid_ip(self, address):
|
||||
"""Whether or not address is a valid ipv4 address or ipv6 address"""
|
||||
|
||||
@@ -1,114 +1,104 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django_countries.fields
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
import xmodule_django.models
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding model 'EmbargoedCourse'
|
||||
db.create_table('embargo_embargoedcourse', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('course_id', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255, db_index=True)),
|
||||
('embargoed', self.gf('django.db.models.fields.BooleanField')(default=False)),
|
||||
))
|
||||
db.send_create_signal('embargo', ['EmbargoedCourse'])
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
# Adding model 'EmbargoedState'
|
||||
db.create_table('embargo_embargoedstate', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('change_date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
|
||||
('changed_by', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, on_delete=models.PROTECT)),
|
||||
('enabled', self.gf('django.db.models.fields.BooleanField')(default=False)),
|
||||
('embargoed_countries', self.gf('django.db.models.fields.TextField')(blank=True)),
|
||||
))
|
||||
db.send_create_signal('embargo', ['EmbargoedState'])
|
||||
|
||||
# Adding model 'IPFilter'
|
||||
db.create_table('embargo_ipfilter', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('change_date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
|
||||
('changed_by', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, on_delete=models.PROTECT)),
|
||||
('enabled', self.gf('django.db.models.fields.BooleanField')(default=False)),
|
||||
('whitelist', self.gf('django.db.models.fields.TextField')(blank=True)),
|
||||
('blacklist', self.gf('django.db.models.fields.TextField')(blank=True)),
|
||||
))
|
||||
db.send_create_signal('embargo', ['IPFilter'])
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting model 'EmbargoedCourse'
|
||||
db.delete_table('embargo_embargoedcourse')
|
||||
|
||||
# Deleting model 'EmbargoedState'
|
||||
db.delete_table('embargo_embargoedstate')
|
||||
|
||||
# Deleting model 'IPFilter'
|
||||
db.delete_table('embargo_ipfilter')
|
||||
|
||||
|
||||
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'})
|
||||
},
|
||||
'embargo.embargoedcourse': {
|
||||
'Meta': {'object_name': 'EmbargoedCourse'},
|
||||
'course_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
|
||||
'embargoed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||
},
|
||||
'embargo.embargoedstate': {
|
||||
'Meta': {'object_name': 'EmbargoedState'},
|
||||
'change_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.PROTECT'}),
|
||||
'embargoed_countries': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||
},
|
||||
'embargo.ipfilter': {
|
||||
'Meta': {'object_name': 'IPFilter'},
|
||||
'blacklist': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'change_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.PROTECT'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'whitelist': ('django.db.models.fields.TextField', [], {'blank': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['embargo']
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Country',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('country', django_countries.fields.CountryField(help_text='Two character ISO country code.', unique=True, max_length=2, db_index=True)),
|
||||
],
|
||||
options={
|
||||
'ordering': ['country'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CountryAccessRule',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('rule_type', models.CharField(default=b'blacklist', help_text='Whether to include or exclude the given course. If whitelist countries are specified, then ONLY users from whitelisted countries will be able to access the course. If blacklist countries are specified, then users from blacklisted countries will NOT be able to access the course.', max_length=255, choices=[(b'whitelist', b'Whitelist (allow only these countries)'), (b'blacklist', b'Blacklist (block these countries)')])),
|
||||
('country', models.ForeignKey(help_text='The country to which this rule applies.', to='embargo.Country')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CourseAccessRuleHistory',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('timestamp', models.DateTimeField(auto_now_add=True, db_index=True)),
|
||||
('course_key', xmodule_django.models.CourseKeyField(max_length=255, db_index=True)),
|
||||
('snapshot', models.TextField(null=True, blank=True)),
|
||||
],
|
||||
options={
|
||||
'get_latest_by': 'timestamp',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EmbargoedCourse',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('course_id', xmodule_django.models.CourseKeyField(unique=True, max_length=255, db_index=True)),
|
||||
('embargoed', models.BooleanField(default=False)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='EmbargoedState',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('change_date', models.DateTimeField(auto_now_add=True, verbose_name='Change date')),
|
||||
('enabled', models.BooleanField(default=False, verbose_name='Enabled')),
|
||||
('embargoed_countries', models.TextField(help_text=b'A comma-separated list of country codes that fall under U.S. embargo restrictions', blank=True)),
|
||||
('changed_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, editable=False, to=settings.AUTH_USER_MODEL, null=True, verbose_name='Changed by')),
|
||||
],
|
||||
options={
|
||||
'ordering': ('-change_date',),
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='IPFilter',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('change_date', models.DateTimeField(auto_now_add=True, verbose_name='Change date')),
|
||||
('enabled', models.BooleanField(default=False, verbose_name='Enabled')),
|
||||
('whitelist', models.TextField(help_text=b'A comma-separated list of IP addresses that should not fall under embargo restrictions.', blank=True)),
|
||||
('blacklist', models.TextField(help_text=b'A comma-separated list of IP addresses that should fall under embargo restrictions.', blank=True)),
|
||||
('changed_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, editable=False, to=settings.AUTH_USER_MODEL, null=True, verbose_name='Changed by')),
|
||||
],
|
||||
options={
|
||||
'ordering': ('-change_date',),
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='RestrictedCourse',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('course_key', xmodule_django.models.CourseKeyField(help_text='The course key for the restricted course.', unique=True, max_length=255, db_index=True)),
|
||||
('enroll_msg_key', models.CharField(default=b'default', help_text='The message to show when a user is blocked from enrollment.', max_length=255, choices=[(b'default', b'Default'), (b'embargo', b'Embargo')])),
|
||||
('access_msg_key', models.CharField(default=b'default', help_text='The message to show when a user is blocked from accessing a course.', max_length=255, choices=[(b'default', b'Default'), (b'embargo', b'Embargo')])),
|
||||
('disable_access_check', models.BooleanField(default=False, help_text='Allow users who enrolled in an allowed country to access restricted courses from excluded countries.')),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='countryaccessrule',
|
||||
name='restricted_course',
|
||||
field=models.ForeignKey(help_text='The course to which this rule applies.', to='embargo.RestrictedCourse'),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='countryaccessrule',
|
||||
unique_together=set([('restricted_course', 'country')]),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,141 +0,0 @@
|
||||
# -*- 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 'Country'
|
||||
db.create_table('embargo_country', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('country', self.gf('django_countries.fields.CountryField')(unique=True, max_length=2, db_index=True)),
|
||||
))
|
||||
db.send_create_signal('embargo', ['Country'])
|
||||
|
||||
# Adding model 'RestrictedCourse'
|
||||
db.create_table('embargo_restrictedcourse', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('course_key', self.gf('xmodule_django.models.CourseKeyField')(unique=True, max_length=255, db_index=True)),
|
||||
('enroll_msg_key', self.gf('django.db.models.fields.CharField')(default='default', max_length=255)),
|
||||
('access_msg_key', self.gf('django.db.models.fields.CharField')(default='default', max_length=255)),
|
||||
))
|
||||
db.send_create_signal('embargo', ['RestrictedCourse'])
|
||||
|
||||
# Adding model 'CountryAccessRule'
|
||||
db.create_table('embargo_countryaccessrule', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('rule_type', self.gf('django.db.models.fields.CharField')(default='blacklist', max_length=255)),
|
||||
('restricted_course', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['embargo.RestrictedCourse'])),
|
||||
('country', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['embargo.Country'])),
|
||||
))
|
||||
db.send_create_signal('embargo', ['CountryAccessRule'])
|
||||
|
||||
# Adding unique constraint on 'CountryAccessRule', fields ['restricted_course', 'country']
|
||||
db.create_unique('embargo_countryaccessrule', ['restricted_course_id', 'country_id'])
|
||||
|
||||
|
||||
# Changing field 'EmbargoedCourse.course_id'
|
||||
db.alter_column('embargo_embargoedcourse', 'course_id', self.gf('xmodule_django.models.CourseKeyField')(unique=True, max_length=255))
|
||||
|
||||
def backwards(self, orm):
|
||||
# Removing unique constraint on 'CountryAccessRule', fields ['restricted_course', 'country']
|
||||
db.delete_unique('embargo_countryaccessrule', ['restricted_course_id', 'country_id'])
|
||||
|
||||
# Deleting model 'Country'
|
||||
db.delete_table('embargo_country')
|
||||
|
||||
# Deleting model 'RestrictedCourse'
|
||||
db.delete_table('embargo_restrictedcourse')
|
||||
|
||||
# Deleting model 'CountryAccessRule'
|
||||
db.delete_table('embargo_countryaccessrule')
|
||||
|
||||
|
||||
# Changing field 'EmbargoedCourse.course_id'
|
||||
db.alter_column('embargo_embargoedcourse', 'course_id', self.gf('django.db.models.fields.CharField')(max_length=255, unique=True))
|
||||
|
||||
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'})
|
||||
},
|
||||
'embargo.country': {
|
||||
'Meta': {'ordering': "['country']", 'object_name': 'Country'},
|
||||
'country': ('django_countries.fields.CountryField', [], {'unique': 'True', 'max_length': '2', 'db_index': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||
},
|
||||
'embargo.countryaccessrule': {
|
||||
'Meta': {'unique_together': "(('restricted_course', 'country'),)", 'object_name': 'CountryAccessRule'},
|
||||
'country': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['embargo.Country']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'restricted_course': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['embargo.RestrictedCourse']"}),
|
||||
'rule_type': ('django.db.models.fields.CharField', [], {'default': "'blacklist'", 'max_length': '255'})
|
||||
},
|
||||
'embargo.embargoedcourse': {
|
||||
'Meta': {'object_name': 'EmbargoedCourse'},
|
||||
'course_id': ('xmodule_django.models.CourseKeyField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
|
||||
'embargoed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||
},
|
||||
'embargo.embargoedstate': {
|
||||
'Meta': {'object_name': 'EmbargoedState'},
|
||||
'change_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.PROTECT'}),
|
||||
'embargoed_countries': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||
},
|
||||
'embargo.ipfilter': {
|
||||
'Meta': {'object_name': 'IPFilter'},
|
||||
'blacklist': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'change_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.PROTECT'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'whitelist': ('django.db.models.fields.TextField', [], {'blank': 'True'})
|
||||
},
|
||||
'embargo.restrictedcourse': {
|
||||
'Meta': {'object_name': 'RestrictedCourse'},
|
||||
'access_msg_key': ('django.db.models.fields.CharField', [], {'default': "'default'", 'max_length': '255'}),
|
||||
'course_key': ('xmodule_django.models.CourseKeyField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
|
||||
'enroll_msg_key': ('django.db.models.fields.CharField', [], {'default': "'default'", 'max_length': '255'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['embargo']
|
||||
@@ -0,0 +1,32 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# Converted from the original South migration 0003_add_countries.py
|
||||
|
||||
from django.db import migrations, models
|
||||
from django_countries import countries
|
||||
|
||||
|
||||
def create_embargo_countries(apps, schema_editor):
|
||||
"""Populate the available countries with all 2-character ISO country codes. """
|
||||
country_model = apps.get_model("embargo", "Country")
|
||||
db_alias = schema_editor.connection.alias
|
||||
for country_code, __ in list(countries):
|
||||
country_model.objects.using(db_alias).get_or_create(country=country_code)
|
||||
|
||||
def remove_embargo_countries(apps, schema_editor):
|
||||
"""Clear all available countries. """
|
||||
country_model = apps.get_model("embargo", "Country")
|
||||
db_alias = schema_editor.connection.alias
|
||||
country_model.objects.using(db_alias).all().delete()
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('embargo', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(create_embargo_countries, remove_embargo_countries),
|
||||
]
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import DataMigration
|
||||
from django.db import models
|
||||
from django_countries import countries
|
||||
|
||||
class Migration(DataMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
"""Populate the available countries with all 2-character ISO country codes. """
|
||||
for country_code, __ in list(countries):
|
||||
orm.Country.objects.get_or_create(country=country_code)
|
||||
|
||||
def backwards(self, orm):
|
||||
"""Clear all available countries. """
|
||||
orm.Country.objects.all().delete()
|
||||
|
||||
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'})
|
||||
},
|
||||
'embargo.country': {
|
||||
'Meta': {'object_name': 'Country'},
|
||||
'country': ('django_countries.fields.CountryField', [], {'unique': 'True', 'max_length': '2', 'db_index': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||
},
|
||||
'embargo.countryaccessrule': {
|
||||
'Meta': {'unique_together': "(('restricted_course', 'rule_type'),)", 'object_name': 'CountryAccessRule'},
|
||||
'country': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['embargo.Country']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'restricted_course': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['embargo.RestrictedCourse']"}),
|
||||
'rule_type': ('django.db.models.fields.CharField', [], {'max_length': '255'})
|
||||
},
|
||||
'embargo.embargoedcourse': {
|
||||
'Meta': {'object_name': 'EmbargoedCourse'},
|
||||
'course_id': ('xmodule_django.models.CourseKeyField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
|
||||
'embargoed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||
},
|
||||
'embargo.embargoedstate': {
|
||||
'Meta': {'object_name': 'EmbargoedState'},
|
||||
'change_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.PROTECT'}),
|
||||
'embargoed_countries': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||
},
|
||||
'embargo.ipfilter': {
|
||||
'Meta': {'object_name': 'IPFilter'},
|
||||
'blacklist': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'change_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.PROTECT'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'whitelist': ('django.db.models.fields.TextField', [], {'blank': 'True'})
|
||||
},
|
||||
'embargo.restrictedcourse': {
|
||||
'Meta': {'object_name': 'RestrictedCourse'},
|
||||
'access_msg_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'course_key': ('xmodule_django.models.CourseKeyField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
|
||||
'enroll_msg_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['embargo']
|
||||
symmetrical = True
|
||||
@@ -1,129 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import DataMigration
|
||||
from django.db import models
|
||||
|
||||
class Migration(DataMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
"""Move the current course embargo configuration to the new models. """
|
||||
for old_course in orm.EmbargoedCourse.objects.all():
|
||||
new_course, __ = orm.RestrictedCourse.objects.get_or_create(course_key=old_course.course_id)
|
||||
|
||||
# Set the message keys to 'embargo'
|
||||
new_course.enroll_msg_key = 'embargo'
|
||||
new_course.access_msg_key = 'embargo'
|
||||
new_course.save()
|
||||
|
||||
for country in self._embargoed_countries_list(orm):
|
||||
country_model = orm.Country.objects.get(country=country)
|
||||
orm.CountryAccessRule.objects.get_or_create(
|
||||
country=country_model,
|
||||
rule_type='blacklist',
|
||||
restricted_course=new_course
|
||||
)
|
||||
|
||||
def backwards(self, orm):
|
||||
"""No backwards migration required since the forward migration is idempotent. """
|
||||
pass
|
||||
|
||||
def _embargoed_countries_list(self, orm):
|
||||
"""Retrieve the list of embargoed countries from the existing tables. """
|
||||
# We need to replicate some application logic here, because South
|
||||
# doesn't give us access to class methods on the Django model objects.
|
||||
try:
|
||||
current_config = orm.EmbargoedState.objects.order_by('-change_date')[0]
|
||||
if current_config.enabled and current_config.embargoed_countries:
|
||||
return [
|
||||
country.strip().upper() for country
|
||||
in current_config.embargoed_countries.split(',')
|
||||
]
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
return []
|
||||
|
||||
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'})
|
||||
},
|
||||
'embargo.country': {
|
||||
'Meta': {'ordering': "['country']", 'object_name': 'Country'},
|
||||
'country': ('django_countries.fields.CountryField', [], {'unique': 'True', 'max_length': '2', 'db_index': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||
},
|
||||
'embargo.countryaccessrule': {
|
||||
'Meta': {'unique_together': "(('restricted_course', 'country'),)", 'object_name': 'CountryAccessRule'},
|
||||
'country': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['embargo.Country']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'restricted_course': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['embargo.RestrictedCourse']"}),
|
||||
'rule_type': ('django.db.models.fields.CharField', [], {'default': "'blacklist'", 'max_length': '255'})
|
||||
},
|
||||
'embargo.embargoedcourse': {
|
||||
'Meta': {'object_name': 'EmbargoedCourse'},
|
||||
'course_id': ('xmodule_django.models.CourseKeyField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
|
||||
'embargoed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||
},
|
||||
'embargo.embargoedstate': {
|
||||
'Meta': {'object_name': 'EmbargoedState'},
|
||||
'change_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.PROTECT'}),
|
||||
'embargoed_countries': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||
},
|
||||
'embargo.ipfilter': {
|
||||
'Meta': {'object_name': 'IPFilter'},
|
||||
'blacklist': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'change_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.PROTECT'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'whitelist': ('django.db.models.fields.TextField', [], {'blank': 'True'})
|
||||
},
|
||||
'embargo.restrictedcourse': {
|
||||
'Meta': {'object_name': 'RestrictedCourse'},
|
||||
'access_msg_key': ('django.db.models.fields.CharField', [], {'default': "'default'", 'max_length': '255'}),
|
||||
'course_key': ('xmodule_django.models.CourseKeyField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
|
||||
'enroll_msg_key': ('django.db.models.fields.CharField', [], {'default': "'default'", 'max_length': '255'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['embargo']
|
||||
symmetrical = True
|
||||
@@ -1,114 +0,0 @@
|
||||
# -*- 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 'CourseAccessRuleHistory'
|
||||
db.create_table('embargo_courseaccessrulehistory', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('timestamp', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, db_index=True, blank=True)),
|
||||
('course_key', self.gf('xmodule_django.models.CourseKeyField')(max_length=255, db_index=True)),
|
||||
('snapshot', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
|
||||
))
|
||||
db.send_create_signal('embargo', ['CourseAccessRuleHistory'])
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting model 'CourseAccessRuleHistory'
|
||||
db.delete_table('embargo_courseaccessrulehistory')
|
||||
|
||||
|
||||
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'})
|
||||
},
|
||||
'embargo.country': {
|
||||
'Meta': {'ordering': "['country']", 'object_name': 'Country'},
|
||||
'country': ('django_countries.fields.CountryField', [], {'unique': 'True', 'max_length': '2', 'db_index': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||
},
|
||||
'embargo.countryaccessrule': {
|
||||
'Meta': {'unique_together': "(('restricted_course', 'country'),)", 'object_name': 'CountryAccessRule'},
|
||||
'country': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['embargo.Country']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'restricted_course': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['embargo.RestrictedCourse']"}),
|
||||
'rule_type': ('django.db.models.fields.CharField', [], {'default': "'blacklist'", 'max_length': '255'})
|
||||
},
|
||||
'embargo.courseaccessrulehistory': {
|
||||
'Meta': {'object_name': 'CourseAccessRuleHistory'},
|
||||
'course_key': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'snapshot': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'})
|
||||
},
|
||||
'embargo.embargoedcourse': {
|
||||
'Meta': {'object_name': 'EmbargoedCourse'},
|
||||
'course_id': ('xmodule_django.models.CourseKeyField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
|
||||
'embargoed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||
},
|
||||
'embargo.embargoedstate': {
|
||||
'Meta': {'object_name': 'EmbargoedState'},
|
||||
'change_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.PROTECT'}),
|
||||
'embargoed_countries': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||
},
|
||||
'embargo.ipfilter': {
|
||||
'Meta': {'object_name': 'IPFilter'},
|
||||
'blacklist': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'change_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.PROTECT'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'whitelist': ('django.db.models.fields.TextField', [], {'blank': 'True'})
|
||||
},
|
||||
'embargo.restrictedcourse': {
|
||||
'Meta': {'object_name': 'RestrictedCourse'},
|
||||
'access_msg_key': ('django.db.models.fields.CharField', [], {'default': "'default'", 'max_length': '255'}),
|
||||
'course_key': ('xmodule_django.models.CourseKeyField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
|
||||
'enroll_msg_key': ('django.db.models.fields.CharField', [], {'default': "'default'", 'max_length': '255'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['embargo']
|
||||
@@ -1,111 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from south.utils import datetime_utils as datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding field 'RestrictedCourse.disable_access_check'
|
||||
db.add_column('embargo_restrictedcourse', 'disable_access_check',
|
||||
self.gf('django.db.models.fields.BooleanField')(default=False),
|
||||
keep_default=False)
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting field 'RestrictedCourse.disable_access_check'
|
||||
db.delete_column('embargo_restrictedcourse', 'disable_access_check')
|
||||
|
||||
|
||||
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'})
|
||||
},
|
||||
'embargo.country': {
|
||||
'Meta': {'ordering': "['country']", 'object_name': 'Country'},
|
||||
'country': ('django_countries.fields.CountryField', [], {'unique': 'True', 'max_length': '2', 'db_index': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||
},
|
||||
'embargo.countryaccessrule': {
|
||||
'Meta': {'unique_together': "(('restricted_course', 'country'),)", 'object_name': 'CountryAccessRule'},
|
||||
'country': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['embargo.Country']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'restricted_course': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['embargo.RestrictedCourse']"}),
|
||||
'rule_type': ('django.db.models.fields.CharField', [], {'default': "'blacklist'", 'max_length': '255'})
|
||||
},
|
||||
'embargo.courseaccessrulehistory': {
|
||||
'Meta': {'object_name': 'CourseAccessRuleHistory'},
|
||||
'course_key': ('xmodule_django.models.CourseKeyField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'snapshot': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'})
|
||||
},
|
||||
'embargo.embargoedcourse': {
|
||||
'Meta': {'object_name': 'EmbargoedCourse'},
|
||||
'course_id': ('xmodule_django.models.CourseKeyField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
|
||||
'embargoed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||
},
|
||||
'embargo.embargoedstate': {
|
||||
'Meta': {'object_name': 'EmbargoedState'},
|
||||
'change_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.PROTECT'}),
|
||||
'embargoed_countries': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||
},
|
||||
'embargo.ipfilter': {
|
||||
'Meta': {'object_name': 'IPFilter'},
|
||||
'blacklist': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'change_date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'changed_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'on_delete': 'models.PROTECT'}),
|
||||
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'whitelist': ('django.db.models.fields.TextField', [], {'blank': 'True'})
|
||||
},
|
||||
'embargo.restrictedcourse': {
|
||||
'Meta': {'object_name': 'RestrictedCourse'},
|
||||
'access_msg_key': ('django.db.models.fields.CharField', [], {'default': "'default'", 'max_length': '255'}),
|
||||
'course_key': ('xmodule_django.models.CourseKeyField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
|
||||
'disable_access_check': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
|
||||
'enroll_msg_key': ('django.db.models.fields.CharField', [], {'default': "'default'", 'max_length': '255'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['embargo']
|
||||
@@ -159,7 +159,6 @@ class EmbargoCheckAccessApiTests(ModuleStoreTestCase):
|
||||
# (because when we save it, it will set the database field to an empty string instead of NULL)
|
||||
query = "UPDATE auth_userprofile SET country = NULL WHERE id = %s"
|
||||
connection.cursor().execute(query, [str(self.user.profile.id)])
|
||||
transaction.commit_unless_managed()
|
||||
|
||||
# Verify that we can check the user's access without error
|
||||
result = embargo_api.check_course_access(self.course.id, user=self.user, ip_address='0.0.0.0')
|
||||
@@ -171,7 +170,7 @@ class EmbargoCheckAccessApiTests(ModuleStoreTestCase):
|
||||
# (restricted course, but pass all the checks)
|
||||
# This is the worst case, so it will hit all of the
|
||||
# caching code.
|
||||
with self.assertNumQueries(4):
|
||||
with self.assertNumQueries(3):
|
||||
embargo_api.check_course_access(self.course.id, user=self.user, ip_address='0.0.0.0')
|
||||
|
||||
with self.assertNumQueries(0):
|
||||
|
||||
@@ -3,7 +3,7 @@ Enrollment API for creating, updating, and deleting enrollments. Also provides a
|
||||
course level, such as available course modes.
|
||||
|
||||
"""
|
||||
from django.utils import importlib
|
||||
import importlib
|
||||
import logging
|
||||
from django.conf import settings
|
||||
from django.core.cache import cache
|
||||
|
||||
@@ -1,90 +1,34 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding model 'ExternalAuthMap'
|
||||
db.create_table('external_auth_externalauthmap', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('external_id', self.gf('django.db.models.fields.CharField')(max_length=255, db_index=True)),
|
||||
('external_domain', self.gf('django.db.models.fields.CharField')(max_length=255, db_index=True)),
|
||||
('external_credentials', self.gf('django.db.models.fields.TextField')(blank=True)),
|
||||
('external_email', self.gf('django.db.models.fields.CharField')(max_length=255, db_index=True)),
|
||||
('external_name', self.gf('django.db.models.fields.CharField')(db_index=True, max_length=255, blank=True)),
|
||||
('user', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['auth.User'], unique=True, null=True)),
|
||||
('internal_password', self.gf('django.db.models.fields.CharField')(max_length=31, blank=True)),
|
||||
('dtcreated', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
|
||||
('dtsignup', self.gf('django.db.models.fields.DateTimeField')(null=True)),
|
||||
))
|
||||
db.send_create_signal('external_auth', ['ExternalAuthMap'])
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
# Adding unique constraint on 'ExternalAuthMap', fields ['external_id', 'external_domain']
|
||||
db.create_unique('external_auth_externalauthmap', ['external_id', 'external_domain'])
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Removing unique constraint on 'ExternalAuthMap', fields ['external_id', 'external_domain']
|
||||
db.delete_unique('external_auth_externalauthmap', ['external_id', 'external_domain'])
|
||||
|
||||
# Deleting model 'ExternalAuthMap'
|
||||
db.delete_table('external_auth_externalauthmap')
|
||||
|
||||
|
||||
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'})
|
||||
},
|
||||
'external_auth.externalauthmap': {
|
||||
'Meta': {'unique_together': "(('external_id', 'external_domain'),)", 'object_name': 'ExternalAuthMap'},
|
||||
'dtcreated': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
'dtsignup': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
|
||||
'external_credentials': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
|
||||
'external_domain': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'external_email': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'external_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'external_name': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'internal_password': ('django.db.models.fields.CharField', [], {'max_length': '31', 'blank': 'True'}),
|
||||
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True', 'null': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['external_auth']
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ExternalAuthMap',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('external_id', models.CharField(max_length=255, db_index=True)),
|
||||
('external_domain', models.CharField(max_length=255, db_index=True)),
|
||||
('external_credentials', models.TextField(blank=True)),
|
||||
('external_email', models.CharField(max_length=255, db_index=True)),
|
||||
('external_name', models.CharField(db_index=True, max_length=255, blank=True)),
|
||||
('internal_password', models.CharField(max_length=31, blank=True)),
|
||||
('dtcreated', models.DateTimeField(auto_now_add=True, verbose_name=b'creation date')),
|
||||
('dtsignup', models.DateTimeField(null=True, verbose_name=b'signup date')),
|
||||
('user', models.OneToOneField(null=True, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='externalauthmap',
|
||||
unique_together=set([('external_id', 'external_domain')]),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -15,6 +15,7 @@ from django.contrib.auth.models import User
|
||||
|
||||
class ExternalAuthMap(models.Model):
|
||||
class Meta(object):
|
||||
app_label = "external_auth"
|
||||
unique_together = (('external_id', 'external_domain'), )
|
||||
|
||||
external_id = models.CharField(max_length=255, db_index=True)
|
||||
|
||||
@@ -13,7 +13,7 @@ from django.test.client import RequestFactory, Client as DjangoTestClient
|
||||
from django.test.utils import override_settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.contrib.auth.models import AnonymousUser, User
|
||||
from django.utils.importlib import import_module
|
||||
from importlib import import_module
|
||||
from edxmako.tests import mako_middleware_process_request
|
||||
from external_auth.models import ExternalAuthMap
|
||||
from external_auth.views import (
|
||||
|
||||
@@ -195,7 +195,7 @@ def _external_login_or_signup(request,
|
||||
if settings.AUTHENTICATION_BACKENDS:
|
||||
auth_backend = settings.AUTHENTICATION_BACKENDS[0]
|
||||
else:
|
||||
auth_backend = 'django.contrib.auth.backends.ModelBackend'
|
||||
auth_backend = 'ratelimitbackend.backends.RateLimitModelBackend'
|
||||
user.backend = auth_backend
|
||||
if settings.FEATURES['SQUELCH_PII_IN_LOGS']:
|
||||
AUDIT_LOG.info(u'Linked user.id: {0} logged in via Shibboleth'.format(user.id))
|
||||
@@ -204,7 +204,7 @@ def _external_login_or_signup(request,
|
||||
elif uses_certs:
|
||||
# Certificates are trusted, so just link the user and log the action
|
||||
user = internal_user
|
||||
user.backend = 'django.contrib.auth.backends.ModelBackend'
|
||||
user.backend = 'ratelimitbackend.backends.RateLimitModelBackend'
|
||||
if settings.FEATURES['SQUELCH_PII_IN_LOGS']:
|
||||
AUDIT_LOG.info(u'Linked user_id {0} logged in via SSL certificate'.format(user.id))
|
||||
else:
|
||||
@@ -923,7 +923,7 @@ def provider_identity(request):
|
||||
|
||||
response = render_to_response('identity.xml',
|
||||
{'url': get_xrds_url('login', request)},
|
||||
mimetype='text/xml')
|
||||
content_type='text/xml')
|
||||
|
||||
# custom XRDS header necessary for discovery process
|
||||
response['X-XRDS-Location'] = get_xrds_url('identity', request)
|
||||
@@ -937,7 +937,7 @@ def provider_xrds(request):
|
||||
|
||||
response = render_to_response('xrds.xml',
|
||||
{'url': get_xrds_url('login', request)},
|
||||
mimetype='text/xml')
|
||||
content_type='text/xml')
|
||||
|
||||
# custom XRDS header necessary for discovery process
|
||||
response['X-XRDS-Location'] = get_xrds_url('xrds', request)
|
||||
|
||||
@@ -6,14 +6,13 @@ import json
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db.utils import DatabaseError
|
||||
from django.test.client import Client
|
||||
from django.test.testcases import TestCase
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from mock import patch
|
||||
|
||||
from xmodule.exceptions import HeartbeatFailure
|
||||
|
||||
|
||||
@patch('heartbeat.views.modulestore')
|
||||
class HeartbeatTestCase(TestCase):
|
||||
class HeartbeatTestCase(ModuleStoreTestCase):
|
||||
"""
|
||||
Test the heartbeat
|
||||
"""
|
||||
@@ -23,19 +22,20 @@ class HeartbeatTestCase(TestCase):
|
||||
self.heartbeat_url = reverse('heartbeat')
|
||||
return super(HeartbeatTestCase, self).setUp()
|
||||
|
||||
def test_success(self, mock_modulestore): # pylint: disable=unused-argument
|
||||
def test_success(self):
|
||||
response = self.client.get(self.heartbeat_url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
@patch('heartbeat.views.connection')
|
||||
def test_sql_fail(self, mock_connection, mock_modulestore): # pylint: disable=unused-argument
|
||||
mock_connection.cursor.return_value.execute.side_effect = DatabaseError
|
||||
response = self.client.get(self.heartbeat_url)
|
||||
self.assertEqual(response.status_code, 503)
|
||||
response_dict = json.loads(response.content)
|
||||
self.assertIn('SQL', response_dict)
|
||||
def test_sql_fail(self):
|
||||
with patch('heartbeat.views.connection') as mock_connection:
|
||||
mock_connection.cursor.return_value.execute.side_effect = DatabaseError
|
||||
response = self.client.get(self.heartbeat_url)
|
||||
self.assertEqual(response.status_code, 503)
|
||||
response_dict = json.loads(response.content)
|
||||
self.assertIn('SQL', response_dict)
|
||||
|
||||
def test_modulestore_fail(self, mock_modulestore): # pylint: disable=unused-argument
|
||||
mock_modulestore.return_value.heartbeat.side_effect = HeartbeatFailure('msg', 'service')
|
||||
response = self.client.get(self.heartbeat_url)
|
||||
self.assertEqual(response.status_code, 503)
|
||||
def test_modulestore_fail(self):
|
||||
with patch('heartbeat.views.modulestore') as mock_modulestore:
|
||||
mock_modulestore.return_value.heartbeat.side_effect = HeartbeatFailure('msg', 'service')
|
||||
response = self.client.get(self.heartbeat_url)
|
||||
self.assertEqual(response.status_code, 503)
|
||||
|
||||
@@ -4,9 +4,7 @@ Middleware for Language Preferences
|
||||
|
||||
from openedx.core.djangoapps.user_api.preferences.api import get_user_preference
|
||||
from lang_pref import LANGUAGE_KEY
|
||||
# TODO PLAT-671 Import from Django 1.8
|
||||
# from django.utils.translation import LANGUAGE_SESSION_KEY
|
||||
from django_locale.trans_real import LANGUAGE_SESSION_KEY
|
||||
from django.utils.translation import LANGUAGE_SESSION_KEY
|
||||
|
||||
|
||||
class LanguagePreferenceMiddleware(object):
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
from django.test import TestCase
|
||||
from django.test.client import RequestFactory
|
||||
from django.contrib.sessions.middleware import SessionMiddleware
|
||||
# TODO PLAT-671 Import from Django 1.8
|
||||
# from django.utils.translation import LANGUAGE_SESSION_KEY
|
||||
from django_locale.trans_real import LANGUAGE_SESSION_KEY
|
||||
from django.utils.translation import LANGUAGE_SESSION_KEY
|
||||
|
||||
from lang_pref.middleware import LanguagePreferenceMiddleware
|
||||
from openedx.core.djangoapps.user_api.preferences.api import set_user_preference
|
||||
|
||||
@@ -13,7 +13,7 @@ made here will affect the entire platform.
|
||||
That said, if you've decided you really need to monkey-patch the
|
||||
platform (and you've convinced enough people that this is best
|
||||
solution), kindly follow these guidelines:
|
||||
- Reference django_utils_translation.py for a sample implementation.
|
||||
- Reference django_18_upgrade.py for a sample implementation.
|
||||
- Name your module by replacing periods with underscores for the
|
||||
module to be patched:
|
||||
- patching 'django.utils.translation'
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
"""
|
||||
Monkey-patch `django.utils.translation` to not dump header info
|
||||
|
||||
Modify Django's translation module, such that the *gettext functions
|
||||
always return an empty string when attempting to translate an empty
|
||||
string. This overrides the default behavior [0]:
|
||||
> It is convention with GNU gettext to include meta-data as the
|
||||
> translation for the empty string.
|
||||
|
||||
Affected Methods:
|
||||
- gettext
|
||||
- ugettext
|
||||
|
||||
Note: The *ngettext and *pgettext functions are intentionally omitted,
|
||||
as they already behave as expected. The *_lazy functions are implicitly
|
||||
patched, as they wrap their nonlazy equivalents.
|
||||
|
||||
Django's translation module contains a good deal of indirection. For us
|
||||
to patch the module with our own functions, we have to patch
|
||||
`django.utils.translation._trans`. This ensures that the patched
|
||||
behavior will still be used, even if code elsewhere caches a reference
|
||||
to one of the translation functions. If you're curious, check out
|
||||
Django's source code [1].
|
||||
|
||||
[0] https://docs.python.org/2.7/library/gettext.html#the-gnutranslations-class
|
||||
[1] https://github.com/django/django/blob/1.4.8/django/utils/translation/__init__.py#L66
|
||||
"""
|
||||
from django.utils.translation import _trans as translation
|
||||
|
||||
import monkey_patch
|
||||
|
||||
ATTRIBUTES = [
|
||||
'gettext',
|
||||
'ugettext',
|
||||
]
|
||||
|
||||
|
||||
def is_patched():
|
||||
"""
|
||||
Check if the translation module has been monkey-patched
|
||||
"""
|
||||
patched = True
|
||||
for attribute in ATTRIBUTES:
|
||||
if not monkey_patch.is_patched(translation, attribute):
|
||||
patched = False
|
||||
break
|
||||
return patched
|
||||
|
||||
|
||||
def patch():
|
||||
"""
|
||||
Monkey-patch the translation functions
|
||||
|
||||
Affected Methods:
|
||||
- gettext
|
||||
- ugettext
|
||||
"""
|
||||
def decorate(function, message_default=u''):
|
||||
"""
|
||||
Decorate a translation function
|
||||
|
||||
Default message is a unicode string, but gettext overrides this
|
||||
value to return a UTF8 string.
|
||||
"""
|
||||
def dont_translate_empty_string(message):
|
||||
"""
|
||||
Return the empty string when passed a falsey message
|
||||
"""
|
||||
if message:
|
||||
message = function(message)
|
||||
else:
|
||||
message = message_default
|
||||
return message
|
||||
return dont_translate_empty_string
|
||||
gettext = decorate(translation.gettext, '')
|
||||
ugettext = decorate(translation.ugettext)
|
||||
monkey_patch.patch(translation, 'gettext', gettext)
|
||||
monkey_patch.patch(translation, 'ugettext', ugettext)
|
||||
return is_patched()
|
||||
|
||||
|
||||
def unpatch():
|
||||
"""
|
||||
Un-monkey-patch the translation functions
|
||||
"""
|
||||
was_patched = False
|
||||
for name in ATTRIBUTES:
|
||||
# was_patched must be the second half of the or-clause, to avoid
|
||||
# short-circuiting the expression
|
||||
was_patched = monkey_patch.unpatch(translation, name) or was_patched
|
||||
return was_patched
|
||||
@@ -1,337 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Test methods exposed in common/lib/monkey_patch/django_utils_translation.py
|
||||
|
||||
Verify that the Django translation functions (gettext, ngettext,
|
||||
pgettext, ugettext, and derivatives) all return the correct values
|
||||
before, during, and after monkey-patching the django.utils.translation
|
||||
module.
|
||||
|
||||
gettext, ngettext, pgettext, and ugettext must return a translation as
|
||||
output for nonempty input.
|
||||
|
||||
ngettext, pgettext, npgettext, and ungettext must return an empty string
|
||||
for an empty string as input.
|
||||
|
||||
gettext and ugettext will return translation headers, before and after
|
||||
patching.
|
||||
|
||||
gettext and ugettext must return the empty string for any falsey input,
|
||||
while patched.
|
||||
|
||||
*_noop must return the input text.
|
||||
|
||||
*_lazy must return the same text as their non-lazy counterparts.
|
||||
"""
|
||||
# pylint: disable=invalid-name
|
||||
# Let names like `gettext_*` stay lowercase; makes matching easier.
|
||||
# pylint: disable=missing-docstring
|
||||
# All major functions are documented, the rest are self-evident shells.
|
||||
# pylint: disable=no-member
|
||||
# Pylint doesn't see our decorator `translate_with` add the `_` method.
|
||||
# pylint: disable=test-inherits-tests
|
||||
# This test file intentionally defines one base test class (UgettextTest) and
|
||||
# patches the gettext function under test for all subsequent inheriting classes.
|
||||
from unittest import TestCase
|
||||
|
||||
from ddt import data
|
||||
from ddt import ddt
|
||||
from django.utils.translation import _trans
|
||||
from django.utils.translation import gettext
|
||||
from django.utils.translation import gettext_lazy
|
||||
from django.utils.translation import gettext_noop
|
||||
from django.utils.translation import ngettext
|
||||
from django.utils.translation import ngettext_lazy
|
||||
from django.utils.translation import npgettext
|
||||
from django.utils.translation import npgettext_lazy
|
||||
from django.utils.translation import pgettext
|
||||
from django.utils.translation import pgettext_lazy
|
||||
from django.utils.translation import ugettext
|
||||
from django.utils.translation import ugettext_lazy
|
||||
from django.utils.translation import ugettext_noop
|
||||
from django.utils.translation import ungettext
|
||||
from django.utils.translation import ungettext_lazy
|
||||
|
||||
from monkey_patch.django_utils_translation import ATTRIBUTES as attributes_patched
|
||||
from monkey_patch.django_utils_translation import is_patched
|
||||
from monkey_patch.django_utils_translation import patch
|
||||
from monkey_patch.django_utils_translation import unpatch
|
||||
|
||||
# Note: The commented-out function names are explicitly excluded, as
|
||||
# they are not attributes of `django.utils.translation._trans`.
|
||||
# https://github.com/django/django/blob/1.4.8/django/utils/translation/__init__.py#L69
|
||||
attributes_not_patched = [
|
||||
'gettext_noop',
|
||||
'ngettext',
|
||||
'npgettext',
|
||||
'pgettext',
|
||||
'ungettext',
|
||||
# 'gettext_lazy',
|
||||
# 'ngettext_lazy',
|
||||
# 'npgettext_lazy',
|
||||
# 'pgettext_lazy',
|
||||
# 'ugettext_lazy',
|
||||
# 'ugettext_noop',
|
||||
# 'ungettext_lazy',
|
||||
]
|
||||
|
||||
|
||||
class MonkeyPatchTest(TestCase):
|
||||
def setUp(self):
|
||||
"""
|
||||
Remember the current state, then reset
|
||||
"""
|
||||
super(MonkeyPatchTest, self).setUp()
|
||||
self.was_patched = unpatch()
|
||||
self.unpatch_all()
|
||||
self.addCleanup(self.cleanup)
|
||||
|
||||
def cleanup(self):
|
||||
"""
|
||||
Revert translation functions to previous state
|
||||
|
||||
Since the end state varies, we always unpatch to remove any
|
||||
changes, then repatch again iff the module was already
|
||||
patched when the test began.
|
||||
"""
|
||||
self.unpatch_all()
|
||||
if self.was_patched:
|
||||
patch()
|
||||
|
||||
def unpatch_all(self):
|
||||
"""
|
||||
Unpatch the module recursively
|
||||
"""
|
||||
while is_patched():
|
||||
unpatch()
|
||||
|
||||
|
||||
@ddt
|
||||
class PatchTest(MonkeyPatchTest):
|
||||
"""
|
||||
Verify monkey-patching and un-monkey-patching
|
||||
"""
|
||||
@data(*attributes_not_patched)
|
||||
def test_not_patch(self, attribute_name):
|
||||
"""
|
||||
Test that functions are not patched unintentionally
|
||||
"""
|
||||
self.unpatch_all()
|
||||
old_attribute = getattr(_trans, attribute_name)
|
||||
patch()
|
||||
new_attribute = getattr(_trans, attribute_name)
|
||||
self.assertIs(old_attribute, new_attribute)
|
||||
|
||||
@data(*attributes_patched)
|
||||
def test_unpatch(self, attribute):
|
||||
"""
|
||||
Test that unpatch gracefully handles unpatched functions
|
||||
"""
|
||||
patch()
|
||||
self.assertTrue(is_patched())
|
||||
self.unpatch_all()
|
||||
self.assertFalse(is_patched())
|
||||
old_attribute = getattr(_trans, attribute)
|
||||
self.unpatch_all()
|
||||
new_attribute = getattr(_trans, attribute)
|
||||
self.assertIs(old_attribute, new_attribute)
|
||||
self.assertFalse(is_patched())
|
||||
|
||||
@data(*attributes_patched)
|
||||
def test_patch_attributes(self, attribute):
|
||||
"""
|
||||
Test that patch changes the attribute
|
||||
"""
|
||||
self.unpatch_all()
|
||||
self.assertFalse(is_patched())
|
||||
old_attribute = getattr(_trans, attribute)
|
||||
patch()
|
||||
new_attribute = getattr(_trans, attribute)
|
||||
self.assertIsNot(old_attribute, new_attribute)
|
||||
self.assertTrue(is_patched())
|
||||
old_attribute = getattr(_trans, attribute)
|
||||
patch()
|
||||
new_attribute = getattr(_trans, attribute)
|
||||
self.assertIsNot(old_attribute, new_attribute)
|
||||
self.assertTrue(is_patched())
|
||||
|
||||
|
||||
def translate_with(function):
|
||||
"""
|
||||
Decorate a class by setting its `_` translation function
|
||||
"""
|
||||
def decorate(cls):
|
||||
def _(self, *args):
|
||||
# pylint: disable=unused-argument
|
||||
return function(*args)
|
||||
cls._ = _
|
||||
return cls
|
||||
return decorate
|
||||
|
||||
|
||||
@translate_with(ugettext)
|
||||
class UgettextTest(MonkeyPatchTest):
|
||||
"""
|
||||
Test a Django translation function
|
||||
|
||||
Here we consider `ugettext` to be the base/default case. All other
|
||||
translation functions extend, as needed.
|
||||
"""
|
||||
is_unicode = True
|
||||
needs_patched = True
|
||||
header = 'Project-Id-Version: '
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Restore translation text and functions
|
||||
"""
|
||||
super(UgettextTest, self).setUp()
|
||||
if self.is_unicode:
|
||||
self.empty = u''
|
||||
self.nonempty = u'(╯°□°)╯︵ ┻━┻'
|
||||
else:
|
||||
self.empty = ''
|
||||
self.nonempty = 'Hey! Where are you?!'
|
||||
|
||||
def assert_translations(self):
|
||||
"""
|
||||
Assert that the empty and nonempty translations are correct
|
||||
|
||||
The `empty = empty[:]` syntax is intentional. Since subclasses
|
||||
may implement a lazy translation, we must perform a "string
|
||||
operation" to coerce it to a string value. We don't use `str` or
|
||||
`unicode` because we also assert the string type.
|
||||
"""
|
||||
empty, nonempty = self.get_translations()
|
||||
empty = empty[:]
|
||||
nonempty = nonempty[:]
|
||||
if self.is_unicode:
|
||||
self.assertTrue(isinstance(empty, unicode))
|
||||
self.assertTrue(isinstance(nonempty, unicode))
|
||||
else:
|
||||
self.assertTrue(isinstance(empty, str))
|
||||
self.assertTrue(isinstance(nonempty, str))
|
||||
if self.needs_patched and not is_patched():
|
||||
self.assertIn(self.header, empty)
|
||||
else:
|
||||
self.assertNotIn(self.header, empty)
|
||||
self.assertNotIn(self.header, nonempty)
|
||||
|
||||
def get_translations(self):
|
||||
"""
|
||||
Translate the empty and nonempty strings, per `self._`
|
||||
"""
|
||||
empty = self._(self.empty)
|
||||
nonempty = self._(self.nonempty)
|
||||
return (empty, nonempty)
|
||||
|
||||
def test_patch(self):
|
||||
"""
|
||||
Test that `self._` correctly translates text before, during, and
|
||||
after being monkey-patched.
|
||||
"""
|
||||
self.assert_translations()
|
||||
was_successful = patch()
|
||||
self.assertTrue(was_successful)
|
||||
self.assert_translations()
|
||||
was_successful = unpatch()
|
||||
self.assertTrue(was_successful)
|
||||
self.assert_translations()
|
||||
|
||||
|
||||
@translate_with(gettext)
|
||||
class GettextTest(UgettextTest):
|
||||
is_unicode = False
|
||||
|
||||
|
||||
@translate_with(pgettext)
|
||||
class PgettextTest(UgettextTest):
|
||||
needs_patched = False
|
||||
l18n_context = 'monkey_patch'
|
||||
|
||||
def get_translations(self):
|
||||
empty = self._(self.l18n_context, self.empty)
|
||||
nonempty = self._(self.l18n_context, self.nonempty)
|
||||
return (empty, nonempty)
|
||||
|
||||
|
||||
@translate_with(ngettext)
|
||||
class NgettextTest(GettextTest):
|
||||
number = 1
|
||||
needs_patched = False
|
||||
|
||||
def get_translations(self):
|
||||
empty = self._(self.empty, self.empty, self.number)
|
||||
nonempty = self._(self.nonempty, self.nonempty, self.number)
|
||||
return (empty, nonempty)
|
||||
|
||||
|
||||
@translate_with(npgettext)
|
||||
class NpgettextTest(PgettextTest):
|
||||
number = 1
|
||||
|
||||
def get_translations(self):
|
||||
empty = self._(self.l18n_context, self.empty, self.empty, self.number)
|
||||
nonempty = self._(self.l18n_context, self.nonempty, self.nonempty, self.number)
|
||||
return (empty, nonempty)
|
||||
|
||||
|
||||
class NpgettextPluralTest(NpgettextTest):
|
||||
number = 2
|
||||
|
||||
|
||||
class NgettextPluralTest(NgettextTest):
|
||||
number = 2
|
||||
|
||||
|
||||
@translate_with(gettext_noop)
|
||||
class GettextNoopTest(GettextTest):
|
||||
needs_patched = False
|
||||
|
||||
|
||||
@translate_with(ugettext_noop)
|
||||
class UgettextNoopTest(UgettextTest):
|
||||
needs_patched = False
|
||||
|
||||
|
||||
@translate_with(ungettext)
|
||||
class UngettextTest(NgettextTest):
|
||||
is_unicode = True
|
||||
|
||||
|
||||
class UngettextPluralTest(UngettextTest):
|
||||
number = 2
|
||||
|
||||
|
||||
@translate_with(gettext_lazy)
|
||||
class GettextLazyTest(GettextTest):
|
||||
pass
|
||||
|
||||
|
||||
@translate_with(ugettext_lazy)
|
||||
class UgettextLazyTest(UgettextTest):
|
||||
pass
|
||||
|
||||
|
||||
@translate_with(pgettext_lazy)
|
||||
class PgettextLazyTest(PgettextTest):
|
||||
pass
|
||||
|
||||
|
||||
@translate_with(ngettext_lazy)
|
||||
class NgettextLazyTest(NgettextTest):
|
||||
pass
|
||||
|
||||
|
||||
@translate_with(npgettext_lazy)
|
||||
class NpgettextLazyTest(NpgettextTest):
|
||||
pass
|
||||
|
||||
|
||||
class NpgettextLazyPluralTest(NpgettextLazyTest):
|
||||
number = 2
|
||||
|
||||
|
||||
@translate_with(ungettext_lazy)
|
||||
class UngettextLazyTest(UngettextTest):
|
||||
pass
|
||||
38
common/djangoapps/monkey_patch/third_party_auth.py
Normal file
38
common/djangoapps/monkey_patch/third_party_auth.py
Normal file
@@ -0,0 +1,38 @@
|
||||
"""
|
||||
Monkey patch implementation for a python_social_auth Django ORM method that is not Django 1.8-compatible.
|
||||
Remove once the module fully supports Django 1.8!
|
||||
"""
|
||||
|
||||
from django.db import transaction
|
||||
from social.storage.django_orm import DjangoUserMixin
|
||||
from social.apps.django_app.default.models import (
|
||||
UserSocialAuth, Nonce, Association, Code
|
||||
)
|
||||
|
||||
|
||||
def patch():
|
||||
"""
|
||||
Monkey-patch the DjangoUserMixin class.
|
||||
"""
|
||||
def create_social_auth_wrapper(wrapped_func):
|
||||
# pylint: disable=missing-docstring
|
||||
wrapped_func = wrapped_func.__func__
|
||||
|
||||
def _create_social_auth(*args, **kwargs):
|
||||
# The entire reason for this monkey-patch is to wrap the create_social_auth call
|
||||
# in an atomic transaction. The call can sometime raise an IntegrityError, which is
|
||||
# caught and dealt with by python_social_auth - but not inside of an atomic transaction.
|
||||
# In Django 1.8, unless the exception is raised in an atomic transaction, the transaction
|
||||
# becomes unusable after the IntegrityError exception is raised.
|
||||
with transaction.atomic():
|
||||
return wrapped_func(*args, **kwargs)
|
||||
return classmethod(_create_social_auth)
|
||||
|
||||
DjangoUserMixin.create_social_auth = create_social_auth_wrapper(DjangoUserMixin.create_social_auth)
|
||||
|
||||
# Monkey-patch some social auth models' Meta class to squelch Django19 warnings.
|
||||
# pylint: disable=protected-access
|
||||
UserSocialAuth._meta.app_label = "default"
|
||||
Nonce._meta.app_label = "default"
|
||||
Association._meta.app_label = "default"
|
||||
Code._meta.app_label = "default"
|
||||
@@ -19,9 +19,11 @@ def _get_request_header(request, header_name, default=''):
|
||||
|
||||
|
||||
def _get_request_value(request, value_name, default=''):
|
||||
"""Helper method to get header values from a request's REQUEST dict, if present."""
|
||||
if request is not None and hasattr(request, 'REQUEST') and value_name in request.REQUEST:
|
||||
return request.REQUEST[value_name]
|
||||
"""Helper method to get header values from a request's GET or POST dicts, if present."""
|
||||
if request is not None and hasattr(request, 'GET') and value_name in request.GET:
|
||||
return request.GET[value_name]
|
||||
elif request is not None and hasattr(request, 'POST') and value_name in request.POST:
|
||||
return request.POST[value_name]
|
||||
else:
|
||||
return default
|
||||
|
||||
|
||||
@@ -51,8 +51,11 @@ except:
|
||||
%></%def>
|
||||
|
||||
<%def name="include(path)"><%
|
||||
from django.template.loaders.filesystem import _loader
|
||||
source, template_path = _loader.load_template_source(path)
|
||||
from django.conf import settings
|
||||
from django.template.engine import Engine
|
||||
from django.template.loaders.filesystem import Loader
|
||||
engine = Engine(dirs=settings.DEFAULT_TEMPLATE_ENGINE['DIRS'])
|
||||
source, template_path = Loader(engine).load_template_source(path)
|
||||
%>${source}</%def>
|
||||
|
||||
<%def name="require_module(module_name, class_name)">
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
# -*- 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 'MidcourseReverificationWindow'
|
||||
db.create_table('reverification_midcoursereverificationwindow', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('course_id', self.gf('django.db.models.fields.CharField')(max_length=255, db_index=True)),
|
||||
('start_date', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True, blank=True)),
|
||||
('end_date', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True, blank=True)),
|
||||
))
|
||||
db.send_create_signal('reverification', ['MidcourseReverificationWindow'])
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting model 'MidcourseReverificationWindow'
|
||||
db.delete_table('reverification_midcoursereverificationwindow')
|
||||
|
||||
|
||||
models = {
|
||||
'reverification.midcoursereverificationwindow': {
|
||||
'Meta': {'object_name': 'MidcourseReverificationWindow'},
|
||||
'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
|
||||
'end_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'start_date': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True', 'blank': 'True'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['reverification']
|
||||
@@ -1,31 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from south.utils import datetime_utils as datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
|
||||
def forwards(self, orm):
|
||||
# Deleting model 'MidcourseReverificationWindow'
|
||||
db.delete_table('reverification_midcoursereverificationwindow')
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Adding model 'MidcourseReverificationWindow'
|
||||
db.create_table('reverification_midcoursereverificationwindow', (
|
||||
('course_id', self.gf('django.db.models.fields.CharField')(max_length=255, db_index=True)),
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('end_date', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True, blank=True)),
|
||||
('start_date', self.gf('django.db.models.fields.DateTimeField')(default=None, null=True, blank=True)),
|
||||
))
|
||||
db.send_create_signal('reverification', ['MidcourseReverificationWindow'])
|
||||
|
||||
|
||||
models = {
|
||||
|
||||
}
|
||||
|
||||
complete_apps = ['reverification']
|
||||
@@ -28,7 +28,7 @@ def celery_status(_):
|
||||
"""
|
||||
stats = celery.control.inspect().stats() or {}
|
||||
return HttpResponse(json.dumps(stats, indent=4),
|
||||
mimetype="application/json")
|
||||
content_type="application/json")
|
||||
|
||||
|
||||
@dog_stats_api.timed('status.service.celery.ping')
|
||||
@@ -56,4 +56,4 @@ def celery_ping(_):
|
||||
}
|
||||
|
||||
return HttpResponse(json.dumps(output, indent=4),
|
||||
mimetype="application/json")
|
||||
content_type="application/json")
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
###
|
||||
|
||||
from django.core.management.base import NoArgsCommand
|
||||
from django.core.cache import get_cache
|
||||
from django.core.cache import caches
|
||||
|
||||
|
||||
class Command(NoArgsCommand):
|
||||
help = 'Import the specified data directory into the default ModuleStore'
|
||||
|
||||
def handle_noargs(self, **options):
|
||||
staticfiles_cache = get_cache('staticfiles')
|
||||
staticfiles_cache = caches['staticfiles']
|
||||
staticfiles_cache.clear()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user