Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cms/djangoapps/contentstore/context_processors.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

import ConfigParser
from django.conf import settings

Expand Down
12 changes: 5 additions & 7 deletions cms/djangoapps/contentstore/features/common.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
# pylint: disable=C0111
# pylint: disable=W0621

import time
import os
from lettuce import world, step
from nose.tools import assert_true, assert_in # pylint: disable=no-name-in-module
from django.conf import settings

from student.roles import CourseRole, CourseStaffRole, CourseInstructorRole
from student.roles import CourseStaffRole, CourseInstructorRole, GlobalStaff
from student.models import get_user

from selenium.webdriver.common.keys import Keys
Expand Down Expand Up @@ -162,7 +161,7 @@ def add_course_author(user, course):
"""
global_admin = AdminFactory()
for role in (CourseStaffRole, CourseInstructorRole):
auth.add_users(global_admin, role(course.location), user)
auth.add_users(global_admin, role(course.id), user)


def create_a_course():
Expand Down Expand Up @@ -379,18 +378,17 @@ def create_other_user(_step, name, has_extra_perms, role_name):
user = create_studio_user(uname=name, password="test", email=email)
if has_extra_perms:
if role_name == "is_staff":
user.is_staff = True
user.save()
GlobalStaff().add_users(user)
else:
if role_name == "admin":
# admins get staff privileges, as well
roles = (CourseStaffRole, CourseInstructorRole)
else:
roles = (CourseStaffRole,)
location = world.scenario_dict["COURSE"].location
course_key = world.scenario_dict["COURSE"].id
global_admin = AdminFactory()
for role in roles:
auth.add_users(global_admin, role(location), user)
auth.add_users(global_admin, role(course_key), user)


@step('I log out')
Expand Down
9 changes: 8 additions & 1 deletion cms/djangoapps/contentstore/features/course-export.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from lettuce import world, step
from component_settings_editor_helpers import enter_xml_in_advanced_problem
from nose.tools import assert_true, assert_equal
from xmodule.modulestore.locations import SlashSeparatedCourseKey
from contentstore.utils import reverse_usage_url


@step('I export the course$')
Expand Down Expand Up @@ -43,4 +45,9 @@ def get_an_error_dialog(step):
def i_click_on_error_dialog(step):
world.click_link_by_text('Correct failed component')
assert_true(world.css_html("span.inline-error").startswith("Problem i4x://MITx/999/problem"))
assert_equal(1, world.browser.url.count("unit/MITx.999.Robot_Super_Course/branch/draft/block/vertical"))
course_key = SlashSeparatedCourseKey("MITx", "999", "Robot_Super_Course")
# we don't know the actual ID of the vertical. So just check that we did go to a
# vertical page in the course (there should only be one).
vertical_usage_key = course_key.make_usage_key("vertical", "")
vertical_url = reverse_usage_url('unit_handler', vertical_usage_key)
assert_equal(1, world.browser.url.count(vertical_url))
14 changes: 8 additions & 6 deletions cms/djangoapps/contentstore/features/grading.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
from lettuce import world, step
from common import *
from terrain.steps import reload_the_page
from selenium.common.exceptions import (
InvalidElementStateException, WebDriverException)
from selenium.common.exceptions import InvalidElementStateException
from xmodule.modulestore.locations import SlashSeparatedCourseKey
from contentstore.utils import reverse_course_url
from nose.tools import assert_in, assert_not_in, assert_equal, assert_not_equal # pylint: disable=E0611


Expand Down Expand Up @@ -68,11 +69,12 @@ def change_assignment_name(step, old_name, new_name):
@step(u'I go back to the main course page')
def main_course_page(step):
course_name = world.scenario_dict['COURSE'].display_name.replace(' ', '_')
main_page_link = '/course/{org}.{number}.{name}/branch/draft/block/{name}'.format(
org=world.scenario_dict['COURSE'].org,
number=world.scenario_dict['COURSE'].number,
name=course_name
course_key = SlashSeparatedCourseKey(
world.scenario_dict['COURSE'].org,
world.scenario_dict['COURSE'].number,
course_name
)
main_page_link = reverse_course_url('course_handler', course_key)

world.visit(main_page_link)
assert_in('Course Outline', world.css_text('h1.page-header'))
Expand Down
8 changes: 4 additions & 4 deletions cms/djangoapps/contentstore/features/signup.feature
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ Feature: CMS.Sign in
Scenario: Login with a valid redirect
Given I have opened a new course in Studio
And I am not logged in
And I visit the url "/course/MITx.999.Robot_Super_Course/branch/draft/block/Robot_Super_Course"
And I should see that the path is "/signin?next=/course/MITx.999.Robot_Super_Course/branch/draft/block/Robot_Super_Course"
And I visit the url "/course/slashes:MITx+999+Robot_Super_Course"
And I should see that the path is "/signin?next=/course/slashes%3AMITx%2B999%2BRobot_Super_Course"
When I fill in and submit the signin form
And I wait for "2" seconds
Then I should see that the path is "/course/MITx.999.Robot_Super_Course/branch/draft/block/Robot_Super_Course"
Then I should see that the path is "/course/slashes:MITx+999+Robot_Super_Course"

Scenario: Login with an invalid redirect
Given I have opened a new course in Studio
And I am not logged in
And I visit the url "/signin?next=http://www.google.com/"
When I fill in and submit the signin form
And I wait for "2" seconds
Then I should see that the path is "/course"
Then I should see that the path is "/course/"
3 changes: 1 addition & 2 deletions cms/djangoapps/contentstore/features/transcripts.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,7 @@ def remove_transcripts_from_store(_step, subs_id):
"""Remove from store, if transcripts content exists."""
filename = 'subs_{0}.srt.sjson'.format(subs_id.strip())
content_location = StaticContent.compute_location(
world.scenario_dict['COURSE'].org,
world.scenario_dict['COURSE'].number,
world.scenario_dict['COURSE'].id,
filename
)
try:
Expand Down
2 changes: 1 addition & 1 deletion cms/djangoapps/contentstore/features/upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ def user_foo_is_enrolled_in_the_course(step, name):
world.create_user(name, 'test')
user = User.objects.get(username=name)

course_id = world.scenario_dict['COURSE'].location.course_id
course_id = world.scenario_dict['COURSE'].id
CourseEnrollment.enroll(user, course_id)


Expand Down
6 changes: 3 additions & 3 deletions cms/djangoapps/contentstore/features/video.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,10 @@ def xml_only_video(step):
# Wait for the new unit to be created and to load the page
world.wait(1)

location = world.scenario_dict['COURSE'].location
store = get_modulestore(location)
course = world.scenario_dict['COURSE']
store = get_modulestore(course.location)

parent_location = store.get_items(Location(category='vertical', revision='draft'))[0].location
parent_location = store.get_items(course.id, category='vertical', revision='draft')[0].location

youtube_id = 'ABCDEFG'
world.scenario_dict['YOUTUBE_ID'] = youtube_id
Expand Down
13 changes: 2 additions & 11 deletions cms/djangoapps/contentstore/git_export_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
from django.utils.translation import ugettext_lazy as _

from xmodule.contentstore.django import contentstore
from xmodule.course_module import CourseDescriptor
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.xml_exporter import export_to_xml

Expand Down Expand Up @@ -64,13 +63,10 @@ def cmd_log(cmd, cwd):
return output


def export_to_git(course_loc, repo, user='', rdir=None):
def export_to_git(course_id, repo, user='', rdir=None):
"""Export a course to git."""
# pylint: disable=R0915

if course_loc.startswith('i4x://'):
course_loc = course_loc[6:]

if not GIT_REPO_EXPORT_DIR:
raise GitExportError(GitExportError.NO_EXPORT_DIR)

Expand Down Expand Up @@ -129,15 +125,10 @@ def export_to_git(course_loc, repo, user='', rdir=None):
raise GitExportError(GitExportError.CANNOT_PULL)

# export course as xml before commiting and pushing
try:
location = CourseDescriptor.id_to_location(course_loc)
except ValueError:
raise GitExportError(GitExportError.BAD_COURSE)

root_dir = os.path.dirname(rdirp)
course_dir = os.path.splitext(os.path.basename(rdirp))[0]
try:
export_to_xml(modulestore('direct'), contentstore(), location,
export_to_xml(modulestore('direct'), contentstore(), course_id,
root_dir, course_dir, modulestore())
except (EnvironmentError, AttributeError):
log.exception('Failed export to xml')
Expand Down
29 changes: 9 additions & 20 deletions cms/djangoapps/contentstore/management/commands/check_course.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
from django.core.management.base import BaseCommand, CommandError
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.xml_importer import check_module_metadata_editability
from xmodule.course_module import CourseDescriptor
from xmodule.modulestore import Location
from xmodule.modulestore.keys import CourseKey


class Command(BaseCommand):
help = '''Enumerates through the course and find common errors'''

def handle(self, *args, **options):
if len(args) != 1:
raise CommandError("check_course requires one argument: <location>")
raise CommandError("check_course requires one argument: <course_id>")

loc_str = args[0]
course_key = CourseKey.from_string(args[0])

loc = CourseDescriptor.id_to_location(loc_str)
store = modulestore()

course = store.get_item(loc, depth=3)
course = store.get_course(course_key, depth=3)

err_cnt = 0

Expand All @@ -33,7 +31,7 @@ def _xlint_metadata(module):
def _check_xml_attributes_field(module):
err_cnt = 0
if hasattr(module, 'xml_attributes') and isinstance(module.xml_attributes, basestring):
print 'module = {0} has xml_attributes as a string. It should be a dict'.format(module.location.url())
print 'module = {0} has xml_attributes as a string. It should be a dict'.format(module.location)
err_cnt = err_cnt + 1
for child in module.get_children():
err_cnt = err_cnt + _check_xml_attributes_field(child)
Expand All @@ -45,7 +43,7 @@ def _check_xml_attributes_field(module):
def _get_discussion_items(module):
discussion_items = []
if module.location.category == 'discussion':
discussion_items = discussion_items + [module.location.url()]
discussion_items = discussion_items + [module.location]

for child in module.get_children():
discussion_items = discussion_items + _get_discussion_items(child)
Expand All @@ -55,17 +53,8 @@ def _get_discussion_items(module):
discussion_items = _get_discussion_items(course)

# now query all discussion items via get_items() and compare with the tree-traversal
queried_discussion_items = store.get_items(
Location(
'i4x',
course.location.org,
course.location.course,
'discussion',
None,
None
)
)
queried_discussion_items = store.get_items(course_key=course_key, category='discussion',)

for item in queried_discussion_items:
if item.location.url() not in discussion_items:
print 'Found dangling discussion module = {0}'.format(item.location.url())
if item.location not in discussion_items:
print 'Found dangling discussion module = {0}'.format(item.location)
26 changes: 9 additions & 17 deletions cms/djangoapps/contentstore/management/commands/clone_course.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
from xmodule.modulestore.store_utilities import clone_course
from xmodule.modulestore.django import modulestore
from xmodule.contentstore.django import contentstore
from xmodule.course_module import CourseDescriptor
from student.roles import CourseInstructorRole, CourseStaffRole
from xmodule.modulestore import Location
from xmodule.modulestore.keys import CourseKey


#
Expand All @@ -22,29 +21,22 @@ def handle(self, *args, **options):
if len(args) != 2:
raise CommandError("clone requires 2 arguments: <source-course_id> <dest-course_id>")

source_course_id = args[0]
dest_course_id = args[1]
source_course_id = CourseKey.from_string(args[0])
dest_course_id = CourseKey.from_string(args[1])

mstore = modulestore('direct')
cstore = contentstore()

course_id_dict = Location.parse_course_id(dest_course_id)
mstore.ignore_write_events_on_courses.append('{org}/{course}'.format(**course_id_dict))
mstore.ignore_write_events_on_courses.add(dest_course_id)

print("Cloning course {0} to {1}".format(source_course_id, dest_course_id))

source_location = CourseDescriptor.id_to_location(source_course_id)
dest_location = CourseDescriptor.id_to_location(dest_course_id)

if clone_course(mstore, cstore, source_location, dest_location):
# be sure to recompute metadata inheritance after all those updates
mstore.refresh_cached_metadata_inheritance_tree(dest_location)

if clone_course(mstore, cstore, source_course_id, dest_course_id):
print("copying User permissions...")
# purposely avoids auth.add_user b/c it doesn't have a caller to authorize
CourseInstructorRole(dest_location).add_users(
*CourseInstructorRole(source_location).users_with_role()
CourseInstructorRole(dest_course_id).add_users(
*CourseInstructorRole(source_course_id).users_with_role()
)
CourseStaffRole(dest_location).add_users(
*CourseStaffRole(source_location).users_with_role()
CourseStaffRole(dest_course_id).add_users(
*CourseStaffRole(source_course_id).users_with_role()
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@
from django.core.management.base import BaseCommand, CommandError
from .prompt import query_yes_no
from contentstore.utils import delete_course_and_groups
from xmodule.modulestore.keys import CourseKey


class Command(BaseCommand):
help = '''Delete a MongoDB backed course'''

def handle(self, *args, **options):
if len(args) != 1 and len(args) != 2:
raise CommandError("delete_course requires one or more arguments: <location> |commit|")
raise CommandError("delete_course requires one or more arguments: <course_id> |commit|")

course_id = args[0]
course_id = CourseKey.from_string(args[0])

commit = False
if len(args) == 2:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
from courseware.courses import get_course_by_id

from contentstore.views import tabs
from opaque_keys import InvalidKeyError
from xmodule.modulestore.locations import SlashSeparatedCourseKey
from xmodule.modulestore.keys import CourseKey


def print_course(course):
Expand Down Expand Up @@ -64,7 +67,11 @@ def handle(self, *args, **options):
if not options['course']:
raise CommandError(Command.course_option.help)

course = get_course_by_id(options['course'])
try:
course_key = CourseKey.from_string(options['course'])
except InvalidKeyError:
course_key = SlashSeparatedCourseKey.from_deprecated_string(options['course'])
course = get_course_by_id(course_key)

print 'Warning: this command directly edits the list of course tabs in mongo.'
print 'Tabs before any changes:'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from django.core.management.base import BaseCommand, CommandError
from xmodule.course_module import CourseDescriptor
from xmodule.contentstore.utils import empty_asset_trashcan
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.keys import CourseKey
from .prompt import query_yes_no


Expand All @@ -10,16 +10,12 @@ class Command(BaseCommand):

def handle(self, *args, **options):
if len(args) != 1 and len(args) != 0:
raise CommandError("empty_asset_trashcan requires one or no arguments: |<location>|")

locs = []
raise CommandError("empty_asset_trashcan requires one or no arguments: |<course_id>|")

if len(args) == 1:
locs.append(CourseDescriptor.id_to_location(args[0]))
course_ids = [CourseKey.from_string(args[0])]
else:
courses = modulestore('direct').get_courses()
for course in courses:
locs.append(course.location)
course_ids = [course.id for course in modulestore('direct').get_courses()]

if query_yes_no("Emptying trashcan. Confirm?", default="no"):
empty_asset_trashcan(locs)
empty_asset_trashcan(course_ids)
Loading