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
7 changes: 7 additions & 0 deletions cms/djangoapps/contentstore/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,13 @@ def reverse_course_url(handler_name, course_key, kwargs=None):
return reverse_url(handler_name, 'course_key_string', course_key, kwargs)


def reverse_library_url(handler_name, library_key, kwargs=None):
"""
Creates the URL for handlers that use library_keys as URL parameters.
"""
return reverse_url(handler_name, 'library_key_string', library_key, kwargs)


def reverse_usage_url(handler_name, usage_key, kwargs=None):
"""
Creates the URL for handlers that use usage_keys as URL parameters.
Expand Down
1 change: 1 addition & 0 deletions cms/djangoapps/contentstore/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from .helpers import *
from .item import *
from .import_export import *
from .library import *
from .preview import *
from .public import *
from .export_git import *
Expand Down
30 changes: 25 additions & 5 deletions cms/djangoapps/contentstore/views/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,15 @@
ADVANCED_PROBLEM_TYPES = settings.ADVANCED_PROBLEM_TYPES


CONTAINER_TEMPATES = [
"basic-modal", "modal-button", "edit-xblock-modal",
"editor-mode-button", "upload-dialog", "image-modal",
"add-xblock-component", "add-xblock-component-button", "add-xblock-component-menu",
"add-xblock-component-menu-problem", "xblock-string-field-editor", "publish-xblock", "publish-history",
"unit-outline", "container-message"
]


def _advanced_component_types():
"""
Return advanced component types which can be created.
Expand Down Expand Up @@ -216,14 +225,15 @@ def container_handler(request, usage_key_string):
'xblock_info': xblock_info,
'draft_preview_link': preview_lms_link,
'published_preview_link': lms_link,
'templates': CONTAINER_TEMPATES
})
else:
return HttpResponseBadRequest("Only supports HTML requests")


def get_component_templates(course):
def get_component_templates(courselike, library=False):
"""
Returns the applicable component templates that can be used by the specified course.
Returns the applicable component templates that can be used by the specified course or library.
"""
def create_template_dict(name, cat, boilerplate_name=None, is_common=False):
"""
Expand Down Expand Up @@ -254,7 +264,13 @@ def create_template_dict(name, cat, boilerplate_name=None, is_common=False):
categories = set()
# The component_templates array is in the order of "advanced" (if present), followed
# by the components in the order listed in COMPONENT_TYPES.
for category in COMPONENT_TYPES:
component_types = COMPONENT_TYPES[:]

# Libraries do not support discussions
if library:
component_types = [component for component in component_types if component != 'discussion']

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Kelketek Might want adding discussion-forum to prevent adding discussion XBlock. Not sure if it's really needed though. What was the reason behind disabling discussion XModule?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@e-kolpakov Don't know, the issue on Jira just says to do it, so I did. Disabling discussion-forum shouldn't be needed, since it's an advanced component and advanced components won't be used.


for category in component_types:
templates_for_category = []
component_class = _load_mixed_class(category)
# add the default template with localized display name
Expand All @@ -268,7 +284,7 @@ def create_template_dict(name, cat, boilerplate_name=None, is_common=False):
if hasattr(component_class, 'templates'):
for template in component_class.templates():
filter_templates = getattr(component_class, 'filter_templates', None)
if not filter_templates or filter_templates(template, course):
if not filter_templates or filter_templates(template, courselike):
templates_for_category.append(
create_template_dict(
_(template['metadata'].get('display_name')),
Expand All @@ -293,11 +309,15 @@ def create_template_dict(name, cat, boilerplate_name=None, is_common=False):
"display_name": component_display_names[category]
})

# Libraries do not support advanced components at this time.
if library:
return component_templates

# Check if there are any advanced modules specified in the course policy.
# These modules should be specified as a list of strings, where the strings
# are the names of the modules in ADVANCED_COMPONENT_TYPES that should be
# enabled for the course.
course_advanced_keys = course.advanced_modules
course_advanced_keys = courselike.advanced_modules
advanced_component_templates = {"type": "advanced", "templates": [], "display_name": _("Advanced")}
advanced_component_types = _advanced_component_types()
# Set component types according to course policy file
Expand Down
26 changes: 26 additions & 0 deletions cms/djangoapps/contentstore/views/course.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
add_extra_panel_tab,
remove_extra_panel_tab,
reverse_course_url,
reverse_library_url,
reverse_usage_url,
reverse_url,
remove_all_instructors,
Expand All @@ -56,6 +57,7 @@
ADVANCED_COMPONENT_TYPES,
)
from contentstore.tasks import rerun_course
from .library import LIBRARIES_ENABLED
from .item import create_xblock_info
from course_creators.views import get_course_creator_status, add_user_with_status_unrequested
from contentstore import utils
Expand Down Expand Up @@ -341,6 +343,14 @@ def _accessible_courses_list_from_groups(request):
return courses_list.values(), in_process_course_actions


def _accessible_libraries_list(user):
"""
List all libraries available to the logged in user by iterating through all libraries
"""
# No need to worry about ErrorDescriptors - split's get_libraries() never returns them.
return [lib for lib in modulestore().get_libraries() if has_course_author_access(user, lib.location)]


@login_required
@ensure_csrf_cookie
def course_listing(request):
Expand All @@ -360,6 +370,8 @@ def course_listing(request):
# so fallback to iterating through all courses
courses, in_process_course_actions = _accessible_courses_list(request)

libraries = _accessible_libraries_list(request.user) if LIBRARIES_ENABLED else []

def format_course_for_view(course):
"""
Return a dict of the data which the view requires for each course
Expand Down Expand Up @@ -396,6 +408,18 @@ def format_in_process_course_view(uca):
) if uca.state == CourseRerunUIStateManager.State.FAILED else ''
}

