Skip to content
Merged
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
8 changes: 8 additions & 0 deletions cms/djangoapps/contentstore/tests/test_course_create_rerun.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from cms.djangoapps.contentstore.tests.utils import AjaxEnabledTestClient, parse_json
from cms.djangoapps.course_creators.admin import CourseCreatorAdmin
from cms.djangoapps.course_creators.models import CourseCreator
from cms.djangoapps.contentstore.views.course import get_allowed_organizations, user_can_create_organizations
from common.djangoapps.student.auth import update_org_role
from common.djangoapps.student.roles import CourseInstructorRole, CourseStaffRole, OrgContentCreatorRole
from common.djangoapps.student.tests.factories import AdminFactory, UserFactory
Expand Down Expand Up @@ -227,6 +228,7 @@ def test_course_creation_when_user_in_org_with_creator_role(self, store):
'description': 'Testing Organization Description',
})
update_org_role(self.global_admin, OrgContentCreatorRole, self.user, [self.source_course_key.org])
self.assertIn(self.source_course_key.org, get_allowed_organizations(self.user))
with modulestore().default_store(store):
response = self.client.ajax_post(self.course_create_rerun_url, {
'org': self.source_course_key.org,
Expand Down Expand Up @@ -254,6 +256,8 @@ def test_course_creation_with_all_org_checked(self, store):
self.course_creator_entry.all_organizations = True
self.course_creator_entry.state = CourseCreator.GRANTED
self.creator_admin.save_model(self.request, self.course_creator_entry, None, True)
self.assertIn(self.source_course_key.org, get_allowed_organizations(self.user))
self.assertFalse(user_can_create_organizations(self.user))
with modulestore().default_store(store):
response = self.client.ajax_post(self.course_create_rerun_url, {
'org': self.source_course_key.org,
Expand Down Expand Up @@ -283,6 +287,8 @@ def test_course_creation_with_permission_for_specific_organization(self, store):
self.creator_admin.save_model(self.request, self.course_creator_entry, None, True)
dc_org_object = Organization.objects.get(name='Test Organization')
self.course_creator_entry.organizations.add(dc_org_object)
self.assertIn(self.source_course_key.org, get_allowed_organizations(self.user))
self.assertFalse(user_can_create_organizations(self.user))
with modulestore().default_store(store):
response = self.client.ajax_post(self.course_create_rerun_url, {
'org': self.source_course_key.org,
Expand Down Expand Up @@ -319,6 +325,8 @@ def test_course_creation_without_permission_for_specific_organization(self, stor
# When the user tries to create course under `Test Organization` it throws a 403.
dc_org_object = Organization.objects.get(name='DC')
self.course_creator_entry.organizations.add(dc_org_object)
self.assertNotIn(self.source_course_key.org, get_allowed_organizations(self.user))
self.assertFalse(user_can_create_organizations(self.user))
with modulestore().default_store(store):
response = self.client.ajax_post(self.course_create_rerun_url, {
'org': self.source_course_key.org,
Expand Down
38 changes: 37 additions & 1 deletion cms/djangoapps/contentstore/views/course.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
from rest_framework.exceptions import ValidationError

from cms.djangoapps.course_creators.views import add_user_with_status_unrequested, get_course_creator_status
from cms.djangoapps.course_creators.models import CourseCreator
from cms.djangoapps.models.settings.course_grading import CourseGradingModel
from cms.djangoapps.models.settings.course_metadata import CourseMetadata
from cms.djangoapps.models.settings.encoder import CourseSettingsEncoder
Expand Down Expand Up @@ -72,6 +73,7 @@
from openedx.features.content_type_gating.models import ContentTypeGatingConfig
from openedx.features.content_type_gating.partitions import CONTENT_TYPE_GATING_SCHEME
from openedx.features.course_experience.waffle import ENABLE_COURSE_ABOUT_SIDEBAR_HTML
from organizations.models import Organization
from xmodule.contentstore.content import StaticContent # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.course_module import CourseBlock, DEFAULT_START_DATE, CourseFields # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.error_module import ErrorBlock # lint-amnesty, pylint: disable=wrong-import-order
Expand Down Expand Up @@ -586,7 +588,9 @@ def format_in_process_course_view(uca):
'allow_unicode_course_id': settings.FEATURES.get('ALLOW_UNICODE_COURSE_ID', False),
'allow_course_reruns': settings.FEATURES.get('ALLOW_COURSE_RERUNS', True),
'optimization_enabled': optimization_enabled,
'active_tab': 'courses'
'active_tab': 'courses',
'allowed_organizations': get_allowed_organizations(user),
'can_create_organizations': user_can_create_organizations(user),
})


Expand Down Expand Up @@ -1907,3 +1911,35 @@ def _get_course_creator_status(user):
course_creator_status = 'granted'

return course_creator_status


def get_allowed_organizations(user):
"""
Helper method for returning the list of organizations for which the user is allowed to create courses.
"""
if settings.FEATURES.get('ENABLE_CREATOR_GROUP', False):
return get_organizations(user)
else:
return []


def user_can_create_organizations(user):
"""
Returns True if the user can create organizations.
"""
return user.is_staff or not settings.FEATURES.get('ENABLE_CREATOR_GROUP', False)


def get_organizations(user):
"""
Returns the list of organizations for which the user is allowed to create courses.
"""
course_creator = CourseCreator.objects.filter(user=user).first()
if not course_creator:
return []
elif course_creator.all_organizations:
organizations = Organization.objects.all().values_list('short_name', flat=True)
else:
organizations = course_creator.organizations.all().values_list('short_name', flat=True)

return organizations
4 changes: 4 additions & 0 deletions cms/static/sass/views/_course-create.scss
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@
width: 100%;
}

.new-course-org{
padding: 10px;
}

.rerun-course-number,
.rerun-course-number-label {
color: #a0a0a0;
Expand Down
8 changes: 7 additions & 1 deletion cms/static/sass/views/_dashboard.scss
Original file line number Diff line number Diff line change
Expand Up @@ -676,10 +676,16 @@
.new-course-org,
.new-course-number,
.new-course-name,
.new-course-run {
.new-course-run,
.new-library-org {
width: 100%;
}

.new-course-org,
.new-library-org {
padding: 10px;
}

.course-run-text-direction {
direction: ltr;
text-align: right;
Expand Down
20 changes: 18 additions & 2 deletions cms/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,15 @@ <h3 class="title">${_("Create a New Course")}</h3>
<label for="new-course-org">${_("Organization")}</label>
## Translators: This is an example for the name of the organization sponsoring a course, seen when filling out the form to create a new course. The organization name cannot contain spaces.
## Translators: "e.g. UniversityX or OrganizationX" is a placeholder displayed when user put no data into this field.
<input class="new-course-org" id="new-course-org" type="text" name="new-course-org" required placeholder="${_('e.g. UniversityX or OrganizationX')}" aria-describedby="tip-new-course-org tip-error-new-course-org" />
% if can_create_organizations:
<input class="new-course-org" id="new-course-org" type="text" name="new-course-org" required placeholder="${_('e.g. UniversityX or OrganizationX')}" aria-describedby="tip-new-course-org tip-error-new-course-org" />
% else:
<select class="new-course-org" id="new-course-org" name="new-course-org" required aria-describedby="tip-new-course-org tip-error-new-course-org">
% for org in allowed_organizations:
<option value="${org}">${org}</option>
% endfor
</select>
% endif
<span class="tip" id="tip-new-course-org">${Text(_("The name of the organization sponsoring the course. {strong_start}Note: The organization name is part of the course URL.{strong_end} This cannot be changed, but you can set a different display name in Advanced Settings later.")).format(
strong_start=HTML('<strong>'),
strong_end=HTML('</strong>'),
Expand Down Expand Up @@ -155,7 +163,15 @@ <h3 class="title">${_("Create a New Library")}</h3>
</li>
<li class="field text required">
<label for="new-library-org">${_("Organization")}</label>
<input class="new-library-org" id="new-library-org" type="text" name="new-library-org" required placeholder="${_('e.g. UniversityX or OrganizationX')}" aria-describedby="tip-new-library-org tip-error-new-library-org" />
% if can_create_organizations:
<input class="new-library-org" id="new-library-org" type="text" name="new-library-org" required placeholder="${_('e.g. UniversityX or OrganizationX')}" aria-describedby="tip-new-library-org tip-error-new-library-org" />
% else:
<select class="new-library-org" id="new-library-org" name="new-library-org" required aria-describedby="tip-new-library-org tip-error-new-library-org">
% for org in allowed_organizations:
<option value="${org}">${org}</option>
% endfor
</select>
% endif
<span class="tip" id="tip-new-library-org">${_("The public organization name for your library.")} ${_("This cannot be changed.")}</span>
<span class="tip tip-error is-hiding" id="tip-error-new-library-org"></span>
</li>
Expand Down