Ejemplo n.º 1
0
def send_user_notification_callback(sender, **kwargs):  # lint-amnesty, pylint: disable=unused-argument
    """
    Callback for notifying user about course creator status change.
    """
    user = kwargs['user']
    updated_state = kwargs['state']

    studio_request_email = settings.FEATURES.get('STUDIO_REQUEST_EMAIL', '')
    context = {'studio_request_email': studio_request_email}

    subject = render_to_string('emails/course_creator_subject.txt', context)
    subject = ''.join(subject.splitlines())
    if updated_state == CourseCreator.GRANTED:
        message_template = 'emails/course_creator_granted.txt'
    elif updated_state == CourseCreator.DENIED:
        message_template = 'emails/course_creator_denied.txt'
    else:
        # changed to unrequested or pending
        message_template = 'emails/course_creator_revoked.txt'
    message = render_to_string(message_template, context)

    try:
        user.email_user(subject, message, studio_request_email)
    except:  # lint-amnesty, pylint: disable=bare-except
        log.warning("Unable to send course creator status e-mail to %s",
                    user.email)
Ejemplo n.º 2
0
def send_task_complete_email(self, task_name, task_state_text, dest_addr, detail_url, olx_validation_text=None):
    """
    Sending an email to the users when an async task completes.
    """
    retries = self.request.retries

    context = {
        'task_name': task_name,
        'task_status': task_state_text,
        'detail_url': detail_url,
        'olx_validation_errors': False
    }
    if olx_validation_text:
        try:
            context['olx_validation_errors'] = json.loads(olx_validation_text)
        except ValueError:  # includes simplejson.decoder.JSONDecodeError
            LOGGER.error(f'Unable to parse CourseOlx validation text: {olx_validation_text}')

    subject = render_to_string('emails/user_task_complete_email_subject.txt', context)
    # Eliminate any newlines
    subject = ''.join(subject.splitlines())
    message = render_to_string('emails/user_task_complete_email.txt', context)

    from_address = configuration_helpers.get_value(
        'email_from_address',
        settings.DEFAULT_FROM_EMAIL
    )

    try:
        mail.send_mail(subject, message, from_address, [dest_addr], fail_silently=False)
        LOGGER.info("Task complete email has been sent to User %s", dest_addr)
    except NoAuthHandlerFound:
        LOGGER.info(
            'Retrying sending email to user %s, attempt # %s of %s',
            dest_addr,
            retries,
            TASK_COMPLETE_EMAIL_MAX_RETRIES
        )
        try:
            self.retry(countdown=TASK_COMPLETE_EMAIL_TIMEOUT, max_retries=TASK_COMPLETE_EMAIL_MAX_RETRIES)
        except MaxRetriesExceededError:
            LOGGER.error(
                'Unable to send task completion email to user from "%s" to "%s"',
                from_address,
                dest_addr,
                exc_info=True
            )
    except Exception:  # pylint: disable=broad-except
        LOGGER.exception(
            'Unable to send task completion email to user from "%s" to "%s"',
            from_address,
            dest_addr,
            exc_info=True
        )
Ejemplo n.º 3
0
 def render_template(self, template_name, dictionary, namespace='main'):
     """
     Render a mako template
     """
     warnings.warn(
         "Use of runtime.render_template is deprecated. "
         "Use xblockutils.resources.ResourceLoader.render_mako_template or a JavaScript-based template instead.",
         DeprecationWarning, stacklevel=2,
     )
     try:
         return render_to_string(template_name, dictionary, namespace=namespace)
     except TemplateDoesNotExist:
         # From Studio, some templates might be in the LMS namespace only
         return render_to_string(template_name, dictionary, namespace="lms." + namespace)
Ejemplo n.º 4
0
def _send_decision_email(instance):
    """ Send an email to requesting user with the decision made about their request. """
    context = {
        'name': instance.user.username,
        'api_management_url': urlunsplit(
            (
                'https' if settings.HTTPS == 'on' else 'http',
                instance.site.domain,
                reverse('api_admin:api-status'),
                '',
                '',
            )
        ),
        'authentication_docs_url': settings.AUTH_DOCUMENTATION_URL,
        'api_docs_url': settings.API_DOCUMENTATION_URL,
        'support_email_address': settings.API_ACCESS_FROM_EMAIL,
        'platform_name': configuration_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME)
    }

    message = render_to_string(
        'api_admin/api_access_request_email_{status}.txt'.format(status=instance.status),
        context
    )
    try:
        send_mail(
            _u('API access request'),
            message,
            settings.API_ACCESS_FROM_EMAIL,
            [instance.user.email],
            fail_silently=False
        )
        instance.contacted = True
    except SMTPException:
        log.exception(u'Error sending API user notification email for request [%s].', instance.id)
