Merge branch 'master' into feature/btalbot/studio-alerts
This commit is contained in:
@@ -21,18 +21,26 @@ class Command(BaseCommand):
|
||||
'''Delete a MongoDB backed course'''
|
||||
|
||||
def handle(self, *args, **options):
|
||||
if len(args) != 1:
|
||||
raise CommandError("delete_course requires one argument: <location>")
|
||||
if len(args) != 1 and len(args) != 2:
|
||||
raise CommandError("delete_course requires one or more arguments: <location> |commit|")
|
||||
|
||||
loc_str = args[0]
|
||||
|
||||
commit = False
|
||||
if len(args) == 2:
|
||||
commit = args[1] == 'commit'
|
||||
|
||||
if commit:
|
||||
print 'Actually going to delete the course from DB....'
|
||||
|
||||
ms = modulestore('direct')
|
||||
cs = contentstore()
|
||||
|
||||
if query_yes_no("Deleting course {0}. Confirm?".format(loc_str), default="no"):
|
||||
if query_yes_no("Are you sure. This action cannot be undone!", default="no"):
|
||||
loc = CourseDescriptor.id_to_location(loc_str)
|
||||
if delete_course(ms, cs, loc) == True:
|
||||
if delete_course(ms, cs, loc, commit) == True:
|
||||
print 'removing User permissions from course....'
|
||||
# in the django layer, we need to remove all the user permissions groups associated with this course
|
||||
_delete_course_group(loc)
|
||||
if commit:
|
||||
_delete_course_group(loc)
|
||||
|
||||
@@ -171,7 +171,7 @@ class ContentStoreToyCourseTest(ModuleStoreTestCase):
|
||||
|
||||
location = CourseDescriptor.id_to_location('edX/full/6.002_Spring_2012')
|
||||
|
||||
delete_course(ms, cs, location)
|
||||
delete_course(ms, cs, location, commit=True)
|
||||
|
||||
items = ms.get_items(Location(['i4x', 'edX', 'full', 'vertical', None]))
|
||||
self.assertEqual(len(items), 0)
|
||||
@@ -465,7 +465,9 @@ class ContentStoreTest(ModuleStoreTestCase):
|
||||
# check for grace period definition which should be defined at the course level
|
||||
self.assertIn('graceperiod', new_module.metadata)
|
||||
|
||||
self.assertEqual(course.metadata['graceperiod'], new_module.metadata['graceperiod'])
|
||||
self.assertEqual(parent.metadata['graceperiod'], new_module.metadata['graceperiod'])
|
||||
|
||||
self.assertEqual(course.metadata['xqa_key'], new_module.metadata['xqa_key'])
|
||||
|
||||
#
|
||||
# now let's define an override at the leaf node level
|
||||
|
||||
@@ -80,10 +80,15 @@ def get_lms_link_for_item(location, preview=False, course_id=None):
|
||||
course_id = get_course_id(location)
|
||||
|
||||
if settings.LMS_BASE is not None:
|
||||
lms_link = "//{preview}{lms_base}/courses/{course_id}/jump_to/{location}".format(
|
||||
preview='preview.' if preview else '',
|
||||
lms_base=settings.LMS_BASE,
|
||||
course_id= course_id,
|
||||
if preview:
|
||||
lms_base = settings.MITX_FEATURES.get('PREVIEW_LMS_BASE',
|
||||
'preview.' + settings.LMS_BASE)
|
||||
else:
|
||||
lms_base = settings.LMS_BASE
|
||||
|
||||
lms_link = "//{lms_base}/courses/{course_id}/jump_to/{location}".format(
|
||||
lms_base=lms_base,
|
||||
course_id=course_id,
|
||||
location=Location(location)
|
||||
)
|
||||
else:
|
||||
|
||||
@@ -320,8 +320,11 @@ def edit_unit(request, location):
|
||||
break
|
||||
index = index + 1
|
||||
|
||||
preview_lms_link = '//{preview}{lms_base}/courses/{org}/{course}/{course_name}/courseware/{section}/{subsection}/{index}'.format(
|
||||
preview='preview.',
|
||||
preview_lms_base = settings.MITX_FEATURES.get('PREVIEW_LMS_BASE',
|
||||
'preview.' + settings.LMS_BASE)
|
||||
|
||||
preview_lms_link = '//{preview_lms_base}/courses/{org}/{course}/{course_name}/courseware/{section}/{subsection}/{index}'.format(
|
||||
preview_lms_base=preview_lms_base,
|
||||
lms_base=settings.LMS_BASE,
|
||||
org=course.location.org,
|
||||
course=course.location.course,
|
||||
|
||||
@@ -172,6 +172,9 @@ LANGUAGE_CODE = 'en' # http://www.i18nguy.com/unicode/language-identi
|
||||
USE_I18N = True
|
||||
USE_L10N = True
|
||||
|
||||
# Tracking
|
||||
TRACK_MAX_EVENT = 10000
|
||||
|
||||
# Messages
|
||||
MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage'
|
||||
|
||||
@@ -275,6 +278,10 @@ INSTALLED_APPS = (
|
||||
'auth',
|
||||
'student', # misleading name due to sharing with lms
|
||||
'course_groups', # not used in cms (yet), but tests run
|
||||
|
||||
# tracking
|
||||
'track',
|
||||
|
||||
# For asset pipelining
|
||||
'pipeline',
|
||||
'staticfiles',
|
||||
|
||||
@@ -97,7 +97,7 @@ CMS.Views.Settings.Details = CMS.Views.ValidatingView.extend({
|
||||
}
|
||||
var newVal = new Date(date.getTime() + time * 1000);
|
||||
if (!cacheModel.has(fieldName) || cacheModel.get(fieldName).getTime() !== newVal.getTime()) {
|
||||
cacheModel.save(fieldName, newVal, { error: CMS.ServerError});
|
||||
cacheModel.save(fieldName, newVal);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -90,8 +90,7 @@ CMS.Views.Settings.Grading = CMS.Views.ValidatingView.extend({
|
||||
setGracePeriod : function(event) {
|
||||
event.data.clearValidationErrors();
|
||||
var newVal = event.data.model.dateToGracePeriod($(event.currentTarget).timepicker('getTime'));
|
||||
if (event.data.model.get('grace_period') != newVal) event.data.model.save('grace_period', newVal,
|
||||
{ error : CMS.ServerError});
|
||||
if (event.data.model.get('grace_period') != newVal) event.data.model.save('grace_period', newVal);
|
||||
},
|
||||
updateModel : function(event) {
|
||||
if (!this.selectorToField[event.currentTarget.id]) return;
|
||||
@@ -227,8 +226,7 @@ CMS.Views.Settings.Grading = CMS.Views.ValidatingView.extend({
|
||||
object[cutoff['designation']] = cutoff['cutoff'] / 100.0;
|
||||
return object;
|
||||
},
|
||||
{}),
|
||||
{ error : CMS.ServerError});
|
||||
{}));
|
||||
},
|
||||
|
||||
addNewGrade: function(e) {
|
||||
|
||||
@@ -1,23 +1,35 @@
|
||||
<form class="choicegroup capa_inputtype" id="inputtype_${id}">
|
||||
<div class="indicator_container">
|
||||
% if status == 'unsubmitted':
|
||||
<span class="unanswered" style="display:inline-block;" id="status_${id}"></span>
|
||||
% elif status == 'correct':
|
||||
<span class="correct" id="status_${id}"></span>
|
||||
% elif status == 'incorrect':
|
||||
<span class="incorrect" id="status_${id}"></span>
|
||||
% elif status == 'incomplete':
|
||||
<span class="incorrect" id="status_${id}"></span>
|
||||
% endif
|
||||
</div>
|
||||
|
||||
<fieldset>
|
||||
% for choice_id, choice_description in choices:
|
||||
<label for="input_${id}_${choice_id}"> <input type="${input_type}" name="input_${id}${name_array_suffix}" id="input_${id}_${choice_id}" value="${choice_id}"
|
||||
<label for="input_${id}_${choice_id}">
|
||||
|
||||
% if choice_id in value:
|
||||
<span class="indicator_container">
|
||||
% if status == 'unsubmitted':
|
||||
<span class="unanswered" style="display:inline-block;" id="status_${id}"></span>
|
||||
% elif status == 'correct':
|
||||
<span class="correct" id="status_${id}"></span>
|
||||
% elif status == 'incorrect':
|
||||
<span class="incorrect" id="status_${id}"></span>
|
||||
% elif status == 'incomplete':
|
||||
<span class="incorrect" id="status_${id}"></span>
|
||||
% endif
|
||||
</span>
|
||||
% else:
|
||||
<span class="indicator_container"> </span>
|
||||
% endif
|
||||
|
||||
<input type="${input_type}" name="input_${id}${name_array_suffix}" id="input_${id}_${choice_id}" value="${choice_id}"
|
||||
% if choice_id in value:
|
||||
checked="true"
|
||||
% endif
|
||||
/> ${choice_description} </label>
|
||||
/>
|
||||
|
||||
${choice_description}
|
||||
|
||||
</label>
|
||||
|
||||
% endfor
|
||||
<span id="answer_${id}"></span>
|
||||
</fieldset>
|
||||
|
||||
@@ -227,7 +227,7 @@ section.problem {
|
||||
background: url('../images/correct-icon.png') center center no-repeat;
|
||||
height: 20px;
|
||||
position: relative;
|
||||
top: 6px;
|
||||
top: 3px;
|
||||
width: 25px;
|
||||
}
|
||||
|
||||
@@ -237,7 +237,7 @@ section.problem {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
position: relative;
|
||||
top: 6px;
|
||||
top: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ def clone_course(modulestore, contentstore, source_location, dest_location, dele
|
||||
return True
|
||||
|
||||
|
||||
def delete_course(modulestore, contentstore, source_location):
|
||||
def delete_course(modulestore, contentstore, source_location, commit = False):
|
||||
# first check to see if the modulestore is Mongo backed
|
||||
if not isinstance(modulestore, MongoModuleStore):
|
||||
raise Exception("Expected a MongoModuleStore in the runtime. Aborting....")
|
||||
@@ -107,7 +107,8 @@ def delete_course(modulestore, contentstore, source_location):
|
||||
thumb_loc = Location(thumb["_id"])
|
||||
id = StaticContent.get_id_from_location(thumb_loc)
|
||||
print "Deleting {0}...".format(id)
|
||||
contentstore.delete(id)
|
||||
if commit:
|
||||
contentstore.delete(id)
|
||||
|
||||
# then delete all of the assets
|
||||
assets = contentstore.get_all_content_for_course(source_location)
|
||||
@@ -115,7 +116,8 @@ def delete_course(modulestore, contentstore, source_location):
|
||||
asset_loc = Location(asset["_id"])
|
||||
id = StaticContent.get_id_from_location(asset_loc)
|
||||
print "Deleting {0}...".format(id)
|
||||
contentstore.delete(id)
|
||||
if commit:
|
||||
contentstore.delete(id)
|
||||
|
||||
# then delete all course modules
|
||||
modules = modulestore.get_items([source_location.tag, source_location.org, source_location.course, None, None, None])
|
||||
@@ -123,10 +125,12 @@ def delete_course(modulestore, contentstore, source_location):
|
||||
for module in modules:
|
||||
if module.category != 'course': # save deleting the course module for last
|
||||
print "Deleting {0}...".format(module.location)
|
||||
modulestore.delete_item(module.location)
|
||||
if commit:
|
||||
modulestore.delete_item(module.location)
|
||||
|
||||
# finally delete the top-level course module itself
|
||||
print "Deleting {0}...".format(source_location)
|
||||
modulestore.delete_item(source_location)
|
||||
if commit:
|
||||
modulestore.delete_item(source_location)
|
||||
|
||||
return True
|
||||
|
||||
@@ -79,7 +79,7 @@ if Backbone?
|
||||
#determined in the coffeescript based on whether or not there's a
|
||||
#group id
|
||||
|
||||
if response.is_cohorted
|
||||
if response.is_cohorted and response.is_moderator
|
||||
source = "script#_inline_discussion_cohorted"
|
||||
else
|
||||
source = "script#_inline_discussion"
|
||||
|
||||
@@ -96,24 +96,25 @@ def create_thread(request, course_id, commentable_id):
|
||||
|
||||
#kevinchugh because the new requirement is that all groups will be determined
|
||||
#by the group id in the request this all goes away
|
||||
#not anymore, only for admins
|
||||
|
||||
# Cohort the thread if the commentable is cohorted.
|
||||
#if is_commentable_cohorted(course_id, commentable_id):
|
||||
# user_group_id = get_cohort_id(user, course_id)
|
||||
if is_commentable_cohorted(course_id, commentable_id):
|
||||
user_group_id = get_cohort_id(user, course_id)
|
||||
|
||||
# TODO (vshnayder): once we have more than just cohorts, we'll want to
|
||||
# change this to a single get_group_for_user_and_commentable function
|
||||
# that can do different things depending on the commentable_id
|
||||
# if cached_has_permission(request.user, "see_all_cohorts", course_id):
|
||||
if cached_has_permission(request.user, "see_all_cohorts", course_id):
|
||||
# admins can optionally choose what group to post as
|
||||
# group_id = post.get('group_id', user_group_id)
|
||||
# else:
|
||||
group_id = post.get('group_id', user_group_id)
|
||||
else:
|
||||
# regular users always post with their own id.
|
||||
# group_id = user_group_id
|
||||
group_id = post.get('group_id')
|
||||
group_id = user_group_id
|
||||
|
||||
if group_id:
|
||||
thread.update_attributes(group_id=group_id)
|
||||
|
||||
log.debug("Saving thread %r", thread.attributes)
|
||||
thread.save()
|
||||
|
||||
if post.get('auto_subscribe', 'false').lower() == 'true':
|
||||
|
||||
@@ -133,32 +133,23 @@ def inline_discussion(request, course_id, discussion_id):
|
||||
#if the commentable is cohorted, otherwise everything is not cohorted
|
||||
#and no one has the option of choosing a cohort
|
||||
is_cohorted = is_course_cohorted(course_id) and is_commentable_cohorted(course_id, discussion_id)
|
||||
|
||||
is_moderator = cached_has_permission(request.user, "see_all_cohorts", course_id)
|
||||
|
||||
cohorts_list = list()
|
||||
|
||||
if is_cohorted:
|
||||
cohorts_list.append({'name':'All Groups','id':None})
|
||||
|
||||
#if you're a mod, send all cohorts and let you pick
|
||||
if cached_has_permission(request.user, "see_all_cohorts", course_id):
|
||||
|
||||
if is_moderator:
|
||||
cohorts = get_course_cohorts(course_id)
|
||||
for c in cohorts:
|
||||
cohorts_list.append({'name':c.name, 'id':c.id})
|
||||
|
||||
else:
|
||||
#otherwise, just make a dictionary of two
|
||||
user_cohort = get_cohort(cc_user, course_id)
|
||||
if user_cohort:
|
||||
user_cohort_name = user_cohort.name
|
||||
user_cohort_id = user_cohort.id
|
||||
else:
|
||||
user_cohort_name = user_cohort_id = None
|
||||
|
||||
if user_cohort:
|
||||
cohorts_list.append({'name':user_cohort_name, 'id':user_cohort_id})
|
||||
else:
|
||||
cohorts_list = None
|
||||
|
||||
#students don't get to choose
|
||||
cohorts_list = None
|
||||
|
||||
return utils.JsonResponse({
|
||||
'discussion_data': map(utils.safe_content, threads),
|
||||
@@ -170,6 +161,7 @@ def inline_discussion(request, course_id, discussion_id):
|
||||
'allow_anonymous_to_peers': allow_anonymous_to_peers,
|
||||
'allow_anonymous': allow_anonymous,
|
||||
'cohorts': cohorts_list,
|
||||
'is_moderator': is_moderator,
|
||||
'is_cohorted': is_cohorted
|
||||
})
|
||||
|
||||
|
||||
@@ -46,21 +46,18 @@
|
||||
%elif course.metadata.get("allow_anonymous_to_peers", False):
|
||||
<input type="checkbox" name="anonymous_to_peers" class="discussion-anonymous-to-peers" id="new-post-anonymous-to-peers"><label for="new-post-anonymous-to-peers">post anonymously to classmates</label>
|
||||
%endif
|
||||
%if is_course_cohorted:
|
||||
%if is_course_cohorted and is_moderator:
|
||||
<div class="form-group-label choose-cohort">
|
||||
Make visible to:
|
||||
<select class="group-filter-select new-post-group" name = "group_id">
|
||||
<option value="">All Groups</option>
|
||||
%if is_moderator:
|
||||
%for c in cohorts:
|
||||
<option value="${c.id}">${c.name}</option>
|
||||
%endfor
|
||||
%else:
|
||||
%if user_cohort:
|
||||
<option value="${user_cohort}">My Cohort</option>
|
||||
%for c in cohorts:
|
||||
<option value="${c.id}"
|
||||
%if user_cohort and str(user_cohort) == str(c.id):
|
||||
selected
|
||||
%endif
|
||||
%endif
|
||||
|
||||
>${c.name}</option>
|
||||
%endfor
|
||||
</select>
|
||||
</div>
|
||||
%endif
|
||||
|
||||
5
rakefile
5
rakefile
@@ -415,7 +415,10 @@ end
|
||||
namespace :cms do
|
||||
desc "Delete existing MongoDB based course"
|
||||
task :delete_course do
|
||||
if ENV['LOC']
|
||||
|
||||
if ENV['LOC'] and ENV['COMMIT']
|
||||
sh(django_admin(:cms, :dev, :delete_course, ENV['LOC'], ENV['COMMIT']))
|
||||
elsif ENV['LOC']
|
||||
sh(django_admin(:cms, :dev, :delete_course, ENV['LOC']))
|
||||
else
|
||||
raise "You must pass in a LOC parameter"
|
||||
|
||||
Reference in New Issue
Block a user