def test_get_default_time_display_notz(): test_time = datetime(1992, 3, 12, 15, 3, 30) assert_equals("Mar 12, 1992 at 15:03 UTC", get_default_time_display(test_time)) assert_equals("Mar 12, 1992 at 15:03 UTC", get_default_time_display(test_time, True)) assert_equals("Mar 12, 1992 at 15:03", get_default_time_display(test_time, False))
def test_get_default_time_display(): assert_equals("", get_default_time_display(None)) test_time = datetime(1992, 3, 12, 15, 3, 30, tzinfo=UTC) assert_equals("Mar 12, 1992 at 15:03 UTC", get_default_time_display(test_time)) assert_equals("Mar 12, 1992 at 15:03 UTC", get_default_time_display(test_time, True)) assert_equals("Mar 12, 1992 at 15:03", get_default_time_display(test_time, False))
def test_get_default_time_display(): assert_equals("", date_utils.get_default_time_display(None)) test_time = time.struct_time((1992, 3, 12, 15, 3, 30, 1, 71, 0)) assert_equals("Mar 12, 1992 at 15:03 UTC", date_utils.get_default_time_display(test_time)) assert_equals("Mar 12, 1992 at 15:03 UTC", date_utils.get_default_time_display(test_time, True)) assert_equals("Mar 12, 1992 at 15:03", date_utils.get_default_time_display(test_time, False))
def test_get_default_time_display_no_tzname(): assert_equals("", get_default_time_display(None)) test_time = datetime(1992, 3, 12, 15, 3, 30, tzinfo=NamelessTZ()) assert_equals("Mar 12, 1992 at 15:03-0300", get_default_time_display(test_time)) assert_equals("Mar 12, 1992 at 15:03-0300", get_default_time_display(test_time, True)) assert_equals("Mar 12, 1992 at 15:03", get_default_time_display(test_time, False))
def test_get_default_time_display_notz(): test_time = datetime(1992, 3, 12, 15, 3, 30) assert_equals( "Mar 12, 1992 at 15:03 UTC", get_default_time_display(test_time)) assert_equals( "Mar 12, 1992 at 15:03 UTC", get_default_time_display(test_time, True)) assert_equals( "Mar 12, 1992 at 15:03", get_default_time_display(test_time, False))
def test_get_default_time_display(): assert_equals("", get_default_time_display(None)) test_time = datetime(1992, 3, 12, 15, 3, 30, tzinfo=UTC) assert_equals( "Mar 12, 1992 at 15:03 UTC", get_default_time_display(test_time)) assert_equals( "Mar 12, 1992 at 15:03 UTC", get_default_time_display(test_time, True)) assert_equals( "Mar 12, 1992 at 15:03", get_default_time_display(test_time, False))
def test_get_default_time_display_no_tzname(): assert_equals("", get_default_time_display(None)) test_time = datetime(1992, 3, 12, 15, 3, 30, tzinfo=NamelessTZ()) assert_equals( "Mar 12, 1992 at 15:03-0300", get_default_time_display(test_time)) assert_equals( "Mar 12, 1992 at 15:03-0300", get_default_time_display(test_time, True)) assert_equals( "Mar 12, 1992 at 15:03", get_default_time_display(test_time, False))
def test_get_default_time_display(): assert_equals("", date_utils.get_default_time_display(None)) test_time = time.struct_time((1992, 3, 12, 15, 3, 30, 1, 71, 0)) assert_equals( "Mar 12, 1992 at 15:03 UTC", date_utils.get_default_time_display(test_time)) assert_equals( "Mar 12, 1992 at 15:03 UTC", date_utils.get_default_time_display(test_time, True)) assert_equals( "Mar 12, 1992 at 15:03", date_utils.get_default_time_display(test_time, False))
def render_make_chapter(context,chapter): __M_caller = context.caller_stack._push_frame() try: course_id = context.get('course_id', UNDEFINED) show_timezone = context.get('show_timezone', UNDEFINED) enumerate = context.get('enumerate', UNDEFINED) __M_writer = context.writer() # SOURCE LINE 7 __M_writer(u'\n <div class="chapter">\n ') # SOURCE LINE 9 if chapter.get('active'): aria_label = _('{chapter}, current chapter').format(chapter=chapter['display_name']) active_class = ' class="active"' else: aria_label = chapter['display_name'] active_class = '' # SOURCE LINE 16 __M_writer(u'\n <h3 ') # SOURCE LINE 17 __M_writer(filters.decode.utf8(active_class)) __M_writer(u' aria-label="') __M_writer(filters.decode.utf8(aria_label)) __M_writer(u'">\n <a href="#">\n ') # SOURCE LINE 19 __M_writer(filters.decode.utf8(chapter['display_name'])) __M_writer(u'\n </a>\n </h3>\n\n <ul>\n') # SOURCE LINE 24 for i,section in enumerate(chapter['sections']): # SOURCE LINE 25 __M_writer(u' <li class="') __M_writer(filters.decode.utf8('active' if 'active' in section and section['active'] else '')) __M_writer(u' ') __M_writer(filters.decode.utf8('graded' if 'graded' in section and section['graded'] else '')) __M_writer(u'">\n <a href="') # SOURCE LINE 26 __M_writer(filters.decode.utf8(reverse('courseware_section', args=[course_id, chapter['url_name'], section['url_name']]))) __M_writer(u'" class="section_link">\n <p>') # SOURCE LINE 27 __M_writer(filters.decode.utf8(section['display_name'])) __M_writer(u' ') __M_writer(filters.decode.utf8('<span class="sr">, current section</span>' if 'active' in section and section['active'] else '')) __M_writer(u'</p>\n<!--@begin:Hide subtitle-->\n<!--@date:2013-11-11--> \n <p class="subtitle" style="display:none;">') # SOURCE LINE 30 __M_writer(filters.decode.utf8(section['format'])) __M_writer(u' ') __M_writer(filters.decode.utf8("due " + get_default_time_display(section['due'], show_timezone) if section.get('due') is not None else '')) __M_writer(u'</p>\n<!--@end-->\n </a>\n </li>\n') # SOURCE LINE 35 __M_writer(u' </ul>\n </div>\n') return '' finally: context.caller_stack._pop_frame()
def asset_index(request, org, course, name): """ Display an editable asset library org, course, name: Attributes of the Location for the item to edit """ location = get_location_and_verify_access(request, org, course, name) upload_asset_callback_url = reverse('upload_asset', kwargs={ 'org': org, 'course': course, 'coursename': name }) course_module = modulestore().get_item(location) course_reference = StaticContent.compute_location(org, course, name) assets = contentstore().get_all_content_for_course(course_reference) # sort in reverse upload date order assets = sorted(assets, key=lambda asset: asset['uploadDate'], reverse=True) asset_display = [] for asset in assets: asset_id = asset['_id'] display_info = {} display_info['displayname'] = asset['displayname'] display_info['uploadDate'] = get_default_time_display( asset['uploadDate'].timetuple()) asset_location = StaticContent.compute_location( asset_id['org'], asset_id['course'], asset_id['name']) display_info['url'] = StaticContent.get_url_path_from_location( asset_location) # note, due to the schema change we may not have a 'thumbnail_location' # in the result set _thumbnail_location = asset.get('thumbnail_location', None) thumbnail_location = Location( _thumbnail_location) if _thumbnail_location is not None else None display_info['thumb_url'] = StaticContent.get_url_path_from_location( thumbnail_location) if thumbnail_location is not None else None asset_display.append(display_info) return render_to_response( 'asset_index.html', { 'active_tab': 'assets', 'context_course': course_module, 'assets': asset_display, 'upload_asset_callback_url': upload_asset_callback_url })
def asset_index(request, org, course, name): """ Display an editable asset library org, course, name: Attributes of the Location for the item to edit """ location = get_location_and_verify_access(request, org, course, name) upload_asset_callback_url = reverse('upload_asset', kwargs={ 'org': org, 'course': course, 'coursename': name }) course_module = modulestore().get_item(location) course_reference = StaticContent.compute_location(org, course, name) assets = contentstore().get_all_content_for_course(course_reference) # sort in reverse upload date order assets = sorted(assets, key=lambda asset: asset['uploadDate'], reverse=True) if request.META.get('HTTP_ACCEPT', "").startswith("application/json"): return JsonResponse(assets_to_json_dict(assets)) asset_display = [] for asset in assets: asset_id = asset['_id'] display_info = {} display_info['displayname'] = asset['displayname'] display_info['uploadDate'] = get_default_time_display(asset['uploadDate']) asset_location = StaticContent.compute_location(asset_id['org'], asset_id['course'], asset_id['name']) display_info['url'] = StaticContent.get_url_path_from_location(asset_location) display_info['portable_url'] = StaticContent.get_static_path_from_location(asset_location) # note, due to the schema change we may not have a 'thumbnail_location' in the result set _thumbnail_location = asset.get('thumbnail_location', None) thumbnail_location = Location(_thumbnail_location) if _thumbnail_location is not None else None display_info['thumb_url'] = StaticContent.get_url_path_from_location(thumbnail_location) if thumbnail_location is not None else None asset_display.append(display_info) return render_to_response('asset_index.html', { 'context_course': course_module, 'assets': asset_display, 'upload_asset_callback_url': upload_asset_callback_url, 'remove_asset_callback_url': reverse('remove_asset', kwargs={ 'org': org, 'course': course, 'name': name }) })
def _get_asset_json(display_name, date, location, thumbnail_location, locked): """ Helper method for formatting the asset information to send to client. """ asset_url = StaticContent.get_url_path_from_location(location) return { 'display_name': display_name, 'date_added': get_default_time_display(date), 'url': asset_url, 'portable_url': StaticContent.get_static_path_from_location(location), 'thumbnail': StaticContent.get_url_path_from_location(thumbnail_location) if thumbnail_location is not None else None, 'locked': locked, # Needed for Backbone delete/update. 'id': asset_url }
def asset_index(request, org, course, name): """ Display an editable asset library org, course, name: Attributes of the Location for the item to edit """ location = get_location_and_verify_access(request, org, course, name) upload_asset_callback_url = reverse("upload_asset", kwargs={"org": org, "course": course, "coursename": name}) course_module = modulestore().get_item(location) course_reference = StaticContent.compute_location(org, course, name) assets = contentstore().get_all_content_for_course(course_reference) # sort in reverse upload date order assets = sorted(assets, key=lambda asset: asset["uploadDate"], reverse=True) asset_display = [] for asset in assets: asset_id = asset["_id"] display_info = {} display_info["displayname"] = asset["displayname"] display_info["uploadDate"] = get_default_time_display(asset["uploadDate"].timetuple()) asset_location = StaticContent.compute_location(asset_id["org"], asset_id["course"], asset_id["name"]) display_info["url"] = StaticContent.get_url_path_from_location(asset_location) # note, due to the schema change we may not have a 'thumbnail_location' in the result set _thumbnail_location = asset.get("thumbnail_location", None) thumbnail_location = Location(_thumbnail_location) if _thumbnail_location is not None else None display_info["thumb_url"] = ( StaticContent.get_url_path_from_location(thumbnail_location) if thumbnail_location is not None else None ) asset_display.append(display_info) return render_to_response( "asset_index.html", { "active_tab": "assets", "context_course": course_module, "assets": asset_display, "upload_asset_callback_url": upload_asset_callback_url, }, )
def edit_unit(request, location): """ Display an editing page for the specified module. Expects a GET request with the parameter `id`. id: A Location URL """ try: course = get_course_for_item(location) except InvalidLocationError: return HttpResponseBadRequest() if not has_access(request.user, course.location): raise PermissionDenied() try: item = modulestore().get_item(location, depth=1) except ItemNotFoundError: return HttpResponseBadRequest() lms_link = get_lms_link_for_item( item.location, course_id=course.location.course_id ) component_templates = defaultdict(list) for category in COMPONENT_TYPES: component_class = XModuleDescriptor.load_class(category) # add the default template component_templates[category].append(( component_class.display_name.default or 'Blank', category, False, # No defaults have markdown (hardcoded current default) None # no boilerplate for overrides )) # add boilerplates for template in component_class.templates(): component_templates[category].append(( template['metadata'].get('display_name'), category, template['metadata'].get('markdown') is not None, template.get('template_id') )) # 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 # Set component types according to course policy file if isinstance(course_advanced_keys, list): for category in course_advanced_keys: if category in ADVANCED_COMPONENT_TYPES: # Do I need to allow for boilerplates or just defaults on the # class? i.e., can an advanced have more than one entry in the # menu? one for default and others for prefilled boilerplates? try: component_class = XModuleDescriptor.load_class(category) component_templates['advanced'].append(( component_class.display_name.default or category, category, False, None # don't override default data )) except PluginMissingError: # dhm: I got this once but it can happen any time the # course author configures an advanced component which does # not exist on the server. This code here merely # prevents any authors from trying to instantiate the # non-existent component type by not showing it in the menu pass else: log.error( "Improper format for course advanced keys! %", course_advanced_keys ) components = [ component.location.url() for component in item.get_children() ] # TODO (cpennington): If we share units between courses, # this will need to change to check permissions correctly so as # to pick the correct parent subsection containing_subsection_locs = modulestore().get_parent_locations( location, None ) containing_subsection = modulestore().get_item(containing_subsection_locs[0]) containing_section_locs = modulestore().get_parent_locations( containing_subsection.location, None ) containing_section = modulestore().get_item(containing_section_locs[0]) # cdodge hack. We're having trouble previewing drafts via jump_to redirect # so let's generate the link url here # need to figure out where this item is in the list of children as the # preview will need this index = 1 for child in containing_subsection.get_children(): if child.location == item.location: break index = index + 1 preview_lms_base = settings.MITX_FEATURES.get('PREVIEW_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, course_name=course.location.name, section=containing_section.location.name, subsection=containing_subsection.location.name, index=index ) unit_state = compute_unit_state(item) return render_to_response('unit.html', { 'context_course': course, 'unit': item, 'unit_location': location, 'components': components, 'component_templates': component_templates, 'draft_preview_link': preview_lms_link, 'published_preview_link': lms_link, 'subsection': containing_subsection, 'release_date': get_default_time_display(containing_subsection.lms.start) if containing_subsection.lms.start is not None else None, 'section': containing_section, 'new_unit_category': 'vertical', 'unit_state': unit_state, 'published_date': get_default_time_display(item.cms.published_date) if item.cms.published_date is not None else None })
def edit_unit(request, location): """ Display an editing page for the specified module. Expects a GET request with the parameter 'id'. id: A Location URL """ try: course = get_course_for_item(location) except InvalidLocationError: return HttpResponseBadRequest() if not has_access(request.user, course.location): raise PermissionDenied() try: item = modulestore().get_item(location, depth=1) except ItemNotFoundError: return HttpResponseBadRequest() lms_link = get_lms_link_for_item(item.location, course_id=course.location.course_id) component_templates = defaultdict(list) # 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 # Set component types according to course policy file component_types = list(COMPONENT_TYPES) if isinstance(course_advanced_keys, list): course_advanced_keys = [ c for c in course_advanced_keys if c in ADVANCED_COMPONENT_TYPES ] if len(course_advanced_keys) > 0: component_types.append(ADVANCED_COMPONENT_CATEGORY) else: log.error("Improper format for course advanced keys! {0}".format( course_advanced_keys)) templates = modulestore().get_items(Location('i4x', 'edx', 'templates')) for template in templates: category = template.location.category if category in course_advanced_keys: category = ADVANCED_COMPONENT_CATEGORY if category in component_types: # This is a hack to create categories for different xmodules component_templates[category].append( (template.display_name_with_default, template.location.url(), hasattr(template, 'markdown') and template.markdown is not None)) components = [ component.location.url() for component in item.get_children() ] # TODO (cpennington): If we share units between courses, # this will need to change to check permissions correctly so as # to pick the correct parent subsection containing_subsection_locs = modulestore().get_parent_locations( location, None) containing_subsection = modulestore().get_item( containing_subsection_locs[0]) containing_section_locs = modulestore().get_parent_locations( containing_subsection.location, None) containing_section = modulestore().get_item(containing_section_locs[0]) # cdodge hack. We're having trouble previewing drafts via jump_to redirect # so let's generate the link url here # need to figure out where this item is in the list of children as the preview will need this index = 1 for child in containing_subsection.get_children(): if child.location == item.location: break index = index + 1 preview_lms_base = settings.MITX_FEATURES.get('PREVIEW_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, course_name=course.location.name, section=containing_section.location.name, subsection=containing_subsection.location.name, index=index) unit_state = compute_unit_state(item) return render_to_response( 'unit.html', { 'context_course': course, 'active_tab': 'courseware', 'unit': item, 'unit_location': location, 'components': components, 'component_templates': component_templates, 'draft_preview_link': preview_lms_link, 'published_preview_link': lms_link, 'subsection': containing_subsection, 'release_date': get_default_time_display(containing_subsection.lms.start) if containing_subsection.lms.start is not None else None, 'section': containing_section, 'create_new_unit_template': Location('i4x', 'edx', 'templates', 'vertical', 'Empty'), 'unit_state': unit_state, 'published_date': item.cms.published_date.strftime('%B %d, %Y') if item.cms.published_date is not None else None, })
def edit_unit(request, location): """ Display an editing page for the specified module. Expects a GET request with the parameter `id`. id: A Location URL """ try: course = get_course_for_item(location) except InvalidLocationError: return HttpResponseBadRequest() if not has_access(request.user, course.location): raise PermissionDenied() try: item = modulestore().get_item(location, depth=1) except ItemNotFoundError: return HttpResponseBadRequest() lms_link = get_lms_link_for_item( item.location, course_id=course.location.course_id ) # Note that the unit_state (draft, public, private) does not match up with the published value # passed to translate_location. The two concepts are different at this point. unit_locator = loc_mapper().translate_location( course.location.course_id, Location(location), False, True ) component_templates = defaultdict(list) for category in COMPONENT_TYPES: component_class = load_mixed_class(category) # add the default template # TODO: Once mixins are defined per-application, rather than per-runtime, # this should use a cms mixed-in class. (cpennington) if hasattr(component_class, 'display_name'): display_name = component_class.display_name.default or 'Blank' else: display_name = 'Blank' component_templates[category].append(( display_name, category, False, # No defaults have markdown (hardcoded current default) None # no boilerplate for overrides )) # add boilerplates 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): component_templates[category].append(( template['metadata'].get('display_name'), category, template['metadata'].get('markdown') is not None, template.get('template_id') )) # 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 # Set component types according to course policy file if isinstance(course_advanced_keys, list): for category in course_advanced_keys: if category in ADVANCED_COMPONENT_TYPES: # Do I need to allow for boilerplates or just defaults on the # class? i.e., can an advanced have more than one entry in the # menu? one for default and others for prefilled boilerplates? try: component_class = load_mixed_class(category) component_templates['advanced'].append(( component_class.display_name.default or category, category, False, None # don't override default data )) except PluginMissingError: # dhm: I got this once but it can happen any time the # course author configures an advanced component which does # not exist on the server. This code here merely # prevents any authors from trying to instantiate the # non-existent component type by not showing it in the menu pass else: log.error( "Improper format for course advanced keys! %s", course_advanced_keys ) components = [ [ component.location.url(), loc_mapper().translate_location( course.location.course_id, component.location, False, True ) ] for component in item.get_children() ] # TODO (cpennington): If we share units between courses, # this will need to change to check permissions correctly so as # to pick the correct parent subsection containing_subsection_locs = modulestore().get_parent_locations(location, None) containing_subsection = modulestore().get_item(containing_subsection_locs[0]) containing_section_locs = modulestore().get_parent_locations( containing_subsection.location, None ) containing_section = modulestore().get_item(containing_section_locs[0]) # cdodge hack. We're having trouble previewing drafts via jump_to redirect # so let's generate the link url here # need to figure out where this item is in the list of children as the # preview will need this index = 1 for child in containing_subsection.get_children(): if child.location == item.location: break index = index + 1 preview_lms_base = settings.MITX_FEATURES.get('PREVIEW_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, course_name=course.location.name, section=containing_section.location.name, subsection=containing_subsection.location.name, index=index ) return render_to_response('unit.html', { 'context_course': course, 'unit': item, # Still needed for creating a draft. 'unit_location': location, 'unit_locator': unit_locator, 'components': components, 'component_templates': component_templates, 'draft_preview_link': preview_lms_link, 'published_preview_link': lms_link, 'subsection': containing_subsection, 'release_date': ( get_default_time_display(containing_subsection.start) if containing_subsection.start is not None else None ), 'section': containing_section, 'new_unit_category': 'vertical', 'unit_state': compute_unit_state(item), 'published_date': ( get_default_time_display(item.published_date) if item.published_date is not None else None ), })
def registration_end_date_text(self): return date_utils.get_default_time_display( self.registration_end_date)
def registration_end_date_text(self): return date_utils.get_default_time_display(self.registration_end_date)
def upload_asset(request, org, course, coursename): """ cdodge: this method allows for POST uploading of files into the course asset library, which will be supported by GridFS in MongoDB. """ if request.method != "POST": # (cdodge) @todo: Is there a way to do a - say - 'raise Http400'? return HttpResponseBadRequest() # construct a location from the passed in path location = get_location_and_verify_access(request, org, course, coursename) # Does the course actually exist?!? Get anything from it to prove its existance try: modulestore().get_item(location) except: # no return it as a Bad Request response logging.error("Could not find course" + location) return HttpResponseBadRequest() # compute a 'filename' which is similar to the location formatting, we're using the 'filename' # nomenclature since we're using a FileSystem paradigm here. We're just imposing # the Location string formatting expectations to keep things a bit more consistent filename = request.FILES["file"].name mime_type = request.FILES["file"].content_type filedata = request.FILES["file"].read() content_loc = StaticContent.compute_location(org, course, filename) content = StaticContent(content_loc, filename, mime_type, filedata) # first let's see if a thumbnail can be created (thumbnail_content, thumbnail_location) = contentstore().generate_thumbnail(content) # delete cached thumbnail even if one couldn't be created this time (else the old thumbnail will continue to show) del_cached_content(thumbnail_location) # now store thumbnail location only if we could create it if thumbnail_content is not None: content.thumbnail_location = thumbnail_location # then commit the content contentstore().save(content) del_cached_content(content.location) # readback the saved content - we need the database timestamp readback = contentstore().find(content.location) response_payload = { "displayname": content.name, "uploadDate": get_default_time_display(readback.last_modified_at.timetuple()), "url": StaticContent.get_url_path_from_location(content.location), "thumb_url": StaticContent.get_url_path_from_location(thumbnail_location) if thumbnail_content is not None else None, "msg": "Upload completed", } response = HttpResponse(json.dumps(response_payload)) response["asset_url"] = StaticContent.get_url_path_from_location(content.location) return response
def unit_handler(request, tag=None, course_id=None, branch=None, version_guid=None, block=None): """ The restful handler for unit-specific requests. GET html: return html page for editing a unit json: not currently supported """ if 'text/html' in request.META.get('HTTP_ACCEPT', 'text/html'): locator = BlockUsageLocator(course_id=course_id, branch=branch, version_guid=version_guid, usage_id=block) try: old_location, course, item, lms_link = _get_item_in_course( request, locator) except ItemNotFoundError: return HttpResponseBadRequest() component_templates = defaultdict(list) for category in COMPONENT_TYPES: component_class = _load_mixed_class(category) # add the default template # TODO: Once mixins are defined per-application, rather than per-runtime, # this should use a cms mixed-in class. (cpennington) if hasattr(component_class, 'display_name'): display_name = component_class.display_name.default or 'Blank' else: display_name = 'Blank' component_templates[category].append(( display_name, category, False, # No defaults have markdown (hardcoded current default) None # no boilerplate for overrides )) # add boilerplates 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): component_templates[category].append( (template['metadata'].get('display_name'), category, template['metadata'].get('markdown') is not None, template.get('template_id'))) # 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 # Set component types according to course policy file if isinstance(course_advanced_keys, list): for category in course_advanced_keys: if category in ADVANCED_COMPONENT_TYPES: # Do I need to allow for boilerplates or just defaults on the # class? i.e., can an advanced have more than one entry in the # menu? one for default and others for prefilled boilerplates? try: component_class = _load_mixed_class(category) component_templates['advanced'].append(( component_class.display_name.default or category, category, False, None # don't override default data )) except PluginMissingError: # dhm: I got this once but it can happen any time the # course author configures an advanced component which does # not exist on the server. This code here merely # prevents any authors from trying to instantiate the # non-existent component type by not showing it in the menu pass else: log.error("Improper format for course advanced keys! %s", course_advanced_keys) components = [ loc_mapper().translate_location(course.location.course_id, component.location, False, True) for component in item.get_children() ] # TODO (cpennington): If we share units between courses, # this will need to change to check permissions correctly so as # to pick the correct parent subsection containing_subsection_locs = modulestore().get_parent_locations( old_location, None) containing_subsection = modulestore().get_item( containing_subsection_locs[0]) containing_section_locs = modulestore().get_parent_locations( containing_subsection.location, None) containing_section = modulestore().get_item(containing_section_locs[0]) # cdodge hack. We're having trouble previewing drafts via jump_to redirect # so let's generate the link url here # need to figure out where this item is in the list of children as the # preview will need this index = 1 for child in containing_subsection.get_children(): if child.location == item.location: break index = index + 1 preview_lms_base = settings.FEATURES.get('PREVIEW_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, course_name=course.location.name, section=containing_section.location.name, subsection=containing_subsection.location.name, index=index) return render_to_response( 'unit.html', { 'context_course': course, 'unit': item, 'unit_locator': locator, 'components': components, 'component_templates': component_templates, 'draft_preview_link': preview_lms_link, 'published_preview_link': lms_link, 'subsection': containing_subsection, 'release_date': (get_default_time_display(containing_subsection.start) if containing_subsection.start is not None else None), 'section': containing_section, 'new_unit_category': 'vertical', 'unit_state': compute_unit_state(item), 'published_date': (get_default_time_display(item.published_date) if item.published_date is not None else None), }) else: return HttpResponseBadRequest("Only supports html requests")
def upload_asset(request, org, course, coursename): ''' This method allows for POST uploading of files into the course asset library, which will be supported by GridFS in MongoDB. ''' # construct a location from the passed in path location = get_location_and_verify_access(request, org, course, coursename) # Does the course actually exist?!? Get anything from it to prove its # existence try: modulestore().get_item(location) except: # no return it as a Bad Request response logging.error('Could not find course' + location) return HttpResponseBadRequest() if 'file' not in request.FILES: return HttpResponseBadRequest() # compute a 'filename' which is similar to the location formatting, we're # using the 'filename' nomenclature since we're using a FileSystem paradigm # here. We're just imposing the Location string formatting expectations to # keep things a bit more consistent upload_file = request.FILES['file'] filename = upload_file.name mime_type = upload_file.content_type content_loc = StaticContent.compute_location(org, course, filename) chunked = upload_file.multiple_chunks() sc_partial = partial(StaticContent, content_loc, filename, mime_type) if chunked: content = sc_partial(upload_file.chunks()) tempfile_path = upload_file.temporary_file_path() else: content = sc_partial(upload_file.read()) tempfile_path = None thumbnail_content = None thumbnail_location = None # first let's see if a thumbnail can be created (thumbnail_content, thumbnail_location) = contentstore().generate_thumbnail( content, tempfile_path=tempfile_path ) # delete cached thumbnail even if one couldn't be created this time (else # the old thumbnail will continue to show) del_cached_content(thumbnail_location) # now store thumbnail location only if we could create it if thumbnail_content is not None: content.thumbnail_location = thumbnail_location # then commit the content contentstore().save(content) del_cached_content(content.location) # readback the saved content - we need the database timestamp readback = contentstore().find(content.location) response_payload = { 'displayname': content.name, 'uploadDate': get_default_time_display(readback.last_modified_at), 'url': StaticContent.get_url_path_from_location(content.location), 'portable_url': StaticContent.get_static_path_from_location(content.location), 'thumb_url': StaticContent.get_url_path_from_location(thumbnail_location) if thumbnail_content is not None else None, 'msg': 'Upload completed' } response = JsonResponse(response_payload) return response
def edit_unit(request, location): """ Display an editing page for the specified module. Expects a GET request with the parameter 'id'. id: A Location URL """ try: course = get_course_for_item(location) except InvalidLocationError: return HttpResponseBadRequest() if not has_access(request.user, course.location): raise PermissionDenied() try: item = modulestore().get_item(location, depth=1) except ItemNotFoundError: return HttpResponseBadRequest() lms_link = get_lms_link_for_item(item.location, course_id=course.location.course_id) component_templates = defaultdict(list) for category in COMPONENT_TYPES: component_class = XModuleDescriptor.load_class(category) # add the default template component_templates[category].append( ( component_class.display_name.default or "Blank", category, False, # No defaults have markdown (hardcoded current default) None, # no boilerplate for overrides ) ) # add boilerplates for template in component_class.templates(): component_templates[category].append( ( template["metadata"].get("display_name"), category, template["metadata"].get("markdown") is not None, template.get("template_id"), ) ) # 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 # Set component types according to course policy file if isinstance(course_advanced_keys, list): for category in course_advanced_keys: if category in ADVANCED_COMPONENT_TYPES: # Do I need to allow for boilerplates or just defaults on the class? i.e., can an advanced # have more than one entry in the menu? one for default and others for prefilled boilerplates? try: component_class = XModuleDescriptor.load_class(category) component_templates["advanced"].append( ( component_class.display_name.default or category, category, False, None, # don't override default data ) ) except PluginMissingError: # dhm: I got this once but it can happen any time the course author configures # an advanced component which does not exist on the server. This code here merely # prevents any authors from trying to instantiate the non-existent component type # by not showing it in the menu pass else: log.error("Improper format for course advanced keys! {0}".format(course_advanced_keys)) components = [component.location.url() for component in item.get_children()] # TODO (cpennington): If we share units between courses, # this will need to change to check permissions correctly so as # to pick the correct parent subsection containing_subsection_locs = modulestore().get_parent_locations(location, None) containing_subsection = modulestore().get_item(containing_subsection_locs[0]) containing_section_locs = modulestore().get_parent_locations(containing_subsection.location, None) containing_section = modulestore().get_item(containing_section_locs[0]) # cdodge hack. We're having trouble previewing drafts via jump_to redirect # so let's generate the link url here # need to figure out where this item is in the list of children as the preview will need this index = 1 for child in containing_subsection.get_children(): if child.location == item.location: break index = index + 1 preview_lms_base = settings.MITX_FEATURES.get("PREVIEW_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, course_name=course.location.name, section=containing_section.location.name, subsection=containing_subsection.location.name, index=index, ) unit_state = compute_unit_state(item) return render_to_response( "unit.html", { "context_course": course, "unit": item, "unit_location": location, "components": components, "component_templates": component_templates, "draft_preview_link": preview_lms_link, "published_preview_link": lms_link, "subsection": containing_subsection, "release_date": get_default_time_display(containing_subsection.lms.start) if containing_subsection.lms.start is not None else None, "section": containing_section, "new_unit_category": "vertical", "unit_state": unit_state, "published_date": get_default_time_display(item.cms.published_date) if item.cms.published_date is not None else None, }, )
def unit_handler(request, tag=None, package_id=None, branch=None, version_guid=None, block=None): """ The restful handler for unit-specific requests. GET html: return html page for editing a unit json: not currently supported """ if 'text/html' in request.META.get('HTTP_ACCEPT', 'text/html'): locator = BlockUsageLocator(package_id=package_id, branch=branch, version_guid=version_guid, block_id=block) try: old_location, course, item, lms_link = _get_item_in_course(request, locator) except ItemNotFoundError: return HttpResponseBadRequest() component_templates = defaultdict(list) for category in COMPONENT_TYPES: component_class = _load_mixed_class(category) # add the default template # TODO: Once mixins are defined per-application, rather than per-runtime, # this should use a cms mixed-in class. (cpennington) if hasattr(component_class, 'display_name'): display_name = component_class.display_name.default or 'Blank' else: display_name = 'Blank' component_templates[category].append(( display_name, category, False, # No defaults have markdown (hardcoded current default) None # no boilerplate for overrides )) # add boilerplates 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): component_templates[category].append(( template['metadata'].get('display_name'), category, template['metadata'].get('markdown') is not None, template.get('template_id') )) # 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 # Set component types according to course policy file if isinstance(course_advanced_keys, list): for category in course_advanced_keys: if category in ADVANCED_COMPONENT_TYPES: # Do I need to allow for boilerplates or just defaults on the # class? i.e., can an advanced have more than one entry in the # menu? one for default and others for prefilled boilerplates? try: component_class = _load_mixed_class(category) component_templates['advanced'].append( ( component_class.display_name.default or category, category, False, None # don't override default data ) ) except PluginMissingError: # dhm: I got this once but it can happen any time the # course author configures an advanced component which does # not exist on the server. This code here merely # prevents any authors from trying to instantiate the # non-existent component type by not showing it in the menu pass else: log.error( "Improper format for course advanced keys! %s", course_advanced_keys ) components = [ loc_mapper().translate_location( course.location.course_id, component.location, False, True ) for component in item.get_children() ] # TODO (cpennington): If we share units between courses, # this will need to change to check permissions correctly so as # to pick the correct parent subsection containing_subsection_locs = modulestore().get_parent_locations(old_location, None) containing_subsection = modulestore().get_item(containing_subsection_locs[0]) containing_section_locs = modulestore().get_parent_locations( containing_subsection.location, None ) containing_section = modulestore().get_item(containing_section_locs[0]) # cdodge hack. We're having trouble previewing drafts via jump_to redirect # so let's generate the link url here # need to figure out where this item is in the list of children as the # preview will need this index = 1 for child in containing_subsection.get_children(): if child.location == item.location: break index = index + 1 preview_lms_base = settings.FEATURES.get('PREVIEW_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, course_name=course.location.name, section=containing_section.location.name, subsection=containing_subsection.location.name, index=index ) return render_to_response('unit.html', { 'context_course': course, 'unit': item, 'unit_locator': locator, 'components': components, 'component_templates': component_templates, 'draft_preview_link': preview_lms_link, 'published_preview_link': lms_link, 'subsection': containing_subsection, 'release_date': ( get_default_time_display(containing_subsection.start) if containing_subsection.start is not None else None ), 'section': containing_section, 'new_unit_category': 'vertical', 'unit_state': compute_unit_state(item), 'published_date': ( get_default_time_display(item.published_date) if item.published_date is not None else None ), }) else: return HttpResponseBadRequest("Only supports html requests")
def upload_asset(request, org, course, coursename): ''' This method allows for POST uploading of files into the course asset library, which will be supported by GridFS in MongoDB. ''' # construct a location from the passed in path location = get_location_and_verify_access(request, org, course, coursename) # Does the course actually exist?!? Get anything from it to prove its # existence try: modulestore().get_item(location) except: # no return it as a Bad Request response logging.error('Could not find course' + location) return HttpResponseBadRequest() if 'files[]' not in request.FILES: return HttpResponseBadRequest() # compute a 'filename' which is similar to the location formatting, we're # using the 'filename' nomenclature since we're using a FileSystem paradigm # here. We're just imposing the Location string formatting expectations to # keep things a bit more consistent upload_file = request.FILES['files[]'] filename = upload_file.name mime_type = upload_file.content_type content_loc = StaticContent.compute_location(org, course, filename) chunked = upload_file.multiple_chunks() sc_partial = partial(StaticContent, content_loc, filename, mime_type) if chunked: content = sc_partial(upload_file.chunks()) tempfile_path = upload_file.temporary_file_path() else: content = sc_partial(upload_file.read()) tempfile_path = None thumbnail_content = None thumbnail_location = None # first let's see if a thumbnail can be created (thumbnail_content, thumbnail_location) = contentstore().generate_thumbnail( content, tempfile_path=tempfile_path ) # delete cached thumbnail even if one couldn't be created this time (else # the old thumbnail will continue to show) del_cached_content(thumbnail_location) # now store thumbnail location only if we could create it if thumbnail_content is not None: content.thumbnail_location = thumbnail_location # then commit the content contentstore().save(content) del_cached_content(content.location) # readback the saved content - we need the database timestamp readback = contentstore().find(content.location) response_payload = { 'displayname': content.name, 'uploadDate': get_default_time_display(readback.last_modified_at), 'url': StaticContent.get_url_path_from_location(content.location), 'portable_url': StaticContent.get_static_path_from_location(content.location), 'thumb_url': StaticContent.get_url_path_from_location(thumbnail_location) if thumbnail_content is not None else None, 'msg': 'Upload completed' } response = JsonResponse(response_payload) return response
def edit_unit(request, location): """ Display an editing page for the specified module. Expects a GET request with the parameter 'id'. id: A Location URL """ course = get_course_for_item(location) if not has_access(request.user, course.location): raise PermissionDenied() item = modulestore().get_item(location, depth=1) lms_link = get_lms_link_for_item(item.location, course_id=course.location.course_id) component_templates = defaultdict(list) # 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 # Set component types according to course policy file component_types = list(COMPONENT_TYPES) if isinstance(course_advanced_keys, list): course_advanced_keys = [c for c in course_advanced_keys if c in ADVANCED_COMPONENT_TYPES] if len(course_advanced_keys) > 0: component_types.append(ADVANCED_COMPONENT_CATEGORY) else: log.error("Improper format for course advanced keys! {0}".format(course_advanced_keys)) templates = modulestore().get_items(Location('i4x', 'edx', 'templates')) for template in templates: category = template.location.category if category in course_advanced_keys: category = ADVANCED_COMPONENT_CATEGORY if category in component_types: # This is a hack to create categories for different xmodules component_templates[category].append(( template.display_name_with_default, template.location.url(), hasattr(template, 'markdown') and template.markdown is not None )) components = [ component.location.url() for component in item.get_children() ] # TODO (cpennington): If we share units between courses, # this will need to change to check permissions correctly so as # to pick the correct parent subsection containing_subsection_locs = modulestore().get_parent_locations(location, None) containing_subsection = modulestore().get_item(containing_subsection_locs[0]) containing_section_locs = modulestore().get_parent_locations(containing_subsection.location, None) containing_section = modulestore().get_item(containing_section_locs[0]) # cdodge hack. We're having trouble previewing drafts via jump_to redirect # so let's generate the link url here # need to figure out where this item is in the list of children as the preview will need this index = 1 for child in containing_subsection.get_children(): if child.location == item.location: break index = index + 1 preview_lms_base = settings.MITX_FEATURES.get('PREVIEW_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, course_name=course.location.name, section=containing_section.location.name, subsection=containing_subsection.location.name, index=index) unit_state = compute_unit_state(item) return render_to_response('unit.html', { 'context_course': course, 'active_tab': 'courseware', 'unit': item, 'unit_location': location, 'components': components, 'component_templates': component_templates, 'draft_preview_link': preview_lms_link, 'published_preview_link': lms_link, 'subsection': containing_subsection, 'release_date': get_default_time_display(containing_subsection.lms.start) if containing_subsection.lms.start is not None else None, 'section': containing_section, 'create_new_unit_template': Location('i4x', 'edx', 'templates', 'vertical', 'Empty'), 'unit_state': unit_state, 'published_date': item.cms.published_date.strftime('%B %d, %Y') if item.cms.published_date is not None else None, })
def upload_asset(request, org, course, coursename): ''' cdodge: this method allows for POST uploading of files into the course asset library, which will be supported by GridFS in MongoDB. ''' if request.method != 'POST': # (cdodge) @todo: Is there a way to do a - say - 'raise Http400'? return HttpResponseBadRequest() # construct a location from the passed in path location = get_location_and_verify_access(request, org, course, coursename) # Does the course actually exist?!? Get anything from it to prove its existance try: modulestore().get_item(location) except: # no return it as a Bad Request response logging.error('Could not find course' + location) return HttpResponseBadRequest() if 'file' not in request.FILES: return HttpResponseBadRequest() # compute a 'filename' which is similar to the location formatting, we're using the 'filename' # nomenclature since we're using a FileSystem paradigm here. We're just imposing # the Location string formatting expectations to keep things a bit more consistent filename = request.FILES['file'].name mime_type = request.FILES['file'].content_type filedata = request.FILES['file'].read() content_loc = StaticContent.compute_location(org, course, filename) content = StaticContent(content_loc, filename, mime_type, filedata) # first let's see if a thumbnail can be created (thumbnail_content, thumbnail_location) = contentstore().generate_thumbnail(content) # delete cached thumbnail even if one couldn't be created this time (else the old thumbnail will continue to show) del_cached_content(thumbnail_location) # now store thumbnail location only if we could create it if thumbnail_content is not None: content.thumbnail_location = thumbnail_location # then commit the content contentstore().save(content) del_cached_content(content.location) # readback the saved content - we need the database timestamp readback = contentstore().find(content.location) response_payload = { 'displayname': content.name, 'uploadDate': get_default_time_display(readback.last_modified_at), 'url': StaticContent.get_url_path_from_location(content.location), 'thumb_url': StaticContent.get_url_path_from_location(thumbnail_location) if thumbnail_content is not None else None, 'msg': 'Upload completed' } response = HttpResponse(json.dumps(response_payload)) response['asset_url'] = StaticContent.get_url_path_from_location( content.location) return response