Ejemplo n.º 5
0
 def test_render_to_string_when_no_global_context_lms(self):
     """
     Test render_to_string() when makomiddleware has not initialized
     the threadlocal REQUEST_CONTEXT.context. This is meant to run in LMS.
     """
     assert 'this module is temporarily unavailable' in render_to_string(
         'courseware/error-message.html', None)
def render_from_lms(template_name, dictionary, namespace='main'):
    """
    Render a template using the LMS Mako templates
    """
    return render_to_string(template_name,
                            dictionary,
                            namespace="lms." + namespace)
Ejemplo n.º 7
0
 def render_template(self, template_file, dictionary, namespace='main'):
     """
     Takes (template_file, dictionary) and returns rendered HTML.
     """
     return render_to_string(template_file,
                             dictionary,
                             namespace=self.namespace_prefix + namespace)
Ejemplo n.º 8
0
    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, ProblemBlock):
            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, six.string_types):
                    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'')
Ejemplo n.º 9
0
 def test_render_to_string_when_no_global_context_cms(self):
     """
     Test render_to_string() when makomiddleware has not initialized
     the threadlocal REQUEST_CONTEXT.context. This is meant to run in CMS.
     """
     assert "We're having trouble rendering your component" in render_to_string(
         'html_error.html', None)
Ejemplo n.º 10
0
def _send_new_pending_email(instance):
    """ Send an email to settings.API_ACCESS_MANAGER_EMAIL with the contents of this API access request. """
    context = {
        'approval_url':
        urlunsplit((
            'https' if settings.HTTPS == 'on' else 'http',
            instance.site.domain,
            reverse('admin:api_admin_apiaccessrequest_change',
                    args=(instance.id, )),
            '',
            '',
        )),
        'api_request':
        instance
    }

    message = render_to_string(
        'api_admin/api_access_request_email_new_request.txt', context)
    try:
        send_mail(_u(u'API access request from {company}').format(
            company=instance.company_name),
                  message,
                  settings.API_ACCESS_FROM_EMAIL,
                  [settings.API_ACCESS_MANAGER_EMAIL],
                  fail_silently=False)
    except SMTPException:
        log.exception(
            u'Error sending API user notification email for request [%s].',
            instance.id)
Ejemplo n.º 11
0
def render_js(package, path):  # lint-amnesty, pylint: disable=missing-function-docstring
    template_name = package.template_name or "mako/js.html"
    context = package.extra_context
    context.update({
        'type': guess_type(path, 'text/javascript'),
        'url': try_staticfiles_lookup(path)
    })
    return render_to_string(template_name, context)
Ejemplo n.º 12
0
def render_js(package, path):
    template_name = package.template_name or "mako/js.html"
    context = package.extra_context
    context.update({
        'type': guess_type(path, 'text/javascript'),
        'url': try_staticfiles_lookup(path)
    })
    return render_to_string(template_name, context)
Ejemplo n.º 13
0
def render_429(request, exception=None):  # lint-amnesty, pylint: disable=unused-argument
    """
    Render the rate limit template as an HttpResponse.
    """
    request.view_name = '429'
    return HttpResponse(render_to_string('static_templates/429.html', {},
                                         request=request),
                        status=429)
