def student_view_aside(self, block, context): # pylint: disable=unused-argument """ Display the tag selector with specific categories and allowed values, depending on the context. """ if isinstance(block, CapaModule): tags = [] for tag in self.get_available_tags(): tag_available_values = tag.get_values() tag_current_values = self.saved_tags.get(tag.name, []) if isinstance(tag_current_values, basestring): tag_current_values = [tag_current_values] tag_values_not_exists = [cur_val for cur_val in tag_current_values if cur_val not in tag_available_values] tag_values_available_to_choose = tag_available_values + tag_values_not_exists tag_values_available_to_choose.sort() tags.append({ 'key': tag.name, 'title': tag.title, 'values': tag_values_available_to_choose, 'current_values': tag_current_values, }) fragment = Fragment(render_to_string('structured_tags_block.html', {'tags': tags, 'tags_count': len(tags), 'block_location': block.location})) fragment.add_javascript_url(self._get_studio_resource_url('/js/xblock_asides/structured_tags.js')) fragment.initialize_js('StructuredTagsInit') return fragment else: return Fragment(u'')
def _student_view(self, context, prereq_met, prereq_meta_info, banner_text=None): """ Returns the rendered student view of the content of this sequential. If banner_text is given, it is added to the content. """ display_items = self.get_display_items() self._update_position(context, len(display_items)) if prereq_met and not self._is_gate_fulfilled(): banner_text = _('This section is a prerequisite. You must complete this section in order to unlock additional content.') fragment = Fragment() params = { 'items': self._render_student_view_for_items(context, display_items, fragment) if prereq_met else [], 'element_id': self.location.html_id(), 'item_id': text_type(self.location), 'position': self.position, 'tag': self.location.block_type, 'ajax_url': self.system.ajax_url, 'next_url': context.get('next_url'), 'prev_url': context.get('prev_url'), 'banner_text': banner_text, 'save_position': self.is_user_authenticated(context), 'show_completion': self.is_user_authenticated(context), 'gated_content': self._get_gated_content_info(prereq_met, prereq_meta_info) } fragment.add_content(self.system.render_template("seq_module.html", params)) self._capture_full_seq_item_metrics(display_items) self._capture_current_unit_metrics(display_items) return fragment
def student_view(self, context): """ Renders parameters to template. """ context = { 'course_key': self.runtime.course_id, 'display_name': self.display_name_with_default_escaped, 'tag': self.instructor_tags, 'source': self.source, 'instructions_html': self.instructions, 'content_html': self.content, 'token': retrieve_token(self.user_email, self.annotation_token_secret), 'diacritic_marks': self.diacritics, 'annotation_storage': self.annotation_storage_url, 'default_tab': self.default_tab, 'instructor_email': self.instructor_email, 'annotation_mode': self.annotation_mode, 'is_course_staff': self.is_course_staff, } fragment = Fragment(self.system.render_template('textannotation.html', context)) # TinyMCE already exists in Studio so we should not load the files again # get_real_user always returns "None" in Studio since its runtimes contains no anonymous ids if self.runtime.get_real_user is not None: fragment.add_javascript_url(self.runtime.STATIC_URL + "js/vendor/tinymce/js/tinymce/tinymce.full.min.js") fragment.add_javascript_url(self.runtime.STATIC_URL + "js/vendor/tinymce/js/tinymce/jquery.tinymce.min.js") return fragment
def create_fragment(self, content=None): """ Create a fragment. """ fragment = Fragment(content) fragment.add_css('body {background-color:red;}') fragment.add_javascript('alert("Hi!");') return fragment
def wrap_fragment(fragment, new_content): """ Returns a new Fragment that has `new_content` and all as its content, and all of the resources from fragment """ wrapper_frag = Fragment(content=new_content) wrapper_frag.add_fragment_resources(fragment) return wrapper_frag
def author_view(self, context=None): # pylint: disable=unused-argument """ Renders author view for Studio. """ fragment = Fragment() fragment.add_content(self.runtime.render_template( 'discussion/_discussion_inline_studio.html', {'discussion_id': self.discussion_id} )) return fragment
def student_view(self, context): """ Renders the student view of the block in the LMS. """ fragment = Fragment() contents = [] if context: child_context = copy(context) else: child_context = {} if 'bookmarked' not in child_context: bookmarks_service = self.runtime.service(self, 'bookmarks') child_context['bookmarked'] = bookmarks_service.is_bookmarked(usage_key=self.location), # pylint: disable=no-member if 'username' not in child_context: user_service = self.runtime.service(self, 'user') child_context['username'] = user_service.get_current_user().opt_attrs['edx-platform.username'] child_blocks = self.get_display_items() child_blocks_to_complete_on_view = set() completion_service = self.runtime.service(self, 'completion') if completion_service and completion_service.completion_tracking_enabled(): child_blocks_to_complete_on_view = completion_service.blocks_to_mark_complete_on_view(child_blocks) complete_on_view_delay = completion_service.get_complete_on_view_delay_ms() child_context['child_of_vertical'] = True is_child_of_vertical = context.get('child_of_vertical', False) # pylint: disable=no-member for child in child_blocks: child_block_context = copy(child_context) if child in child_blocks_to_complete_on_view: child_block_context['wrap_xblock_data'] = { 'mark-completed-on-view-after-delay': complete_on_view_delay } rendered_child = child.render(STUDENT_VIEW, child_block_context) fragment.add_fragment_resources(rendered_child) contents.append({ 'id': six.text_type(child.location), 'content': rendered_child.content }) fragment.add_content(self.system.render_template('vert_module.html', { 'items': contents, 'xblock_context': context, 'unit_title': self.display_name_with_default if not is_child_of_vertical else None, 'show_bookmark_button': child_context.get('show_bookmark_button', not is_child_of_vertical), 'bookmarked': child_context['bookmarked'], 'bookmark_id': u"{},{}".format(child_context['username'], unicode(self.location)), # pylint: disable=no-member })) for tag in webpack_loader.utils.get_as_tags('VerticalStudentView'): fragment.add_resource(tag, mimetype='text/html', placement='head') fragment.initialize_js('VerticalStudentView') return fragment
def render_to_fragment(self, request, course_id=None, discussion_id=None, thread_id=None, **kwargs): """ Render the discussion board to a fragment. Args: request: The Django request. course_id: The id of the course in question. discussion_id: An optional discussion ID to be focused upon. thread_id: An optional ID of the thread to be shown. Returns: Fragment: The fragment representing the discussion board """ course_key = CourseKey.from_string(course_id) try: base_context = _create_base_discussion_view_context(request, course_key) # Note: # After the thread is rendered in this fragment, an AJAX # request is made and the thread is completely loaded again # (yes, this is something to fix). Because of this, we pass in # raise_event=False to _load_thread_for_viewing avoid duplicate # tracking events. thread = ( _load_thread_for_viewing( request, base_context['course'], discussion_id=discussion_id, thread_id=thread_id, raise_event=False, ) if thread_id else None ) context = _create_discussion_board_context(request, base_context, thread=thread) html = render_to_string('discussion/discussion_board_fragment.html', context) inline_js = render_to_string('discussion/discussion_board_js.template', context) fragment = Fragment(html) self.add_fragment_resource_urls(fragment) fragment.add_javascript(inline_js) if not settings.REQUIRE_DEBUG: fragment.add_javascript_url(staticfiles_storage.url('discussion/js/discussion_board_factory.js')) return fragment except cc.utils.CommentClientMaintenanceError: log.warning('Forum is in maintenance mode') html = render_to_response('discussion/maintenance_fragment.html', { 'disable_courseware_js': True, 'uses_pattern_library': True, }) return Fragment(html)
def student_view(self, context): """ Renders the output that a student will see. """ fragment = Fragment() fragment.add_content(self.system.render_template('word_cloud.html', { 'ajax_url': self.system.ajax_url, 'display_name': self.display_name, 'instructions': self.instructions, 'element_class': self.location.block_type, 'element_id': self.location.html_id(), 'num_inputs': self.num_inputs, 'submitted': self.submitted, })) return fragment
def author_view(self, context): """ Renders the Studio preview by rendering each child so that they can all be seen and edited. """ fragment = Fragment() root_xblock = context.get('root_xblock') is_root = root_xblock and root_xblock.location == self.location active_groups_preview = None inactive_groups_preview = None if is_root: [active_children, inactive_children] = self.descriptor.active_and_inactive_children() active_groups_preview = self.studio_render_children( fragment, active_children, context ) inactive_groups_preview = self.studio_render_children( fragment, inactive_children, context ) fragment.add_content(self.system.render_template('split_test_author_view.html', { 'split_test': self, 'is_root': is_root, 'is_configured': self.is_configured, 'active_groups_preview': active_groups_preview, 'inactive_groups_preview': inactive_groups_preview, 'group_configuration_url': self.descriptor.group_configuration_url, })) fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/split_test_author_view.js')) fragment.initialize_js('SplitTestAuthorView') return fragment
def author_view(self, context): """ Renders the Studio views. Normal studio view: If block is properly configured, displays library status summary Studio container view: displays a preview of all possible children. """ fragment = Fragment() root_xblock = context.get('root_xblock') is_root = root_xblock and root_xblock.location == self.location if is_root: # User has clicked the "View" link. Show a preview of all possible children: if self.children: # pylint: disable=no-member fragment.add_content(self.system.render_template("library-block-author-preview-header.html", { 'max_count': self.max_count, 'display_name': self.display_name or self.url_name, })) context['can_edit_visibility'] = False context['can_move'] = False self.render_children(context, fragment, can_reorder=False, can_add=False) # else: When shown on a unit page, don't show any sort of preview - # just the status of this block in the validation area. # The following JS is used to make the "Update now" button work on the unit page and the container view: fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/library_content_edit.js')) fragment.initialize_js('LibraryContentAuthorView') return fragment
def student_view(self, context): """ Renders the student view of the block in the LMS. """ fragment = Fragment() contents = [] if context: child_context = copy(context) else: child_context = {} if 'bookmarked' not in child_context: bookmarks_service = self.runtime.service(self, 'bookmarks') child_context['bookmarked'] = bookmarks_service.is_bookmarked(usage_key=self.location), # pylint: disable=no-member if 'username' not in child_context: user_service = self.runtime.service(self, 'user') child_context['username'] = user_service.get_current_user().opt_attrs['edx-platform.username'] completion_service = self.runtime.service(self, 'completion') child_context['child_of_vertical'] = True is_child_of_vertical = context.get('child_of_vertical', False) # pylint: disable=no-member for child in self.get_display_items(): rendered_child = child.render(STUDENT_VIEW, child_context) fragment.add_fragment_resources(rendered_child) contents.append({ 'id': six.text_type(child.location), 'content': rendered_child.content }) fragment.add_content(self.system.render_template('vert_module.html', { 'items': contents, 'xblock_context': context, 'unit_title': self.display_name_with_default if not is_child_of_vertical else None, 'show_bookmark_button': child_context.get('show_bookmark_button', not is_child_of_vertical), 'bookmarked': child_context['bookmarked'], 'bookmark_id': u"{},{}".format(child_context['username'], unicode(self.location)), # pylint: disable=no-member 'watched_completable_blocks': self.get_completable_by_viewing(completion_service), 'completion_delay_ms': self.get_completion_delay_ms(completion_service), })) fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/vertical_student_view.js')) fragment.initialize_js('VerticalStudentView') return fragment
def render_to_fragment(self, request, course_id=None, **kwargs): """ Renders the user's course bookmarks as a fragment. """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True) context = { 'csrf': csrf(request)['csrf_token'], 'course': course, 'bookmarks_api_url': reverse('bookmarks'), 'language_preference': 'en', # TODO: } html = render_to_string('course_bookmarks/course-bookmarks-fragment.html', context) inline_js = render_to_string('course_bookmarks/course_bookmarks_js.template', context) fragment = Fragment(html) self.add_fragment_resource_urls(fragment) fragment.add_javascript(inline_js) return fragment
def _wrap_ele(self, block, view, frag, extra_data=None): """ Does the guts of the wrapping the same way for both xblocks and asides. Their wrappers provide other info in extra_data which gets put into the dom data- attrs. """ wrapped = Fragment() data = { 'usage': block.scope_ids.usage_id, 'block-type': block.scope_ids.block_type, } data.update(extra_data) if frag.js_init_fn: data['init'] = frag.js_init_fn data['runtime-version'] = frag.js_init_version json_init = "" # TODO/Note: We eventually want to remove: hasattr(frag, 'json_init_args') # However, I'd like to maintain backwards-compatibility with older XBlock # for at least a little while so as not to adversely effect developers. # pmitros/Jun 28, 2014. if hasattr(frag, 'json_init_args') and frag.json_init_args is not None: json_init = ( '<script type="json/xblock-args" class="xblock_json_init_args">' '{data}</script>' ).format(data=json.dumps(frag.json_init_args)) block_css_entrypoint = block.entry_point.replace('.', '-') css_classes = [ block_css_entrypoint, '{}-{}'.format(block_css_entrypoint, view), ] html = "<div class='{}'{properties}>{body}{js}</div>".format( markupsafe.escape(' '.join(css_classes)), properties="".join(" data-%s='%s'" % item for item in list(data.items())), body=frag.body_html(), js=json_init) wrapped.add_content(html) wrapped.add_fragment_resources(frag) return wrapped
def student_view(self, context): fragment = Fragment() contents = [] child_context = {} if not context else copy(context) for child in self._get_selected_child_blocks(): for displayable in child.displayable_items(): rendered_child = displayable.render(STUDENT_VIEW, child_context) fragment.add_fragment_resources(rendered_child) contents.append({ 'id': text_type(displayable.location), 'content': rendered_child.content, }) fragment.add_content(self.system.render_template('vert_module.html', { 'items': contents, 'xblock_context': context, 'show_bookmark_button': False, 'watched_completable_blocks': set(), })) return fragment
def _student_view(self, context, banner_text=None): """ Returns the rendered student view of the content of this sequential. If banner_text is given, it is added to the content. """ display_items = self.get_display_items() self._update_position(context, len(display_items)) prereq_met = True prereq_meta_info = {} if self._required_prereq(): if self.runtime.user_is_staff: banner_text = _('This subsection is unlocked for learners when they meet the prerequisite requirements.') else: # check if prerequisite has been met prereq_met, prereq_meta_info = self._compute_is_prereq_met(True) if prereq_met and not self._is_gate_fulfilled(): banner_text = _('This section is a prerequisite. You must complete this section in order to unlock additional content.') fragment = Fragment() params = { 'items': self._render_student_view_for_items(context, display_items, fragment) if prereq_met else [], 'element_id': self.location.html_id(), 'item_id': text_type(self.location), 'position': self.position, 'tag': self.location.block_type, 'ajax_url': self.system.ajax_url, 'next_url': context.get('next_url'), 'prev_url': context.get('prev_url'), 'banner_text': banner_text, 'disable_navigation': not self.is_user_authenticated(context), 'gated_content': self._get_gated_content_info(prereq_met, prereq_meta_info) } fragment.add_content(self.system.render_template("seq_module.html", params)) self._capture_full_seq_item_metrics(display_items) self._capture_current_unit_metrics(display_items) return fragment
def render_to_fragment(self, request, course_id=None, discussion_id=None, thread_id=None, **kwargs): """ Render the discussion board to a fragment. Args: request: The Django request. course_id: The id of the course in question. discussion_id: An optional discussion ID to be focused upon. thread_id: An optional ID of the thread to be shown. Returns: Fragment: The fragment representing the discussion board """ course_key = CourseKey.from_string(course_id) try: context = _create_discussion_board_context( request, course_key, discussion_id=discussion_id, thread_id=thread_id, ) html = render_to_string('discussion/discussion_board_fragment.html', context) inline_js = render_to_string('discussion/discussion_board_js.template', context) fragment = Fragment(html) self.add_fragment_resource_urls(fragment) fragment.add_javascript(inline_js) if not settings.REQUIRE_DEBUG: fragment.add_javascript_url(staticfiles_storage.url('discussion/js/discussion_board_factory.js')) return fragment except cc.utils.CommentClientMaintenanceError: log.warning('Forum is in maintenance mode') html = render_to_response('discussion/maintenance_fragment.html', { 'disable_courseware_js': True, 'uses_pattern_library': True, }) return Fragment(html)
def layout_asides(self, block, context, frag, view_name, aside_frag_fns): position_for_asides = '<!-- footer for xblock_aside -->' result = Fragment() result.add_fragment_resources(frag) for aside, aside_fn in aside_frag_fns: aside_frag = aside_fn(block, context) if aside_frag.content != u'': aside_frag_wrapped = self.wrap_aside(block, aside, view_name, aside_frag, context) aside.save() result.add_fragment_resources(aside_frag_wrapped) replacement = position_for_asides + aside_frag_wrapped.content frag.content = frag.content.replace(position_for_asides, replacement) result.add_content(frag.content) return result
def visibility_view(self, _context=None): """ Render the view to manage an xblock's visibility settings in Studio. Args: _context: Not actively used for this view. Returns: (Fragment): An HTML fragment for editing the visibility of this XBlock. """ fragment = Fragment() from contentstore.utils import reverse_course_url fragment.add_content(self.system.render_template('visibility_editor.html', { 'xblock': self, 'manage_groups_url': reverse_course_url('group_configurations_list_handler', self.location.course_key), })) fragment.add_javascript_url(self._get_studio_resource_url('/js/xblock/authoring.js')) fragment.initialize_js('VisibilityEditorInit') return fragment
def render_to_fragment(self, request, course_id=None, page_context=None, **kwargs): """ Renders the course outline as a fragment. """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True) _, course_position = get_last_accessed_courseware( course, request, request.user) course_usage_key = modulestore().make_course_usage_key(course_key) all_blocks = get_blocks( request, course_usage_key, user=request.user, nav_depth=3, requested_fields=['children', 'display_name', 'type'], block_types_filter=['course', 'chapter', 'sequential']) course_block_tree = all_blocks['blocks'][ all_blocks['root']] # Get the root of the block tree context = { 'csrf': csrf(request)['csrf_token'], 'course': course, # Recurse through the block tree, fleshing out each child object 'blocks': self.populate_children(course_block_tree, all_blocks['blocks'], course_position) } html = render_to_string( 'course_experience/course-outline-fragment.html', context) return Fragment(html)
def student_view(self, context=None): # pylint: disable=unused-argument """ The primary view of the DoneXBlock, shown to students when viewing courses. """ html_resource = resource_string("static/html/done.html") html = html_resource.format(done=self.done, id=uuid.uuid1(0)) (unchecked_png, checked_png) = (self.runtime.local_resource_url(self, x) for x in ('public/check-empty.png', 'public/check-full.png')) frag = Fragment(html) frag.add_css(resource_string("static/css/done.css")) frag.add_javascript(resource_string("static/js/src/done.js")) frag.initialize_js( "DoneXBlock", { 'state': self.done, 'unchecked': unchecked_png, 'checked': checked_png, 'align': self.align.lower() }) return frag
def render_to_fragment(self, request, course_id=None, page_context=None, **kwargs): """ Renders the course outline as a fragment. """ course_key = CourseKey.from_string(course_id) course_overview = get_course_overview_with_access( request.user, 'load', course_key, check_if_enrolled=True) course = modulestore().get_course(course_key) course_block_tree = get_course_outline_block_tree(request, course_id) if not course_block_tree: return None context = { 'csrf': csrf(request)['csrf_token'], 'course': course_overview, 'due_date_display_format': course.due_date_display_format, 'blocks': course_block_tree, 'user': request.user, 'course_id': course_id } print(course_block_tree) resume_block = get_resume_block(course_block_tree) if not resume_block: self.mark_first_unit_to_resume(course_block_tree) xblock_display_names = self.create_xblock_id_and_name_dict( course_block_tree) gated_content = self.get_content_milestones(request, course_key) context['gated_content'] = gated_content context['xblock_display_names'] = xblock_display_names #badge can be issued here on complete status. An error occured here html = render_to_string( 'course_experience/course-outline-fragment.html', context) return Fragment(html)
def dashboard_view(self, context): fragment = Fragment() children_context = context.copy() stage_fragments = self._render_children('dashboard_view', children_context, self.stages) stage_contents = [frag.content for frag in stage_fragments] for stage_fragment in stage_fragments: fragment.add_fragment_resources(stage_fragment) render_context = {'activity': self, 'stage_contents': stage_contents} fragment.add_content( self.render_template('dashboard_view', render_context)) return fragment
def visibility_view(self, _context=None): """ Render the view to manage an xblock's visibility settings in Studio. Args: _context: Not actively used for this view. Returns: (Fragment): An HTML fragment for editing the visibility of this XBlock. """ fragment = Fragment() from contentstore.utils import reverse_course_url fragment.add_content( self.system.render_template( 'visibility_editor.html', { 'xblock': self, 'manage_groups_url': reverse_course_url('group_configurations_list_handler', self.location.course_key), })) fragment.add_javascript_url( self._get_studio_resource_url('/js/xblock/authoring.js')) fragment.initialize_js('VisibilityEditorInit') return fragment
def layout_asides(self, block, context, frag, view_name, aside_frag_fns): """ Execute and layout the aside_frags wrt the block's frag. Runtimes should feel free to override this method to control execution, place, and style the asides appropriately for their application This default method appends the aside_frags after frag. If you override this, you must call wrap_aside around each aside as per this function. Args: block (XBlock): the block being rendered frag (html): The result from rendering the block aside_frag_fns list((aside, aside_fn)): The asides and closures for rendering to call """ result = Fragment(frag.content) result.add_fragment_resources(frag) for aside, aside_fn in aside_frag_fns: aside_frag = self.wrap_aside(block, aside, view_name, aside_fn(block, context), context) aside.save() result.add_content(aside_frag.content) result.add_fragment_resources(aside_frag) return result
def student_view_aside(self, block, context): # pylint: disable=unused-argument """ Display the tag selector with specific categories and allowed values, depending on the context. """ if isinstance(block, CapaModule): tags = [] for tag in self.get_available_tags(): tag_available_values = tag.get_values() tag_current_values = self.saved_tags.get(tag.name, []) if isinstance(tag_current_values, basestring): tag_current_values = [tag_current_values] tag_values_not_exists = [ cur_val for cur_val in tag_current_values if cur_val not in tag_available_values ] tag_values_available_to_choose = tag_available_values + tag_values_not_exists tag_values_available_to_choose.sort() tags.append({ 'key': tag.name, 'title': tag.title, 'values': tag_values_available_to_choose, 'current_values': tag_current_values, }) fragment = Fragment( render_to_string( 'structured_tags_block.html', { 'tags': tags, 'tags_count': len(tags), 'block_location': block.location })) fragment.add_javascript_url( self._get_studio_resource_url( '/js/xblock_asides/structured_tags.js')) fragment.initialize_js('StructuredTagsInit') return fragment else: return Fragment(u'')
def build_fragment( self, template='', context=None, css=None, js=None, js_init=None, ): """ Creates a fragment for display. """ context = context or {} css = css or [] js = js or [] rendered_template = '' if template: # pragma: no cover template = 'templates/' + template rendered_template = self.loader.render_django_template( template, context=Context(context), i18n_service=self.runtime.service(self, 'i18n'), ) fragment = Fragment(rendered_template) for item in css: if item.startswith('/'): url = item else: item = 'public/' + item url = self.runtime.local_resource_url(self, item) fragment.add_css_url(url) for item in js: item = 'public/' + item url = self.runtime.local_resource_url(self, item) fragment.add_javascript_url(url) if js_init: # pragma: no cover fragment.initialize_js(js_init) return fragment
def get_first_purchase_offer_banner_fragment(user, course): """ Return an HTML Fragment with First Purcahse Discount message, which has the discount_expiration_date, price, discount percentage and a link to upgrade. """ if user and not user.is_anonymous and course: now = datetime.now(tz=pytz.UTC).strftime(u"%Y-%m-%d %H:%M:%S%z") saw_banner = ExperimentData.objects.filter( user=user, experiment_id=REV1008_EXPERIMENT_ID, key=str(course) ) if not saw_banner: ExperimentData.objects.create( user=user, experiment_id=REV1008_EXPERIMENT_ID, key=str(course), value=now ) discount_expiration_date = get_discount_expiration_date(user, course) if (discount_expiration_date and can_receive_discount(user=user, course=course, discount_expiration_date=discount_expiration_date)): # Translator: xgettext:no-python-format offer_message = _(u'{banner_open} Upgrade by {discount_expiration_date} and save {percentage}% ' u'[{strikeout_price}]{span_close}{br}Discount will be automatically applied at checkout. ' u'{a_open}Upgrade Now{a_close}{div_close}') return Fragment(HTML(offer_message).format( a_open=HTML(u'<a href="{upgrade_link}">').format( upgrade_link=verified_upgrade_deadline_link(user=user, course=course) ), a_close=HTML('</a>'), br=HTML('<br>'), banner_open=HTML( '<div class="first-purchase-offer-banner" role="note">' '<span class="first-purchase-offer-banner-bold">' ), discount_expiration_date=discount_expiration_date.strftime(u'%B %d'), percentage=discount_percentage(course), span_close=HTML('</span>'), div_close=HTML('</div>'), strikeout_price=HTML(format_strikeout_price(user, course, check_for_discount=False)[0]) )) return None
def render_to_fragment(self, request, course_id=None, page_context=None, **kwargs): """ Renders the course outline as a fragment. """ course_key = CourseKey.from_string(course_id) course_overview = get_course_overview_with_access( request.user, 'load', course_key, check_if_enrolled=True) course_block_tree = get_course_outline_block_tree(request, course_id) context = { 'csrf': csrf(request)['csrf_token'], 'course': course_overview, 'blocks': course_block_tree } html = render_to_string( 'course_experience/course-outline-fragment.html', context) return Fragment(html)
def test_that_timed_sequence_gating_respects_access_configurations(self): """ Verify that if a time limited sequence contains content type gated problems, we gate the sequence """ # the one problem in this sequence needs to have graded set to true in order to test content type gating self.sequence_5_1.get_children()[0].get_children()[0].graded = True gated_fragment = Fragment('i_am_gated') # When a time limited sequence contains content type gated problems, the sequence itself is gated self.sequence_5_1.runtime._services['content_type_gating'] = Mock(return_value=Mock( # pylint: disable=protected-access check_children_for_content_type_gating_paywall=Mock(return_value=gated_fragment.content), )) view = self._get_rendered_view( self.sequence_5_1, extra_context=dict(next_url='NextSequential', prev_url='PrevSequential'), view=STUDENT_VIEW ) assert 'i_am_gated' in view # check a few elements to ensure the correct page was loaded assert 'seq_module.html' in view assert 'NextSequential' in view assert 'PrevSequential' in view
def render(self, block, view_name, context=None): """ Render a specific view of an XBlock. """ # Users who aren't logged in are not allowed to view any views other # than public_view. They may call any handlers though. if (self.user is None or self.user.is_anonymous) and view_name != 'public_view': raise PermissionDenied # We also need to override this method because some XBlocks in the # edx-platform codebase use methods like add_webpack_to_fragment() # which create relative URLs (/static/studio/bundles/webpack-foo.js). # We want all resource URLs to be absolute, such as is done when # local_resource_url() is used. fragment = super().render(block, view_name, context) needs_fix = False for resource in fragment.resources: if resource.kind == 'url' and resource.data.startswith('/'): needs_fix = True break if needs_fix: log.warning("XBlock %s returned relative resource URLs, which are deprecated", block.scope_ids.usage_id) # The Fragment API is mostly immutable, so changing a resource requires this: frag_data = fragment.to_dict() for resource in frag_data['resources']: if resource['kind'] == 'url' and resource['data'].startswith('/'): log.debug("-> Relative resource URL: %s", resource['data']) resource['data'] = get_xblock_app_config().get_site_root_url() + resource['data'] fragment = Fragment.from_dict(frag_data) # Apply any required transforms to the fragment. # We could move to doing this in wrap_xblock() and/or use an array of # wrapper methods like the ConfigurableFragmentWrapper mixin does. fragment = wrap_fragment( fragment, ReplaceURLService(xblock=block, lookup_asset_url=self._lookup_asset_url).replace_urls(fragment.content) ) return fragment
def render_to_fragment(self, request, course_id=None, **kwargs): """ Render the course dates fragment. """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=False) course_date_blocks = get_course_date_blocks(course, request.user) context = { 'course_date_blocks': [ block for block in course_date_blocks if block.title != 'current_datetime' ] } html = render_to_string(self.template_name, context) dates_fragment = Fragment(html) self.add_fragment_resource_urls(dates_fragment) return dates_fragment
def render_to_fragment(self, request, course_id=None, **kwargs): """ Renders the welcome message fragment for the specified course. Returns: A fragment, or None if there is no welcome message. """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True) welcome_message_html = self.welcome_message_html(request, course) if not welcome_message_html: return None context = { 'welcome_message_html': welcome_message_html, } html = render_to_string( 'course_experience/welcome-message-fragment.html', context) return Fragment(html)
def render_to_fragment(self, request, course_id=None, **kwargs): # lint-amnesty, pylint: disable=arguments-differ """ Renders the latest update message fragment for the specified course. Returns: A fragment, or None if there is no latest update message. """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True) update_html = self.latest_update_html(request, course) if not update_html: return None context = { 'update_html': update_html, } html = render_to_string( 'course_experience/latest-update-fragment.html', context) return Fragment(html)
def student_view(self, context=None): # pylint: disable=W0613 """Provide default student view.""" # Get the attempts for all problems in my parent. if self.parent: # these two lines are equivalent, and both work: attempts = list( self.runtime.query(self).parent().descendants().attr( "problem_attempted")) attempts = list( self.runtime.querypath(self, "..//@problem_attempted")) num_problems = len(attempts) attempted = sum(attempts) if num_problems == 0: content = "There are no problems here..." elif attempted == num_problems: content = "Great! You attempted all %d problems!" % num_problems else: content = "Hmm, you've only tried %d out of %d problems..." % ( attempted, num_problems) else: content = "I have nothing to live for! :(" return Fragment(content)
def student_view(self, context): _ = self.runtime.service(self, "i18n").ugettext context = context or {} self._capture_basic_metrics() banner_text = None prereq_met = True prereq_meta_info = {} if self._required_prereq(): if self.runtime.user_is_staff: banner_text = _('This subsection is unlocked for learners when they meet the prerequisite requirements.') else: # check if prerequisite has been met prereq_met, prereq_meta_info = self._compute_is_prereq_met(True) if prereq_met: special_html_view = self._hidden_content_student_view(context) or self._special_exam_student_view() if special_html_view: masquerading_as_specific_student = context.get('specific_masquerade', False) banner_text, special_html = special_html_view if special_html and not masquerading_as_specific_student: return Fragment(special_html) return self._student_or_public_view(context, prereq_met, prereq_meta_info, banner_text)
def student_view(self, context): """ Player view, displayed to the student """ fragment = Fragment() current_stage_id = context.get( Constants.CURRENT_STAGE_ID_PARAMETER_NAME, None) target_stage = self.get_stage_to_display(current_stage_id) if not target_stage: fragment.add_content(self._(messages.NO_STAGES)) else: stage_fragment = target_stage.render('student_view', context) fragment.add_fragment_resources(stage_fragment) render_context = { 'activity': self, 'stage_content': stage_fragment.content, } render_context.update(context) fragment.add_content( self.render_template('student_view', render_context)) return fragment
def author_view(self, context): """ Renders the Studio preview view. """ fragment = Fragment() root_xblock = context.get('root_xblock') is_root = root_xblock and root_xblock.location == self.location # pylint: disable=no-member # If block ID is not defined, ask user for the component ID in the author_view itself. # We don't display the editor if is_root as that page should represent the student_view without any ambiguity if not self.source_block_id and not is_root: fragment.add_content( loader.render_django_template('templates/library-sourced-block-author-view.html', { 'save_url': handler_url(self, 'submit_studio_edits') }) ) fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/library_source_block.js')) fragment.initialize_js('LibrarySourceBlockAuthorView') return fragment context = {} if not context else copy(context) # Isolate context - without this there are weird bugs in Studio # EditableChildrenMixin.render_children will render HTML that allows instructors to make edits to the children context['can_move'] = False self.render_children(context, fragment, can_reorder=False, can_add=False) return fragment
def student_view(self, context): """ Renders the contents of the chosen condition for students, and all the conditions for staff. """ if self.child is None: # raise error instead? In fact, could complain on descriptor load... return Fragment(content=u"<div>Nothing here. Move along.</div>") if self.system.user_is_staff: return self._staff_view(context) else: child_fragment = self.child.render(STUDENT_VIEW, context) fragment = Fragment(self.system.render_template('split_test_student_view.html', { 'child_content': child_fragment.content, 'child_id': self.child.scope_ids.usage_id, })) fragment.add_fragment_resources(child_fragment) fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/split_test_student.js')) fragment.initialize_js('SplitTestStudentView') return fragment
def render_to_fragment(self, request, **kwargs): """ Render the program listing fragment. """ user = request.user programs_config = kwargs.get( 'programs_config') or ProgramsApiConfig.current() if not programs_config.enabled or not user.is_authenticated(): raise Http404 meter = ProgramProgressMeter(request.site, user) context = { 'marketing_url': get_program_marketing_url(programs_config), 'programs': meter.engaged_programs, 'progress': meter.progress() } html = render_to_string('learner_dashboard/programs_fragment.html', context) programs_fragment = Fragment(html) self.add_fragment_resource_urls(programs_fragment) return programs_fragment
def render_to_fragment(self, request, course_id=None, **kwargs): # lint-amnesty, pylint: disable=arguments-differ """ Renders the welcome message fragment for the specified course. Returns: A fragment, or None if there is no welcome message. """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True) welcome_message_html = self.welcome_message_html(request, course) if not welcome_message_html: return None dismiss_url = reverse( 'openedx.course_experience.dismiss_welcome_message', kwargs={'course_id': str(course_key)} ) context = { 'dismiss_url': dismiss_url, 'welcome_message_html': welcome_message_html, } html = render_to_string('course_experience/welcome-message-fragment.html', context) return Fragment(html)
def student_view(self, context=None): # pylint: disable=W0613 """Returned content has handler urls.""" all_args = [ # Arguments for handler_url ( "send_it_back", ), ("send_it_back", "with_some_suffix"), ("send_it_back", "", "a=123"), ("send_it_back", "", "a=123&b=456&c=789"), ("send_it_back", "another_suffix", "a=123&b=456&c=789"), ("send_it_back_public", ), ("send_it_back_public", "with_some_suffix"), ("send_it_back_public", "", "a=123"), ("send_it_back_public", "", "a=123&b=456&c=789"), ("send_it_back_public", "another_suffix", "a=123&b=456&c=789"), ] urls = [] for args in all_args: thirdparty = (args[0] == "send_it_back_public") urls.append( self.runtime.handler_url(self, *args, thirdparty=thirdparty)) encoded = json.dumps(urls) return Fragment(u":::" + encoded + u":::")
def render_iframe(self) -> str: """ Returns the program LTI iframe if program Lti configuration exists for a program uuid """ if not self.is_configured: return '' lti_embed_html = self._get_lti_embed_code() fragment = Fragment( HTML( """ <iframe id='lti-tab-embed' style='width: 100%; min-height: 800px; border: none' srcdoc='{srcdoc}' > </iframe> """ ).format( srcdoc=lti_embed_html ) ) return fragment.content
def render_to_fragment(self, request, course_id=None, **kwargs): """ Renders the latest update message fragment for the specified course. Returns: A fragment, or None if there is no latest update message. """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True) ordered_updates = get_ordered_updates(request, course) if not ordered_updates: return None context = { 'ordered_updates': ordered_updates, } html = render_to_string( 'course_experience/course-home-updates-fragment.html', context) return Fragment(html)
def test_handle_ajax_metadata_content_type_gated_content(self): """ The contains_content_type_gated_content field should reflect whether the given item contains content type gated content """ self.sequence_5_1.xmodule_runtime._services['bookmarks'] = None # pylint: disable=protected-access ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=datetime( 2018, 1, 1)) metadata = json.loads(self.sequence_5_1.handle_ajax('metadata', {})) self.assertEqual( metadata['items'][0]['contains_content_type_gated_content'], False) # When a block contains content type gated problems, set the contains_content_type_gated_content field self.sequence_5_1.get_children()[0].get_children()[0].graded = True self.sequence_5_1.runtime._services['content_type_gating'] = Mock( return_value=Mock( # pylint: disable=protected-access enabled_for_enrollment=Mock(return_value=True), content_type_gate_for_block=Mock( return_value=Fragment('i_am_gated')))) metadata = json.loads(self.sequence_5_1.handle_ajax('metadata', {})) self.assertEqual( metadata['items'][0]['contains_content_type_gated_content'], True)
def student_view(self, context=None): """ Renders student view for LMS. """ fragment = Fragment() self.add_resource_urls(fragment) login_msg = '' if not self.django_user.is_authenticated(): qs = urllib.urlencode({ 'course_id': self.course_key, 'enrollment_action': 'enroll', 'email_opt_in': False, }) login_msg = Text(_("You are not signed in. To view the discussion content, {sign_in_link} or " "{register_link}, and enroll in this course.")).format( sign_in_link=HTML('<a href="{url}">{sign_in_label}</a>').format( sign_in_label=_('sign in'), url='{}?{}'.format(reverse('signin_user'), qs), ), register_link=HTML('<a href="/{url}">{register_label}</a>').format( register_label=_('register'), url='{}?{}'.format(reverse('register_user'), qs), ), ) context = { 'discussion_id': self.discussion_id, 'display_name': self.display_name if self.display_name else _("Discussion"), 'user': self.django_user, 'course_id': self.course_key, 'discussion_category': self.discussion_category, 'discussion_target': self.discussion_target, 'can_create_thread': self.has_permission("create_thread"), 'can_create_comment': self.has_permission("create_comment"), 'can_create_subcomment': self.has_permission("create_sub_comment"), 'login_msg': login_msg, } fragment.add_content(self.runtime.render_template('discussion/_discussion_inline.html', context)) fragment.initialize_js('DiscussionInlineBlock') return fragment
def render_to_fragment(self, request, course_id, user_access, **kwargs): """ Renders a course message fragment for the specified course. """ course_key = CourseKey.from_string(course_id) course = get_course_with_access(request.user, 'load', course_key) # Get time until the start date, if already started, or no start date, value will be zero or negative now = datetime.now(UTC()) already_started = course.start and now > course.start days_until_start_string = "started" if already_started else format_timedelta(course.start - now, locale=to_locale(get_language())) course_start_data = { 'course_start_date': format_date(course.start, locale=to_locale(get_language())), 'already_started': already_started, 'days_until_start_string': days_until_start_string } # Register the course home messages to be loaded on the page self.register_course_home_messages(request, course, user_access, course_start_data) # Grab the relevant messages course_home_messages = list(CourseHomeMessages.user_messages(request)) # Return None if user is enrolled and course has begun if user_access['is_enrolled'] and already_started: return None # Grab the logo image_src = "course_experience/images/home_message_author.png" context = { 'course_home_messages': course_home_messages, 'image_src': image_src, } html = render_to_string('course_experience/course-messages-fragment.html', context) return Fragment(html)
def dashboard_view(self, context): fragment = Fragment() children_context = self._sanitize_context(context) self._add_students_and_workgroups_to_context(children_context) activity_fragments = self._render_children('dashboard_view', children_context, self.activities) activity_contents = [frag.content for frag in activity_fragments] for activity_fragment in activity_fragments: fragment.add_fragment_resources(activity_fragment) render_context = {'project': self, 'activity_contents': activity_contents} fragment.add_content(self.render_template('dashboard_view', render_context)) add_resource(self, 'css', 'public/css/group_project_common.css', fragment) add_resource(self, 'css', 'public/css/group_project_dashboard.css', fragment) add_resource(self, 'css', 'public/css/vendor/font-awesome/font-awesome.css', fragment, via_url=True) return fragment
def get_first_purchase_offer_banner_fragment(user, course): """ Return an HTML Fragment with First Purcahse Discount message, which has the discount_expiration_date, price, discount percentage and a link to upgrade. """ if user and course: discount_expiration_date = get_discount_expiration_date(user, course) if (discount_expiration_date and can_receive_discount( user=user, course=course, discount_expiration_date=discount_expiration_date)): # Translator: xgettext:no-python-format offer_message = _( u'{banner_open} Upgrade by {discount_expiration_date} and save {percentage}% ' u'[{strikeout_price}]{span_close}{br}Discount will be automatically applied at checkout. ' u'{a_open}Upgrade Now{a_close}{div_close}') return Fragment( HTML(offer_message).format( a_open=HTML(u'<a href="{upgrade_link}">').format( upgrade_link=verified_upgrade_deadline_link( user=user, course=course)), a_close=HTML('</a>'), br=HTML('<br>'), banner_open=HTML( '<div class="first-purchase-offer-banner"><span class="first-purchase-offer-banner-bold">' ), discount_expiration_date=discount_expiration_date.strftime( u'%B %d'), percentage=discount_percentage(), span_close=HTML('</span>'), div_close=HTML('</div>'), strikeout_price=HTML( format_strikeout_price(user, course, check_for_discount=False)[0]))) return None
def render_to_fragment(self, request, course_id, user_is_enrolled=True, **kwargs): # pylint: disable=arguments-differ """ Renders the course outline as a fragment. """ course_key = CourseKey.from_string(course_id) course_overview = get_course_overview_with_access( request.user, 'load', course_key, check_if_enrolled=user_is_enrolled ) course = modulestore().get_course(course_key) course_block_tree = get_course_outline_block_tree( request, course_id, request.user if user_is_enrolled else None ) if not course_block_tree: return None context = { 'csrf': csrf(request)['csrf_token'], 'course': course_overview, 'due_date_display_format': course.due_date_display_format, 'blocks': course_block_tree, 'enable_links': user_is_enrolled or course.course_visibility == COURSE_VISIBILITY_PUBLIC, } resume_block = get_resume_block(course_block_tree) if user_is_enrolled else None if not resume_block: self.mark_first_unit_to_resume(course_block_tree) xblock_display_names = self.create_xblock_id_and_name_dict(course_block_tree) gated_content = self.get_content_milestones(request, course_key) context['gated_content'] = gated_content context['xblock_display_names'] = xblock_display_names html = render_to_string('course_experience/course-outline-fragment.html', context) return Fragment(html)
def layout_asides(self, block, context, frag, view_name, aside_frag_fns): """ Execute and layout the aside_frags wrt the block's frag. Runtimes should feel free to override this method to control execution, place, and style the asides appropriately for their application This default method appends the aside_frags after frag. If you override this, you must call wrap_aside around each aside as per this function. Args: block (XBlock): the block being rendered frag (html): The result from rendering the block aside_frag_fns list((aside, aside_fn)): The asides and closures for rendering to call """ result = Fragment(frag.content) result.add_fragment_resources(frag) for aside, aside_fn in aside_frag_fns: aside_frag = self.wrap_aside(block, aside, view_name, aside_fn(block, context), context) aside.save() result.add_content(aside_frag.content) result.add_fragment_resources(aside_frag) return result
def _staff_view(self, context): """ Render the staff view for a split test module. """ fragment = Fragment() active_contents = [] inactive_contents = [] for child_location in self.children: # pylint: disable=no-member child_descriptor = self.get_child_descriptor_by_location(child_location) child = self.system.get_module(child_descriptor) rendered_child = child.render(STUDENT_VIEW, context) fragment.add_fragment_resources(rendered_child) group_name, updated_group_id = self.get_data_for_vertical(child) if updated_group_id is None: # inactive group group_name = child.display_name updated_group_id = [g_id for g_id, loc in self.group_id_to_child.items() if loc == child_location][0] inactive_contents.append({ 'group_name': _(u'{group_name} (inactive)').format(group_name=group_name), 'id': text_type(child.location), 'content': rendered_child.content, 'group_id': updated_group_id, }) continue active_contents.append({ 'group_name': group_name, 'id': text_type(child.location), 'content': rendered_child.content, 'group_id': updated_group_id, }) # Sort active and inactive contents by group name. sorted_active_contents = sorted(active_contents, key=itemgetter('group_name')) sorted_inactive_contents = sorted(inactive_contents, key=itemgetter('group_name')) # Use the new template fragment.add_content(self.system.render_template('split_test_staff_view.html', { 'items': sorted_active_contents + sorted_inactive_contents, })) fragment.add_css('.split-test-child { display: none; }') fragment.add_javascript_url(self.runtime.local_resource_url(self, 'public/js/split_test_staff.js')) fragment.initialize_js('ABTestSelector') return fragment