def format_library_for_view(library):
"""
Return a dict of the data which the view requires for each library
"""
return {
'display_name': library.display_name,
'library_key': unicode(library.location.library_key),
'url': reverse_library_url('library_handler', unicode(library.location.library_key)),
'org': library.display_org_with_default,
'number': library.display_number_with_default,
}

# remove any courses in courses that are also in the in_process_course_actions list
in_process_action_course_keys = [uca.course_key for uca in in_process_course_actions]
courses = [
Expand All @@ -409,6 +433,8 @@ def format_in_process_course_view(uca):
return render_to_response('index.html', {
'courses': courses,
'in_process_course_actions': in_process_course_actions,
'libraries_enabled': LIBRARIES_ENABLED,
'libraries': [format_library_for_view(lib) for lib in libraries],
'user': request.user,
'request_course_creator_url': reverse('contentstore.views.request_course_creator'),
'course_creator_status': _get_course_creator_status(request.user),
Expand Down
5 changes: 4 additions & 1 deletion cms/djangoapps/contentstore/views/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from edxmako.shortcuts import render_to_string, render_to_response
from xblock.core import XBlock
from xmodule.modulestore.django import modulestore
from contentstore.utils import reverse_course_url, reverse_usage_url
from contentstore.utils import reverse_course_url, reverse_library_url, reverse_usage_url

__all__ = ['edge', 'event', 'landing']

Expand Down Expand Up @@ -106,6 +106,9 @@ def xblock_studio_url(xblock, parent_xblock=None):
url=reverse_course_url('course_handler', xblock.location.course_key),
usage_key=urllib.quote(unicode(xblock.location))
)
elif category == 'library':
library_key = xblock.location.course_key
return reverse_library_url('library_handler', library_key)
else:
return reverse_usage_url('container_handler', xblock.location)

Expand Down
26 changes: 20 additions & 6 deletions cms/djangoapps/contentstore/views/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
from models.settings.course_grading import CourseGradingModel
from cms.lib.xblock.runtime import handler_url, local_resource_url
from opaque_keys.edx.keys import UsageKey, CourseKey
from opaque_keys.edx.locator import LibraryUsageLocator

__all__ = ['orphan_handler', 'xblock_handler', 'xblock_view_handler', 'xblock_outline_handler']

Expand Down Expand Up @@ -459,6 +460,13 @@ def _create_item(request):
if not has_course_author_access(request.user, usage_key.course_key):
raise PermissionDenied()

if isinstance(usage_key, LibraryUsageLocator):
# Only these categories are supported at this time.
if category not in ['html', 'problem', 'video']:
return HttpResponseBadRequest(
"Category '%s' not supported for Libraries" % category, content_type='text/plain'
)

store = modulestore()
with store.bulk_operations(usage_key.course_key):
parent = store.get_item(usage_key)
Expand Down Expand Up @@ -649,7 +657,9 @@ def _get_module_info(xblock, rewrite_static_links=True):
)

# Pre-cache has changes for the entire course because we'll need it for the ancestor info
modulestore().has_changes(modulestore().get_course(xblock.location.course_key, depth=None))
# Except library blocks which don't [yet] use draft/publish
if not isinstance(xblock.location, LibraryUsageLocator):
modulestore().has_changes(modulestore().get_course(xblock.location.course_key, depth=None))

# Note that children aren't being returned until we have a use case.
return create_xblock_info(xblock, data=data, metadata=own_metadata(xblock), include_ancestor_info=True)
Expand Down Expand Up @@ -690,12 +700,16 @@ def safe_get_username(user_id):

return None

is_library_block = isinstance(xblock.location, LibraryUsageLocator)
is_xblock_unit = is_unit(xblock, parent_xblock)
# this should not be calculated for Sections and Subsections on Unit page
has_changes = modulestore().has_changes(xblock) if (is_xblock_unit or course_outline) else None
# this should not be calculated for Sections and Subsections on Unit page or for library blocks
has_changes = modulestore().has_changes(xblock) if (is_xblock_unit or course_outline) and not is_library_block else None

if graders is None:
graders = CourseGradingModel.fetch(xblock.location.course_key).graders
if not is_library_block:
graders = CourseGradingModel.fetch(xblock.location.course_key).graders
else:
graders = []

# Compute the child info first so it can be included in aggregate information for the parent
should_visit_children = include_child_info and (course_outline and not is_xblock_unit or not course_outline)
Expand All @@ -715,15 +729,15 @@ def safe_get_username(user_id):
visibility_state = _compute_visibility_state(xblock, child_info, is_xblock_unit and has_changes)
else:
visibility_state = None
published = modulestore().has_published_version(xblock)
published = modulestore().has_published_version(xblock) if not is_library_block else None

xblock_info = {
"id": unicode(xblock.location),
"display_name": xblock.display_name_with_default,
"category": xblock.category,
"edited_on": get_default_time_display(xblock.subtree_edited_on) if xblock.subtree_edited_on else None,
"published": published,
"published_on": get_default_time_display(xblock.published_on) if xblock.published_on else None,
"published_on": get_default_time_display(xblock.published_on) if published and xblock.published_on else None,
"studio_url": xblock_studio_url(xblock, parent_xblock),
"released_to_students": datetime.now(UTC) > xblock.start,
"release_date": release_date,
Expand Down
Loading