Ejemplo n.º 14
0
def _studio_wrap_xblock(xblock, view, frag, context, display_name_only=False):
    """
    Wraps the results of rendering an XBlock view in a div which adds a header and Studio action buttons.
    """
    # Only add the Studio wrapper when on the container page. The "Pages" page will remain as is for now.
    if not context.get('is_pages_view', None) and view in PREVIEW_VIEWS:
        root_xblock = context.get('root_xblock')
        is_root = root_xblock and xblock.location == root_xblock.location
        is_reorderable = _is_xblock_reorderable(xblock, context)
        selected_groups_label = get_visibility_partition_info(
            xblock)['selected_groups_label']
        if selected_groups_label:
            selected_groups_label = _('Access restricted to: {list_of_groups}').format(list_of_groups=selected_groups_label)  # lint-amnesty, pylint: disable=line-too-long
        course = modulestore().get_course(xblock.location.course_key)
        template_context = {
            'xblock_context':
            context,
            'xblock':
            xblock,
            'show_preview':
            context.get('show_preview', True),
            'content':
            frag.content,
            'is_root':
            is_root,
            'is_reorderable':
            is_reorderable,
            'can_edit':
            context.get('can_edit', True),
            'can_edit_visibility':
            context.get('can_edit_visibility',
                        xblock.scope_ids.usage_id.context_key.is_course),
            'selected_groups_label':
            selected_groups_label,
            'can_add':
            context.get('can_add', True),
            'can_move':
            context.get('can_move',
                        xblock.scope_ids.usage_id.context_key.is_course),
            'language':
            getattr(course, 'language', None)
        }

        if isinstance(xblock, (XModule, XModuleDescriptor)):
            # Add the webpackified asset tags
            class_name = getattr(xblock.__class__, 'unmixed_class',
                                 xblock.__class__).__name__
            add_webpack_to_fragment(frag, class_name)

        add_webpack_to_fragment(frag, "js/factories/xblock_validation")

        html = render_to_string('studio_xblock_wrapper.html', template_context)
        frag = wrap_fragment(frag, html)
    return frag
Ejemplo n.º 15
0
def render_css(package, path, raw=False):  # lint-amnesty, pylint: disable=missing-function-docstring
    template_name = package.template_name or "mako/css.html"
    context = package.extra_context

    url = try_staticfiles_lookup(path)
    if raw:
        url += "?raw"
    context.update({
        'type': guess_type(path, 'text/css'),
        'url': url,
    })
    return render_to_string(template_name, context)
Ejemplo n.º 16
0
def render_css(package, path, raw=False):
    template_name = package.template_name or "mako/css.html"
    context = package.extra_context

    url = try_staticfiles_lookup(path)
    if raw:
        url += "?raw"
    context.update({
        'type': guess_type(path, 'text/css'),
        'url': url,
    })
    return render_to_string(template_name, context)
Ejemplo n.º 17
0
    def get_html(self, *args, **kwargs):
        """
        Returns raw html for the component.
        """
        # Import is placed here to avoid model import at project startup.
        from .helpers import (generate_uid, get_edxnotes_id_token,
                              get_public_endpoint, get_token_url,
                              is_feature_enabled)

        if not settings.FEATURES.get("ENABLE_EDXNOTES"):
            return original_get_html(self, *args, **kwargs)

        runtime = getattr(self, 'descriptor', self).runtime
        if not hasattr(runtime, 'modulestore'):
            return original_get_html(self, *args, **kwargs)

        is_studio = getattr(self.system, "is_author_mode", False)
        course = getattr(self, 'descriptor',
                         self).runtime.modulestore.get_course(
                             self.runtime.course_id)

        # Must be disabled when:
        # - in Studio
        # - Harvard Annotation Tool is enabled for the course
        # - the feature flag or `edxnotes` setting of the course is set to False
        # - the user is not authenticated
        user = self.runtime.get_real_user(self.runtime.anonymous_student_id)

        if is_studio or not is_feature_enabled(course, user):
            return original_get_html(self, *args, **kwargs)
        else:
            return render_to_string(
                "edxnotes_wrapper.html",
                {
                    "content":
                    original_get_html(self, *args, **kwargs),
                    "uid":
                    generate_uid(),
                    "edxnotes_visibility":
                    json.dumps(
                        getattr(self, 'edxnotes_visibility',
                                course.edxnotes_visibility)),
                    "params": {
                        # Use camelCase to name keys.
                        "usageId": str(self.scope_ids.usage_id),
                        "courseId": str(self.runtime.course_id),
                        "token": get_edxnotes_id_token(user),
                        "tokenUrl": get_token_url(self.runtime.course_id),
                        "endpoint": get_public_endpoint(),
                        "debug": settings.DEBUG,
                        "eventStringLimit": settings.TRACK_MAX_EVENT / 6,
                    },
                })
Ejemplo n.º 18
0
def render_accordion(request, course, table_of_contents):
    """
    Returns the HTML that renders the navigation for the given course.
    Expects the table_of_contents to have data on each chapter and section,
    including which ones are active.
    """
    context = dict([
        ('toc', table_of_contents),
        ('course_id', six.text_type(course.id)),
        ('csrf', csrf(request)['csrf_token']),
        ('due_date_display_format', course.due_date_display_format),
    ] + list(TEMPLATE_IMPORTS.items()))
    return render_to_string('courseware/accordion.html', context)
Ejemplo n.º 19
0
def send_admin_notification_callback(sender, **kwargs):  # lint-amnesty, pylint: disable=unused-argument
    """
    Callback for notifying admin of a user in the 'pending' state.
    """
    user = kwargs['user']

    studio_request_email = settings.FEATURES.get('STUDIO_REQUEST_EMAIL', '')
    context = {'user_name': user.username, 'user_email': user.email}

    subject = render_to_string('emails/course_creator_admin_subject.txt',
                               context)
    subject = ''.join(subject.splitlines())
    message = render_to_string('emails/course_creator_admin_user_pending.txt',
                               context)

    try:
        send_mail(subject,
                  message,
                  studio_request_email, [studio_request_email],
                  fail_silently=False)
    except SMTPException:
        log.warning("Failure sending 'pending state' e-mail for %s to %s",
                    user.email, studio_request_email)
Ejemplo n.º 20
0
def password_reset(request):
    """
    Attempts to send a password reset e-mail.
    """
    user = request.user
    # Prefer logged-in user's email
    email = user.email if user.is_authenticated else request.POST.get('email')
    AUDIT_LOG.info("Password reset initiated for email %s.", email)

    if getattr(request, 'limited', False):
        AUDIT_LOG.warning("Password reset rate limit exceeded for email %s.",
                          email)
        return JsonResponse(
            {
                'success':
                False,
                'value':
                _("Your previous request is in progress, please try again in a few moments."
                  )
            },
            status=403)

    form = PasswordResetFormNoActive(request.POST)
    if form.is_valid():
        form.save(use_https=request.is_secure(),
                  from_email=configuration_helpers.get_value(
                      'email_from_address', settings.DEFAULT_FROM_EMAIL),
                  request=request)
        # When password change is complete, a "edx.user.settings.changed" event will be emitted.
        # But because changing the password is multi-step, we also emit an event here so that we can
        # track where the request was initiated.
        tracker.emit(
            SETTING_CHANGE_INITIATED, {
                "setting": "password",
                "old": None,
                "new": None,
                "user_id": request.user.id,
            })
        destroy_oauth_tokens(request.user)
    else:
        # bad user? tick the rate limiter counter
        AUDIT_LOG.info("Bad password_reset user passed in.")

    return JsonResponse({
        'success':
        True,
        'value':
        render_to_string('registration/password_reset_done.html', {}),
    })
Ejemplo n.º 21
0
def get_preview_fragment(request, descriptor, context):
    """
    Returns the HTML returned by the XModule's student_view or author_view (if available),
    specified by the descriptor and idx.
    """
    module = _load_preview_module(request, descriptor)

    preview_view = AUTHOR_VIEW if has_author_view(module) else STUDENT_VIEW

    try:
        fragment = module.render(preview_view, context)
    except Exception as exc:                          # pylint: disable=broad-except
        log.warning(u"Unable to render %s for %r", preview_view, module, exc_info=True)
        fragment = Fragment(render_to_string('html_error.html', {'message': str(exc)}))
    return fragment
Ejemplo n.º 22
0
def _create_recent_enrollment_message(course_enrollments, course_modes):  # lint-amnesty, pylint: disable=unused-argument
    """
    Builds a recent course enrollment message.

    Constructs a new message template based on any recent course enrollments
    for the student.

    Args:
        course_enrollments (list[CourseEnrollment]): a list of course enrollments.
        course_modes (dict): Mapping of course ID's to course mode dictionaries.

    Returns:
        A string representing the HTML message output from the message template.
        None if there are no recently enrolled courses.

    """
    recently_enrolled_courses = _get_recently_enrolled_courses(
        course_enrollments)

    if recently_enrolled_courses:
        enrollments_count = len(recently_enrolled_courses)
        course_name_separator = ', '
        # If length of enrolled course 2, join names with 'and'
        if enrollments_count == 2:
            course_name_separator = _(' and ')

        course_names = course_name_separator.join([
            enrollment.course_overview.display_name
            for enrollment in recently_enrolled_courses
        ])

        platform_name = configuration_helpers.get_value(
            'platform_name', settings.PLATFORM_NAME)

        return render_to_string(
            'enrollment/course_enrollment_message.html', {
                'course_names':
                course_names,
                'enrollments_count':
                enrollments_count,
                'platform_name':
                platform_name,
                'course_id':
                recently_enrolled_courses[0].course_overview.id
                if enrollments_count == 1 else None
            })
Ejemplo n.º 23
0
def send_verification_status_email(context):
    """
    Spins a task to send verification status email to the learner
    """
    subject = context.get('subject')
    message = render_to_string(context.get('template'), context.get('email_vars'))
    from_addr = configuration_helpers.get_value(
        'email_from_address',
        settings.DEFAULT_FROM_EMAIL
    )
    dest_addr = context.get('email')

    try:
        msg = EmailMessage(subject, message, from_addr, [dest_addr])
        msg.content_subtype = 'html'
        msg.send(fail_silently=False)
    except SMTPException:
        log.warning(u"Failure in sending verification status e-mail to %s", dest_addr)
Ejemplo n.º 24
0
def get_course_info_section(request, user, course, section_key):
    """
    This returns the snippet of html to be rendered on the course info page,
    given the key for the section.

    Valid keys:
    - handouts
    - guest_handouts
    - updates
    - guest_updates
    """
    info_module = get_course_info_section_module(request, user, course,
                                                 section_key)

    html = ''
    if info_module is not None:
        try:
            html = info_module.render(STUDENT_VIEW).content.strip()
        except Exception:  # pylint: disable=broad-except
            html = render_to_string('courseware/error-message.html', None)
            log.exception("Error rendering course_id=%s, section_key=%s",
                          str(course.id), section_key)

    return html
def wrap_xblock(
        runtime_class,
        block,
        view,
        frag,
        context,
        usage_id_serializer,
        request_token,                  # pylint: disable=redefined-outer-name
        display_name_only=False,
        extra_data=None
):
    """
    Wraps the results of rendering an XBlock view in a standard <section> with identifying
    data so that the appropriate javascript module can be loaded onto it.

    :param runtime_class: The name of the javascript runtime class to use to load this block
    :param block: An XBlock (that may be an XModule or XModuleDescriptor)
    :param view: The name of the view that rendered the fragment being wrapped
    :param frag: The :class:`Fragment` to be wrapped
    :param context: The context passed to the view being rendered
    :param usage_id_serializer: A function to serialize the block's usage_id for use by the
        front-end Javascript Runtime.
    :param request_token: An identifier that is unique per-request, so that only xblocks
        rendered as part of this request will have their javascript initialized.
    :param display_name_only: If true, don't render the fragment content at all.
        Instead, just render the `display_name` of `block`
    :param extra_data: A dictionary with extra data values to be set on the wrapper
    """
    if extra_data is None:
        extra_data = {}

    # If any mixins have been applied, then use the unmixed class
    class_name = getattr(block, 'unmixed_class', block.__class__).__name__

    data = {}
    data.update(extra_data)

    if context:
        data.update(context.get('wrap_xblock_data', {}))

    css_classes = [
        'xblock',
        'xblock-{}'.format(markupsafe.escape(view)),
        'xblock-{}-{}'.format(
            markupsafe.escape(view),
            markupsafe.escape(block.scope_ids.block_type),
        )
    ]

    if view == STUDENT_VIEW and getattr(block, 'HIDDEN', False):
        css_classes.append('is-hidden')

    if isinstance(block, (XModule, XModuleDescriptor)) or getattr(block, 'uses_xmodule_styles_setup', False):
        if view in PREVIEW_VIEWS:
            # The block is acting as an XModule
            css_classes.append('xmodule_display')
        elif view == STUDIO_VIEW:
            # The block is acting as an XModuleDescriptor
            css_classes.append('xmodule_edit')

        css_classes.append('xmodule_' + markupsafe.escape(class_name))

    if isinstance(block, (XModule, XModuleDescriptor)):
        data['type'] = block.js_module_name
        shim_xmodule_js(frag, block.js_module_name)

    if frag.js_init_fn:
        data['init'] = frag.js_init_fn
        data['runtime-class'] = runtime_class
        data['runtime-version'] = frag.js_init_version

    data['block-type'] = block.scope_ids.block_type
    data['usage-id'] = usage_id_serializer(block.scope_ids.usage_id)
    data['request-token'] = request_token
    data['graded'] = getattr(block, 'graded', False)
    data['has-score'] = getattr(block, 'has_score', False)

    if block.name:
        data['name'] = block.name

    template_context = {
        'content': block.display_name if display_name_only else frag.content,
        'classes': css_classes,
        'display_name': block.display_name_with_default_escaped,  # xss-lint: disable=python-deprecated-display-name
        'data_attributes': ' '.join('data-{}="{}"'.format(markupsafe.escape(key), markupsafe.escape(value))
                                    for key, value in data.items()),
    }

    if hasattr(frag, 'json_init_args') and frag.json_init_args is not None:
        template_context['js_init_parameters'] = frag.json_init_args
    else:
        template_context['js_init_parameters'] = ""

    if isinstance(block, (XModule, XModuleDescriptor)):
        # Add the webpackified asset tags
        add_webpack_to_fragment(frag, class_name)

    return wrap_fragment(frag, render_to_string('xblock_wrapper.html', template_context))
def add_staff_markup(user, disable_staff_debug_info, block, view, frag, context):  # pylint: disable=unused-argument
    """
    Updates the supplied module with a new get_html function that wraps
    the output of the old get_html function with additional information
    for admin users only, including a histogram of student answers, the
    definition of the xmodule, and a link to view the module in Studio
    if it is a Studio edited, mongo stored course.

    Does nothing if module is a SequenceBlock.
    """
    if context and context.get('hide_staff_markup', False):
        # If hide_staff_markup is passed, don't add the markup
        return frag
    # TODO: make this more general, eg use an XModule attribute instead
    if isinstance(block, VerticalBlock) and (not context or not context.get('child_of_vertical', False)):
        return frag

    if isinstance(block, SequenceBlock) or getattr(block, 'HIDDEN', False):
        return frag

    block_id = block.location
    if block.has_score and settings.FEATURES.get('DISPLAY_HISTOGRAMS_TO_STAFF'):
        histogram = grade_histogram(block_id)
        render_histogram = len(histogram) > 0
    else:
        histogram = None
        render_histogram = False

    if settings.FEATURES.get('ENABLE_LMS_MIGRATION') and hasattr(block.runtime, 'filestore'):
        [filepath, filename] = getattr(block, 'xml_attributes', {}).get('filename', ['', None])
        osfs = block.runtime.filestore
        if filename is not None and osfs.exists(filename):
            # if original, unmangled filename exists then use it (github
            # doesn't like symlinks)
            filepath = filename
        data_dir = block.static_asset_path or osfs.root_path.rsplit('/')[-1]
        giturl = block.giturl or 'https://github.com/MITx'
        edit_link = f"{giturl}/{data_dir}/tree/master/{filepath}"
    else:
        edit_link = False
        # Need to define all the variables that are about to be used
        giturl = ""
        data_dir = ""

    source_file = block.source_file  # source used to generate the problem XML, eg latex or word

    # Useful to indicate to staff if problem has been released or not.
    # TODO (ichuang): use _has_access_descriptor.can_load in lms.courseware.access,
    # instead of now>mstart comparison here.
    now = datetime.datetime.now(UTC)
    is_released = "unknown"
    mstart = block.start

    if mstart is not None:
        is_released = "<font color='red'>Yes!</font>" if (now > mstart) else "<font color='green'>Not yet</font>"

    field_contents = []
    for name, field in block.fields.items():
        try:
            field_contents.append((name, field.read_from(block)))
        except InvalidScopeError:
            log.warning("Unable to read field in Staff Debug information", exc_info=True)
            field_contents.append((name, "WARNING: Unable to read field"))

    staff_context = {
        'fields': field_contents,
        'xml_attributes': getattr(block, 'xml_attributes', {}),
        'tags': block._class_tags,  # pylint: disable=protected-access
        'location': block.location,
        'xqa_key': block.xqa_key,
        'source_file': source_file,
        'source_url': f'{giturl}/{data_dir}/tree/master/{source_file}',
        'category': str(block.__class__.__name__),
        'element_id': sanitize_html_id(block.location.html_id()),
        'edit_link': edit_link,
        'user': user,
        'xqa_server': settings.FEATURES.get('XQA_SERVER', "http://your_xqa_server.com"),
        'histogram': json.dumps(histogram),
        'render_histogram': render_histogram,
        'block_content': frag.content,
        'is_released': is_released,
        'can_reset_attempts': 'attempts' in block.fields,
        'can_rescore_problem': hasattr(block, 'rescore'),
        'can_override_problem_score': isinstance(block, ScorableXBlockMixin),
        'disable_staff_debug_info': disable_staff_debug_info,
    }
    if isinstance(block, ScorableXBlockMixin):
        staff_context['max_problem_score'] = block.max_score()

    return wrap_fragment(frag, render_to_string("staff_problem_info.html", staff_context))
def wrap_xblock_aside(
        runtime_class,
        aside,
        view,
        frag,
        context,                        # pylint: disable=unused-argument
        usage_id_serializer,
        request_token,                   # pylint: disable=redefined-outer-name
        extra_data=None,
        extra_classes=None
):
    """
    Wraps the results of rendering an XBlockAside view in a standard <section> with identifying
    data so that the appropriate javascript module can be loaded onto it.

    :param runtime_class: The name of the javascript runtime class to use to load this block
    :param aside: An XBlockAside
    :param view: The name of the view that rendered the fragment being wrapped
    :param frag: The :class:`Fragment` to be wrapped
    :param context: The context passed to the view being rendered
    :param usage_id_serializer: A function to serialize the block's usage_id for use by the
        front-end Javascript Runtime.
    :param request_token: An identifier that is unique per-request, so that only xblocks
        rendered as part of this request will have their javascript initialized.
    :param extra_data: A dictionary with extra data values to be set on the wrapper
    :param extra_classes: A list with extra classes to be set on the wrapper element
    """

    if extra_data is None:
        extra_data = {}

    data = {}
    data.update(extra_data)

    css_classes = [
        'xblock-{}'.format(markupsafe.escape(view)),
        'xblock-{}-{}'.format(
            markupsafe.escape(view),
            markupsafe.escape(aside.scope_ids.block_type),
        ),
        'xblock_asides-v1'
    ]
    if extra_classes:
        css_classes.extend(extra_classes)

    if frag.js_init_fn:
        data['init'] = frag.js_init_fn
        data['runtime-class'] = runtime_class
        data['runtime-version'] = frag.js_init_version

    data['block-type'] = aside.scope_ids.block_type
    data['usage-id'] = usage_id_serializer(aside.scope_ids.usage_id)
    data['request-token'] = request_token

    template_context = {
        'content': frag.content,
        'classes': css_classes,
        'data_attributes': ' '.join('data-{}="{}"'.format(markupsafe.escape(key), markupsafe.escape(value))
                                    for key, value in data.items()),
    }

    if hasattr(frag, 'json_init_args') and frag.json_init_args is not None:
        template_context['js_init_parameters'] = frag.json_init_args
    else:
        template_context['js_init_parameters'] = ""

    return wrap_fragment(frag, render_to_string('xblock_wrapper.html', template_context))
Ejemplo n.º 28
0
def render_500(request):
    return HttpResponseServerError(
        render_to_string('500.html', {}, request=request))
Ejemplo n.º 29
0
def get_course_about_section(request, course, section_key):
    """
    This returns the snippet of html to be rendered on the course about page,
    given the key for the section.

    Valid keys:
    - overview
    - about_sidebar_html
    - short_description
    - description
    - key_dates (includes start, end, exams, etc)
    - video
    - course_staff_short
    - course_staff_extended
    - requirements
    - syllabus
    - textbook
    - faq
    - effort
    - more_info
    - ocw_links
    """

    # Many of these are stored as html files instead of some semantic
    # markup. This can change without effecting this interface when we find a
    # good format for defining so many snippets of text/html.

    html_sections = {
        'short_description', 'description', 'key_dates', 'video',
        'course_staff_short', 'course_staff_extended', 'requirements',
        'syllabus', 'textbook', 'faq', 'more_info', 'overview', 'effort',
        'end_date', 'prerequisites', 'about_sidebar_html', 'ocw_links'
    }

    if section_key in html_sections:
        try:
            loc = course.location.replace(category='about', name=section_key)

            # Use an empty cache
            field_data_cache = FieldDataCache([], course.id, request.user)
            about_module = get_module(
                request.user,
                request,
                loc,
                field_data_cache,
                log_if_not_found=False,
                wrap_xmodule_display=False,
                static_asset_path=course.static_asset_path,
                course=course)

            html = ''

            if about_module is not None:
                try:
                    html = about_module.render(STUDENT_VIEW).content
                except Exception:  # pylint: disable=broad-except
                    html = render_to_string('courseware/error-message.html',
                                            None)
                    log.exception("Error rendering course=%s, section_key=%s",
                                  course, section_key)
            return html

        except ItemNotFoundError:
            log.warning("Missing about section %s in course %s", section_key,
                        str(course.location))
            return None

    raise KeyError("Invalid about key " + str(section_key))
Ejemplo n.º 30
0
def user_details_force_sync(auth_entry,
                            strategy,
                            details,
                            user=None,
                            *args,
                            **kwargs):  # lint-amnesty, pylint: disable=keyword-arg-before-vararg
    """
    Update normally protected user details using data from provider.

    This step in the pipeline is akin to `social_core.pipeline.user.user_details`, which updates
    the user details but has an unconfigurable protection over updating the username & email, and
    is unable to update information such as the user's full name which isn't on the user model, but
    rather on the user profile model.

    Additionally, because the email field is normally used to log in, if the email is changed by this
    forced synchronization, we send an email to both the old and new emails, letting the user know.

    This step is controlled by the `sync_learner_profile_data` flag on the provider's configuration.
    """
    current_provider = provider.Registry.get_from_pipeline({
        'backend':
        strategy.request.backend.name,
        'kwargs':
        kwargs
    })
    if user and current_provider.sync_learner_profile_data:
        # Keep track of which incoming values get applied.
        changed = {}

        # Map each incoming field from the provider to the name on the user model (by default, they always match).
        field_mapping = {
            field: (user, field)
            for field in details.keys() if hasattr(user, field)
        }

        # This is a special case where the field mapping should go to the user profile object and not the user object,
        # in some cases with differing field names (i.e. 'fullname' vs. 'name').
        field_mapping.update({
            'fullname': (user.profile, 'name'),
            'country': (user.profile, 'country'),
        })

        # Remove username from list of fields for update
        field_mapping.pop('username', None)

        # Track any fields that would raise an integrity error if there was a conflict.
        integrity_conflict_fields = {
            'email': user.email,
            'username': user.username
        }

        for provider_field, (model, field) in field_mapping.items():
            provider_value = details.get(provider_field)
            current_value = getattr(model, field)
            if provider_value is not None and current_value != provider_value:
                if field in integrity_conflict_fields and User.objects.filter(
                        **{
                            field: provider_value
                        }).exists():
                    logger.warning(
                        '[THIRD_PARTY_AUTH] Profile data synchronization conflict. '
                        'UserId: {user_id}, Provider: {provider}, ConflictField: {conflict_field}, '
                        'ConflictValue: {conflict_value}'.format(
                            user_id=user.id,
                            provider=current_provider.name,
                            conflict_field=field,
                            conflict_value=provider_value))
                    continue
                changed[provider_field] = current_value
                setattr(model, field, provider_value)

        if changed:
            logger.info(
                '[THIRD_PARTY_AUTH] User performed SSO and data was synchronized. '
                'Username: {username}, Provider: {provider}, UpdatedKeys: {updated_keys}'
                .format(username=user.username,
                        provider=current_provider.name,
                        updated_keys=list(changed.keys())))

            # Save changes to user and user.profile models.
            strategy.storage.user.changed(user)
            user.profile.save()

            # Send an email to the old and new email to alert the user that their login email changed.
            if changed.get('email'):
                old_email = changed['email']
                new_email = user.email
                email_context = {
                    'old_email': old_email,
                    'new_email': new_email
                }
                # Subjects shouldn't have new lines.
                subject = ''.join(
                    render_to_string(
                        'emails/sync_learner_profile_data_email_change_subject.txt',
                        email_context).splitlines())
                body = render_to_string(
                    'emails/sync_learner_profile_data_email_change_body.txt',
                    email_context)
                from_email = configuration_helpers.get_value(
                    'email_from_address', settings.DEFAULT_FROM_EMAIL)

                email = EmailMessage(subject=subject,
                                     body=body,
                                     from_email=from_email,
                                     to=[old_email, new_email])
                email.content_subtype = "html"
                try:
                    email.send()
                except SMTPException:
                    logger.exception(
                        '[THIRD_PARTY_AUTH] Error sending IdP learner data sync-initiated email change '
                        'notification email. Username: {username}'.format(
                            username=user.username))